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.
This commit is contained in:
Jose Olarte III
2025-09-10 18:19:17 +08:00
parent 8815f36596
commit 9bdd66b9c9
4 changed files with 40 additions and 23 deletions

View File

@@ -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;
};

View File

@@ -734,7 +734,7 @@ export default class ClaimView extends Vue {
*/
extractOfferFulfillment() {
this.detailsForGiveOfferFulfillment = libsUtil.extractOfferFulfillment(
this.detailsForGive?.fullClaim?.fulfills
this.detailsForGive?.fullClaim?.fulfills,
);
}

View File

@@ -719,7 +719,7 @@ export default class ConfirmGiftView extends Vue {
*/
private extractOfferFulfillment() {
this.giveDetailsOfferFulfillment = libsUtil.extractOfferFulfillment(
this.giveDetails?.fullClaim?.fulfills
this.giveDetails?.fullClaim?.fulfills,
);
}

View File

@@ -32,9 +32,9 @@
@click="expandOffersToUserAndMarkRead()"
/>
</div>
<router-link to="/recent-offers-to-user" class="text-blue-500">
<a @click="handleSeeAllOffersToUser" class="text-blue-500 cursor-pointer">
See&nbsp;all
</router-link>
</a>
</div>
<div v-if="showOffersDetails" class="ml-4 mt-4">
@@ -99,9 +99,9 @@
@click="expandOffersToUserProjectsAndMarkRead()"
/>
</div>
<router-link to="/recent-offers-to-user-projects" class="text-blue-500">
<a @click="handleSeeAllOffersToUserProjects" class="text-blue-500 cursor-pointer">
See&nbsp;all
</router-link>
</a>
</div>
<div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4">
@@ -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");
}
}
</script>