diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index e5c356fe..391ed0b7 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -3,7 +3,7 @@
import { Vue, Component, Prop, Watch } from "vue-facing-decorator"; +import { Vue, Component, Prop, Watch } from "vue-facing-decorator"; import { NotificationIface } from "../constants/app"; import { @@ -59,6 +60,7 @@ import { didInfo, serverMessageForUser, getHeaders, + getHeaders, } from "../libs/endorserServer"; import * as libsUtil from "../libs/util"; import { db, retrieveSettingsForActiveAccount } from "../db/index"; @@ -99,6 +101,23 @@ export default class GiftedDialog extends Vue { this.updateEntityTypes(); } + @Watch("toProjectId") + onToProjectIdChange() { + this.updateEntityTypes(); + } + @Prop({ default: false }) showProjects = false; + @Prop() isFromProjectView = false; + + @Watch("showProjects") + onShowProjectsChange() { + this.updateEntityTypes(); + } + + @Watch("fromProjectId") + onFromProjectIdChange() { + this.updateEntityTypes(); + } + @Watch("toProjectId") onToProjectIdChange() { this.updateEntityTypes(); @@ -113,14 +132,14 @@ export default class GiftedDialog extends Vue { callbackOnSuccess?: (amount: number) => void = () => {}; customTitle?: string; description = ""; + firstStep = true; // true = Step 1 (giver/recipient selection), false = Step 2 (amount/description) giver?: libsUtil.GiverReceiverInputInfo; // undefined means no identified giver agent offerId = ""; prompt = ""; receiver?: libsUtil.GiverReceiverInputInfo; unitCode = "HUR"; visible = false; - currentStep = 1; - + libsUtil = libsUtil; projects: PlanData[] = []; @@ -222,12 +241,15 @@ export default class GiftedDialog extends Vue { this.amountInput = "0"; this.callbackOnSuccess = callbackOnSuccess; this.offerId = offerId || ""; - this.currentStep = giver ? 2 : 1; + this.firstStep = !giver; this.stepType = "giver"; // Update entity types based on current props this.updateEntityTypes(); + // Update entity types based on current props + this.updateEntityTypes(); + try { let settings = await databaseUtil.retrieveSettingsForActiveAccount(); this.apiServer = settings.apiServer || ""; @@ -313,7 +335,7 @@ export default class GiftedDialog extends Vue { this.amountInput = "0"; this.prompt = ""; this.unitCode = "HUR"; - this.currentStep = 1; + this.firstStep = true; } async confirm() { @@ -355,6 +377,20 @@ export default class GiftedDialog extends Vue { ); return; } + + // Check for person conflict + if (this.hasPersonConflict) { + this.$notify( + { + group: "alert", + type: "danger", + title: "Error", + text: "You cannot select the same person as both giver and recipient.", + }, + 3000, + ); + return; + } // Check for person conflict if (this.hasPersonConflict) { @@ -446,14 +482,18 @@ export default class GiftedDialog extends Vue { this.activeDid, fromDid, toDid, + fromDid, + toDid, description, amount, unitCode, fulfillsProjectHandleId, + fulfillsProjectHandleId, this.offerId, false, undefined, providerPlanHandleId, + providerPlanHandleId, ); if (!result.success) { @@ -540,12 +580,12 @@ export default class GiftedDialog extends Vue { name: "Unnamed", }; } - this.currentStep = 2; + this.firstStep = false; } goBackToStep1(step: string) { this.stepType = step; - this.currentStep = 1; + this.firstStep = true; } async loadProjects() { @@ -588,7 +628,7 @@ export default class GiftedDialog extends Vue { did: this.activeDid, name: "You", }; - this.currentStep = 2; + this.firstStep = false; } selectRecipient(contact?: Contact) { @@ -603,7 +643,7 @@ export default class GiftedDialog extends Vue { name: "Unnamed", }; } - this.currentStep = 2; + this.firstStep = false; } selectRecipientProject(project: PlanData) { @@ -613,7 +653,7 @@ export default class GiftedDialog extends Vue { image: project.image, handleId: project.handleId, }; - this.currentStep = 2; + this.firstStep = false; } // Computed property for the query parameters diff --git a/src/libs/fontawesome.ts b/src/libs/fontawesome.ts index 5121f4c3..2fa70e3c 100644 --- a/src/libs/fontawesome.ts +++ b/src/libs/fontawesome.ts @@ -61,6 +61,7 @@ import { faLightbulb, faLink, faLocationDot, + faLock, faLongArrowAltLeft, faLongArrowAltRight, faMagnifyingGlass, @@ -145,6 +146,7 @@ library.add( faLightbulb, faLink, faLocationDot, + faLock, faLongArrowAltLeft, faLongArrowAltRight, faMagnifyingGlass, diff --git a/src/libs/util.ts b/src/libs/util.ts index db18c2fe..7f0628f9 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -39,6 +39,8 @@ import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto"; export interface GiverReceiverInputInfo { did?: string; name?: string; + image?: string; + handleId?: string; } export enum OnboardPage { diff --git a/src/views/ContactGiftingView.vue b/src/views/ContactGiftingView.vue index 613705ea..a7f9efc8 100644 --- a/src/views/ContactGiftingView.vue +++ b/src/views/ContactGiftingView.vue @@ -4,14 +4,14 @@
-

+

- Given by... + {{ stepType === "giver" ? "Given by..." : "Given to..." }}

@@ -19,19 +19,18 @@
  • - - + - Unnamed/Unknown + (Unnamed/Unknown) @@ -44,13 +43,14 @@ class="border-b border-slate-300 py-3" >

    - + - {{ contact.name || "(no name)" }} + {{ contact.name }} + (No name)

- +
@@ -96,6 +102,24 @@ export default class ContactGiftingView extends Vue { description = ""; projectId = ""; prompt = ""; + recipientProjectName = ""; + recipientProjectImage = ""; + recipientProjectHandleId = ""; + + // New context parameters + stepType = "giver"; + giverEntityType = "person" as "person" | "project"; + recipientEntityType = "person" as "person" | "project"; + giverProjectId = ""; + giverProjectName = ""; + giverProjectImage = ""; + giverProjectHandleId = ""; + giverDid = ""; + recipientDid = ""; + fromProjectId = ""; + toProjectId = ""; + showProjects = false; + isFromProjectView = false; async created() { try { @@ -111,9 +135,41 @@ export default class ContactGiftingView extends Vue { dbAllContacts, ) as unknown as Contact[]; - this.projectId = (this.$route.query["projectId"] as string) || ""; + this.projectId = + (this.$route.query["recipientProjectId"] as string) || ""; + this.recipientProjectName = + (this.$route.query["recipientProjectName"] as string) || ""; + this.recipientProjectImage = + (this.$route.query["recipientProjectImage"] as string) || ""; + this.recipientProjectHandleId = + (this.$route.query["recipientProjectHandleId"] as string) || ""; this.prompt = (this.$route.query["prompt"] as string) ?? this.prompt; + // Read new context parameters + this.stepType = (this.$route.query["stepType"] as string) || "giver"; + this.giverEntityType = + (this.$route.query["giverEntityType"] as "person" | "project") || + "person"; + this.recipientEntityType = + (this.$route.query["recipientEntityType"] as "person" | "project") || + "person"; + this.giverProjectId = + (this.$route.query["giverProjectId"] as string) || ""; + this.giverProjectName = + (this.$route.query["giverProjectName"] as string) || ""; + this.giverProjectImage = + (this.$route.query["giverProjectImage"] as string) || ""; + this.giverProjectHandleId = + (this.$route.query["giverProjectHandleId"] as string) || ""; + this.giverDid = (this.$route.query["giverDid"] as string) || ""; + this.recipientDid = (this.$route.query["recipientDid"] as string) || ""; + this.fromProjectId = (this.$route.query["fromProjectId"] as string) || ""; + this.toProjectId = (this.$route.query["toProjectId"] as string) || ""; + this.showProjects = + (this.$route.query["showProjects"] as string) === "true"; + this.isFromProjectView = + (this.$route.query["isFromProjectView"] as string) === "true"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { logger.error("Error retrieving settings & contacts:", err); @@ -131,17 +187,108 @@ export default class ContactGiftingView extends Vue { } } - openDialog(giver?: GiverReceiverInputInfo) { - const recipient = this.projectId - ? undefined - : { did: this.activeDid, name: "you" }; - (this.$refs.customDialog as GiftedDialog).open( - giver, - recipient, - undefined, - "Given by " + (giver?.name || "someone not named"), - this.prompt, - ); + openDialog(contact?: GiverReceiverInputInfo | "Unnamed") { + if (contact === "Unnamed") { + // Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected + let recipient: GiverReceiverInputInfo; + let giver: GiverReceiverInputInfo | undefined; + + if (this.stepType === "giver") { + // We're selecting a giver, so recipient is either a project or the current user + if (this.recipientEntityType === "project") { + recipient = { + did: this.recipientProjectHandleId, + name: this.recipientProjectName, + image: this.recipientProjectImage, + handleId: this.recipientProjectHandleId, + }; + } else { + recipient = { did: this.activeDid, name: "You" }; + } + giver = undefined; // Will be set to "Unnamed" in GiftedDialog + } else { + // We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context + recipient = { did: "", name: "Unnamed" }; + + // Preserve the existing giver from the context + if (this.giverEntityType === "project") { + giver = { + // no did, because it's a project + name: this.giverProjectName, + image: this.giverProjectImage, + handleId: this.giverProjectHandleId, + }; + } else if (this.giverDid) { + giver = { + did: this.giverDid, + name: this.giverProjectName || "Someone", + }; + } else { + giver = { did: this.activeDid, name: "You" }; + } + } + + (this.$refs.customDialog as GiftedDialog).open( + giver, + recipient, + undefined, + this.stepType === "giver" ? "Given by Unnamed" : "Given to Unnamed", + this.prompt, + ); + // Immediately select "Unnamed" and move to Step 2 + (this.$refs.customDialog as GiftedDialog).selectGiver(); + } else { + // Regular case: contact is a GiverReceiverInputInfo + let giver: GiverReceiverInputInfo; + let recipient: GiverReceiverInputInfo; + + if (this.stepType === "giver") { + // We're selecting a giver, so the contact becomes the giver + giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined + + // Recipient is either a project or the current user + if (this.recipientEntityType === "project") { + recipient = { + did: this.recipientProjectHandleId, + name: this.recipientProjectName, + image: this.recipientProjectImage, + handleId: this.recipientProjectHandleId, + }; + } else { + recipient = { did: this.activeDid, name: "You" }; + } + } else { + // We're selecting a recipient, so the contact becomes the recipient + recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined + + // Preserve the existing giver from the context + if (this.giverEntityType === "project") { + giver = { + did: this.giverProjectHandleId, + name: this.giverProjectName, + image: this.giverProjectImage, + handleId: this.giverProjectHandleId, + }; + } else if (this.giverDid) { + giver = { + did: this.giverDid, + name: this.giverProjectName || "Someone", + }; + } else { + giver = { did: this.activeDid, name: "You" }; + } + } + + (this.$refs.customDialog as GiftedDialog).open( + giver, + recipient, + undefined, + this.stepType === "giver" + ? "Given by " + (contact?.name || "someone not named") + : "Given to " + (contact?.name || "someone not named"), + this.prompt, + ); + } } } diff --git a/src/views/GiftedDetailsView.vue b/src/views/GiftedDetailsView.vue index ff3add99..1209b2ec 100644 --- a/src/views/GiftedDetailsView.vue +++ b/src/views/GiftedDetailsView.vue @@ -4,87 +4,91 @@
- -
-

- + +
+

+ +
+ +
+ What Was Given

+ +

+
+ From + {{ + providedByProject + ? providerProjectName + : providedByGiver + ? giverName + : "someone not named" + }} +
+
+ to + {{ + givenToProject + ? fulfillsProjectName + : givenToRecipient + ? recipientName + : "someone not named" + }} +
+

- -

What Was Given

- -

- - From - {{ - providedByProject - ? providerProjectName - : providedByGiver - ? giverName - : "someone not named" - }} - -
- - to - {{ - givenToProject - ? fulfillsProjectName - : givenToRecipient - ? recipientName - : "someone not named" - }} -