forked from trent_larson/crowd-funder-for-time-pwa
start with offer-edit
This commit is contained in:
@@ -48,7 +48,7 @@ export interface ClaimResult {
|
||||
}
|
||||
|
||||
export interface GenericVerifiableCredential {
|
||||
"@context"?: string;
|
||||
"@context"?: string; // optional when embedded, eg. in an Agree
|
||||
"@type": string;
|
||||
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
@@ -139,13 +139,13 @@ export interface GiveVerifiableCredential extends GenericVerifiableCredential {
|
||||
|
||||
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id8
|
||||
export interface OfferVerifiableCredential {
|
||||
"@context"?: string; // optional when embedded, eg. in an Agree
|
||||
export interface OfferVerifiableCredential extends GenericVerifiableCredential {
|
||||
"@context"?: string; // optional when embedded... though it doesn't make sense to agree to an offer
|
||||
"@type": "Offer";
|
||||
description?: string;
|
||||
description?: string; // conditions for the offer
|
||||
includesObject?: { amountOfThisGood: number; unitCode: string };
|
||||
itemOffered?: {
|
||||
description?: string;
|
||||
description?: string; // description of the item
|
||||
isPartOf?: { identifier?: string; lastClaimId?: string; "@type"?: string };
|
||||
};
|
||||
offeredBy?: { identifier: string };
|
||||
@@ -155,7 +155,7 @@ export interface OfferVerifiableCredential {
|
||||
|
||||
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id7
|
||||
export interface PlanVerifiableCredential {
|
||||
export interface PlanVerifiableCredential extends GenericVerifiableCredential {
|
||||
"@context": "https://schema.org";
|
||||
"@type": "PlanAction";
|
||||
name: string;
|
||||
@@ -563,6 +563,8 @@ export async function setPlanInCache(
|
||||
|
||||
/**
|
||||
* Construct GiveAction VC for submission to server
|
||||
*
|
||||
* @param lastClaimId supplied when editing a previous claim
|
||||
*/
|
||||
export function hydrateGive(
|
||||
vcClaimOrig?: GiveVerifiableCredential,
|
||||
@@ -587,6 +589,7 @@ export function hydrateGive(
|
||||
};
|
||||
|
||||
if (lastClaimId) {
|
||||
// this is an edit
|
||||
vcClaim.lastClaimId = lastClaimId;
|
||||
delete vcClaim.identifier;
|
||||
}
|
||||
@@ -594,16 +597,17 @@ export function hydrateGive(
|
||||
vcClaim.agent = fromDid ? { identifier: fromDid } : undefined;
|
||||
vcClaim.recipient = toDid ? { identifier: toDid } : undefined;
|
||||
vcClaim.description = description || undefined;
|
||||
vcClaim.object = amount
|
||||
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
|
||||
: undefined;
|
||||
vcClaim.object =
|
||||
amount && !isNaN(amount)
|
||||
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
|
||||
: undefined;
|
||||
|
||||
// ensure fulfills is an array
|
||||
if (!Array.isArray(vcClaim.fulfills)) {
|
||||
vcClaim.fulfills = vcClaim.fulfills ? [vcClaim.fulfills] : [];
|
||||
}
|
||||
// ... and replace or add each element, ending with Trade or Donate
|
||||
// I realize this doesn't change any elements that are not PlanAction or Offer or Trade/Action.
|
||||
// I realize the following doesn't change any elements that are not PlanAction or Offer or Trade/Action.
|
||||
vcClaim.fulfills = vcClaim.fulfills.filter(
|
||||
(elem) => elem["@type"] !== "PlanAction",
|
||||
);
|
||||
@@ -639,8 +643,8 @@ export function hydrateGive(
|
||||
*
|
||||
* @param fromDid may be null
|
||||
* @param toDid
|
||||
* @param description may be null; should have this or amount
|
||||
* @param amount may be null; should have this or description
|
||||
* @param description may be null
|
||||
* @param amount may be null
|
||||
*/
|
||||
export async function createAndSubmitGive(
|
||||
axios: Axios,
|
||||
@@ -667,6 +671,7 @@ export async function createAndSubmitGive(
|
||||
fulfillsOfferHandleId,
|
||||
isTrade,
|
||||
imageUrl,
|
||||
undefined,
|
||||
);
|
||||
return createAndSubmitClaim(
|
||||
vcClaim as GenericVerifiableCredential,
|
||||
@@ -680,9 +685,9 @@ export async function createAndSubmitGive(
|
||||
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
|
||||
*
|
||||
* @param fromDid may be null
|
||||
* @param toDid
|
||||
* @param description may be null; should have this or amount
|
||||
* @param amount may be null; should have this or description
|
||||
* @param toDid may be null if project is provided
|
||||
* @param description may be null
|
||||
* @param amount may be null
|
||||
*/
|
||||
export async function editAndSubmitGive(
|
||||
axios: Axios,
|
||||
@@ -720,13 +725,69 @@ export async function editAndSubmitGive(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Offer VC for submission to server
|
||||
*
|
||||
* @param lastClaimId supplied when editing a previous claim
|
||||
*/
|
||||
export function hydrateOffer(
|
||||
vcClaimOrig?: OfferVerifiableCredential,
|
||||
fromDid?: string,
|
||||
toDid?: string,
|
||||
conditionDescription?: string,
|
||||
amount?: number,
|
||||
unitCode?: string,
|
||||
offeringDescription?: string,
|
||||
fulfillsProjectHandleId?: string,
|
||||
validThrough?: string,
|
||||
lastClaimId?: string,
|
||||
): OfferVerifiableCredential {
|
||||
// Remember: replace values or erase if it's null
|
||||
|
||||
const vcClaim: OfferVerifiableCredential = vcClaimOrig
|
||||
? R.clone(vcClaimOrig)
|
||||
: {
|
||||
"@context": SCHEMA_ORG_CONTEXT,
|
||||
"@type": "Offer",
|
||||
};
|
||||
|
||||
if (lastClaimId) {
|
||||
// this is an edit
|
||||
vcClaim.lastClaimId = lastClaimId;
|
||||
delete vcClaim.identifier;
|
||||
}
|
||||
|
||||
vcClaim.offeredBy = fromDid ? { identifier: fromDid } : undefined;
|
||||
vcClaim.recipient = toDid ? { identifier: toDid } : undefined;
|
||||
vcClaim.description = conditionDescription || undefined;
|
||||
|
||||
vcClaim.includesObject =
|
||||
amount && !isNaN(amount)
|
||||
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
|
||||
: undefined;
|
||||
|
||||
if (offeringDescription || fulfillsProjectHandleId) {
|
||||
vcClaim.itemOffered = vcClaim.itemOffered || {};
|
||||
vcClaim.itemOffered.description = offeringDescription || undefined;
|
||||
if (fulfillsProjectHandleId) {
|
||||
vcClaim.itemOffered.isPartOf = {
|
||||
"@type": "PlanAction",
|
||||
identifier: fulfillsProjectHandleId,
|
||||
};
|
||||
}
|
||||
}
|
||||
vcClaim.validThrough = validThrough || undefined;
|
||||
|
||||
return vcClaim;
|
||||
}
|
||||
|
||||
/**
|
||||
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
|
||||
*
|
||||
* @param identity
|
||||
* @param description may be null; should have this or amount
|
||||
* @param amount may be null; should have this or description
|
||||
* @param expirationDate ISO 8601 date string YYYY-MM-DD (may be null)
|
||||
* @param description may be null
|
||||
* @param amount may be null
|
||||
* @param validThrough ISO 8601 date string YYYY-MM-DD (may be null)
|
||||
* @param fulfillsProjectHandleId ID of project to which this contributes (may be null)
|
||||
*/
|
||||
export async function createAndSubmitOffer(
|
||||
@@ -736,35 +797,54 @@ export async function createAndSubmitOffer(
|
||||
description?: string,
|
||||
amount?: number,
|
||||
unitCode?: string,
|
||||
expirationDate?: string,
|
||||
validThrough?: string,
|
||||
recipientDid?: string,
|
||||
fulfillsProjectHandleId?: string,
|
||||
): Promise<CreateAndSubmitClaimResult> {
|
||||
const vcClaim: OfferVerifiableCredential = {
|
||||
"@context": SCHEMA_ORG_CONTEXT,
|
||||
"@type": "Offer",
|
||||
offeredBy: { identifier: issuerDid },
|
||||
validThrough: expirationDate || undefined,
|
||||
};
|
||||
if (amount) {
|
||||
vcClaim.includesObject = {
|
||||
amountOfThisGood: amount,
|
||||
unitCode: unitCode || "HUR",
|
||||
};
|
||||
}
|
||||
if (description) {
|
||||
vcClaim.itemOffered = { description };
|
||||
}
|
||||
if (recipientDid) {
|
||||
vcClaim.recipient = { identifier: recipientDid };
|
||||
}
|
||||
if (fulfillsProjectHandleId) {
|
||||
vcClaim.itemOffered = vcClaim.itemOffered || {};
|
||||
vcClaim.itemOffered.isPartOf = {
|
||||
"@type": "PlanAction",
|
||||
identifier: fulfillsProjectHandleId,
|
||||
};
|
||||
}
|
||||
const vcClaim = hydrateOffer(
|
||||
undefined,
|
||||
issuerDid,
|
||||
recipientDid,
|
||||
description,
|
||||
amount,
|
||||
unitCode,
|
||||
description,
|
||||
fulfillsProjectHandleId,
|
||||
validThrough,
|
||||
undefined,
|
||||
);
|
||||
return createAndSubmitClaim(
|
||||
vcClaim as OfferVerifiableCredential,
|
||||
issuerDid,
|
||||
apiServer,
|
||||
axios,
|
||||
);
|
||||
}
|
||||
|
||||
export async function editAndSubmitOffer(
|
||||
axios: Axios,
|
||||
apiServer: string,
|
||||
fullClaim: GenericCredWrapper<OfferVerifiableCredential>,
|
||||
issuerDid: string,
|
||||
description?: string,
|
||||
amount?: number,
|
||||
unitCode?: string,
|
||||
validThrough?: string,
|
||||
recipientDid?: string,
|
||||
fulfillsProjectHandleId?: string,
|
||||
): Promise<CreateAndSubmitClaimResult> {
|
||||
const vcClaim = hydrateOffer(
|
||||
fullClaim.claim,
|
||||
issuerDid,
|
||||
recipientDid,
|
||||
description,
|
||||
amount,
|
||||
unitCode,
|
||||
description,
|
||||
fulfillsProjectHandleId,
|
||||
validThrough,
|
||||
fullClaim.id,
|
||||
);
|
||||
return createAndSubmitClaim(
|
||||
vcClaim as OfferVerifiableCredential,
|
||||
issuerDid,
|
||||
|
||||
@@ -694,7 +694,6 @@ export default class GiftedDetails extends Vue {
|
||||
constructGiveParam() {
|
||||
const recipientDid = this.givenToRecipient ? this.recipientDid : undefined;
|
||||
const projectId = this.givenToProject ? this.projectId : undefined;
|
||||
// const giveClaim = constructGive(
|
||||
const giveClaim = hydrateGive(
|
||||
this.prevCredToEdit?.claim as GiveVerifiableCredential,
|
||||
this.giverDid,
|
||||
|
||||
624
src/views/OfferDetails.vue
Normal file
624
src/views/OfferDetails.vue
Normal file
@@ -0,0 +1,624 @@
|
||||
<template>
|
||||
<QuickNav />
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- Back -->
|
||||
<div
|
||||
v-if="!hideBackButton"
|
||||
class="text-lg text-center font-light relative px-7"
|
||||
>
|
||||
<h1
|
||||
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||
@click="cancelBack()"
|
||||
>
|
||||
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Heading -->
|
||||
<h1 class="text-4xl text-center font-light px-4 mb-4">What Was Offered</h1>
|
||||
|
||||
<h1 class="text-xl font-bold text-center mb-4">
|
||||
<span>From {{ giverName }}</span>
|
||||
<span>
|
||||
to
|
||||
{{
|
||||
offeredToProject
|
||||
? projectName
|
||||
: offeredToRecipient
|
||||
? recipientName
|
||||
: "someone unidentified"
|
||||
}}</span
|
||||
>
|
||||
</h1>
|
||||
<textarea
|
||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||
placeholder="What was offered"
|
||||
v-model="description"
|
||||
/>
|
||||
<div class="flex flex-row justify-center">
|
||||
<span
|
||||
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center text-blue-500 px-2 py-2 w-20"
|
||||
@click="changeUnitCode()"
|
||||
>
|
||||
{{ libsUtil.UNIT_SHORT[unitCode] || unitCode }}
|
||||
</span>
|
||||
<div
|
||||
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="amountInput === '0' ? null : decrement()"
|
||||
>
|
||||
<fa icon="chevron-left" />
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
class="border border-r-0 border-slate-400 px-2 py-2 text-center w-20"
|
||||
v-model="amountInput"
|
||||
/>
|
||||
<div
|
||||
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="increment()"
|
||||
>
|
||||
<fa icon="chevron-right" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-7 mt-4 flex">
|
||||
<input
|
||||
v-if="projectId && !offeredToRecipient"
|
||||
type="checkbox"
|
||||
class="h-6 w-6 mr-2"
|
||||
v-model="offeredToProject"
|
||||
/>
|
||||
<fa
|
||||
v-else
|
||||
icon="square"
|
||||
class="bg-slate-500 text-slate-500 h-5 w-5 px-0.5 py-0.5 mr-2 rounded"
|
||||
@click="notifyUserOfProject()"
|
||||
/>
|
||||
<label class="text-sm mt-1">
|
||||
{{
|
||||
projectId
|
||||
? "This was given to " + projectName
|
||||
: "No project was chosen"
|
||||
}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="h-7 mt-4 flex">
|
||||
<input
|
||||
v-if="recipientDid && !offeredToProject"
|
||||
type="checkbox"
|
||||
class="h-6 w-6 mr-2"
|
||||
v-model="offeredToRecipient"
|
||||
/>
|
||||
<fa
|
||||
v-else
|
||||
icon="square"
|
||||
class="bg-slate-500 text-slate-500 h-5 w-5 px-0.5 py-0.5 mr-2 rounded"
|
||||
@click="notifyUserOfRecipient()"
|
||||
/>
|
||||
<label class="text-sm mt-1">
|
||||
{{
|
||||
recipientDid
|
||||
? "This was given to " + recipientName
|
||||
: "No recipient was chosen."
|
||||
}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'claim-add-raw',
|
||||
query: {
|
||||
claim: constructOfferParam(),
|
||||
},
|
||||
}"
|
||||
class="text-blue-500"
|
||||
>
|
||||
Edit & Submit Raw
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<p class="text-center mb-2 mt-6 italic">
|
||||
Sign & Send to publish to the world
|
||||
<fa
|
||||
icon="circle-info"
|
||||
class="pl-2 text-blue-500 cursor-pointer"
|
||||
@click="explainData()"
|
||||
/>
|
||||
</p>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<button
|
||||
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import TopMessage from "@/components/TopMessage.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { accountsDB, db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
||||
import {
|
||||
createAndSubmitOffer,
|
||||
didInfo,
|
||||
editAndSubmitOffer,
|
||||
GenericCredWrapper,
|
||||
getPlanFromCache,
|
||||
hydrateOffer,
|
||||
OfferVerifiableCredential,
|
||||
} from "@/libs/endorserServer";
|
||||
import * as libsUtil from "@/libs/util";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
QuickNav,
|
||||
TopMessage,
|
||||
},
|
||||
})
|
||||
export default class OfferDetails extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
activeDid = "";
|
||||
apiServer = "";
|
||||
|
||||
amountInput = "0";
|
||||
description = "";
|
||||
destinationPathAfter = "";
|
||||
offeredToProject = false;
|
||||
offeredToRecipient = false;
|
||||
giverDid: string | undefined;
|
||||
giverName = "";
|
||||
hideBackButton = false;
|
||||
isTrade = false;
|
||||
message = "";
|
||||
offerId = "";
|
||||
prevCredToEdit?: GenericCredWrapper<OfferVerifiableCredential>;
|
||||
projectId = "";
|
||||
projectName = "a project";
|
||||
recipientDid = "";
|
||||
recipientName = "";
|
||||
unitCode = "HUR";
|
||||
validThroughDate = null;
|
||||
|
||||
libsUtil = libsUtil;
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
this.prevCredToEdit = (this.$route as Router).query["prevCredToEdit"]
|
||||
? (JSON.parse(
|
||||
(this.$route as Router).query["prevCredToEdit"],
|
||||
) as GenericCredWrapper<OfferVerifiableCredential>)
|
||||
: undefined;
|
||||
} catch (error) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Retrieval Error",
|
||||
text: "The previous record isn't available for editing. If you submit, you'll create a new record.",
|
||||
},
|
||||
6000,
|
||||
);
|
||||
}
|
||||
|
||||
const prevAmount = this.prevCredToEdit?.claim?.object?.amountOfThisGood;
|
||||
this.amountInput =
|
||||
(this.$route as Router).query["amountInput"] ||
|
||||
(prevAmount ? String(prevAmount) : "") ||
|
||||
this.amountInput;
|
||||
this.description =
|
||||
(this.$route as Router).query["description"] ||
|
||||
this.prevCredToEdit?.claim?.description ||
|
||||
this.description;
|
||||
this.destinationPathAfter = (this.$route as Router).query[
|
||||
"destinationPathAfter"
|
||||
];
|
||||
this.giverDid = ((this.$route as Router).query["giverDid"] ||
|
||||
this.prevCredToEdit?.claim?.agent?.identifier ||
|
||||
this.giverDid) as string;
|
||||
this.giverName =
|
||||
((this.$route as Router).query["giverName"] as string) || "";
|
||||
this.hideBackButton =
|
||||
(this.$route as Router).query["hideBackButton"] === "true";
|
||||
this.message = ((this.$route as Router).query["message"] as string) || "";
|
||||
// find any offer ID
|
||||
const fulfills = this.prevCredToEdit?.claim?.fulfills;
|
||||
const fulfillsArray = Array.isArray(fulfills)
|
||||
? fulfills
|
||||
: fulfills
|
||||
? [fulfills]
|
||||
: [];
|
||||
const offer = fulfillsArray.find((rec) => rec["@type"] === "Offer");
|
||||
this.offerId = ((this.$route as Router).query["offerId"] ||
|
||||
offer?.identifier ||
|
||||
this.offerId) as string;
|
||||
|
||||
// find any project ID
|
||||
const project = fulfillsArray.find((rec) => rec["@type"] === "PlanAction");
|
||||
this.projectId = ((this.$route as Router).query["projectId"] ||
|
||||
project?.identifier ||
|
||||
this.projectId) as string;
|
||||
|
||||
this.recipientDid = ((this.$route as Router).query["recipientDid"] ||
|
||||
this.prevCredToEdit?.claim?.recipient?.identifier) as string;
|
||||
this.recipientName =
|
||||
((this.$route as Router).query["recipientName"] as string) || "";
|
||||
this.unitCode = ((this.$route as Router).query["unitCode"] ||
|
||||
this.prevCredToEdit?.claim?.object?.unitCode ||
|
||||
this.unitCode) as string;
|
||||
|
||||
// this is an endpoint for sharing project info to highlight something given
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target
|
||||
if ((this.$route as Router).query["shareTitle"]) {
|
||||
this.description =
|
||||
((this.$route as Router).query["shareTitle"] as string) +
|
||||
(this.description ? "\n" + this.description : "");
|
||||
}
|
||||
if ((this.$route as Router).query["shareText"]) {
|
||||
this.description =
|
||||
(this.description ? this.description + "\n" : "") +
|
||||
((this.$route as Router).query["shareText"] as string);
|
||||
}
|
||||
if ((this.$route as Router).query["shareUrl"]) {
|
||||
this.imageUrl = (this.$route as Router).query["shareUrl"] as string;
|
||||
}
|
||||
|
||||
try {
|
||||
await db.open();
|
||||
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
|
||||
this.apiServer = settings?.apiServer || "";
|
||||
this.activeDid = settings?.activeDid || "";
|
||||
|
||||
let allContacts: Contact[] = [];
|
||||
let allMyDids: string[] = [];
|
||||
if (
|
||||
(this.giverDid && !this.giverName) ||
|
||||
(this.recipientDid && !this.recipientName)
|
||||
) {
|
||||
allContacts = await db.contacts.toArray();
|
||||
|
||||
await accountsDB.open();
|
||||
const allAccounts = await accountsDB.accounts.toArray();
|
||||
allMyDids = allAccounts.map((acc) => acc.did);
|
||||
if (this.giverDid && !this.giverName) {
|
||||
this.giverName = didInfo(
|
||||
this.giverDid,
|
||||
this.activeDid,
|
||||
allMyDids,
|
||||
allContacts,
|
||||
);
|
||||
}
|
||||
if (this.recipientDid && !this.recipientName) {
|
||||
this.recipientName = didInfo(
|
||||
this.recipientDid,
|
||||
this.activeDid,
|
||||
allMyDids,
|
||||
allContacts,
|
||||
);
|
||||
}
|
||||
}
|
||||
this.offeredToProject = !!this.projectId;
|
||||
this.offeredToRecipient = !this.offeredToProject && !!this.recipientDid;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings from database:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: err.message || "There was an error retrieving your settings.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.projectId) {
|
||||
// console.log("Getting project name from cache", this.projectId);
|
||||
const project = await getPlanFromCache(
|
||||
this.projectId,
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
);
|
||||
this.projectName = project?.name
|
||||
? "the project: " + project.name
|
||||
: "a project";
|
||||
}
|
||||
}
|
||||
|
||||
changeUnitCode() {
|
||||
const units = Object.keys(this.libsUtil.UNIT_SHORT);
|
||||
const index = units.indexOf(this.unitCode);
|
||||
this.unitCode = units[(index + 1) % units.length];
|
||||
}
|
||||
|
||||
increment() {
|
||||
this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`;
|
||||
}
|
||||
|
||||
decrement() {
|
||||
this.amountInput = `${Math.max(
|
||||
0,
|
||||
(parseFloat(this.amountInput) || 1) - 1,
|
||||
)}`;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
if (this.destinationPathAfter) {
|
||||
(this.$router as Router).push({ path: this.destinationPathAfter });
|
||||
} else {
|
||||
(this.$router as Router).back();
|
||||
}
|
||||
}
|
||||
|
||||
cancelBack() {
|
||||
(this.$router as Router).back();
|
||||
}
|
||||
|
||||
async confirm() {
|
||||
if (!this.activeDid) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "You must select an identifier before you can record a offer.",
|
||||
},
|
||||
2000,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (parseFloat(this.amountInput) < 0) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
text: "You may not send a negative number.",
|
||||
title: "",
|
||||
},
|
||||
2000,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.description && !parseFloat(this.amountInput)) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: `You must enter a description or some number of ${
|
||||
this.libsUtil.UNIT_LONG[this.unitCode]
|
||||
}.`,
|
||||
},
|
||||
2000,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "toast",
|
||||
text: "Recording the give...",
|
||||
title: "",
|
||||
},
|
||||
1000,
|
||||
);
|
||||
|
||||
// this is asynchronous, but we don't need to wait for it to complete
|
||||
await this.recordOffer();
|
||||
}
|
||||
|
||||
notifyUserOfProject() {
|
||||
if (!this.projectId) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "To assign to a project, you must open this dialog through a project.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else {
|
||||
// must be because offeredToRecipient is true
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "You cannot assign both to a project and to a recipient.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
notifyUserOfRecipient() {
|
||||
if (!this.recipientDid) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "To assign to a recipient, you must open this dialog from a contact.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else {
|
||||
// must be because offeredToProject is true
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "You cannot assign both to a recipient and to a project.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param giverDid may be null
|
||||
* @param description may be an empty string
|
||||
* @param amountInput may be 0
|
||||
* @param unitCode may be omitted, defaults to "HUR"
|
||||
*/
|
||||
public async recordOffer() {
|
||||
try {
|
||||
const recipientDid = this.offeredToRecipient
|
||||
? this.recipientDid
|
||||
: undefined;
|
||||
const projectId = this.offeredToProject ? this.projectId : undefined;
|
||||
let result;
|
||||
if (this.prevCredToEdit) {
|
||||
// don't create from a blank one in case some properties were set from a different interface
|
||||
result = await editAndSubmitOffer(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.prevCredToEdit,
|
||||
this.activeDid,
|
||||
this.description,
|
||||
parseFloat(this.amountInput),
|
||||
this.unitCode,
|
||||
this.validThroughDate,
|
||||
recipientDid,
|
||||
projectId,
|
||||
);
|
||||
} else {
|
||||
result = await createAndSubmitOffer(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.description,
|
||||
parseFloat(this.amountInput),
|
||||
this.unitCode,
|
||||
this.validThroughDate,
|
||||
recipientDid,
|
||||
projectId,
|
||||
);
|
||||
}
|
||||
|
||||
if (result.type === "error" || this.isCreationError(result.response)) {
|
||||
const errorMessage = this.getCreationErrorMessage(result);
|
||||
console.error("Error with give creation result:", result);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: errorMessage || "There was an error creating the give.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Success",
|
||||
text: `That ${this.isTrade ? "trade" : "gift"} was recorded.`,
|
||||
},
|
||||
5000,
|
||||
);
|
||||
localStorage.removeItem("imageUrl");
|
||||
if (this.destinationPathAfter) {
|
||||
(this.$router as Router).push({ path: this.destinationPathAfter });
|
||||
} else {
|
||||
(this.$router as Router).back();
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Error with give recordation caught:", error);
|
||||
const errorMessage =
|
||||
error.userMessage ||
|
||||
error.response?.data?.error?.message ||
|
||||
"There was an error recording the give.";
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: errorMessage,
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
constructOfferParam() {
|
||||
const recipientDid = this.offeredToRecipient
|
||||
? this.recipientDid
|
||||
: undefined;
|
||||
const projectId = this.offeredToProject ? this.projectId : undefined;
|
||||
const giveClaim = hydrateOffer(
|
||||
this.prevCredToEdit?.claim as OfferVerifiableCredential,
|
||||
this.activeDid,
|
||||
recipientDid,
|
||||
this.description,
|
||||
parseFloat(this.amountInput),
|
||||
this.unitCode,
|
||||
"",
|
||||
projectId,
|
||||
this.validThroughDate,
|
||||
this.prevCredToEdit?.id as string,
|
||||
);
|
||||
const claimStr = JSON.stringify(giveClaim);
|
||||
return claimStr;
|
||||
}
|
||||
|
||||
// Helper functions for readability
|
||||
|
||||
/**
|
||||
* @param result response "data" from the server
|
||||
* @returns true if the result indicates an error
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
isCreationError(result: any) {
|
||||
return result.status !== 201 || result.data?.error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
|
||||
* @returns best guess at an error message
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getCreationErrorMessage(result: any) {
|
||||
return (
|
||||
result.error?.userMessage ||
|
||||
result.error?.error ||
|
||||
result.response?.data?.error?.message
|
||||
);
|
||||
}
|
||||
|
||||
explainData() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Data Sharing",
|
||||
text: libsUtil.PRIVACY_MESSAGE,
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user