Compare commits

..

8 Commits

Author SHA1 Message Date
Jose Olarte III
a9d9df32e1 WIP: QuickNav notification badges
- Cleanup of unused mockups
- Minor tweaks
2025-09-26 21:48:53 +08:00
Jose Olarte III
e655082af6 WIP: sticky tabs 2025-09-25 21:54:13 +08:00
Jose Olarte III
ea2fa30903 WIP: alternative notification UI 2025-09-24 21:19:22 +08:00
Jose Olarte III
39dbbb08f7 WIP: HomeView notification badge 2025-09-22 22:25:38 +08:00
Jose Olarte III
eb21d3c247 WIP: notification system adjustments
- Re-organize tabs
- Remove unneeded "Unread only" toggle (limiting functionality to chronological isUnread)
- Added "read line"
2025-09-19 23:41:03 +08:00
Jose Olarte III
213f5f0555 Merge branch 'master' into notification-system 2025-09-19 16:38:15 +08:00
Jose Olarte III
2db2c39830 WIP: notification view improvements
- Notification count badge per tab
- "Unread only" filter toggle
- Notification dot size adjustment
2025-09-17 22:13:34 +08:00
Jose Olarte III
106cefab51 WIP: notification system redesign
- Tabbed interface to expand the view's capabilities
- Added controls for managing notifications individually or in bulk
- Streamlined list design for increased information density
2025-09-15 21:43:39 +08:00
10 changed files with 329 additions and 246 deletions

View File

@@ -293,7 +293,7 @@ const inputImageFileNameRef = ref<Blob>();
export default class ImageMethodDialog extends Vue {
$notify!: NotifyFunction;
$router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
notify = createNotifyHelpers(this.$notify);
/** Active DID for user authentication */
activeDid = "";
@@ -498,9 +498,6 @@ export default class ImageMethodDialog extends Vue {
* @throws {Error} When settings retrieval fails
*/
async mounted() {
// Initialize notification helpers
this.notify = createNotifyHelpers(this.$notify);
try {
// Get activeDid from active_identity table (single source of truth)
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -14,11 +14,20 @@
'text-slate-500': selected !== 'Home',
}"
>
<router-link :to="{ name: 'home' }" class="block text-center py-2 px-1">
<router-link
:to="{ name: 'home' }"
class="relative block text-center py-2 px-1"
>
<div class="flex flex-col items-center">
<font-awesome icon="house-chimney" class="fa-fw" />
<span class="text-xs mt-1">feed</span>
</div>
<!-- Notification dot - show while the user has unread notifications -->
<font-awesome
icon="circle"
class="absolute left-1/2 top-1 translate-x-2 text-rose-500 text-[10px] border border-white rounded-full"
></font-awesome>
</router-link>
</li>
<!-- Search -->
@@ -89,7 +98,7 @@
>
<router-link
:to="{ name: 'account' }"
class="block text-center py-2 px-1"
class="relative block text-center py-2 px-1"
>
<div class="flex flex-col items-center">
<font-awesome icon="circle-user" class="fa-fw" />
@@ -102,6 +111,12 @@
-->
<span class="text-xs mt-1">profile</span>
</div>
<!-- Notification dot - show while the user has not yet backed up their seed phrase -->
<font-awesome
icon="circle"
class="absolute left-1/2 top-1 translate-x-2 text-rose-500 text-[10px] border border-white rounded-full"
></font-awesome>
</router-link>
</li>
</ul>

View File

@@ -80,6 +80,7 @@ import {
faQuestion,
faRightFromBracket,
faRotate,
faScroll,
faShareNodes,
faSpinner,
faSquare,
@@ -169,6 +170,7 @@ library.add(
faQrcode,
faQuestion,
faRotate,
faScroll,
faRightFromBracket,
faShareNodes,
faSpinner,

View File

@@ -394,7 +394,7 @@ export default class HelpNotificationsView extends Vue {
notifyingReminderTime = "";
// Notification helper system
notify!: ReturnType<typeof createNotifyHelpers>;
notify = createNotifyHelpers(this.$notify);
/**
* Computed property for consistent button styling
@@ -430,9 +430,6 @@ export default class HelpNotificationsView extends Vue {
* Handles errors gracefully with proper logging without exposing sensitive data.
*/
async mounted() {
// Initialize notification helpers
this.notify = createNotifyHelpers(this.$notify);
try {
const registration = await navigator.serviceWorker?.ready;
const fullSub = await registration?.pushManager.getSubscription();

View File

@@ -80,53 +80,51 @@ Raymer * @version 1.0.0 */
</router-link>
</div>
<div class="mb-8">
<!--
They should have an identifier, even if it's an auto-generated one that they'll never use.
Identity creation is now handled by router navigation guard.
-->
<div class="mb-4">
<RegistrationNotice
v-if="!isUserRegistered"
:passkeys-enabled="PASSKEYS_ENABLED"
:given-name="givenName"
message="To share, someone must register you."
/>
<!--
They should have an identifier, even if it's an auto-generated one that they'll never use.
Identity creation is now handled by router navigation guard.
-->
<div class="mb-6">
<RegistrationNotice
v-if="!isUserRegistered"
:passkeys-enabled="PASSKEYS_ENABLED"
:given-name="givenName"
message="To share, someone must register you."
/>
<div v-if="isUserRegistered" id="sectionRecordSomethingGiven">
<!-- Record Quick-Action -->
<div class="mb-6">
<div class="flex gap-2 items-center mb-2">
<h2 class="text-xl font-bold">Record something given by:</h2>
<button
class="block ms-auto text-center text-white bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-2 rounded-full"
@click="openGiftedPrompts()"
>
<font-awesome
icon="lightbulb"
class="block text-center w-[1em]"
/>
</button>
</div>
<div v-if="isUserRegistered" id="sectionRecordSomethingGiven">
<!-- Record Quick-Action -->
<div class="bg-slate-200 rounded-lg overflow-hidden p-3 pt-2.5">
<div class="flex gap-2 items-center mb-2">
<h2 class="font-bold">Record something given by:</h2>
<button
class="block ms-auto text-center text-white bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-1.5 rounded-full"
@click="openGiftedPrompts()"
>
<font-awesome
icon="lightbulb"
class="block text-center text-sm w-[1em]"
/>
</button>
</div>
<div class="grid grid-cols-2 gap-2">
<button
type="button"
class="text-center text-base uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-2 rounded-lg"
@click="openPersonDialog()"
>
<font-awesome icon="user" />
Person
</button>
<button
type="button"
class="text-center text-base uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-2 rounded-lg"
@click="openProjectDialog()"
>
<font-awesome icon="folder-open" />
Project
</button>
</div>
<div class="grid grid-cols-2 gap-2">
<button
type="button"
class="text-center text-base uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-2 rounded-md"
@click="openPersonDialog()"
>
<font-awesome icon="user" />
Person
</button>
<button
type="button"
class="text-center text-base uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-2 rounded-md"
@click="openProjectDialog()"
>
<font-awesome icon="folder-open" />
Project
</button>
</div>
</div>
</div>
@@ -138,74 +136,90 @@ Raymer * @version 1.0.0 */
:recipient-entity-type="'person'"
/>
<GiftedPrompts ref="giftedPrompts" />
<FeedFilters ref="feedFilters" />
<!-- Results List -->
<div class="mt-4 mb-4">
<div class="flex gap-2 items-center mb-3">
<h2 class="text-xl font-bold">Latest Activity</h2>
<!-- ALTERNATIVE UI: Feed + Notification Tabs -->
<div
class="sticky top-0 z-50 grid grid-cols-5 text-xl sm:text-2xl pt-4 pb-1 px-1 -mt-3 -mx-1 mb-4 bg-white rounded-b-[10px]"
>
<button
v-if="resultsAreFiltered()"
class="block ms-auto text-center text-white bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-2 rounded-full"
@click="openFeedFilters()"
class="relative text-center bg-slate-400 text-white px-1 pt-3 pb-2 first:rounded-s-md last:rounded-e-md border-r border-slate-300 last:border-r-0 leading-none"
>
<font-awesome
icon="filter"
class="block text-center w-[1em] translate-y-[0.05em]"
/>
<font-awesome icon="scroll" />
<div class="text-xs sm:text-sm mt-1">activity</div>
</button>
<button
v-else
class="block ms-auto text-center text-white bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-2 rounded-full"
@click="openFeedFilters()"
class="relative text-center bg-slate-200 text-slate-500 px-1 pt-3 pb-2 first:rounded-s-md last:rounded-e-md border-r border-slate-300 last:border-r-0 leading-none"
>
<font-awesome
icon="filter"
class="block text-center w-[1em] translate-y-[0.05em]"
/>
<font-awesome icon="hand-holding-heart" />
<div class="text-xs sm:text-sm mt-1">offers</div>
<!-- Unread count -->
<span
class="absolute -top-2 -translate-x-1/2 bg-rose-500 text-white border border-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em]"
>2</span
>
</button>
<button
class="relative text-center bg-slate-200 text-slate-500 px-1 pt-3 pb-2 first:rounded-s-md last:rounded-e-md border-r border-slate-300 last:border-r-0 leading-none"
>
<font-awesome icon="folder-open" />
<div class="text-xs sm:text-sm mt-1">projects</div>
<!-- Unread count -->
<span
class="absolute -top-2 -translate-x-1/2 bg-rose-500 text-white border border-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em]"
>50+</span
>
</button>
<button
class="relative text-center bg-slate-200 text-slate-500 px-1 pt-3 pb-2 first:rounded-s-md last:rounded-e-md border-r border-slate-300 last:border-r-0 leading-none"
>
<font-awesome icon="users" />
<div class="text-xs sm:text-sm mt-1">people</div>
<!-- Unread count -->
<span
class="absolute -top-2 -translate-x-1/2 bg-rose-500 text-white border border-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em]"
>4</span
>
</button>
<button
class="relative text-center bg-slate-200 text-slate-500 px-1 pt-3 pb-2 first:rounded-s-md last:rounded-e-md border-r border-slate-300 last:border-r-0 leading-none"
>
<font-awesome icon="image" />
<div class="text-xs sm:text-sm mt-1">items</div>
<!-- Unread count -->
<span
class="absolute -top-2 -translate-x-1/2 bg-rose-500 text-white border border-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em]"
>7</span
>
</button>
</div>
<div
class="border-t p-2 border-slate-300"
@click="goToActivityToUserPage()"
>
<div class="flex justify-center">
<div
v-if="numNewOffersToUser"
class="bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] m-1 px-4 py-4 rounded-md text-white"
>
<span
class="block text-center text-6xl"
data-testId="newDirectOffersActivityNumber"
>
{{ numNewOffersToUser }}{{ newOffersToUserHitLimit ? "+" : "" }}
</span>
<p class="text-center">
new offer{{ numNewOffersToUser === 1 ? "" : "s" }} to you
</p>
</div>
<div
v-if="numNewOffersToUserProjects"
class="bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] m-1 px-4 py-4 rounded-md text-white"
>
<span
class="block text-center text-6xl"
data-testId="newOffersToUserProjectsActivityNumber"
>
{{ numNewOffersToUserProjects
}}{{ newOffersToUserProjectsHitLimit ? "+" : "" }}
</span>
<p class="text-center">
new offer{{ numNewOffersToUserProjects === 1 ? "" : "s" }} to your
projects
</p>
</div>
</div>
<div class="flex justify-end mt-2">
<button class="text-blue-500">View All New Activity For You</button>
</div>
<div class="flex gap-2 items-center justify-between mb-2 text-sm">
<h2 class="text-base font-bold">Latest Activity</h2>
<button
v-if="resultsAreFiltered()"
class="flex items-center justify-end gap-2 bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1 rounded"
@click="openFeedFilters()"
>
Filter
<font-awesome icon="filter"></font-awesome>
</button>
<button
v-else
class="flex items-center justify-end gap-2 bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1 rounded"
@click="openFeedFilters()"
>
Filter
<font-awesome icon="filter"></font-awesome>
</button>
</div>
<FeedFilters ref="feedFilters" />
<InfiniteScroll @reached-bottom="loadMoreGives">
<ul id="listLatestActivity" class="space-y-4">
<ActivityListItem

View File

@@ -3,108 +3,198 @@
<!-- 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">
<div class="mb-2">
<h1 class="text-2xl text-center font-semibold relative px-7">
<!-- Back -->
<font-awesome
icon="chevron-left"
class="fa-fw text-lg text-center px-2 py-1 absolute -left-2 -top-1"
class="fa-fw text-lg text-center px-2 py-1 absolute -left-2 top-[0.2em]"
@click="$router.back()"
/>
New Activity For You
Notifications
</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
>
<font-awesome
v-if="newOffersToUser.length > 0"
:icon="showOffersDetails ? 'chevron-down' : 'chevron-right'"
class="cursor-pointer ml-4 mr-4 text-lg"
@click="expandOffersToUserAndMarkRead()"
/>
</div>
<a class="text-blue-500 cursor-pointer" @click="handleSeeAllOffersToUser">
See&nbsp;all
</a>
</div>
<!-- Main Tabs -->
<div class="text-center text-slate-500 border-b border-slate-300 mt-4 mb-2">
<ul class="flex flex-wrap justify-center gap-4 -mb-px">
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 active text-black border-black font-semibold"
>
Offers
</a>
<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"
<!-- Unread count -->
<span
class="inline-block bg-rose-500 text-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em] -me-1.5 -mt-[2px]"
>3</span
>
<font-awesome
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
class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center"
@click="markOffersAsReadStartingWith(offer.jwtId)"
</li>
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 text-blue-600 border-transparent hover:border-slate-400"
>
<span class="inline-block w-8 h-px bg-gray-500 mr-2" />
Click to keep all above as new offers
</div>
Projects
</a>
<!-- Unread count -->
<span
class="inline-block bg-rose-500 text-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em] -me-1.5 -mt-[2px]"
>9+</span
>
</li>
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 text-blue-600 border-transparent hover:border-slate-400"
>
People
</a>
</li>
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 text-blue-600 border-transparent hover:border-slate-400"
>
Items
</a>
</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
<!-- Sub Tabs - Offers -->
<div class="text-center text-slate-500 border-b border-slate-300 mb-2">
<ul class="flex flex-wrap justify-center gap-4 text-sm -mb-px">
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 active text-black border-black font-semibold"
>
To You
</a>
<!-- Unread count -->
<span
class="inline-block bg-rose-500 text-white text-xs font-semibold leading-none rounded-full px-[0.4em] py-[0.15em] -me-1.5 -mt-[2px]"
>2</span
>
</li>
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 text-blue-600 border-transparent hover:border-slate-400"
>
Your Projects
</a>
</li>
<li class="flex items-center gap-[0.175em]">
<a
href="#"
class="inline-block py-2 rounded-t-lg border-b-2 text-blue-600 border-transparent hover:border-slate-400"
>
Favorites
</a>
</li>
</ul>
</div>
<!-- Offers to You -->
<div v-if="showOffersDetails" class="mt-4">
<div class="flex justify-end items-center text-sm mb-2">
<a
href="#"
class="flex items-center justify-end gap-2 bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-1 rounded"
>
<span class="text-lg font-medium ml-4"
>New Offer{{ newOffersToUserProjects.length === 1 ? "" : "s" }} To
Your Projects</span
>
<font-awesome
v-if="newOffersToUserProjects.length > 0"
:icon="
showOffersToUserProjectsDetails ? 'chevron-down' : 'chevron-right'
"
class="cursor-pointer ml-4 mr-4 text-lg"
@click="expandOffersToUserProjectsAndMarkRead()"
/>
Mark all as read
<font-awesome icon="check"></font-awesome>
</a>
</div>
<a
class="text-blue-500 cursor-pointer"
@click="handleSeeAllOffersToUserProjects"
>
See&nbsp;all
</a>
<ul class="text-sm border-t border-slate-300">
<li
v-for="offer in newOffersToUser"
:key="offer.jwtId"
class="flex justify-between items-center gap-4 border-b border-slate-300 py-2"
>
<router-link
:to="{ path: '/claim/' + encodeURIComponent(offer.jwtId) }"
class="block"
>
<span class="font-semibold">{{
didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts)
}}</span>
offered
<span
v-if="offer.objectDescription"
class="font-semibold text-blue-600"
>{{ offer.objectDescription }}</span
>{{ offer.objectDescription && offer.amount ? " and " : "" }}
<span v-if="offer.amount" class="font-semibold text-blue-600">{{
displayAmount(offer.unit, offer.amount)
}}</span>
</router-link>
<!-- Unread indicator -->
<font-awesome
icon="circle"
class="text-rose-500 text-[8px] border border-rose-500 rounded-full"
></font-awesome>
</li>
<!-- Sample read item -->
<li class="border-b border-slate-300 py-2">
<!-- Last viewed separator -->
<div
class="border-t border-dashed border-slate-300 text-orange-400 mt-4 mb-2 font-bold text-sm"
>
<span class="block w-fit mx-auto -mt-2.5 bg-white px-2">
You've already seen all the following
</span>
<hr class="border-slate-300 mt-4" />
</div>
<div class="flex justify-between items-center gap-4">
<!-- Notification details -->
<a href="#" class="block text-slate-400">
<span class="font-semibold">User One</span>
offered
<span class="font-semibold">Sample read notification item</span>
and
<span class="font-semibold">50 USD</span>
</a>
<!-- Read indicator -->
<font-awesome
icon="circle"
class="text-transparent text-[8px] border border-slate-300 rounded-full"
></font-awesome>
</div>
</li>
<!-- Sample read item -->
<li class="border-b border-slate-300 py-2">
<div class="flex justify-between items-center gap-4">
<!-- Notification details -->
<a href="#" class="block text-slate-400">
<span class="font-semibold">User One</span>
offered
<span class="font-semibold">Sample read notification item</span>
and
<span class="font-semibold">50 USD</span>
</a>
<!-- Read indicator -->
<font-awesome
icon="circle"
class="text-transparent text-[8px] border border-slate-300 rounded-full"
></font-awesome>
</div>
</li>
</ul>
</div>
<div v-if="showOffersToUserProjectsDetails" class="ml-4 mt-4">
@@ -194,7 +284,7 @@ export default class NewActivityView extends Vue {
newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = [];
newOffersToUserProjectsHitLimit = false;
showOffersDetails = false;
showOffersDetails = true;
showOffersToUserProjectsDetails = false;
didInfo = didInfo;
displayAmount = displayAmount;
@@ -249,7 +339,7 @@ export default class NewActivityView extends Vue {
async expandOffersToUserAndMarkRead() {
this.showOffersDetails = !this.showOffersDetails;
if (this.showOffersDetails && this.newOffersToUser.length > 0) {
if (this.showOffersDetails) {
await this.$updateSettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
});
@@ -286,10 +376,7 @@ export default class NewActivityView extends Vue {
async expandOffersToUserProjectsAndMarkRead() {
this.showOffersToUserProjectsDetails =
!this.showOffersToUserProjectsDetails;
if (
this.showOffersToUserProjectsDetails &&
this.newOffersToUserProjects.length > 0
) {
if (this.showOffersToUserProjectsDetails) {
await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId,
@@ -297,7 +384,7 @@ export default class NewActivityView extends Vue {
// 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 marked as viewed. Click in the list to keep them as new.",
"The offers are now marked as viewed. Click in the list to keep them as new.",
TIMEOUTS.LONG,
);
}
@@ -325,13 +412,5 @@ export default class NewActivityView extends Vue {
TIMEOUTS.STANDARD,
);
}
async handleSeeAllOffersToUser() {
this.$router.push("/recent-offers-to-user");
}
async handleSeeAllOffersToUserProjects() {
this.$router.push("/recent-offers-to-user-projects");
}
}
</script>

View File

@@ -99,7 +99,7 @@ export default class QuickActionBvcBeginView extends Vue {
$router!: Router;
// Notification helper system
private notify!: ReturnType<typeof createNotifyHelpers>;
private notify = createNotifyHelpers(this.$notify);
attended = true;
gaveTime = true;
@@ -111,9 +111,6 @@ export default class QuickActionBvcBeginView extends Vue {
* Uses America/Denver timezone for Bountiful location
*/
async mounted() {
// Initialize notification helpers
this.notify = createNotifyHelpers(this.$notify);
logger.debug(
"[QuickActionBvcBeginView] Mounted - calculating meeting date",
);

View File

@@ -32,20 +32,20 @@
</div>
<InfiniteScroll @reached-bottom="loadMoreOffersToUserProjects">
<ul data-testId="listRecentOffersToUserProjects">
<ul
data-testId="listRecentOffersToUserProjects"
class="border-t border-slate-300"
>
<li
v-for="offer in newOffersToUserProjects"
:key="offer.jwtId"
class="mt-4 relative group"
>
<!-- Last viewed separator -->
<div
v-if="offer.jwtId == lastAckedOfferToUserProjectsJwtId"
class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
class="border-b border-slate-300 text-orange-400 pb-2 mb-2 font-bold text-sm"
>
<span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
You've already seen all the following
</span>
You've already seen all the following
</div>
<span>{{
@@ -147,14 +147,6 @@ export default class RecentOffersToUserView extends Vue {
this.newOffersToUserProjects = offersToUserProjectsData.data;
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,
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
logger.error("Error retrieving settings & contacts:", err);

View File

@@ -27,20 +27,20 @@
</p>
</div>
<InfiniteScroll @reached-bottom="loadMoreOffersToUser">
<ul data-testId="listRecentOffersToUser">
<ul
data-testId="listRecentOffersToUser"
class="border-t border-slate-300"
>
<li
v-for="offer in newOffersToUser"
:key="offer.jwtId"
class="mt-4 relative group"
>
<!-- Last viewed separator -->
<div
v-if="offer.jwtId == lastAckedOfferToUserJwtId"
class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
class="border-b border-slate-300 text-orange-400 pb-2 mb-2 font-bold text-sm"
>
<span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
You've already seen all the following
</span>
You've already seen all the following
</div>
<span>{{
@@ -138,13 +138,6 @@ export default class RecentOffersToUserView extends Vue {
this.newOffersToUser = offersToUserData.data;
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,
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
logger.error("Error retrieving settings & contacts:", err);

View File

@@ -162,7 +162,7 @@ export default class SeedBackupView extends Vue {
showSeed = false;
// Notification helper system
notify!: ReturnType<typeof createNotifyHelpers>;
notify = createNotifyHelpers(this.$notify);
/**
* Computed property for consistent copy feedback styling
@@ -204,9 +204,6 @@ export default class SeedBackupView extends Vue {
* Handles errors gracefully with user notifications.
*/
async created() {
// Initialize notification helpers
this.notify = createNotifyHelpers(this.$notify);
try {
let activeDid = "";