feat(notifications): TODO 7 — draft decision toast + email + bell badge

This commit is contained in:
Александр Зимин
2026-05-14 14:40:35 +00:00
parent 50d263745a
commit 9050e2427e
21 changed files with 1992 additions and 82 deletions
+34
View File
@@ -7,6 +7,7 @@ import {
type CascadePlan,
type ChangelogDiff,
type ChangelogResponse,
type NotificationsResponse,
type SchemaDraft,
type SchemaReviewQueuePage,
type DictionaryDefinition,
@@ -804,3 +805,36 @@ export const serverVersionQuery = queryOptions({
})
export const useServerVersion = () => useQuery(serverVersionQuery)
// === Notifications (TODO 7: draft decision toast + email) ===
/**
* Current user's notifications feed. Backend filters by recipient = sub claim;
* returns paginated rows + `totalUnread` для bell badge counter.
*
* <p>Poll каждые 30s — same cadence как /reviews queue. {@code staleTime} 5s
* чтобы query cache не моргал когда user открывает/закрывает drawer.
* {@code refetchIntervalInBackground} = true чтобы tab без focus всё равно
* decremented badge когда mark-as-read fires.
*
* <p>Caveat: `unreadOnly=true` gives a tight badge query (1 unread → badge "1"),
* full feed query (default) used drawer открыт. Splitting queries keeps badge
* fetch cheap when drawer закрыт.
*/
export const myNotificationsQuery = (unreadOnly = false, limit = 20) =>
queryOptions({
queryKey: ['notifications', 'me', { unreadOnly, limit }] as const,
queryFn: async (): Promise<NotificationsResponse> => {
const { data } = await apiClient.get<NotificationsResponse>(
'/me/notifications',
{ params: { unreadOnly, limit } },
)
return data
},
refetchInterval: 30_000,
refetchIntervalInBackground: true,
staleTime: 5_000,
})
export const useMyNotifications = (unreadOnly = false, limit = 20) =>
useQuery(myNotificationsQuery(unreadOnly, limit))