mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-20 23:21:06 +02:00
refactor: enhance announcements functionality to include expired announcements in hooks and components
This commit is contained in:
parent
7bc02ce55b
commit
7af4bc0b49
5 changed files with 55 additions and 32 deletions
|
|
@ -10,7 +10,7 @@ import { useAnnouncements } from "@/hooks/use-announcements";
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export default function AnnouncementsPage() {
|
||||
const { announcements, markAllRead } = useAnnouncements();
|
||||
const { announcements, markAllRead } = useAnnouncements({ includeExpired: true });
|
||||
|
||||
// Auto-mark all visible announcements as read when the page is opened
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { useAnnouncements } from "@/hooks/use-announcements";
|
|||
|
||||
export function AnnouncementsDialog() {
|
||||
const [open, setOpen] = useAtom(announcementsDialogAtom);
|
||||
const { announcements, markAllRead } = useAnnouncements();
|
||||
const { announcements, markAllRead } = useAnnouncements({ includeExpired: true });
|
||||
|
||||
// Auto-mark all visible announcements as read when the dialog opens
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from "@/lib/announcements/announcements-storage";
|
||||
import {
|
||||
getActiveAnnouncements,
|
||||
getPublishedAnnouncements,
|
||||
msUntilNextTransition,
|
||||
} from "@/lib/announcements/announcements-utils";
|
||||
import { isAuthenticated } from "@/lib/auth-utils";
|
||||
|
|
@ -56,10 +57,16 @@ export interface AnnouncementWithState extends Announcement {
|
|||
interface UseAnnouncementsOptions {
|
||||
/** Filter by category */
|
||||
category?: AnnouncementCategory;
|
||||
/**
|
||||
* Include announcements whose visibility window has expired (archive
|
||||
* views). Defaults to false so spotlights, toasts, and unread badges
|
||||
* only consider currently-active announcements.
|
||||
*/
|
||||
includeExpired?: boolean;
|
||||
}
|
||||
|
||||
export function useAnnouncements(options: UseAnnouncementsOptions = {}) {
|
||||
const { category } = options;
|
||||
const { category, includeExpired = false } = options;
|
||||
|
||||
// Subscribe to state changes (re-renders when localStorage state is bumped)
|
||||
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
||||
|
|
@ -71,12 +78,13 @@ export function useAnnouncements(options: UseAnnouncementsOptions = {}) {
|
|||
const enriched: AnnouncementWithState[] = useMemo(() => {
|
||||
const authed = isAuthenticated();
|
||||
const now = new Date();
|
||||
let items: AnnouncementWithState[] = getActiveAnnouncements(announcements, authed, now).map(
|
||||
(a) => ({
|
||||
...a,
|
||||
isRead: isAnnouncementRead(a.id),
|
||||
})
|
||||
);
|
||||
const visible = includeExpired
|
||||
? getPublishedAnnouncements(announcements, authed, now)
|
||||
: getActiveAnnouncements(announcements, authed, now);
|
||||
let items: AnnouncementWithState[] = visible.map((a) => ({
|
||||
...a,
|
||||
isRead: isAnnouncementRead(a.id),
|
||||
}));
|
||||
|
||||
if (category) {
|
||||
items = items.filter((a) => a.category === category);
|
||||
|
|
@ -86,7 +94,7 @@ export function useAnnouncements(options: UseAnnouncementsOptions = {}) {
|
|||
|
||||
return items;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [category, stateVersion, tick]);
|
||||
}, [category, includeExpired, stateVersion, tick]);
|
||||
|
||||
// Schedule a re-render when the next announcement starts or expires
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -54,28 +54,17 @@ export const announcements: Announcement[] = [
|
|||
url: "/changelog",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "announcement-1",
|
||||
title: "Introducing What's New",
|
||||
description: "All major product updates will be posted here.",
|
||||
category: "feature",
|
||||
date: "2026-02-17T00:00:00Z",
|
||||
startTime: "2026-02-17T00:00:00Z",
|
||||
endTime: "2026-02-20T00:00:00Z",
|
||||
audience: "all",
|
||||
isImportant: false,
|
||||
},
|
||||
{
|
||||
id: "announcement-6",
|
||||
title: "Past Test Announcement",
|
||||
description: "This should be seen by nobody, because it's in the past.",
|
||||
category: "maintenance",
|
||||
date: "2026-02-17T00:00:00Z",
|
||||
startTime: "2026-02-15T23:23:00Z",
|
||||
endTime: "2026-02-16T00:00:00Z",
|
||||
audience: "users",
|
||||
isImportant: true,
|
||||
},
|
||||
// {
|
||||
// id: "announcement-1",
|
||||
// title: "Introducing What's New",
|
||||
// description: "All major product updates will be posted here.",
|
||||
// category: "feature",
|
||||
// date: "2026-02-17T00:00:00Z",
|
||||
// startTime: "2026-02-17T00:00:00Z",
|
||||
// endTime: "2026-02-20T00:00:00Z",
|
||||
// audience: "all",
|
||||
// isImportant: false,
|
||||
// },
|
||||
// {
|
||||
// id: "2026-02-10-podcast-improvements",
|
||||
// title: "Podcast Generation Improvements",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,32 @@ export function announcementMatchesAudience(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the announcement has been published (startTime has
|
||||
* passed), regardless of whether its visibility window has expired.
|
||||
* Used for archive views like the announcements page.
|
||||
*/
|
||||
export function isAnnouncementPublished(announcement: Announcement, now = new Date()): boolean {
|
||||
const start = new Date(announcement.startTime).getTime();
|
||||
if (Number.isNaN(start)) return false;
|
||||
return now.getTime() >= start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements to all published ones (including expired) targeted
|
||||
* at the given audience. Powers archive views; use getActiveAnnouncements
|
||||
* for toasts, spotlights, and unread badges.
|
||||
*/
|
||||
export function getPublishedAnnouncements(
|
||||
announcements: Announcement[],
|
||||
isAuthenticated: boolean,
|
||||
now = new Date()
|
||||
): Announcement[] {
|
||||
return announcements.filter(
|
||||
(a) => isAnnouncementPublished(a, now) && announcementMatchesAudience(a, isAuthenticated)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements to only those that are currently active and
|
||||
* targeted at the given audience.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue