From 57f41c1dd28158300ab4c4afcf46233d319a9774 Mon Sep 17 00:00:00 2001 From: Michael Genson Date: Sat, 28 Mar 2026 18:45:21 +0000 Subject: [PATCH] refactor to simplify saving/state logic --- .../Announcement/AnnouncementDialog.vue | 45 +++++----------- frontend/composables/use-announcements.ts | 54 +++++++++++++++++-- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/frontend/components/Domain/Announcement/AnnouncementDialog.vue b/frontend/components/Domain/Announcement/AnnouncementDialog.vue index 1e2c4c683..0866a38d3 100644 --- a/frontend/components/Domain/Announcement/AnnouncementDialog.vue +++ b/frontend/components/Domain/Announcement/AnnouncementDialog.vue @@ -22,7 +22,7 @@ :key="announcement.key" :active="currentAnnouncement.key === announcement.key" rounded - @click="currentAnnouncement = announcement" + @click="setCurrentAnnouncement(announcement)" > {{ announcement.meta?.title }} @@ -73,13 +73,10 @@ diff --git a/frontend/composables/use-announcements.ts b/frontend/composables/use-announcements.ts index fad6db3d3..60dc73651 100644 --- a/frontend/composables/use-announcements.ts +++ b/frontend/composables/use-announcements.ts @@ -1,5 +1,6 @@ import { useHouseholdSelf } from "~/composables/use-households"; import { useGroupSelf } from "~/composables/use-groups"; +import { useUserApi } from "~/composables/api"; export type AnnouncementMeta = { title: string | undefined; @@ -34,8 +35,13 @@ const allAnnouncements: Announcement[] = Object.entries(_announcementsUnsorted) const newAnnouncements = shallowRef([]); +function isWelcomeAnnouncement(key: string) { + return key === allAnnouncements.at(0)!.key; +} + export function useAnnouncements() { const auth = useMealieAuth(); + const api = useUserApi(); const { household } = useHouseholdSelf(); const { group } = useGroupSelf(); @@ -48,7 +54,38 @@ export function useAnnouncements() { ), ); - function refreshUnreadAnnouncements() { + function updateUnreadAnnouncements(lastReadKey: string) { + newAnnouncements.value = allAnnouncements.filter(a => a.key > lastReadKey); + } + + async function setLastRead(key: string) { + const user = auth.user.value!; + + if (!user.lastReadAnnouncement && isWelcomeAnnouncement(key)) { + // The welcome announcement is a special case: it's shown to new users and + // all other announcements are marked as read when they view it + key = allAnnouncements.at(-1)!.key; + } + + if (user.lastReadAnnouncement && key <= user.lastReadAnnouncement) { + // Don't update the last read announcement if it's older than the current one + return; + } + + updateUnreadAnnouncements(key); + + user.lastReadAnnouncement = key; // update immediately so we don't have to wait for the db + await api.users.updateOne( + user.id, + { + ...user, + lastReadAnnouncement: "2026-03-28_1_new-recipe-import-options", // TODO: switch back to key + }, + { suppressAlert: true }, + ); + } + + function initUnreadAnnouncements() { const user = auth.user.value; // Only logged-in users can see announcements @@ -64,17 +101,26 @@ export function useAnnouncements() { } // Return all announcements newer than the last read announcement - newAnnouncements.value = allAnnouncements.filter(a => a.key > user.lastReadAnnouncement!); + updateUnreadAnnouncements(user.lastReadAnnouncement); } - refreshUnreadAnnouncements(); + initUnreadAnnouncements(); + + // If the user changes, re-init + let lastUserId = auth.user.value?.id; watch(auth.user, () => { - refreshUnreadAnnouncements(); + if (auth.user.value?.id === lastUserId) { + return; + } + + lastUserId = auth.user.value?.id; + initUnreadAnnouncements(); }); return { announcementsEnabled, newAnnouncements, allAnnouncements, + setLastRead, }; }