feat(NewActivityView): enhance "See all" links to mark offers as read before navigation #198

Open
jose wants to merge 4 commits from new-activity-mark-read into master
  1. 17
      src/libs/util.ts
  2. 2
      src/views/ClaimView.vue
  3. 2
      src/views/ConfirmGiftView.vue
  4. 46
      src/views/NewActivityView.vue
  5. 21
      src/views/RecentOffersToUserProjectsView.vue
  6. 20
      src/views/RecentOffersToUserView.vue

17
src/libs/util.ts

@ -165,18 +165,25 @@ export interface OfferFulfillment {
offerType: string; offerType: string;
} }
interface FulfillmentObject {
"@type": string;
identifier?: string;
}
/** /**
* Extract offer fulfillment information from the fulfills field * Extract offer fulfillment information from the fulfills field
* Handles both array and single object cases * 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) { if (!fulfills) {
return null; return null;
} }
// Handle both array and single object cases // Handle both array and single object cases
let offerFulfill = null; let offerFulfill = null;
if (Array.isArray(fulfills)) { if (Array.isArray(fulfills)) {
// Find the Offer in the fulfills array // Find the Offer in the fulfills array
offerFulfill = fulfills.find((item) => item["@type"] === "Offer"); offerFulfill = fulfills.find((item) => item["@type"] === "Offer");
@ -184,14 +191,14 @@ export const extractOfferFulfillment = (fulfills: any): OfferFulfillment | null
// fulfills is a single Offer object // fulfills is a single Offer object
offerFulfill = fulfills; offerFulfill = fulfills;
} }
if (offerFulfill) { if (offerFulfill) {
return { return {
offerHandleId: offerFulfill.identifier, offerHandleId: offerFulfill.identifier,
offerType: offerFulfill["@type"], offerType: offerFulfill["@type"],
}; };
} }
return null; return null;
}; };

2
src/views/ClaimView.vue

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

2
src/views/ConfirmGiftView.vue

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

46
src/views/NewActivityView.vue

@ -32,9 +32,9 @@
@click="expandOffersToUserAndMarkRead()" @click="expandOffersToUserAndMarkRead()"
/> />
</div> </div>
<router-link to="/recent-offers-to-user" class="text-blue-500"> <a class="text-blue-500 cursor-pointer" @click="handleSeeAllOffersToUser">
See&nbsp;all See&nbsp;all
</router-link> </a>
</div> </div>
<div v-if="showOffersDetails" class="ml-4 mt-4"> <div v-if="showOffersDetails" class="ml-4 mt-4">
@ -99,9 +99,12 @@
@click="expandOffersToUserProjectsAndMarkRead()" @click="expandOffersToUserProjectsAndMarkRead()"
/> />
</div> </div>
<router-link to="/recent-offers-to-user-projects" class="text-blue-500"> <a
class="text-blue-500 cursor-pointer"
@click="handleSeeAllOffersToUserProjects"
>
See&nbsp;all See&nbsp;all
</router-link> </a>
</div> </div>
<div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4"> <div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4">
@ -239,18 +242,18 @@ export default class NewActivityView extends Vue {
} }
} }
async expandOffersToUserAndMarkRead() { async expandOffersToUserAndMarkRead(fromSeeAll: boolean = false) {
this.showOffersDetails = !this.showOffersDetails; this.showOffersDetails = !this.showOffersDetails;
if (this.showOffersDetails) { if (this.showOffersDetails && this.newOffersToUser.length > 0) {
await this.$updateSettings({ await this.$updateSettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
}); });
// note that we don't update this.lastAckedOfferToUserJwtId in case they // note that we don't update this.lastAckedOfferToUserJwtId in case they
// later choose the last one to keep the offers as new // later choose the last one to keep the offers as new
this.notify.info( const message = fromSeeAll
"The offers are marked as viewed. Click in the list to keep them as new.", ? "The offers are marked as viewed."
TIMEOUTS.LONG, : "The offers are marked as viewed. Click in the list to keep them as new.";
); this.notify.info(message, TIMEOUTS.LONG);
} }
} }
@ -275,20 +278,23 @@ export default class NewActivityView extends Vue {
); );
} }
async expandOffersToUserProjectsAndMarkRead() { async expandOffersToUserProjectsAndMarkRead(fromSeeAll: boolean = false) {
this.showOffersToUserProjectsDetails = this.showOffersToUserProjectsDetails =
!this.showOffersToUserProjectsDetails; !this.showOffersToUserProjectsDetails;
if (this.showOffersToUserProjectsDetails) { if (
this.showOffersToUserProjectsDetails &&
this.newOffersToUserProjects.length > 0
) {
await this.$updateSettings({ await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId, this.newOffersToUserProjects[0].jwtId,
}); });
// note that we don't update this.lastAckedOfferToUserProjectsJwtId in case // note that we don't update this.lastAckedOfferToUserProjectsJwtId in case
// they later choose the last one to keep the offers as new // they later choose the last one to keep the offers as new
this.notify.info( const message = fromSeeAll
"The offers are now marked as viewed. Click in the list to keep them as new.", ? "The offers are marked as viewed."
TIMEOUTS.LONG, : "The offers are marked as viewed. Click in the list to keep them as new.";
); this.notify.info(message, TIMEOUTS.LONG);
} }
} }
@ -314,5 +320,13 @@ export default class NewActivityView extends Vue {
TIMEOUTS.STANDARD, TIMEOUTS.STANDARD,
); );
} }
async handleSeeAllOffersToUser() {
this.$router.push("/recent-offers-to-user");
}
async handleSeeAllOffersToUserProjects() {
this.$router.push("/recent-offers-to-user-projects");
}
} }
</script> </script>

21
src/views/RecentOffersToUserProjectsView.vue

@ -32,20 +32,20 @@
</div> </div>
<InfiniteScroll @reached-bottom="loadMoreOffersToUserProjects"> <InfiniteScroll @reached-bottom="loadMoreOffersToUserProjects">
<ul <ul data-testId="listRecentOffersToUserProjects">
data-testId="listRecentOffersToUserProjects"
class="border-t border-slate-300"
>
<li <li
v-for="offer in newOffersToUserProjects" v-for="offer in newOffersToUserProjects"
:key="offer.jwtId" :key="offer.jwtId"
class="mt-4 relative group" class="mt-4 relative group"
> >
<!-- Last viewed separator -->
<div <div
v-if="offer.jwtId == lastAckedOfferToUserProjectsJwtId" v-if="offer.jwtId == lastAckedOfferToUserProjectsJwtId"
class="border-b border-slate-300 text-orange-400 pb-2 mb-2 font-bold text-sm" class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
> >
You've already seen all the following <span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
You've already seen all the following
</span>
</div> </div>
<span>{{ <span>{{
@ -142,6 +142,15 @@ export default class RecentOffersToUserView extends Vue {
this.newOffersToUserProjects = offersToUserProjectsData.data; this.newOffersToUserProjects = offersToUserProjectsData.data;
this.newOffersToUserProjectsAtEnd = !offersToUserProjectsData.hitLimit; this.newOffersToUserProjectsAtEnd = !offersToUserProjectsData.hitLimit;
// Mark offers as read after data is loaded
if (this.newOffersToUserProjects.length > 0) {
await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId,
});
this.notify.info("The offers are marked as viewed.", TIMEOUTS.LONG);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);

20
src/views/RecentOffersToUserView.vue

@ -27,20 +27,20 @@
</p> </p>
</div> </div>
<InfiniteScroll @reached-bottom="loadMoreOffersToUser"> <InfiniteScroll @reached-bottom="loadMoreOffersToUser">
<ul <ul data-testId="listRecentOffersToUser">
data-testId="listRecentOffersToUser"
class="border-t border-slate-300"
>
<li <li
v-for="offer in newOffersToUser" v-for="offer in newOffersToUser"
:key="offer.jwtId" :key="offer.jwtId"
class="mt-4 relative group" class="mt-4 relative group"
> >
<!-- Last viewed separator -->
<div <div
v-if="offer.jwtId == lastAckedOfferToUserJwtId" v-if="offer.jwtId == lastAckedOfferToUserJwtId"
class="border-b border-slate-300 text-orange-400 pb-2 mb-2 font-bold text-sm" class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
> >
You've already seen all the following <span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
You've already seen all the following
</span>
</div> </div>
<span>{{ <span>{{
@ -133,6 +133,14 @@ export default class RecentOffersToUserView extends Vue {
this.newOffersToUser = offersToUserData.data; this.newOffersToUser = offersToUserData.data;
this.newOffersToUserAtEnd = !offersToUserData.hitLimit; this.newOffersToUserAtEnd = !offersToUserData.hitLimit;
// Mark offers as read after data is loaded
if (this.newOffersToUser.length > 0) {
await this.$updateSettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
});
this.notify.info("The offers are marked as viewed.", TIMEOUTS.LONG);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);

Loading…
Cancel
Save