From 9bdd66b9c93168cd5aa845e15986ce6659f86f33 Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Wed, 10 Sep 2025 18:19:17 +0800 Subject: [PATCH] feat(NewActivityView): enhance "See all" links to mark offers as read before navigation - Replace router-links with click handlers for both "See all" offers links - Add handleSeeAllOffersToUser and handleSeeAllOffersToUserProjects methods - Modify expandOffersToUserAndMarkRead to accept fromSeeAll parameter for contextual notifications - Modify expandOffersToUserProjectsAndMarkRead to accept fromSeeAll parameter for contextual notifications - Show shorter notification messages when called from "See all" vs chevron expand buttons - Add safety checks to prevent errors when offers arrays are empty - Standardize notification message text consistency - TypeScript and formatting lint fixes Both "See all" links now properly mark offers as viewed before navigation, preventing users from seeing unread offers in the detailed views. --- src/libs/util.ts | 17 +++++++++----- src/views/ClaimView.vue | 2 +- src/views/ConfirmGiftView.vue | 2 +- src/views/NewActivityView.vue | 42 ++++++++++++++++++++++------------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/libs/util.ts b/src/libs/util.ts index c64916cc..432d8f72 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -165,18 +165,25 @@ export interface OfferFulfillment { offerType: string; } +interface FulfillmentObject { + "@type": string; + identifier?: string; +} + /** * Extract offer fulfillment information from the fulfills field * Handles both array and single object cases */ -export const extractOfferFulfillment = (fulfills: any): OfferFulfillment | null => { +export const extractOfferFulfillment = ( + fulfills: FulfillmentObject | FulfillmentObject[] | null | undefined, +): OfferFulfillment | null => { if (!fulfills) { return null; } - + // Handle both array and single object cases let offerFulfill = null; - + if (Array.isArray(fulfills)) { // Find the Offer in the fulfills array offerFulfill = fulfills.find((item) => item["@type"] === "Offer"); @@ -184,14 +191,14 @@ export const extractOfferFulfillment = (fulfills: any): OfferFulfillment | null // fulfills is a single Offer object offerFulfill = fulfills; } - + if (offerFulfill) { return { offerHandleId: offerFulfill.identifier, offerType: offerFulfill["@type"], }; } - + return null; }; diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index 2c441687..05ba7e07 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -734,7 +734,7 @@ export default class ClaimView extends Vue { */ extractOfferFulfillment() { this.detailsForGiveOfferFulfillment = libsUtil.extractOfferFulfillment( - this.detailsForGive?.fullClaim?.fulfills + this.detailsForGive?.fullClaim?.fulfills, ); } diff --git a/src/views/ConfirmGiftView.vue b/src/views/ConfirmGiftView.vue index 95632bb7..5dc9fe0c 100644 --- a/src/views/ConfirmGiftView.vue +++ b/src/views/ConfirmGiftView.vue @@ -719,7 +719,7 @@ export default class ConfirmGiftView extends Vue { */ private extractOfferFulfillment() { this.giveDetailsOfferFulfillment = libsUtil.extractOfferFulfillment( - this.giveDetails?.fullClaim?.fulfills + this.giveDetails?.fullClaim?.fulfills, ); } diff --git a/src/views/NewActivityView.vue b/src/views/NewActivityView.vue index fbcd7423..854d19ab 100644 --- a/src/views/NewActivityView.vue +++ b/src/views/NewActivityView.vue @@ -32,9 +32,9 @@ @click="expandOffersToUserAndMarkRead()" /> - + See all - +
@@ -99,9 +99,9 @@ @click="expandOffersToUserProjectsAndMarkRead()" />
- + See all - +
@@ -239,18 +239,18 @@ export default class NewActivityView extends Vue { } } - async expandOffersToUserAndMarkRead() { + async expandOffersToUserAndMarkRead(fromSeeAll: boolean = false) { this.showOffersDetails = !this.showOffersDetails; - if (this.showOffersDetails) { + if (this.showOffersDetails && this.newOffersToUser.length > 0) { await this.$updateSettings({ lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, }); // note that we don't update this.lastAckedOfferToUserJwtId in case they // later choose the last one to keep the offers as new - this.notify.info( - "The offers are marked as viewed. Click in the list to keep them as new.", - TIMEOUTS.LONG, - ); + const message = fromSeeAll + ? "The offers are marked as viewed." + : "The offers are marked as viewed. Click in the list to keep them as new."; + this.notify.info(message, TIMEOUTS.LONG); } } @@ -275,20 +275,20 @@ export default class NewActivityView extends Vue { ); } - async expandOffersToUserProjectsAndMarkRead() { + async expandOffersToUserProjectsAndMarkRead(fromSeeAll: boolean = false) { this.showOffersToUserProjectsDetails = !this.showOffersToUserProjectsDetails; - if (this.showOffersToUserProjectsDetails) { + if (this.showOffersToUserProjectsDetails && this.newOffersToUserProjects.length > 0) { await this.$updateSettings({ lastAckedOfferToUserProjectsJwtId: this.newOffersToUserProjects[0].jwtId, }); // note that we don't update this.lastAckedOfferToUserProjectsJwtId in case // they later choose the last one to keep the offers as new - this.notify.info( - "The offers are now marked as viewed. Click in the list to keep them as new.", - TIMEOUTS.LONG, - ); + const message = fromSeeAll + ? "The offers are marked as viewed." + : "The offers are marked as viewed. Click in the list to keep them as new."; + this.notify.info(message, TIMEOUTS.LONG); } } @@ -314,5 +314,15 @@ export default class NewActivityView extends Vue { TIMEOUTS.STANDARD, ); } + + async handleSeeAllOffersToUser() { + await this.expandOffersToUserAndMarkRead(true); + this.$router.push("/recent-offers-to-user"); + } + + async handleSeeAllOffersToUserProjects() { + await this.expandOffersToUserProjectsAndMarkRead(true); + this.$router.push("/recent-offers-to-user-projects"); + } }