@ttt-productions/notification-core 0.15.16 → 0.15.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/react/components/NotificationEmptyState.d.ts +0 -3
- package/dist/react/components/NotificationEmptyState.d.ts.map +1 -1
- package/dist/react/components/NotificationEmptyState.js +0 -3
- package/dist/react/components/NotificationEmptyState.js.map +1 -1
- package/dist/react/components/NotificationHistoryList.d.ts +1 -7
- package/dist/react/components/NotificationHistoryList.d.ts.map +1 -1
- package/dist/react/components/NotificationHistoryList.js +4 -9
- package/dist/react/components/NotificationHistoryList.js.map +1 -1
- package/dist/react/components/NotificationList.d.ts +1 -4
- package/dist/react/components/NotificationList.d.ts.map +1 -1
- package/dist/react/components/NotificationList.js +26 -38
- package/dist/react/components/NotificationList.js.map +1 -1
- package/dist/react/components/NotificationUnreadBadge.d.ts +0 -5
- package/dist/react/components/NotificationUnreadBadge.d.ts.map +1 -1
- package/dist/react/components/NotificationUnreadBadge.js +1 -6
- package/dist/react/components/NotificationUnreadBadge.js.map +1 -1
- package/dist/react/components/relative-time.d.ts +0 -3
- package/dist/react/components/relative-time.d.ts.map +1 -1
- package/dist/react/components/relative-time.js +0 -3
- package/dist/react/components/relative-time.js.map +1 -1
- package/dist/react/hooks/useActiveNotifications.d.ts +3 -7
- package/dist/react/hooks/useActiveNotifications.d.ts.map +1 -1
- package/dist/react/hooks/useActiveNotifications.js +3 -8
- package/dist/react/hooks/useActiveNotifications.js.map +1 -1
- package/dist/react/hooks/useArchiveAllNotifications.d.ts +0 -30
- package/dist/react/hooks/useArchiveAllNotifications.d.ts.map +1 -1
- package/dist/react/hooks/useArchiveAllNotifications.js +15 -45
- package/dist/react/hooks/useArchiveAllNotifications.js.map +1 -1
- package/dist/react/hooks/useArchiveNotification.d.ts +0 -8
- package/dist/react/hooks/useArchiveNotification.d.ts.map +1 -1
- package/dist/react/hooks/useArchiveNotification.js +0 -8
- package/dist/react/hooks/useArchiveNotification.js.map +1 -1
- package/dist/react/hooks/useNotificationHistory.d.ts +3 -17
- package/dist/react/hooks/useNotificationHistory.d.ts.map +1 -1
- package/dist/react/hooks/useNotificationHistory.js +3 -20
- package/dist/react/hooks/useNotificationHistory.js.map +1 -1
- package/dist/react/hooks/useUnreadCount.d.ts +0 -22
- package/dist/react/hooks/useUnreadCount.d.ts.map +1 -1
- package/dist/react/hooks/useUnreadCount.js +2 -25
- package/dist/react/hooks/useUnreadCount.js.map +1 -1
- package/dist/react/index.d.ts +10 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +10 -5
- package/dist/react/index.js.map +1 -1
- package/dist/react/types.d.ts +30 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +2 -0
- package/dist/react/types.js.map +1 -0
- package/dist/schemas.d.ts +0 -22
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +0 -25
- package/dist/schemas.js.map +1 -1
- package/dist/server/activeNotificationId.d.ts +1 -22
- package/dist/server/activeNotificationId.d.ts.map +1 -1
- package/dist/server/activeNotificationId.js +0 -16
- package/dist/server/activeNotificationId.js.map +1 -1
- package/dist/server/delivery-ledger.d.ts +1 -73
- package/dist/server/delivery-ledger.d.ts.map +1 -1
- package/dist/server/delivery-ledger.js +13 -63
- package/dist/server/delivery-ledger.js.map +1 -1
- package/dist/server/errors.d.ts +0 -9
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +1 -11
- package/dist/server/errors.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/observed-generation.d.ts +1 -41
- package/dist/server/observed-generation.d.ts.map +1 -1
- package/dist/server/observed-generation.js +1 -31
- package/dist/server/observed-generation.js.map +1 -1
- package/dist/server/processBatchHelper.d.ts +0 -19
- package/dist/server/processBatchHelper.d.ts.map +1 -1
- package/dist/server/processBatchHelper.js +5 -51
- package/dist/server/processBatchHelper.js.map +1 -1
- package/dist/server/types.d.ts +0 -9
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +0 -4
- package/dist/server/types.js.map +1 -1
- package/dist/types.d.ts +2 -266
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +1 -1
- package/package.json +5 -5
- package/src/styles/notifications.css +0 -14
- package/dist/react/components/index.d.ts +0 -5
- package/dist/react/components/index.d.ts.map +0 -1
- package/dist/react/components/index.js +0 -5
- package/dist/react/components/index.js.map +0 -1
- package/dist/react/hooks/index.d.ts +0 -6
- package/dist/react/hooks/index.d.ts.map +0 -1
- package/dist/react/hooks/index.js +0 -6
- package/dist/react/hooks/index.js.map +0 -1
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { paginatedPageKey } from '@ttt-productions/query-core';
|
|
3
|
-
import { useFirestorePaginated, useReadRetryState } from '@ttt-productions/query-core/react';
|
|
2
|
+
import { paginatedPageKey, useFirestorePaginated, useReadRetryState, } from '@ttt-productions/query-core/react';
|
|
4
3
|
import { orderBy, where } from 'firebase/firestore';
|
|
5
4
|
const DEFAULT_PAGE_SIZE = 20;
|
|
6
5
|
const DEFAULT_REFETCH_INTERVAL = 30_000;
|
|
7
6
|
const DEFAULT_STALE_TIME = 30_000;
|
|
8
|
-
/**
|
|
9
|
-
* Paginated read of active notifications. The displayed page is re-read every
|
|
10
|
-
* `refetchInterval` ms while mounted — polling, not a realtime listener, for cost.
|
|
11
|
-
*/
|
|
12
7
|
export function useActiveNotifications({ config, userId, category, enabled = true, pageSize = DEFAULT_PAGE_SIZE, refetchInterval = DEFAULT_REFETCH_INTERVAL, staleTime = DEFAULT_STALE_TIME, }) {
|
|
13
8
|
const categoryConfig = config.categories[category];
|
|
14
9
|
if (!categoryConfig) {
|
|
@@ -30,8 +25,8 @@ export function useActiveNotifications({ config, userId, category, enabled = tru
|
|
|
30
25
|
refetchInterval,
|
|
31
26
|
staleTime,
|
|
32
27
|
});
|
|
33
|
-
//
|
|
34
|
-
//
|
|
28
|
+
// A refetch of a never-succeeded page clears `error` while it runs, so the failure is held
|
|
29
|
+
// through the retry.
|
|
35
30
|
return { ...read, retryState: useReadRetryState(read, paginatedPageKey(queryKey, read.page)) };
|
|
36
31
|
}
|
|
37
32
|
//# sourceMappingURL=useActiveNotifications.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useActiveNotifications.js","sourceRoot":"","sources":["../../../src/react/hooks/useActiveNotifications.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,
|
|
1
|
+
{"version":3,"file":"useActiveNotifications.js","sourceRoot":"","sources":["../../../src/react/hooks/useActiveNotifications.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAwB,MAAM,oBAAoB,CAAC;AAG1E,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,QAAQ,GAAG,iBAAiB,EAC5B,eAAe,GAAG,wBAAwB,EAC1C,SAAS,GAAG,kBAAkB,GACA;IAC9B,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,cAAc,GAAG,cAAc,CAAC,UAAU,CAAC;IACjD,MAAM,UAAU,GAAG,cAAc,CAAC,YAAY,KAAK,UAAU,CAAC;IAE9D,MAAM,WAAW,GAAsB;QACrC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC;KAC7B,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7E,MAAM,IAAI,GAAG,qBAAqB,CAAkB;QAClD,cAAc;QACd,QAAQ;QACR,WAAW;QACX,QAAQ;QACR,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,MAAM;QAC5B,eAAe;QACf,SAAS;KACV,CAAC,CAAC;IACH,2FAA2F;IAC3F,qBAAqB;IACrB,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACjG,CAAC","sourcesContent":["'use client';\n\nimport {\n paginatedPageKey,\n useFirestorePaginated,\n useReadRetryState,\n} from '@ttt-productions/query-core/react';\nimport { orderBy, where, type QueryConstraint } from 'firebase/firestore';\nimport type { NotificationDoc, UseActiveNotificationsOptions } from '../../types.js';\n\nconst DEFAULT_PAGE_SIZE = 20;\nconst DEFAULT_REFETCH_INTERVAL = 30_000;\nconst DEFAULT_STALE_TIME = 30_000;\n\nexport function useActiveNotifications({\n config,\n userId,\n category,\n enabled = true,\n pageSize = DEFAULT_PAGE_SIZE,\n refetchInterval = DEFAULT_REFETCH_INTERVAL,\n staleTime = DEFAULT_STALE_TIME,\n}: UseActiveNotificationsOptions) {\n const categoryConfig = config.categories[category];\n if (!categoryConfig) {\n throw new Error(`[notification-core] Unknown category: ${category}`);\n }\n\n const collectionPath = categoryConfig.activePath;\n const isPersonal = categoryConfig.audienceType === 'personal';\n\n const constraints: QueryConstraint[] = [\n ...(isPersonal ? [where('targetUserId', '==', userId)] : []),\n orderBy('updatedAt', 'desc'),\n ];\n\n const queryKey = ['notifications', 'active', category, userId, { pageSize }];\n const read = useFirestorePaginated<NotificationDoc>({\n collectionPath,\n queryKey,\n constraints,\n pageSize,\n enabled: enabled && !!userId,\n refetchInterval,\n staleTime,\n });\n // A refetch of a never-succeeded page clears `error` while it runs, so the failure is held\n // through the retry.\n return { ...read, retryState: useReadRetryState(read, paginatedPageKey(queryKey, read.page)) };\n}\n"]}
|
|
@@ -1,33 +1,3 @@
|
|
|
1
1
|
import type { ArchiveAllPollResult, UseArchiveAllNotificationsOptions } from '../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Trigger + poll a SERVER-OWNED archive-all job for one notification category.
|
|
4
|
-
*
|
|
5
|
-
* This is a THIN STATUS POLLER, not a client loop. It:
|
|
6
|
-
* 1. enqueues ONE server job via `enqueueArchiveAllFn(category)` (the category — the tab the tray
|
|
7
|
-
* was invoked on — flows into the job, preserving per-tab scoping: the worker archives ONLY
|
|
8
|
-
* that category's active cards, never all tabs);
|
|
9
|
-
* 2. polls `getArchiveAllStatusFn(jobId)` until the job reaches a terminal state
|
|
10
|
-
* (`complete` | `incomplete` | `failed`);
|
|
11
|
-
* 3. resolves an {@link ArchiveAllPollResult} the caller gates side effects on, and invalidates
|
|
12
|
-
* the read keys on a completed drain.
|
|
13
|
-
*
|
|
14
|
-
* All paging + ownership-checked per-card deletion happen server-side. The hook performs no client
|
|
15
|
-
* Firestore writes and never chains archive calls. The generic package hardcodes no callable name —
|
|
16
|
-
* the app injects both adapters.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```tsx
|
|
20
|
-
* const enqueue = httpsCallable(functions, 'enqueueArchiveAll');
|
|
21
|
-
* const getStatus = httpsCallable(functions, 'getArchiveAllStatus');
|
|
22
|
-
* const archiveAll = useArchiveAllNotifications({
|
|
23
|
-
* userId: user.uid,
|
|
24
|
-
* category: 'user',
|
|
25
|
-
* enqueueArchiveAllFn: (category) => enqueue({ category }).then((r) => r.data),
|
|
26
|
-
* getArchiveAllStatusFn: (jobId) => getStatus({ jobId }).then((r) => r.data),
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* archiveAll.mutate();
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
2
|
export declare function useArchiveAllNotifications({ userId, category, enqueueArchiveAllFn, getArchiveAllStatusFn, pollIntervalMs, maxPolls, invalidateKeys, }: UseArchiveAllNotificationsOptions): import("@tanstack/react-query").UseMutationResult<ArchiveAllPollResult, Error, void, unknown>;
|
|
33
3
|
//# sourceMappingURL=useArchiveAllNotifications.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArchiveAllNotifications.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useArchiveAllNotifications.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AAS9F,wBAAgB,0BAA0B,CAAC,EACzC,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,cAAyC,EACzC,QAA4B,EAC5B,cAAc,GACf,EAAE,iCAAiC,iGAkEnC"}
|
|
@@ -1,40 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
3
|
const DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
4
|
-
const DEFAULT_MAX_POLLS = 120;
|
|
4
|
+
const DEFAULT_MAX_POLLS = 120;
|
|
5
5
|
function sleep(ms) {
|
|
6
6
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Trigger + poll a SERVER-OWNED archive-all job for one notification category.
|
|
10
|
-
*
|
|
11
|
-
* This is a THIN STATUS POLLER, not a client loop. It:
|
|
12
|
-
* 1. enqueues ONE server job via `enqueueArchiveAllFn(category)` (the category — the tab the tray
|
|
13
|
-
* was invoked on — flows into the job, preserving per-tab scoping: the worker archives ONLY
|
|
14
|
-
* that category's active cards, never all tabs);
|
|
15
|
-
* 2. polls `getArchiveAllStatusFn(jobId)` until the job reaches a terminal state
|
|
16
|
-
* (`complete` | `incomplete` | `failed`);
|
|
17
|
-
* 3. resolves an {@link ArchiveAllPollResult} the caller gates side effects on, and invalidates
|
|
18
|
-
* the read keys on a completed drain.
|
|
19
|
-
*
|
|
20
|
-
* All paging + ownership-checked per-card deletion happen server-side. The hook performs no client
|
|
21
|
-
* Firestore writes and never chains archive calls. The generic package hardcodes no callable name —
|
|
22
|
-
* the app injects both adapters.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```tsx
|
|
26
|
-
* const enqueue = httpsCallable(functions, 'enqueueArchiveAll');
|
|
27
|
-
* const getStatus = httpsCallable(functions, 'getArchiveAllStatus');
|
|
28
|
-
* const archiveAll = useArchiveAllNotifications({
|
|
29
|
-
* userId: user.uid,
|
|
30
|
-
* category: 'user',
|
|
31
|
-
* enqueueArchiveAllFn: (category) => enqueue({ category }).then((r) => r.data),
|
|
32
|
-
* getArchiveAllStatusFn: (jobId) => getStatus({ jobId }).then((r) => r.data),
|
|
33
|
-
* });
|
|
34
|
-
*
|
|
35
|
-
* archiveAll.mutate();
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
8
|
export function useArchiveAllNotifications({ userId, category, enqueueArchiveAllFn, getArchiveAllStatusFn, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, maxPolls = DEFAULT_MAX_POLLS, invalidateKeys, }) {
|
|
39
9
|
const queryClient = useQueryClient();
|
|
40
10
|
const defaultInvalidateKeys = [
|
|
@@ -43,15 +13,8 @@ export function useArchiveAllNotifications({ userId, category, enqueueArchiveAll
|
|
|
43
13
|
['notifications', 'history', category, userId],
|
|
44
14
|
];
|
|
45
15
|
return useMutation({
|
|
46
|
-
// Server-owned archive-all: enqueue ONE job scoped to `category`, then poll its status until it
|
|
47
|
-
// reaches a terminal state. No browser loop, no chain of archive transactions. The mutation
|
|
48
|
-
// resolves with an explicit terminal result (never throws for an incomplete drain) — `complete`
|
|
49
|
-
// is the load-bearing flag callers gate post-clear side effects on.
|
|
50
16
|
mutationFn: async () => {
|
|
51
17
|
const { jobId } = await enqueueArchiveAllFn(category);
|
|
52
|
-
// Poll until terminal or until the guard bound is hit. A job that never terminates resolves
|
|
53
|
-
// `incomplete` (mirrors the old loop's honest "some remain" result) rather than hanging. The
|
|
54
|
-
// first poll happens immediately so a fast/already-complete job resolves without a delay.
|
|
55
18
|
let lastArchived = 0;
|
|
56
19
|
for (let poll = 0; poll < maxPolls; poll += 1) {
|
|
57
20
|
const snap = await getArchiveAllStatusFn(jobId);
|
|
@@ -66,7 +29,13 @@ export function useArchiveAllNotifications({ userId, category, enqueueArchiveAll
|
|
|
66
29
|
};
|
|
67
30
|
}
|
|
68
31
|
if (snap.state === 'incomplete') {
|
|
69
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
status: 'incomplete',
|
|
34
|
+
category,
|
|
35
|
+
archived: lastArchived,
|
|
36
|
+
notArchived: 0,
|
|
37
|
+
complete: false,
|
|
38
|
+
};
|
|
70
39
|
}
|
|
71
40
|
if (snap.state === 'failed') {
|
|
72
41
|
return {
|
|
@@ -78,17 +47,18 @@ export function useArchiveAllNotifications({ userId, category, enqueueArchiveAll
|
|
|
78
47
|
error: snap.error,
|
|
79
48
|
};
|
|
80
49
|
}
|
|
81
|
-
// Still in-progress — wait before the next poll unless this was the last allowed poll.
|
|
82
50
|
if (poll < maxPolls - 1)
|
|
83
51
|
await sleep(pollIntervalMs);
|
|
84
52
|
}
|
|
85
|
-
|
|
86
|
-
|
|
53
|
+
return {
|
|
54
|
+
status: 'incomplete',
|
|
55
|
+
category,
|
|
56
|
+
archived: lastArchived,
|
|
57
|
+
notArchived: 0,
|
|
58
|
+
complete: false,
|
|
59
|
+
};
|
|
87
60
|
},
|
|
88
61
|
onSuccess: (result) => {
|
|
89
|
-
// Only a completed drain changed the active set; still invalidate on an incomplete drain since
|
|
90
|
-
// SOME cards were archived. A hard enqueue/poll failure rejects the mutation (onError), so this
|
|
91
|
-
// never runs for `failed`.
|
|
92
62
|
const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;
|
|
93
63
|
keysToInvalidate.forEach((key) => {
|
|
94
64
|
queryClient.invalidateQueries({ queryKey: [...key], exact: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArchiveAllNotifications.js","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"useArchiveAllNotifications.js","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGpE,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,EACzC,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GAAG,wBAAwB,EACzC,QAAQ,GAAG,iBAAiB,EAC5B,cAAc,GACoB;IAClC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,qBAAqB,GAAG;QAC5B,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC7C,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC;QACnD,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC;KAC/C,CAAC;IAEF,OAAO,WAAW,CAAC;QACjB,UAAU,EAAE,KAAK,IAAmC,EAAE;YACpD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YAEtD,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;gBAChD,YAAY,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAElC,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;oBAC9B,OAAO;wBACL,MAAM,EAAE,UAAU;wBAClB,QAAQ;wBACR,QAAQ,EAAE,YAAY;wBACtB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC;wBAClC,QAAQ,EAAE,IAAI;qBACf,CAAC;gBACJ,CAAC;gBACD,IAAI,IAAI,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;oBAChC,OAAO;wBACL,MAAM,EAAE,YAAY;wBACpB,QAAQ;wBACR,QAAQ,EAAE,YAAY;wBACtB,WAAW,EAAE,CAAC;wBACd,QAAQ,EAAE,KAAK;qBAChB,CAAC;gBACJ,CAAC;gBACD,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC5B,OAAO;wBACL,MAAM,EAAE,QAAQ;wBAChB,QAAQ;wBACR,QAAQ,EAAE,YAAY;wBACtB,WAAW,EAAE,CAAC;wBACd,QAAQ,EAAE,KAAK;wBACf,KAAK,EAAE,IAAI,CAAC,KAAK;qBAClB,CAAC;gBACJ,CAAC;gBAED,IAAI,IAAI,GAAG,QAAQ,GAAG,CAAC;oBAAE,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;YACvD,CAAC;YAED,OAAO;gBACL,MAAM,EAAE,YAAY;gBACpB,QAAQ;gBACR,QAAQ,EAAE,YAAY;gBACtB,WAAW,EAAE,CAAC;gBACd,QAAQ,EAAE,KAAK;aAChB,CAAC;QACJ,CAAC;QACD,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;YACpB,MAAM,gBAAgB,GAAG,cAAc,IAAI,qBAAqB,CAAC;YACjE,gBAAgB,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC/B,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;YACH,KAAK,MAAM,CAAC;QACd,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport type { ArchiveAllPollResult, UseArchiveAllNotificationsOptions } from '../../types.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 1500;\nconst DEFAULT_MAX_POLLS = 120;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function useArchiveAllNotifications({\n userId,\n category,\n enqueueArchiveAllFn,\n getArchiveAllStatusFn,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n maxPolls = DEFAULT_MAX_POLLS,\n invalidateKeys,\n}: UseArchiveAllNotificationsOptions) {\n const queryClient = useQueryClient();\n\n const defaultInvalidateKeys = [\n ['notifications', 'active', category, userId],\n ['notifications', 'unread-count', category, userId],\n ['notifications', 'history', category, userId],\n ];\n\n return useMutation({\n mutationFn: async (): Promise<ArchiveAllPollResult> => {\n const { jobId } = await enqueueArchiveAllFn(category);\n\n let lastArchived = 0;\n for (let poll = 0; poll < maxPolls; poll += 1) {\n const snap = await getArchiveAllStatusFn(jobId);\n lastArchived = snap.archived ?? 0;\n\n if (snap.state === 'complete') {\n return {\n status: 'complete',\n category,\n archived: lastArchived,\n notArchived: snap.notArchived ?? 0,\n complete: true,\n };\n }\n if (snap.state === 'incomplete') {\n return {\n status: 'incomplete',\n category,\n archived: lastArchived,\n notArchived: 0,\n complete: false,\n };\n }\n if (snap.state === 'failed') {\n return {\n status: 'failed',\n category,\n archived: lastArchived,\n notArchived: 0,\n complete: false,\n error: snap.error,\n };\n }\n\n if (poll < maxPolls - 1) await sleep(pollIntervalMs);\n }\n\n return {\n status: 'incomplete',\n category,\n archived: lastArchived,\n notArchived: 0,\n complete: false,\n };\n },\n onSuccess: (result) => {\n const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;\n keysToInvalidate.forEach((key) => {\n queryClient.invalidateQueries({ queryKey: [...key], exact: false });\n });\n void result;\n },\n });\n}\n"]}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import type { NotificationArchiveResult, NotificationDoc, UseArchiveNotificationOptions } from '../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Archive a single rendered notification through an app-supplied callable adapter.
|
|
4
|
-
*
|
|
5
|
-
* The notification system is Cloud-Functions-only: clients never write notification
|
|
6
|
-
* docs. This hook performs no Firestore writes — it delegates to `archiveFn` with the
|
|
7
|
-
* row as rendered, resolves the adapter's `{ archived }` answer, and invalidates the
|
|
8
|
-
* read keys on success.
|
|
9
|
-
*/
|
|
10
2
|
export declare function useArchiveNotification({ userId, category, archiveFn, invalidateKeys, }: UseArchiveNotificationOptions): import("@tanstack/react-query").UseMutationResult<NotificationArchiveResult, Error, NotificationDoc, unknown>;
|
|
11
3
|
//# sourceMappingURL=useArchiveNotification.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArchiveNotification.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,yBAAyB,EACzB,eAAe,EACf,6BAA6B,EAC9B,MAAM,gBAAgB,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"useArchiveNotification.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,yBAAyB,EACzB,eAAe,EACf,6BAA6B,EAC9B,MAAM,gBAAgB,CAAC;AAExB,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,SAAS,EACT,cAAc,GACf,EAAE,6BAA6B,iHAkB/B"}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
/**
|
|
4
|
-
* Archive a single rendered notification through an app-supplied callable adapter.
|
|
5
|
-
*
|
|
6
|
-
* The notification system is Cloud-Functions-only: clients never write notification
|
|
7
|
-
* docs. This hook performs no Firestore writes — it delegates to `archiveFn` with the
|
|
8
|
-
* row as rendered, resolves the adapter's `{ archived }` answer, and invalidates the
|
|
9
|
-
* read keys on success.
|
|
10
|
-
*/
|
|
11
3
|
export function useArchiveNotification({ userId, category, archiveFn, invalidateKeys, }) {
|
|
12
4
|
const queryClient = useQueryClient();
|
|
13
5
|
const defaultInvalidateKeys = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArchiveNotification.js","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAOpE
|
|
1
|
+
{"version":3,"file":"useArchiveNotification.js","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAOpE,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,SAAS,EACT,cAAc,GACgB;IAC9B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,qBAAqB,GAAG;QAC5B,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC7C,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC;QACnD,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC;KAC/C,CAAC;IAEF,OAAO,WAAW,CAAoD;QACpE,UAAU,EAAE,CAAC,YAA6B,EAAE,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC;QACtE,SAAS,EAAE,GAAG,EAAE;YACd,MAAM,gBAAgB,GAAG,cAAc,IAAI,qBAAqB,CAAC;YACjE,gBAAgB,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC/B,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport type {\n NotificationArchiveResult,\n NotificationDoc,\n UseArchiveNotificationOptions,\n} from '../../types.js';\n\nexport function useArchiveNotification({\n userId,\n category,\n archiveFn,\n invalidateKeys,\n}: UseArchiveNotificationOptions) {\n const queryClient = useQueryClient();\n\n const defaultInvalidateKeys = [\n ['notifications', 'active', category, userId],\n ['notifications', 'unread-count', category, userId],\n ['notifications', 'history', category, userId],\n ];\n\n return useMutation<NotificationArchiveResult, Error, NotificationDoc>({\n mutationFn: (notification: NotificationDoc) => archiveFn(notification),\n onSuccess: () => {\n const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;\n keysToInvalidate.forEach((key) => {\n queryClient.invalidateQueries({ queryKey: [...key], exact: false });\n });\n },\n });\n}\n"]}
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
import type { NotificationHistoryItem, UseNotificationHistoryOptions } from '../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Paginated read of ARCHIVED notification history (the two-tier system's history
|
|
4
|
-
* tier). Personal history lives under a per-user path
|
|
5
|
-
* (`userProfiles/{uid}/notificationHistory`) and shared/admin history under a
|
|
6
|
-
* top-level collection (`adminNotificationHistory`) — both resolved from the
|
|
7
|
-
* category's `historyPath`, so no `where` clause is needed either way. Rows are
|
|
8
|
-
* immutable and ordered newest-first by `archivedAt`.
|
|
9
|
-
*
|
|
10
|
-
* Each history doc is a wrapper (`archivedSnapshot` + archive metadata + a
|
|
11
|
-
* native-TTL `expireAt`); the `select` mapper flattens it into a
|
|
12
|
-
* {@link NotificationHistoryItem} so the read surface renders like the active
|
|
13
|
-
* list. Owner-only (user) / admin-only (admin) reads are enforced by Firestore
|
|
14
|
-
* rules — this hook performs no writes.
|
|
15
|
-
*/
|
|
16
2
|
export declare function useNotificationHistory({ config, userId, category, enabled, pageSize, staleTime, }: UseNotificationHistoryOptions): {
|
|
17
3
|
retryState: import("@ttt-productions/query-core/react").ReadRetryState<Error>;
|
|
18
|
-
promise: Promise<import("@ttt-productions/query-core").PaginatedPage<import("@ttt-productions/query-core").WithId<NotificationHistoryItem>>>;
|
|
4
|
+
promise: Promise<import("@ttt-productions/query-core/react").PaginatedPage<import("@ttt-productions/query-core/types").WithId<NotificationHistoryItem>>>;
|
|
19
5
|
error: Error | null;
|
|
20
6
|
isError: boolean;
|
|
21
7
|
isPending: boolean;
|
|
@@ -38,9 +24,9 @@ export declare function useNotificationHistory({ config, userId, category, enabl
|
|
|
38
24
|
isRefetching: boolean;
|
|
39
25
|
isStale: boolean;
|
|
40
26
|
isEnabled: boolean;
|
|
41
|
-
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<import("@ttt-productions/query-core").PaginatedPage<import("@ttt-productions/query-core").WithId<NotificationHistoryItem>>, Error>>;
|
|
27
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<import("@ttt-productions/query-core/react").PaginatedPage<import("@ttt-productions/query-core/types").WithId<NotificationHistoryItem>>, Error>>;
|
|
42
28
|
fetchStatus: import("@tanstack/query-core").FetchStatus;
|
|
43
|
-
data: import("@ttt-productions/query-core").WithId<NotificationHistoryItem>[] | undefined;
|
|
29
|
+
data: import("@ttt-productions/query-core/types").WithId<NotificationHistoryItem>[] | undefined;
|
|
44
30
|
page: number;
|
|
45
31
|
pageSize: number;
|
|
46
32
|
hasNextPage: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNotificationHistory.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useNotificationHistory.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,uBAAuB,EACvB,6BAA6B,EAC9B,MAAM,gBAAgB,CAAC;AAKxB,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAc,EACd,QAA4B,EAC5B,SAA8B,GAC/B,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B/B"}
|
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { paginatedPageKey } from '@ttt-productions/query-core';
|
|
3
|
-
import { useFirestorePaginated, useReadRetryState } from '@ttt-productions/query-core/react';
|
|
2
|
+
import { paginatedPageKey, useFirestorePaginated, useReadRetryState, } from '@ttt-productions/query-core/react';
|
|
4
3
|
import { orderBy } from 'firebase/firestore';
|
|
5
4
|
const DEFAULT_PAGE_SIZE = 20;
|
|
6
5
|
const DEFAULT_STALE_TIME = 60_000;
|
|
7
|
-
/**
|
|
8
|
-
* Paginated read of ARCHIVED notification history (the two-tier system's history
|
|
9
|
-
* tier). Personal history lives under a per-user path
|
|
10
|
-
* (`userProfiles/{uid}/notificationHistory`) and shared/admin history under a
|
|
11
|
-
* top-level collection (`adminNotificationHistory`) — both resolved from the
|
|
12
|
-
* category's `historyPath`, so no `where` clause is needed either way. Rows are
|
|
13
|
-
* immutable and ordered newest-first by `archivedAt`.
|
|
14
|
-
*
|
|
15
|
-
* Each history doc is a wrapper (`archivedSnapshot` + archive metadata + a
|
|
16
|
-
* native-TTL `expireAt`); the `select` mapper flattens it into a
|
|
17
|
-
* {@link NotificationHistoryItem} so the read surface renders like the active
|
|
18
|
-
* list. Owner-only (user) / admin-only (admin) reads are enforced by Firestore
|
|
19
|
-
* rules — this hook performs no writes.
|
|
20
|
-
*/
|
|
21
6
|
export function useNotificationHistory({ config, userId, category, enabled = true, pageSize = DEFAULT_PAGE_SIZE, staleTime = DEFAULT_STALE_TIME, }) {
|
|
22
7
|
const categoryConfig = config.categories[category];
|
|
23
8
|
if (!categoryConfig) {
|
|
@@ -37,15 +22,13 @@ export function useNotificationHistory({ config, userId, category, enabled = tru
|
|
|
37
22
|
const snapshot = (data.archivedSnapshot ?? {});
|
|
38
23
|
return {
|
|
39
24
|
...snapshot,
|
|
40
|
-
// The archive occurrence id is the stable history-doc key; expose
|
|
41
|
-
// archivedAt for ordering/display.
|
|
42
25
|
archiveOccurrenceId: data.id,
|
|
43
26
|
archivedAt: data.archivedAt ?? snapshot.updatedAt ?? 0,
|
|
44
27
|
};
|
|
45
28
|
},
|
|
46
29
|
});
|
|
47
|
-
//
|
|
48
|
-
//
|
|
30
|
+
// A refetch of a never-succeeded page clears `error` while it runs, so the failure is held
|
|
31
|
+
// through the retry.
|
|
49
32
|
return { ...read, retryState: useReadRetryState(read, paginatedPageKey(queryKey, read.page)) };
|
|
50
33
|
}
|
|
51
34
|
//# sourceMappingURL=useNotificationHistory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNotificationHistory.js","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,
|
|
1
|
+
{"version":3,"file":"useNotificationHistory.js","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAwB,MAAM,oBAAoB,CAAC;AAOnE,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,QAAQ,GAAG,iBAAiB,EAC5B,SAAS,GAAG,kBAAkB,GACA;IAC9B,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,cAAc,GAAG,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAE1D,MAAM,WAAW,GAAsB,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAEvE,MAAM,QAAQ,GAAG,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,qBAAqB,CAA0B;QAC1D,cAAc;QACd,QAAQ;QACR,WAAW;QACX,QAAQ;QACR,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,MAAM;QAC5B,SAAS;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAoB,CAAC;YAClE,OAAO;gBACL,GAAG,QAAQ;gBACX,mBAAmB,EAAE,IAAI,CAAC,EAAE;gBAC5B,UAAU,EAAG,IAAI,CAAC,UAAiC,IAAI,QAAQ,CAAC,SAAS,IAAI,CAAC;aAC/E,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IACH,2FAA2F;IAC3F,qBAAqB;IACrB,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACjG,CAAC","sourcesContent":["'use client';\n\nimport {\n paginatedPageKey,\n useFirestorePaginated,\n useReadRetryState,\n} from '@ttt-productions/query-core/react';\nimport { orderBy, type QueryConstraint } from 'firebase/firestore';\nimport type {\n NotificationDoc,\n NotificationHistoryItem,\n UseNotificationHistoryOptions,\n} from '../../types.js';\n\nconst DEFAULT_PAGE_SIZE = 20;\nconst DEFAULT_STALE_TIME = 60_000;\n\nexport function useNotificationHistory({\n config,\n userId,\n category,\n enabled = true,\n pageSize = DEFAULT_PAGE_SIZE,\n staleTime = DEFAULT_STALE_TIME,\n}: UseNotificationHistoryOptions) {\n const categoryConfig = config.categories[category];\n if (!categoryConfig) {\n throw new Error(`[notification-core] Unknown category: ${category}`);\n }\n\n const collectionPath = categoryConfig.historyPath(userId);\n\n const constraints: QueryConstraint[] = [orderBy('archivedAt', 'desc')];\n\n const queryKey = ['notifications', 'history', category, userId, { pageSize }];\n const read = useFirestorePaginated<NotificationHistoryItem>({\n collectionPath,\n queryKey,\n constraints,\n pageSize,\n enabled: enabled && !!userId,\n staleTime,\n select: (data) => {\n const snapshot = (data.archivedSnapshot ?? {}) as NotificationDoc;\n return {\n ...snapshot,\n archiveOccurrenceId: data.id,\n archivedAt: (data.archivedAt as number | undefined) ?? snapshot.updatedAt ?? 0,\n };\n },\n });\n // A refetch of a never-succeeded page clears `error` while it runs, so the failure is held\n // through the retry.\n return { ...read, retryState: useReadRetryState(read, paginatedPageKey(queryKey, read.page)) };\n}\n"]}
|
|
@@ -1,26 +1,4 @@
|
|
|
1
1
|
import type { UseUnreadCountOptions } from '../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Unread-count badge backed by a Firestore `count()` aggregation (a server-side
|
|
4
|
-
* count, not a doc fetch), via query-core's `useFirestoreCount`. Polls for cost
|
|
5
|
-
* control; displays capped (`hasMore` → `99+`).
|
|
6
|
-
*
|
|
7
|
-
* - **personal** categories count **unseen** active items (`seenAt == 0`),
|
|
8
|
-
* scoped to the caller (`targetUserId == uid`);
|
|
9
|
-
* - **shared** categories have no per-admin seen state, so the indicator is
|
|
10
|
-
* existence-based — a count of all active items, with no `seenAt` predicate.
|
|
11
|
-
*
|
|
12
|
-
* The personal `seenAt == 0` predicate needs a composite index, captured as an
|
|
13
|
-
* app-side index step.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* const { count, isLoading } = useUnreadCount({
|
|
18
|
-
* config: TTT_NOTIFICATION_CONFIG,
|
|
19
|
-
* userId: user.uid,
|
|
20
|
-
* category: 'user',
|
|
21
|
-
* });
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
2
|
export declare function useUnreadCount({ config, userId, category, enabled, refetchInterval, countLimit, }: UseUnreadCountOptions): {
|
|
25
3
|
error: Error;
|
|
26
4
|
isError: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUnreadCount.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useUnreadCount.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"useUnreadCount.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useUnreadCount.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAM5D,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAc,EACd,eAA0C,EAC1C,UAAgC,GACjC,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BvB"}
|
|
@@ -3,28 +3,7 @@ import { where } from 'firebase/firestore';
|
|
|
3
3
|
import { useFirestoreCount } from '@ttt-productions/query-core/react';
|
|
4
4
|
const DEFAULT_REFETCH_INTERVAL = 30_000;
|
|
5
5
|
const DEFAULT_COUNT_LIMIT = 99;
|
|
6
|
-
|
|
7
|
-
* Unread-count badge backed by a Firestore `count()` aggregation (a server-side
|
|
8
|
-
* count, not a doc fetch), via query-core's `useFirestoreCount`. Polls for cost
|
|
9
|
-
* control; displays capped (`hasMore` → `99+`).
|
|
10
|
-
*
|
|
11
|
-
* - **personal** categories count **unseen** active items (`seenAt == 0`),
|
|
12
|
-
* scoped to the caller (`targetUserId == uid`);
|
|
13
|
-
* - **shared** categories have no per-admin seen state, so the indicator is
|
|
14
|
-
* existence-based — a count of all active items, with no `seenAt` predicate.
|
|
15
|
-
*
|
|
16
|
-
* The personal `seenAt == 0` predicate needs a composite index, captured as an
|
|
17
|
-
* app-side index step.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* const { count, isLoading } = useUnreadCount({
|
|
22
|
-
* config: TTT_NOTIFICATION_CONFIG,
|
|
23
|
-
* userId: user.uid,
|
|
24
|
-
* category: 'user',
|
|
25
|
-
* });
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
6
|
+
// The personal `seenAt == 0` predicate needs a composite index.
|
|
28
7
|
export function useUnreadCount({ config, userId, category, enabled = true, refetchInterval = DEFAULT_REFETCH_INTERVAL, countLimit = DEFAULT_COUNT_LIMIT, }) {
|
|
29
8
|
const categoryConfig = config.categories[category];
|
|
30
9
|
if (!categoryConfig) {
|
|
@@ -32,9 +11,7 @@ export function useUnreadCount({ config, userId, category, enabled = true, refet
|
|
|
32
11
|
}
|
|
33
12
|
const collectionPath = categoryConfig.activePath;
|
|
34
13
|
const isPersonal = categoryConfig.audienceType === 'personal';
|
|
35
|
-
const constraints = isPersonal && userId
|
|
36
|
-
? [where('targetUserId', '==', userId), where('seenAt', '==', 0)]
|
|
37
|
-
: [];
|
|
14
|
+
const constraints = isPersonal && userId ? [where('targetUserId', '==', userId), where('seenAt', '==', 0)] : [];
|
|
38
15
|
const { data, ...rest } = useFirestoreCount({
|
|
39
16
|
collectionPath,
|
|
40
17
|
queryKey: ['notifications', 'unread-count', category, userId],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUnreadCount.js","sourceRoot":"","sources":["../../../src/react/hooks/useUnreadCount.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,KAAK,EAAwB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B
|
|
1
|
+
{"version":3,"file":"useUnreadCount.js","sourceRoot":"","sources":["../../../src/react/hooks/useUnreadCount.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,KAAK,EAAwB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,eAAe,GAAG,wBAAwB,EAC1C,UAAU,GAAG,mBAAmB,GACV;IACtB,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,cAAc,GAAG,cAAc,CAAC,UAAU,CAAC;IACjD,MAAM,UAAU,GAAG,cAAc,CAAC,YAAY,KAAK,UAAU,CAAC;IAE9D,MAAM,WAAW,GACf,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9F,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,iBAAiB,CAAC;QAC1C,cAAc;QACd,QAAQ,EAAE,CAAC,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC7D,WAAW;QACX,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,MAAM;QAC5B,SAAS,EAAE,eAAe;QAC1B,eAAe;KAChB,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC;IAExB,OAAO;QACL,KAAK;QACL,OAAO,EAAE,KAAK,GAAG,UAAU;QAC3B,GAAG,IAAI;KACR,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { where, type QueryConstraint } from 'firebase/firestore';\nimport { useFirestoreCount } from '@ttt-productions/query-core/react';\nimport type { UseUnreadCountOptions } from '../../types.js';\n\nconst DEFAULT_REFETCH_INTERVAL = 30_000;\nconst DEFAULT_COUNT_LIMIT = 99;\n\n// The personal `seenAt == 0` predicate needs a composite index.\nexport function useUnreadCount({\n config,\n userId,\n category,\n enabled = true,\n refetchInterval = DEFAULT_REFETCH_INTERVAL,\n countLimit = DEFAULT_COUNT_LIMIT,\n}: UseUnreadCountOptions) {\n const categoryConfig = config.categories[category];\n if (!categoryConfig) {\n throw new Error(`[notification-core] Unknown category: ${category}`);\n }\n\n const collectionPath = categoryConfig.activePath;\n const isPersonal = categoryConfig.audienceType === 'personal';\n\n const constraints: QueryConstraint[] =\n isPersonal && userId ? [where('targetUserId', '==', userId), where('seenAt', '==', 0)] : [];\n\n const { data, ...rest } = useFirestoreCount({\n collectionPath,\n queryKey: ['notifications', 'unread-count', category, userId],\n constraints,\n enabled: enabled && !!userId,\n staleTime: refetchInterval,\n refetchInterval,\n });\n\n const count = data ?? 0;\n\n return {\n count,\n hasMore: count > countLimit,\n ...rest,\n };\n}\n"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
export { useActiveNotifications
|
|
2
|
-
export {
|
|
1
|
+
export { useActiveNotifications } from './hooks/useActiveNotifications.js';
|
|
2
|
+
export { useNotificationHistory } from './hooks/useNotificationHistory.js';
|
|
3
|
+
export { useUnreadCount } from './hooks/useUnreadCount.js';
|
|
4
|
+
export { useArchiveNotification } from './hooks/useArchiveNotification.js';
|
|
5
|
+
export { useArchiveAllNotifications } from './hooks/useArchiveAllNotifications.js';
|
|
6
|
+
export { NotificationList } from './components/NotificationList.js';
|
|
7
|
+
export { NotificationHistoryList } from './components/NotificationHistoryList.js';
|
|
8
|
+
export { NotificationEmptyState } from './components/NotificationEmptyState.js';
|
|
9
|
+
export { NotificationUnreadBadge } from './components/NotificationUnreadBadge.js';
|
|
10
|
+
export type { NotificationListProps, NotificationHistoryListProps } from './types.js';
|
|
3
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAElF,YAAY,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
4
|
-
|
|
5
|
-
export {
|
|
1
|
+
'use client';
|
|
2
|
+
export { useActiveNotifications } from './hooks/useActiveNotifications.js';
|
|
3
|
+
export { useNotificationHistory } from './hooks/useNotificationHistory.js';
|
|
4
|
+
export { useUnreadCount } from './hooks/useUnreadCount.js';
|
|
5
|
+
export { useArchiveNotification } from './hooks/useArchiveNotification.js';
|
|
6
|
+
export { useArchiveAllNotifications } from './hooks/useArchiveAllNotifications.js';
|
|
7
|
+
export { NotificationList } from './components/NotificationList.js';
|
|
8
|
+
export { NotificationHistoryList } from './components/NotificationHistoryList.js';
|
|
9
|
+
export { NotificationEmptyState } from './components/NotificationEmptyState.js';
|
|
10
|
+
export { NotificationUnreadBadge } from './components/NotificationUnreadBadge.js';
|
|
6
11
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC","sourcesContent":["'use client';\n\nexport { useActiveNotifications } from './hooks/useActiveNotifications.js';\nexport { useNotificationHistory } from './hooks/useNotificationHistory.js';\nexport { useUnreadCount } from './hooks/useUnreadCount.js';\nexport { useArchiveNotification } from './hooks/useArchiveNotification.js';\nexport { useArchiveAllNotifications } from './hooks/useArchiveAllNotifications.js';\n\nexport { NotificationList } from './components/NotificationList.js';\nexport { NotificationHistoryList } from './components/NotificationHistoryList.js';\nexport { NotificationEmptyState } from './components/NotificationEmptyState.js';\nexport { NotificationUnreadBadge } from './components/NotificationUnreadBadge.js';\n\nexport type { NotificationListProps, NotificationHistoryListProps } from './types.js';\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ArchiveAllJobSnapshot, ArchiveAllPollResult, EnqueueArchiveAllResult, NotificationArchiveFn, NotificationDoc, NotificationHistoryItem, NotificationListErrorState, NotificationRowActions, NotificationSystemConfig } from '../types.js';
|
|
3
|
+
export interface NotificationListProps {
|
|
4
|
+
config: NotificationSystemConfig;
|
|
5
|
+
userId: string;
|
|
6
|
+
category: string;
|
|
7
|
+
archiveFn: NotificationArchiveFn;
|
|
8
|
+
enqueueArchiveAllFn: (category: string) => Promise<EnqueueArchiveAllResult>;
|
|
9
|
+
getArchiveAllStatusFn: (jobId: string) => Promise<ArchiveAllJobSnapshot>;
|
|
10
|
+
title?: ReactNode;
|
|
11
|
+
onClearAll?: (result: ArchiveAllPollResult) => void;
|
|
12
|
+
refetchInterval?: number;
|
|
13
|
+
staleTime?: number;
|
|
14
|
+
emptyText?: string;
|
|
15
|
+
renderError: (state: NotificationListErrorState) => ReactNode;
|
|
16
|
+
onRenderedRowsChange?: (rows: readonly NotificationDoc[]) => void;
|
|
17
|
+
renderRowAction?: (notification: NotificationDoc, actions: NotificationRowActions) => ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export interface NotificationHistoryListProps {
|
|
20
|
+
config: NotificationSystemConfig;
|
|
21
|
+
userId: string;
|
|
22
|
+
category: string;
|
|
23
|
+
pageSize?: number;
|
|
24
|
+
staleTime?: number;
|
|
25
|
+
emptyText?: string;
|
|
26
|
+
renderError: (state: NotificationListErrorState) => ReactNode;
|
|
27
|
+
title?: ReactNode;
|
|
28
|
+
renderRowAction?: (notification: NotificationHistoryItem, actions: NotificationRowActions) => ReactNode;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/react/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,qBAAqB,CAAC;IACjC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC5E,qBAAqB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACzE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAC;IAC9D,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,CAAC;IAClE,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,sBAAsB,KAAK,SAAS,CAAC;CACjG;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAC;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,eAAe,CAAC,EAAE,CAChB,YAAY,EAAE,uBAAuB,EACrC,OAAO,EAAE,sBAAsB,KAC5B,SAAS,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/react/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ReactNode } from 'react';\nimport type {\n ArchiveAllJobSnapshot,\n ArchiveAllPollResult,\n EnqueueArchiveAllResult,\n NotificationArchiveFn,\n NotificationDoc,\n NotificationHistoryItem,\n NotificationListErrorState,\n NotificationRowActions,\n NotificationSystemConfig,\n} from '../types.js';\n\nexport interface NotificationListProps {\n config: NotificationSystemConfig;\n userId: string;\n category: string;\n archiveFn: NotificationArchiveFn;\n enqueueArchiveAllFn: (category: string) => Promise<EnqueueArchiveAllResult>;\n getArchiveAllStatusFn: (jobId: string) => Promise<ArchiveAllJobSnapshot>;\n title?: ReactNode;\n onClearAll?: (result: ArchiveAllPollResult) => void;\n refetchInterval?: number;\n staleTime?: number;\n emptyText?: string;\n renderError: (state: NotificationListErrorState) => ReactNode;\n onRenderedRowsChange?: (rows: readonly NotificationDoc[]) => void;\n renderRowAction?: (notification: NotificationDoc, actions: NotificationRowActions) => ReactNode;\n}\n\nexport interface NotificationHistoryListProps {\n config: NotificationSystemConfig;\n userId: string;\n category: string;\n pageSize?: number;\n staleTime?: number;\n emptyText?: string;\n renderError: (state: NotificationListErrorState) => ReactNode;\n title?: ReactNode;\n renderRowAction?: (\n notification: NotificationHistoryItem,\n actions: NotificationRowActions,\n ) => ReactNode;\n}\n"]}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @ttt-productions/notification-core — Zod schemas
|
|
3
|
-
*
|
|
4
|
-
* Runtime validation schemas for the generic notification document shapes whose
|
|
5
|
-
* TypeScript types live in `./types.ts`. notification-core OWNS these shapes, so
|
|
6
|
-
* it owns their schemas too — the consuming app's data package composes these rather than hand-mirroring them, so the schema can never drift
|
|
7
|
-
* from the type.
|
|
8
|
-
*
|
|
9
|
-
* The interfaces in `./types.ts` remain the documented public type API. Each
|
|
10
|
-
* schema is compile-time-checked against its interface via
|
|
11
|
-
* `satisfies z.ZodType<Shape>` — a shape drift between the interface and the
|
|
12
|
-
* schema fails the build here.
|
|
13
|
-
*
|
|
14
|
-
* zod-only, no React/firebase: safe for both server (Cloud Functions) and UI.
|
|
15
|
-
*/
|
|
16
1
|
import { z } from 'zod';
|
|
17
|
-
/**
|
|
18
|
-
* Active notification document (`NotificationDoc`). Same shape for both the user
|
|
19
|
-
* and admin active collections.
|
|
20
|
-
*/
|
|
21
2
|
export declare const NotificationDocSchema: z.ZodObject<{
|
|
22
3
|
id: z.ZodString;
|
|
23
4
|
type: z.ZodString;
|
|
@@ -36,9 +17,6 @@ export declare const NotificationDocSchema: z.ZodObject<{
|
|
|
36
17
|
createdAt: z.ZodNumber;
|
|
37
18
|
updatedAt: z.ZodNumber;
|
|
38
19
|
}, z.core.$strip>;
|
|
39
|
-
/**
|
|
40
|
-
* Pending notification (`PendingNotification`) — queue item for the batch processor.
|
|
41
|
-
*/
|
|
42
20
|
export declare const PendingNotificationSchema: z.ZodObject<{
|
|
43
21
|
id: z.ZodString;
|
|
44
22
|
type: z.ZodString;
|
package/dist/schemas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;iBAiBK,CAAC;AAExC,eAAO,MAAM,yBAAyB;;;;;;;;iBAQK,CAAC"}
|