Extend read/dismissed functionality to all inbox item types

Approvals, failed runs, and join requests now have the same
unread dot + archive X pattern as issues in the Mine tab:
- Click the blue dot to mark as read, then X appears on hover
- Desktop: animated dismiss with scale/slide transition
- Mobile: swipe-to-archive via SwipeToArchive wrapper
- Dismissed items are filtered out of Mine tab
- Badge count excludes dismissed approvals and join requests
- localStorage-backed read/dismiss state for non-issue items

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-26 10:16:19 -05:00
parent 49c7fb7fbd
commit 2c406d3b8c
3 changed files with 327 additions and 40 deletions

View file

@ -12,6 +12,9 @@ import {
getRecentTouchedIssues,
loadDismissedInboxItems,
saveDismissedInboxItems,
loadReadInboxItems,
saveReadInboxItems,
READ_ITEMS_KEY,
} from "../lib/inbox";
const INBOX_ISSUE_STATUSES = "backlog,todo,in_progress,in_review,blocked,done";
@ -40,6 +43,30 @@ export function useDismissedInboxItems() {
return { dismissed, dismiss };
}
export function useReadInboxItems() {
const [readItems, setReadItems] = useState<Set<string>>(loadReadInboxItems);
useEffect(() => {
const handleStorage = (event: StorageEvent) => {
if (event.key !== READ_ITEMS_KEY) return;
setReadItems(loadReadInboxItems());
};
window.addEventListener("storage", handleStorage);
return () => window.removeEventListener("storage", handleStorage);
}, []);
const markRead = (id: string) => {
setReadItems((prev) => {
const next = new Set(prev);
next.add(id);
saveReadInboxItems(next);
return next;
});
};
return { readItems, markRead };
}
export function useInboxBadge(companyId: string | null | undefined) {
const { dismissed } = useDismissedInboxItems();