import { Bookmark } from '../hooks/useBookmarks'; export type BookmarkSort = 'newest' | 'oldest' | 'room'; const BOOKMARK_SORTS: readonly BookmarkSort[] = ['newest', 'oldest', 'room']; /** Type guard for persisted/untrusted sort values (localStorage can hold stale/corrupt data). */ export function isBookmarkSort(value: unknown): value is BookmarkSort { return typeof value === 'string' && (BOOKMARK_SORTS as readonly string[]).includes(value); } export type BookmarkRoomGroup = { roomId: string; roomName: string; items: Bookmark[]; }; // Deterministic newest-first comparator: most recently saved first, ties broken // by eventId so the order is stable regardless of input order (keeps tests and // re-renders from shuffling equal-timestamp entries). const byNewest = (a: Bookmark, b: Bookmark): number => b.savedAt - a.savedAt || (a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0); /** * Return a new array of bookmarks ordered per the selected sort. `room` has no * flat ordering of its own (grouping is a render concern) and falls back to * newest-first so callers can still render a sensible flat list if they want. */ export function sortBookmarks(bookmarks: Bookmark[], sort: BookmarkSort): Bookmark[] { const copy = [...bookmarks]; if (sort === 'oldest') { // Oldest-first is the reverse ordering; keep the same eventId tie-break shape. return copy.sort( (a, b) => a.savedAt - b.savedAt || (a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0), ); } return copy.sort(byNewest); } /** * Bucket bookmarks by room. Items within a group are newest-first; groups are * ordered by each group's most-recently-saved bookmark (active rooms float up), * with the room's stored name (the panel overrides with the live name when the * room is joined). Deterministic tie-breaks keep the output stable. */ export function groupBookmarksByRoom(bookmarks: Bookmark[]): BookmarkRoomGroup[] { const groups = new Map(); bookmarks.forEach((bk) => { const existing = groups.get(bk.roomId); if (existing) { existing.items.push(bk); } else { groups.set(bk.roomId, { roomId: bk.roomId, roomName: bk.roomName, items: [bk] }); } }); const result = [...groups.values()]; result.forEach((group) => group.items.sort(byNewest)); // Order groups by their newest item's savedAt (desc); tie-break by roomId. result.sort((a, b) => { const diff = b.items[0].savedAt - a.items[0].savedAt; if (diff !== 0) return diff; return a.roomId < b.roomId ? -1 : a.roomId > b.roomId ? 1 : 0; }); return result; }