From 625acb66f005e0cd20e9c1626517ef2ff0b20d21 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 30 Sep 2024 18:11:07 -0600 Subject: [PATCH] allow details on a give for a providing project (so we can attach a picture) --- src/components/GiftedDialog.vue | 2 +- src/libs/endorserServer.ts | 17 +- src/views/ClaimView.vue | 20 ++- src/views/ConfirmGiftView.vue | 2 +- src/views/GiftedDetailsView.vue | 249 +++++++++++++++++--------- src/views/HomeView.vue | 94 +++++++--- src/views/QuickActionBvcBeginView.vue | 2 + src/views/QuickActionBvcEndView.vue | 94 ++++++---- 8 files changed, 326 insertions(+), 154 deletions(-) diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index a74939ee7..91352cbf3 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -47,7 +47,7 @@ giverDid: giver?.did, giverName: giver?.name, offerId, - projectId, + fulfillsProjectId: projectId, recipientDid: receiver?.did, recipientName: receiver?.name, unitCode, diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index c050057e7..25818b2cc 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -91,6 +91,7 @@ export interface GiveSummaryRecord { issuedAt: string; issuerDid: string; jwtId: string; + providerPlanHandleId?: string; recipientDid: string; unit: string; } @@ -530,7 +531,7 @@ const planCache: LRUCache = new LRUCache({ * @param apiServer */ export async function getPlanFromCache( - handleId: string, + handleId: string | undefined, axios: Axios, apiServer: string, requesterDid?: string, @@ -593,7 +594,7 @@ export function hydrateGive( fulfillsOfferHandleId?: string, isTrade: boolean = false, imageUrl?: string, - providers?: Array, // typically @type & identifier + providerPlanHandleId?: string, lastClaimId?: string, ): GiveVerifiableCredential { // Remember: replace values or erase if it's null @@ -652,7 +653,9 @@ export function hydrateGive( vcClaim.image = imageUrl || undefined; - vcClaim.provider = providers || undefined; + vcClaim.provider = providerPlanHandleId + ? [{ "@type": "PlanAction", identifier: providerPlanHandleId }] + : undefined; return vcClaim; } @@ -678,7 +681,7 @@ export async function createAndSubmitGive( fulfillsOfferHandleId?: string, isTrade: boolean = false, imageUrl?: string, - providers?: Array, + providerPlanHandleId?: string, ): Promise { const vcClaim = hydrateGive( undefined, @@ -691,7 +694,7 @@ export async function createAndSubmitGive( fulfillsOfferHandleId, isTrade, imageUrl, - providers, + providerPlanHandleId, undefined, ); return createAndSubmitClaim( @@ -724,7 +727,7 @@ export async function editAndSubmitGive( fulfillsOfferHandleId?: string, isTrade: boolean = false, imageUrl?: string, - providers?: Array, + providerPlanHandleId?: string, ): Promise { const vcClaim = hydrateGive( fullClaim.claim, @@ -737,7 +740,7 @@ export async function editAndSubmitGive( fulfillsOfferHandleId, isTrade, imageUrl, - providers, + providerPlanHandleId, fullClaim.id, ); return createAndSubmitClaim( diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index 523cc10f9..1d9ad79a9 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -86,6 +86,15 @@ +
+ + Go to Project page + +
+ @@ -159,18 +168,13 @@ " class="text-blue-500 mt-4 cursor-pointer" > - {{ - provider.identifier.startsWith("did:") - ? "a person..." - : "an activity..." - }} + an activity... - @@ -552,10 +556,11 @@ export default class ClaimView extends Vue { this.fullClaimDump = ""; this.fullClaimMessage = ""; this.isEditedGlobalId = false; - this.isRegistered = false; this.numConfsNotVisible = 0; + this.providersForGive = []; this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD; this.veriClaimDump = ""; + this.veriClaimDidsVisible = {}; } async created() { @@ -672,7 +677,6 @@ export default class ClaimView extends Vue { this.apiServer + "/api/v2/report/providersToGive?handleId=" + encodeURIComponent(this.veriClaim.handleId as string); - console.log("providerUrl:", providerUrl); const providerHeaders = await serverUtil.getHeaders(userDid); const providerResp = await this.axios.get(providerUrl, { headers: providerHeaders, diff --git a/src/views/ConfirmGiftView.vue b/src/views/ConfirmGiftView.vue index cb641f1d3..c5e770303 100644 --- a/src/views/ConfirmGiftView.vue +++ b/src/views/ConfirmGiftView.vue @@ -655,7 +655,7 @@ export default class ClaimView extends Vue { } if (this.giveDetails.fulfillsPlanHandleId) { this.urlForNewGive += - "&projectId=" + + "&fulfillsProjectId=" + encodeURIComponent(this.giveDetails.fulfillsPlanHandleId); } diff --git a/src/views/GiftedDetailsView.vue b/src/views/GiftedDetailsView.vue index ff2fc0bf3..7bd0c166d 100644 --- a/src/views/GiftedDetailsView.vue +++ b/src/views/GiftedDetailsView.vue @@ -21,12 +21,22 @@

What Was Given

- From {{ giverName || "someone not named" }} + + From + {{ + providedByProject + ? providerProjectName + : providedByGiver + ? giverName + : "someone not named" + }} + +
to {{ givenToProject - ? projectName + ? fulfillsProjectName : givenToRecipient ? recipientName : "someone unidentified" @@ -87,7 +97,29 @@
+ + +
+ +
+
@@ -211,8 +243,10 @@ export default class GiftedDetails extends Vue { amountInput = "0"; description = ""; destinationPathAfter = ""; - givenToProject = false; - givenToRecipient = false; + fulfillsProjectId = ""; + fulfillsProjectName = "a project"; + givenToProject = false; // basically static, based on input; if we allow changing then let's fix things (see below) + givenToRecipient = false; // basically static, based on input; if we allow changing then let's fix things (see below) giverDid: string | undefined; giverName = ""; hideBackButton = false; @@ -221,8 +255,10 @@ export default class GiftedDetails extends Vue { message = ""; offerId = ""; prevCredToEdit?: GenericCredWrapper; - projectId = ""; - projectName = "a project"; + providerProjectId = ""; + providerProjectName = "a project"; + providedByProject = false; // basically static, based on input; if we allow changing then let's fix things (see below) + providedByGiver = false; // basically static, based on input; if we allow changing then let's fix things (see below) recipientDid = ""; recipientName = ""; showGeneralAdvanced = false; @@ -282,11 +318,31 @@ export default class GiftedDetails extends Vue { 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; + // find any fulfills project ID + const fulfillsProject = fulfillsArray.find( + (rec) => rec["@type"] === "PlanAction", + ); + // eslint-disable-next-line prettier/prettier + this.fulfillsProjectId = + ((this.$route as Router).query["fulfillsProjectId"] || + fulfillsProject?.identifier || + this.fulfillsProjectId) as string; + + // find any provider project ID + const provider = this.prevCredToEdit?.claim?.provider; + const providerArray = Array.isArray(provider) + ? provider + : provider + ? [provider] + : []; + const providerProject = providerArray.find( + (rec) => rec["@type"] === "PlanAction", + ); + this.providerProjectId = ((this.$route as Router).query[ + "providerProjectId" + ] || + providerProject?.identifier || + this.providerProjectId) as string; this.recipientDid = ((this.$route as Router).query["recipientDid"] || this.prevCredToEdit?.claim?.recipient?.identifier) as string; @@ -318,69 +374,70 @@ export default class GiftedDetails extends Vue { this.imageUrl = (this.$route as Router).query["shareUrl"] as string; } - try { - const settings = await retrieveSettingsForActiveAccount(); - 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, - ); - } + const settings = await retrieveSettingsForActiveAccount(); + 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, + ); } - // these should be functions but something's wrong with the syntax in the <> conditional - this.givenToProject = !!this.projectId; - this.givenToRecipient = !this.givenToProject && !!this.recipientDid; + if (this.recipientDid && !this.recipientName) { + this.recipientName = didInfo( + this.recipientDid, + this.activeDid, + allMyDids, + allContacts, + ); + } + } + // these should be functions but something's wrong with the syntax in the <> conditional + this.givenToProject = !!this.fulfillsProjectId; + this.givenToRecipient = !this.givenToProject && !!this.recipientDid; - this.showGeneralAdvanced = !!settings.showGeneralAdvanced; + // these should be functions but something's wrong with the syntax in the <> conditional + this.providedByProject = !!this.providerProjectId; + this.providedByGiver = !this.providedByProject && !!this.giverDid; - // 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, + this.showGeneralAdvanced = !!settings.showGeneralAdvanced; + + if (this.fulfillsProjectId) { + // console.log("Getting project name from cache", this.fulfillsProjectId); + const fulfillsProject = await getPlanFromCache( + this.fulfillsProjectId, + this.axios, + this.apiServer, + this.activeDid, ); + this.fulfillsProjectName = fulfillsProject?.name + ? `the project "${fulfillsProject.name}"` + : "a project"; } - - if (this.projectId) { - // console.log("Getting project name from cache", this.projectId); - const project = await getPlanFromCache( - this.projectId, + if (this.providerProjectId) { + // console.log("Getting project name from cache", this.providerProjectId); + const providerProject = await getPlanFromCache( + this.providerProjectId, this.axios, this.apiServer, this.activeDid, ); - this.projectName = project?.name - ? "the project: " + project.name + this.providerProjectName = providerProject?.name + ? `the project "${providerProject.name}"` : "a project"; } } @@ -545,8 +602,35 @@ export default class GiftedDetails extends Vue { await this.recordGive(); } - notifyUserOfProject() { - if (!this.projectId) { + notifyUserOfProvidingProject() { + // we're here because they clicked and either there is no provider project or there is a giver chosen + if (!this.providerProjectId) { + this.$notify( + { + group: "alert", + type: "warning", + title: "Error", + text: "To select a project as a provider, you must open this page through a project.", + }, + 3000, + ); + } else { + // no providing project was chosen + this.$notify( + { + group: "alert", + type: "warning", + title: "Error", + text: "You cannot select both a giving project and person.", + }, + 3000, + ); + } + } + + notifyUserFulfillsProject() { + // we're here because they clicked and either there is no fulfills project or there is a recipient chosen + if (!this.fulfillsProjectId) { this.$notify( { group: "alert", @@ -557,7 +641,7 @@ export default class GiftedDetails extends Vue { 3000, ); } else { - // must be because givenToRecipient is true + // no fulfills project was chosen this.$notify( { group: "alert", @@ -607,7 +691,9 @@ export default class GiftedDetails extends Vue { const recipientDid = this.givenToRecipient ? this.recipientDid : undefined; - const projectId = this.givenToProject ? this.projectId : undefined; + const fulfillsProjectId = this.givenToProject + ? this.fulfillsProjectId + : undefined; let result; if (this.prevCredToEdit) { // don't create from a blank one in case some properties were set from a different interface @@ -621,10 +707,11 @@ export default class GiftedDetails extends Vue { this.description, parseFloat(this.amountInput), this.unitCode, - projectId, + fulfillsProjectId, this.offerId, this.isTrade, this.imageUrl, + this.providerProjectId, ); } else { result = await createAndSubmitGive( @@ -636,11 +723,11 @@ export default class GiftedDetails extends Vue { this.description, parseFloat(this.amountInput), this.unitCode, - projectId, + fulfillsProjectId, this.offerId, this.isTrade, this.imageUrl, - [], + this.providerProjectId, ); } @@ -697,7 +784,9 @@ export default class GiftedDetails extends Vue { constructGiveParam() { const recipientDid = this.givenToRecipient ? this.recipientDid : undefined; - const projectId = this.givenToProject ? this.projectId : undefined; + const fulfillsProjectId = this.givenToProject + ? this.fulfillsProjectId + : undefined; const giveClaim = hydrateGive( this.prevCredToEdit?.claim as GiveVerifiableCredential, this.giverDid, @@ -705,11 +794,11 @@ export default class GiftedDetails extends Vue { this.description, parseFloat(this.amountInput), this.unitCode, - projectId, + fulfillsProjectId, this.offerId, this.isTrade, this.imageUrl, - [], + this.providerProjectId, this.prevCredToEdit?.id as string, ); const claimStr = JSON.stringify(giveClaim); diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 67bdc0a55..44838366d 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -282,6 +282,15 @@ > + + +
@@ -359,6 +368,7 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord { profileImageUrl?: string; }; image?: string; + providerPlanName?: string; recipientProjectName?: string; receiver: { displayName: string; @@ -554,8 +564,8 @@ export default class HomeView extends Vue { // This has indeed proven problematic. See loadMoreGives // We should display it immediately and then get the plan later. - const plan = await getPlanFromCache( - record.fulfillsPlanHandleId || "", + const fulfillsPlan = await getPlanFromCache( + record.fulfillsPlanHandleId, this.axios, this.apiServer, this.activeDid, @@ -570,8 +580,13 @@ export default class HomeView extends Vue { if (!anyMatch && this.isFeedFilteredByNearby) { // check if the associated project has a location inside user's search box if (record.fulfillsPlanHandleId) { - if (plan?.locLat && plan?.locLon) { - if (this.latLongInAnySearchBox(plan.locLat, plan.locLon)) { + if (fulfillsPlan?.locLat && fulfillsPlan?.locLon) { + if ( + this.latLongInAnySearchBox( + fulfillsPlan.locLat, + fulfillsPlan.locLon, + ) + ) { anyMatch = true; } } @@ -581,6 +596,17 @@ export default class HomeView extends Vue { continue; } + // checking for arrays due to legacy data + const provider = Array.isArray(claim.provider) + ? claim.provider[0] + : claim.provider; + const providedByPlan = await getPlanFromCache( + provider?.identifier as string, + this.axios, + this.apiServer, + this.activeDid, + ); + const newRecord: GiveRecordWithContactInfo = { ...record, giver: didInfoForContact( @@ -590,7 +616,9 @@ export default class HomeView extends Vue { this.allMyDids, ), image: claim.image, - recipientProjectName: plan?.name as string, + providerPlanHandleId: provider?.identifier as string, + providerPlanName: providedByPlan?.name as string, + recipientProjectName: fulfillsPlan?.name as string, receiver: didInfoForContact( recipientDid, this.activeDid, @@ -684,46 +712,62 @@ export default class HomeView extends Vue { } /** - * Only show giver and/or receiver info first if they're named. + * Only show giver and/or receiver info first if they're named in your contacts. * - If only giver is named, show "... gave" * - If only receiver is named, show "... received" */ const giverInfo = giveRecord.giver; const recipientInfo = giveRecord.receiver; + + // any specific names should be shown first if (giverInfo.known && recipientInfo.known) { // both giver and recipient are named return `${giverInfo.displayName} gave to ${recipientInfo.displayName}: ${gaveAmount}`; } else if (giverInfo.known) { - // giver is named but recipient is not + // giver is known but recipient is not // show the project name if to one if (giveRecord.recipientProjectName) { - // retrieve the project name - return `${giverInfo.displayName} gave: ${gaveAmount} (to the project ${giveRecord.recipientProjectName})`; + return `${giverInfo.displayName} gave: ${gaveAmount} (to the project "${giveRecord.recipientProjectName}")`; + } else { + // it's not to a project + return `${giverInfo.displayName} gave: ${gaveAmount} (to ${recipientInfo.displayName})`; } - - // it's not to a project - return `${giverInfo.displayName} gave: ${gaveAmount} (to ${recipientInfo.displayName})`; } else if (recipientInfo.known) { - // recipient is named but giver is not - return `${recipientInfo.displayName} received: ${gaveAmount} (from ${giverInfo.displayName})`; - } else { - // neither giver nor recipient are named + // recipient is known but giver is not - // show the project name if to one - if (giveRecord.recipientProjectName) { - // retrieve the project name - return `${gaveAmount} (to the project ${giveRecord.recipientProjectName})`; + // show the project name if from one + if (giveRecord.providerPlanName) { + return `${giverInfo.displayName} received: ${gaveAmount} (from the project "${giveRecord.providerPlanName}")`; + } else { + // it's not from a project + return `${recipientInfo.displayName} received: ${gaveAmount} (from ${giverInfo.displayName})`; } + } else { + // neither giver nor recipient are named - // it's not to a project - let peopleInfo; - if (giverInfo.displayName === recipientInfo.displayName) { - peopleInfo = `between two who are ${giverInfo.displayName}`; + // create the part in parens + let peopleInfo = ""; + if (giveRecord.providerPlanName || giveRecord.recipientProjectName) { + if (giveRecord.providerPlanName) { + peopleInfo = `from the project "${giveRecord.providerPlanName}"`; + } else { + peopleInfo = `from ${giverInfo.displayName}`; + } + if (giveRecord.recipientProjectName) { + peopleInfo += ` to the project "${giveRecord.recipientProjectName}"`; + } else { + peopleInfo += ` to ${recipientInfo.displayName}`; + } } else { - peopleInfo = `from ${giverInfo.displayName} to ${recipientInfo.displayName}`; + if (giverInfo.displayName === recipientInfo.displayName) { + peopleInfo = `between two who are ${giverInfo.displayName}`; + } else { + peopleInfo = `from ${giverInfo.displayName} to ${recipientInfo.displayName}`; + } } + return gaveAmount + " (" + peopleInfo + ")"; } } diff --git a/src/views/QuickActionBvcBeginView.vue b/src/views/QuickActionBvcBeginView.vue index 5b0655adb..e3c5e3dcc 100644 --- a/src/views/QuickActionBvcBeginView.vue +++ b/src/views/QuickActionBvcBeginView.vue @@ -67,6 +67,7 @@