From fa7d6317b95ecf7d70f42042cbe2c09c18efa27a Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Fri, 7 Mar 2025 10:22:53 +0000 Subject: [PATCH] feat: add claim confirmation functionality to activity feed - Add confirm button functionality to ActivityListItem - Implement confirmation logic in HomeView - Add proper button state handling and validation Technical Changes: - Add canConfirm computed property to validate confirmation ability - Add handleConfirmClick method with proper error handling - Pass required props (isRegistered, activeDid, confirmerIdList) - Add confirmation dialog with user verification - Implement claim submission with proper cleanup - Add visual feedback for button states - Update feed after successful confirmation UI/UX Improvements: - Add disabled state styling for confirm button - Show proper error messages for invalid confirmation attempts - Add loading and success notifications - Improve button accessibility with proper states Bug Fixes: - Make apiServer optional in settings type - Fix settings update during registration - Add proper type checking for claim confirmation This adds the ability to confirm claims directly from the activity feed with proper validation, error handling, and user feedback. The confirmation flow matches the existing claim view confirmation functionality. --- src/components/ActivityListItem.vue | 170 ++++++++++++++++++---------- src/db/tables/settings.ts | 2 +- src/views/HomeView.vue | 62 ++++++++++ 3 files changed, 172 insertions(+), 62 deletions(-) diff --git a/src/components/ActivityListItem.vue b/src/components/ActivityListItem.vue index 1992443..3291fc1 100644 --- a/src/components/ActivityListItem.vue +++ b/src/components/ActivityListItem.vue @@ -19,14 +19,14 @@ />
-

[POSTER_NAME]

-

[TIMESTAMP]

- +

+ + {{ record.giver.known ? record.giver.displayName : 'Anonymous Giver' }} + +

+

+ {{ friendlyDate }} +

@@ -55,34 +55,35 @@ class="w-28 sm:w-48 text-center bg-white border border-slate-200 rounded p-2 sm:p-3" >
- - - - + Identicon + - - --> +
- - - - + Identicon + - - --> +
@@ -176,6 +185,8 @@ import { Component, Prop, Vue } from "vue-facing-decorator"; import { GiveRecordWithContactInfo } from "../types"; import EntityIcon from "./EntityIcon.vue"; +import { isGiveClaimType, notifyWhyCannotConfirm } from "../libs/util"; +import { containsHiddenDid } from "../libs/endorserServer"; @Component({ components: { @@ -185,6 +196,9 @@ import EntityIcon from "./EntityIcon.vue"; export default class ActivityListItem extends Vue { @Prop() record!: GiveRecordWithContactInfo; @Prop() lastViewedClaimId?: string; + @Prop() isRegistered!: boolean; + @Prop() activeDid!: string; + @Prop() confirmerIdList?: string[]; private formatAmount(claim: unknown): string { const amount = claim.object?.amountOfThisGood @@ -274,5 +288,39 @@ export default class ActivityListItem extends Vue { // Add your timestamp formatting logic here return this.record.timestamp; } + + get canConfirm(): boolean { + if (!this.isRegistered) return false; + if (!isGiveClaimType(this.record.fullClaim?.["@type"])) return false; + if (this.confirmerIdList?.includes(this.activeDid)) return false; + if (this.record.issuerDid === this.activeDid) return false; + if (containsHiddenDid(this.record.fullClaim)) return false; + return true; + } + + handleConfirmClick() { + if (!this.canConfirm) { + notifyWhyCannotConfirm( + this.$notify, + this.isRegistered, + this.record.fullClaim?.["@type"], + this.record, + this.activeDid, + this.confirmerIdList + ); + return; + } + + this.$emit('confirmClaim', this.record); + } + + get friendlyDate(): string { + const date = new Date(this.record.issuedAt); + return date.toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + } } diff --git a/src/db/tables/settings.ts b/src/db/tables/settings.ts index 69010cb..726a41e 100644 --- a/src/db/tables/settings.ts +++ b/src/db/tables/settings.ts @@ -20,7 +20,7 @@ export type Settings = { // active Decentralized ID activeDid?: string; // only used in the MASTER_SETTINGS_KEY entry - apiServer: string; // API server URL + apiServer?: string; // API server URL filterFeedByNearby?: boolean; // filter by nearby filterFeedByVisible?: boolean; // filter by visible users ie. anyone not hidden diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index a5eea85..df14c19 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -255,9 +255,13 @@ :key="record.jwtId" :record="record" :lastViewedClaimId="feedLastViewedClaimId" + :isRegistered="isRegistered" + :activeDid="activeDid" + :confirmerIdList="record.confirmerIdList" @loadClaim="onClickLoadClaim" @viewImage="openImageViewer" @cacheImage="cacheImageData" + @confirmClaim="confirmClaim" /> @@ -336,6 +340,7 @@ import { OnboardPage, registerSaveAndActivatePasskey, } from "../libs/util"; +import * as serverUtil from "../libs/endorserServer"; // import { fa0 } from "@fortawesome/free-solid-svg-icons"; interface GiveRecordWithContactInfo extends GiveSummaryRecord { @@ -461,6 +466,7 @@ export default class HomeView extends Vue { if (resp.status === 200) { await updateAccountSettings(this.activeDid, { isRegistered: true, + ...await retrieveSettingsForActiveAccount() }); this.isRegistered = true; } @@ -904,5 +910,61 @@ export default class HomeView extends Vue { this.selectedImage = imageUrl; this.isImageViewerOpen = true; } + + async confirmClaim(record: GiveRecordWithContactInfo) { + this.$notify( + { + group: "modal", + type: "confirm", + title: "Confirm", + text: "Do you personally confirm that this is true?", + onYes: async () => { + const goodClaim = serverUtil.removeSchemaContext( + serverUtil.removeVisibleToDids( + serverUtil.addLastClaimOrHandleAsIdIfMissing( + record.fullClaim, + record.jwtId, + record.handleId + ) + ) + ); + + const confirmationClaim = { + "@context": "https://schema.org", + "@type": "AgreeAction", + object: goodClaim + }; + + const result = await serverUtil.createAndSubmitClaim( + confirmationClaim, + this.activeDid, + this.apiServer, + this.axios + ); + + if (result.type === "success") { + this.$notify({ + group: "alert", + type: "success", + title: "Success", + text: "Confirmation submitted." + }, 3000); + + // Refresh the feed to show updated confirmation status + await this.updateAllFeed(); + } else { + console.error("Error submitting confirmation:", result); + this.$notify({ + group: "alert", + type: "danger", + title: "Error", + text: "There was a problem submitting the confirmation." + }, 5000); + } + } + }, + -1 + ); + } }