<template> <QuickNav selected="Home"></QuickNav> <!-- CONTENT --> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <!-- Breadcrumb --> <div id="ViewBreadcrumb" class="mb-8"> <h1 class="text-lg text-center font-light relative px-7"> <!-- Back --> <fa icon="chevron-left" @click="$router.back()" class="fa-fw text-lg text-center px-2 py-1 absolute -left-2 -top-1" /> New Activity For You </h1> </div> <!-- Display a single row with the name of "New Offers To You" with a count. --> <div class="flex justify-between" data-testId="showOffersToUser"> <div> <span class="text-lg font-medium" >{{ newOffersToUser.length }}{{ newOffersToUserHitLimit ? "+" : "" }}</span > <span class="text-lg font-medium ml-4" >New Offer{{ newOffersToUser.length === 1 ? "" : "s" }} To You</span > <fa v-if="newOffersToUser.length > 0" :icon="showOffersDetails ? 'chevron-down' : 'chevron-right'" class="cursor-pointer ml-4 mr-4 text-lg" @click="expandOffersToUserAndMarkRead()" /> </div> <router-link to="/recent-offers-to-user" class="text-blue-500"> See all </router-link> </div> <div v-if="showOffersDetails" class="ml-4 mt-4"> <ul class="list-disc ml-4"> <li v-for="offer in newOffersToUser" :key="offer.jwtId" class="mt-4 relative group" > <span>{{ didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts) }}</span> offered <span v-if="offer.objectDescription">{{ offer.objectDescription }}</span >{{ offer.objectDescription && offer.amount ? ", and " : "" }} <span v-if="offer.amount">{{ displayAmount(offer.unit, offer.amount) }}</span> <router-link :to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }" class="text-blue-500" > <fa icon="file-lines" class="pl-2 text-blue-500 cursor-pointer" /> </router-link> <!-- New line that appears on hover or when the offer is clicked --> <div @click="markOffersAsReadStartingWith(offer.jwtId)" class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center" > <span class="inline-block w-8 h-px bg-gray-500 mr-2" /> Click to keep all above as new offers </div> </li> </ul> </div> <!-- Display a single row with the name of "New Offers To Your Projects" with a count. --> <div class="mt-4 flex justify-between" data-testId="showOffersToUserProjects" > <div> <span class="text-lg font-medium" >{{ newOffersToUserProjects.length }}{{ newOffersToUserProjectsHitLimit ? "+" : "" }}</span > <span class="text-lg font-medium ml-4" >New Offer{{ newOffersToUserProjects.length === 1 ? "" : "s" }} To Your Projects</span > <fa v-if="newOffersToUserProjects.length > 0" :icon=" showOffersToUserProjectsDetails ? 'chevron-down' : 'chevron-right' " class="cursor-pointer ml-4 mr-4 text-lg" @click="expandOffersToUserProjectsAndMarkRead()" /> </div> <router-link to="/recent-offers-to-user-projects" class="text-blue-500"> See all </router-link> </div> <div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4"> <ul class="list-disc ml-4"> <li v-for="offer in newOffersToUserProjects" :key="offer.jwtId" class="mt-4 relative group" > <span>{{ didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts) }}</span> offered <span v-if="offer.objectDescription">{{ offer.objectDescription }}</span >{{ offer.objectDescription && offer.amount ? ", and " : "" }} <span v-if="offer.amount">{{ displayAmount(offer.unit, offer.amount) }}</span> to <span>{{ offer.planName }}</span> <router-link :to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }" class="text-blue-500" > <fa icon="file-lines" class="pl-2 text-blue-500 cursor-pointer" /> </router-link> <!-- New line that appears on hover --> <div @click="markOffersToUserProjectsAsReadStartingWith(offer.jwtId)" class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center" > <span class="inline-block w-8 h-px bg-gray-500 mr-2" /> Click to keep all above as new offers </div> </li> </ul> </div> </section> </template> <script lang="ts"> import { Component, Vue } from "vue-facing-decorator"; import GiftedDialog from "../components/GiftedDialog.vue"; import QuickNav from "../components/QuickNav.vue"; import EntityIcon from "../components/EntityIcon.vue"; import { NotificationIface } from "../constants/app"; import { db, retrieveSettingsForActiveAccount, updateAccountSettings, } from "../db/index"; import { Contact } from "../db/tables/contacts"; import { didInfo, displayAmount, getNewOffersToUser, getNewOffersToUserProjects, OfferSummaryRecord, OfferToPlanSummaryRecord, } from "../libs/endorserServer"; import { retrieveAccountDids } from "../libs/util"; @Component({ components: { GiftedDialog, QuickNav, EntityIcon }, }) export default class NewActivityView extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; activeDid = ""; allContacts: Array<Contact> = []; allMyDids: string[] = []; apiServer = ""; lastAckedOfferToUserJwtId = ""; lastAckedOfferToUserProjectsJwtId = ""; newOffersToUser: Array<OfferSummaryRecord> = []; newOffersToUserHitLimit = false; newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = []; newOffersToUserProjectsHitLimit = false; showOffersDetails = false; showOffersToUserProjectsDetails = false; didInfo = didInfo; displayAmount = displayAmount; async created() { try { const settings = await retrieveSettingsForActiveAccount(); this.apiServer = settings.apiServer || ""; this.activeDid = settings.activeDid || ""; this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || ""; this.lastAckedOfferToUserProjectsJwtId = settings.lastAckedOfferToUserProjectsJwtId || ""; this.allContacts = await db.contacts.toArray(); this.allMyDids = await retrieveAccountDids(); const offersToUserData = await getNewOffersToUser( this.axios, this.apiServer, this.activeDid, this.lastAckedOfferToUserJwtId, ); this.newOffersToUser = offersToUserData.data; this.newOffersToUserHitLimit = offersToUserData.hitLimit; const offersToUserProjectsData = await getNewOffersToUserProjects( this.axios, this.apiServer, this.activeDid, this.lastAckedOfferToUserProjectsJwtId, ); this.newOffersToUserProjects = offersToUserProjectsData.data; this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit; // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { console.error("Error retrieving settings & contacts:", err); this.$notify( { group: "alert", type: "danger", title: "Error", text: err.message || "There was an error retrieving your activity.", }, 5000, ); } } async expandOffersToUserAndMarkRead() { this.showOffersDetails = !this.showOffersDetails; if (this.showOffersDetails) { await updateAccountSettings(this.activeDid, { 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( { group: "alert", type: "info", title: "Marked as Read", text: "The offers are marked as viewed. Click in the list to keep them as new.", }, 5000, ); } } async markOffersAsReadStartingWith(jwtId: string) { const index = this.newOffersToUser.findIndex( (offer) => offer.jwtId === jwtId, ); if (index !== -1 && index < this.newOffersToUser.length - 1) { // Set to the next offer's jwtId await updateAccountSettings(this.activeDid, { lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId, }); } else { // it's the last entry (or not found), so just keep it the same await updateAccountSettings(this.activeDid, { lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId, }); } this.$notify( { group: "alert", type: "info", title: "Marked as Unread", text: "All offers above that line are marked as unread.", }, 3000, ); } async expandOffersToUserProjectsAndMarkRead() { this.showOffersToUserProjectsDetails = !this.showOffersToUserProjectsDetails; if (this.showOffersToUserProjectsDetails) { await updateAccountSettings(this.activeDid, { 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( { group: "alert", type: "info", title: "Marked as Read", text: "The offers are now marked as viewed. Click in the list to keep them as new.", }, 5000, ); } } async markOffersToUserProjectsAsReadStartingWith(jwtId: string) { const index = this.newOffersToUserProjects.findIndex( (offer) => offer.jwtId === jwtId, ); if (index !== -1 && index < this.newOffersToUserProjects.length - 1) { // Set to the next offer's jwtId await updateAccountSettings(this.activeDid, { lastAckedOfferToUserProjectsJwtId: this.newOffersToUserProjects[index + 1].jwtId, }); } else { // it's the last entry (or not found), so just keep it the same await updateAccountSettings(this.activeDid, { lastAckedOfferToUserProjectsJwtId: this.lastAckedOfferToUserProjectsJwtId, }); } this.$notify( { group: "alert", type: "info", title: "Marked as Unread", text: "All offers above that line are marked as unread.", }, 3000, ); } } </script>