@ttt-productions/notification-core 0.12.0 → 0.14.0

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.
Files changed (33) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/react/components/NotificationHistoryList.d.ts +9 -0
  4. package/dist/react/components/NotificationHistoryList.d.ts.map +1 -0
  5. package/dist/react/components/NotificationHistoryList.js +41 -0
  6. package/dist/react/components/NotificationHistoryList.js.map +1 -0
  7. package/dist/react/components/NotificationList.d.ts +1 -1
  8. package/dist/react/components/NotificationList.d.ts.map +1 -1
  9. package/dist/react/components/NotificationList.js +11 -7
  10. package/dist/react/components/NotificationList.js.map +1 -1
  11. package/dist/react/components/index.d.ts +1 -0
  12. package/dist/react/components/index.d.ts.map +1 -1
  13. package/dist/react/components/index.js +1 -0
  14. package/dist/react/components/index.js.map +1 -1
  15. package/dist/react/hooks/index.d.ts +1 -0
  16. package/dist/react/hooks/index.d.ts.map +1 -1
  17. package/dist/react/hooks/index.js +1 -0
  18. package/dist/react/hooks/index.js.map +1 -1
  19. package/dist/react/hooks/useArchiveAllNotifications.d.ts +19 -8
  20. package/dist/react/hooks/useArchiveAllNotifications.d.ts.map +1 -1
  21. package/dist/react/hooks/useArchiveAllNotifications.js +54 -31
  22. package/dist/react/hooks/useArchiveAllNotifications.js.map +1 -1
  23. package/dist/react/hooks/useNotificationHistory.d.ts +17 -0
  24. package/dist/react/hooks/useNotificationHistory.d.ts.map +1 -0
  25. package/dist/react/hooks/useNotificationHistory.js +46 -0
  26. package/dist/react/hooks/useNotificationHistory.js.map +1 -0
  27. package/dist/react/index.d.ts +2 -2
  28. package/dist/react/index.d.ts.map +1 -1
  29. package/dist/react/index.js +2 -2
  30. package/dist/react/index.js.map +1 -1
  31. package/dist/types.d.ts +106 -27
  32. package/dist/types.d.ts.map +1 -1
  33. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export type { NotificationDoc, PendingNotification, NotificationCategoryConfig, NotificationTypeConfig, NotificationSystemConfig, UseActiveNotificationsOptions, UseUnreadCountOptions, UseArchiveNotificationOptions, UseArchiveAllNotificationsOptions, ArchiveAllResult, ArchiveAllLoopResult, NotificationListProps, NotificationEmptyStateProps, NotificationUnreadBadgeProps, } from './types.js';
1
+ export type { NotificationDoc, PendingNotification, NotificationCategoryConfig, NotificationTypeConfig, NotificationSystemConfig, UseActiveNotificationsOptions, UseNotificationHistoryOptions, NotificationHistoryItem, UseUnreadCountOptions, UseArchiveNotificationOptions, UseArchiveAllNotificationsOptions, ArchiveAllJobStatus, EnqueueArchiveAllResult, ArchiveAllJobSnapshot, ArchiveAllPollResult, NotificationListProps, NotificationHistoryListProps, NotificationEmptyStateProps, NotificationUnreadBadgeProps, } from './types.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,EACrB,6BAA6B,EAC7B,iCAAiC,EACjC,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,EAC7B,iCAAiC,EACjC,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { NotificationHistoryListProps } from '../../types.js';
2
+ /**
3
+ * Read-only, paginated list of ARCHIVED notifications (the history tier). Rows do
4
+ * not mutate state on click — archive is one-way; there is no re-archive. When
5
+ * `onNotificationClick` is supplied, rows are interactive (e.g. navigate to
6
+ * `targetPath`); otherwise they render non-interactive.
7
+ */
8
+ export declare function NotificationHistoryList({ config, userId, category, onNotificationClick, pageSize, staleTime, emptyText, }: NotificationHistoryListProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=NotificationHistoryList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationHistoryList.d.ts","sourceRoot":"","sources":["../../../src/react/components/NotificationHistoryList.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA2B,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAE5F;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,SAAS,EACT,SAAS,GACV,EAAE,4BAA4B,+BA8E9B"}
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback } from 'react';
4
+ import { Badge, Button } from '@ttt-productions/ui-core/react';
5
+ import { useNotificationHistory } from '../hooks/useNotificationHistory.js';
6
+ import { NotificationEmptyState } from './NotificationEmptyState.js';
7
+ import { formatRelativeTime } from './relative-time.js';
8
+ /**
9
+ * Read-only, paginated list of ARCHIVED notifications (the history tier). Rows do
10
+ * not mutate state on click — archive is one-way; there is no re-archive. When
11
+ * `onNotificationClick` is supplied, rows are interactive (e.g. navigate to
12
+ * `targetPath`); otherwise they render non-interactive.
13
+ */
14
+ export function NotificationHistoryList({ config, userId, category, onNotificationClick, pageSize, staleTime, emptyText, }) {
15
+ const { data: notifications, isLoading, hasNextPage, nextPage, } = useNotificationHistory({
16
+ config,
17
+ userId,
18
+ category,
19
+ pageSize,
20
+ staleTime,
21
+ });
22
+ const getTypeIcon = useCallback((type) => config.types[type]?.icon ?? '🔔', [config]);
23
+ const interactive = typeof onNotificationClick === 'function';
24
+ return (_jsx("div", { className: "ntf-list ntf-list-history", children: _jsx("div", { className: "ntf-list-body", children: isLoading ? (_jsx("div", { className: "ntf-loading", children: "Loading..." })) : !notifications || notifications.length === 0 ? (_jsx(NotificationEmptyState, { text: emptyText })) : (_jsxs(_Fragment, { children: [notifications.map((notification) => {
25
+ const interactiveProps = interactive
26
+ ? {
27
+ role: 'button',
28
+ tabIndex: 0,
29
+ onClick: () => onNotificationClick?.(notification),
30
+ onKeyDown: (e) => {
31
+ if (e.key === 'Enter' || e.key === ' ') {
32
+ e.preventDefault();
33
+ onNotificationClick?.(notification);
34
+ }
35
+ },
36
+ }
37
+ : {};
38
+ return (_jsxs("div", { className: "ntf-item ntf-item-archived", ...interactiveProps, children: [_jsx("div", { className: "ntf-item-icon", children: getTypeIcon(notification.type) }), _jsxs("div", { className: "ntf-item-content", children: [_jsx("div", { className: "ntf-item-title", children: notification.title }), _jsx("div", { className: "ntf-item-message", children: notification.message }), _jsx("div", { className: "ntf-item-timestamp", children: formatRelativeTime(notification.archivedAt) })] }), notification.count > 1 && (_jsx("div", { className: "ntf-item-count", children: _jsxs(Badge, { variant: "secondary", children: ["\u00D7", notification.count] }) }))] }, notification.archiveOccurrenceId));
39
+ }), hasNextPage && (_jsx("div", { className: "ntf-list-footer", children: _jsx(Button, { variant: "ghost", size: "sm", onClick: nextPage, children: "Load more" }) }))] })) }) }));
40
+ }
41
+ //# sourceMappingURL=NotificationHistoryList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationHistoryList.js","sourceRoot":"","sources":["../../../src/react/components/NotificationHistoryList.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,SAAS,EACT,SAAS,GACoB;IAC7B,MAAM,EACJ,IAAI,EAAE,aAAa,EACnB,SAAS,EACT,WAAW,EACX,QAAQ,GACT,GAAG,sBAAsB,CAAC;QACzB,MAAM;QACN,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,EAClD,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,MAAM,WAAW,GAAG,OAAO,mBAAmB,KAAK,UAAU,CAAC;IAE9D,OAAO,CACL,cAAK,SAAS,EAAC,2BAA2B,YACxC,cAAK,SAAS,EAAC,eAAe,YAC3B,SAAS,CAAC,CAAC,CAAC,CACX,cAAK,SAAS,EAAC,aAAa,2BAAiB,CAC9C,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACjD,KAAC,sBAAsB,IAAC,IAAI,EAAE,SAAS,GAAI,CAC5C,CAAC,CAAC,CAAC,CACF,8BACG,aAAa,CAAC,GAAG,CAAC,CAAC,YAAqC,EAAE,EAAE;wBAC3D,MAAM,gBAAgB,GAAG,WAAW;4BAClC,CAAC,CAAC;gCACE,IAAI,EAAE,QAAQ;gCACd,QAAQ,EAAE,CAAC;gCACX,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,YAAY,CAAC;gCAClD,SAAS,EAAE,CAAC,CAAsB,EAAE,EAAE;oCACpC,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;wCACvC,CAAC,CAAC,cAAc,EAAE,CAAC;wCACnB,mBAAmB,EAAE,CAAC,YAAY,CAAC,CAAC;oCACtC,CAAC;gCACH,CAAC;6BACF;4BACH,CAAC,CAAC,EAAE,CAAC;wBACP,OAAO,CACL,eAEE,SAAS,EAAC,4BAA4B,KAClC,gBAAgB,aAEpB,cAAK,SAAS,EAAC,eAAe,YAAE,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,GAAO,EACrE,eAAK,SAAS,EAAC,kBAAkB,aAC/B,cAAK,SAAS,EAAC,gBAAgB,YAAE,YAAY,CAAC,KAAK,GAAO,EAC1D,cAAK,SAAS,EAAC,kBAAkB,YAAE,YAAY,CAAC,OAAO,GAAO,EAC9D,cAAK,SAAS,EAAC,oBAAoB,YAChC,kBAAkB,CAAC,YAAY,CAAC,UAAU,CAAC,GACxC,IACF,EACL,YAAY,CAAC,KAAK,GAAG,CAAC,IAAI,CACzB,cAAK,SAAS,EAAC,gBAAgB,YAC7B,MAAC,KAAK,IAAC,OAAO,EAAC,WAAW,uBAAG,YAAY,CAAC,KAAK,IAAS,GACpD,CACP,KAhBI,YAAY,CAAC,mBAAmB,CAiBjC,CACP,CAAC;oBACJ,CAAC,CAAC,EACD,WAAW,IAAI,CACd,cAAK,SAAS,EAAC,iBAAiB,YAC9B,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,QAAQ,0BAE1C,GACL,CACP,IACA,CACJ,GACG,GACF,CACP,CAAC;AACJ,CAAC"}
@@ -2,5 +2,5 @@ import type { NotificationListProps } from '../../types.js';
2
2
  /**
3
3
  * Scrollable list of active notifications with click-to-archive and clear-all.
4
4
  */
5
- export declare function NotificationList({ config, userId, category, onNotificationClick, archiveFn, archiveAllFn, onClearAll, refetchInterval, emptyText, }: NotificationListProps): import("react").JSX.Element;
5
+ export declare function NotificationList({ config, userId, category, onNotificationClick, archiveFn, enqueueArchiveAllFn, getArchiveAllStatusFn, onClearAll, refetchInterval, emptyText, }: NotificationListProps): import("react").JSX.Element;
6
6
  //# sourceMappingURL=NotificationList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationList.d.ts","sourceRoot":"","sources":["../../../src/react/components/NotificationList.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,UAAU,EACV,eAAe,EACf,SAAS,GACV,EAAE,qBAAqB,+BA2IvB"}
1
+ {"version":3,"file":"NotificationList.d.ts","sourceRoot":"","sources":["../../../src/react/components/NotificationList.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,UAAU,EACV,eAAe,EACf,SAAS,GACV,EAAE,qBAAqB,+BA+IvB"}
@@ -10,7 +10,7 @@ import { formatRelativeTime } from './relative-time.js';
10
10
  /**
11
11
  * Scrollable list of active notifications with click-to-archive and clear-all.
12
12
  */
13
- export function NotificationList({ config, userId, category, onNotificationClick, archiveFn, archiveAllFn, onClearAll, refetchInterval, emptyText, }) {
13
+ export function NotificationList({ config, userId, category, onNotificationClick, archiveFn, enqueueArchiveAllFn, getArchiveAllStatusFn, onClearAll, refetchInterval, emptyText, }) {
14
14
  const { data: notifications, isLoading, hasNextPage, nextPage, } = useActiveNotifications({
15
15
  config,
16
16
  userId,
@@ -25,7 +25,8 @@ export function NotificationList({ config, userId, category, onNotificationClick
25
25
  const archiveAllMutation = useArchiveAllNotifications({
26
26
  userId,
27
27
  category,
28
- archiveAllFn,
28
+ enqueueArchiveAllFn,
29
+ getArchiveAllStatusFn,
29
30
  });
30
31
  const handleNotificationClick = useCallback(async (notification) => {
31
32
  try {
@@ -36,9 +37,11 @@ export function NotificationList({ config, userId, category, onNotificationClick
36
37
  }
37
38
  onNotificationClick(notification);
38
39
  }, [archiveMutation, onNotificationClick]);
39
- // I3: surface a failed/incomplete clear instead of swallowing it. `onClearAll` (and the
40
- // tray-closing side effects the app wires to it) fires ONLY when the active set was fully
41
- // drained — otherwise the user keeps the "notifications remain — try again" affordance.
40
+ // Surface a failed/incomplete clear instead of swallowing it. `onClearAll` (and the tray-closing
41
+ // side effects the app wires to it) fires ONLY when the server job fully drained the category —
42
+ // otherwise the user keeps the "notifications remain — try again" affordance. The poller resolves
43
+ // with an explicit terminal result (`complete` | `incomplete` | `failed`) and only rejects on a
44
+ // hard enqueue/poll throw.
42
45
  const [clearIncomplete, setClearIncomplete] = useState(false);
43
46
  const handleClearAll = useCallback(async () => {
44
47
  setClearIncomplete(false);
@@ -47,12 +50,13 @@ export function NotificationList({ config, userId, category, onNotificationClick
47
50
  result = await archiveAllMutation.mutateAsync();
48
51
  }
49
52
  catch {
50
- // Hard failure: do not fire onClearAll; surface the retry affordance.
53
+ // Hard failure (enqueue/poll threw): do not fire onClearAll; surface the retry affordance.
51
54
  setClearIncomplete(true);
52
55
  return;
53
56
  }
54
57
  if (!result.complete) {
55
- // Resolved but the active set was not fully drained — keep notifications, prompt a retry.
58
+ // Job terminated without fully draining the category (incomplete/failed) — keep the
59
+ // notifications and prompt a retry.
56
60
  setClearIncomplete(true);
57
61
  return;
58
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationList.js","sourceRoot":"","sources":["../../../src/react/components/NotificationList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NotificationList.js","sourceRoot":"","sources":["../../../src/react/components/NotificationList.tsx"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { NotificationList } from './NotificationList.js';
2
+ export { NotificationHistoryList } from './NotificationHistoryList.js';
2
3
  export { NotificationEmptyState } from './NotificationEmptyState.js';
3
4
  export { NotificationUnreadBadge } from './NotificationUnreadBadge.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { NotificationList } from './NotificationList.js';
2
+ export { NotificationHistoryList } from './NotificationHistoryList.js';
2
3
  export { NotificationEmptyState } from './NotificationEmptyState.js';
3
4
  export { NotificationUnreadBadge } from './NotificationUnreadBadge.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { useActiveNotifications } from './useActiveNotifications.js';
2
+ export { useNotificationHistory } from './useNotificationHistory.js';
2
3
  export { useUnreadCount } from './useUnreadCount.js';
3
4
  export { useArchiveNotification } from './useArchiveNotification.js';
4
5
  export { useArchiveAllNotifications } from './useArchiveAllNotifications.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { useActiveNotifications } from './useActiveNotifications.js';
2
+ export { useNotificationHistory } from './useNotificationHistory.js';
2
3
  export { useUnreadCount } from './useUnreadCount.js';
3
4
  export { useArchiveNotification } from './useArchiveNotification.js';
4
5
  export { useArchiveAllNotifications } from './useArchiveAllNotifications.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
@@ -1,22 +1,33 @@
1
- import type { ArchiveAllLoopResult, UseArchiveAllNotificationsOptions } from '../../types.js';
1
+ import type { ArchiveAllPollResult, UseArchiveAllNotificationsOptions } from '../../types.js';
2
2
  /**
3
- * Archive the caller's whole category through an app-supplied callable adapter.
3
+ * Trigger + poll a SERVER-OWNED archive-all job for one notification category.
4
4
  *
5
- * Like {@link useArchiveNotification}, this performs no client Firestore writes
6
- * paging + ownership-checked deletion happen server-side via the callable the
7
- * app wires into `archiveAllFn`. On success it invalidates the read keys.
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.
8
17
  *
9
18
  * @example
10
19
  * ```tsx
11
- * const archiveNotification = httpsCallable(functions, 'archiveNotification');
20
+ * const enqueue = httpsCallable(functions, 'enqueueArchiveAll');
21
+ * const getStatus = httpsCallable(functions, 'getArchiveAllStatus');
12
22
  * const archiveAll = useArchiveAllNotifications({
13
23
  * userId: user.uid,
14
24
  * category: 'user',
15
- * archiveAllFn: () => archiveNotification({ category: 'user', scope: { kind: 'all' } }),
25
+ * enqueueArchiveAllFn: (category) => enqueue({ category }).then((r) => r.data),
26
+ * getArchiveAllStatusFn: (jobId) => getStatus({ jobId }).then((r) => r.data),
16
27
  * });
17
28
  *
18
29
  * archiveAll.mutate();
19
30
  * ```
20
31
  */
21
- export declare function useArchiveAllNotifications({ userId, category, archiveAllFn, invalidateKeys, }: UseArchiveAllNotificationsOptions): import("@tanstack/react-query").UseMutationResult<ArchiveAllLoopResult, Error, void, unknown>;
32
+ export declare function useArchiveAllNotifications({ userId, category, enqueueArchiveAllFn, getArchiveAllStatusFn, pollIntervalMs, maxPolls, invalidateKeys, }: UseArchiveAllNotificationsOptions): import("@tanstack/react-query").UseMutationResult<ArchiveAllPollResult, Error, void, unknown>;
22
33
  //# 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,EAAE,oBAAoB,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AAE9F;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,EAAE,iCAAiC,iGA0CnC"}
1
+ {"version":3,"file":"useArchiveAllNotifications.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,oBAAoB,EACpB,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AASxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,cAAyC,EACzC,QAA4B,EAC5B,cAAc,GACf,EAAE,iCAAiC,iGAqDnC"}
@@ -1,25 +1,41 @@
1
1
  'use client';
2
2
  import { useMutation, useQueryClient } from '@tanstack/react-query';
3
+ const DEFAULT_POLL_INTERVAL_MS = 1500;
4
+ const DEFAULT_MAX_POLLS = 120; // ~3 min at the default interval
5
+ function sleep(ms) {
6
+ return new Promise((resolve) => setTimeout(resolve, ms));
7
+ }
3
8
  /**
4
- * Archive the caller's whole category through an app-supplied callable adapter.
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.
5
19
  *
6
- * Like {@link useArchiveNotification}, this performs no client Firestore writes
7
- * paging + ownership-checked deletion happen server-side via the callable the
8
- * app wires into `archiveAllFn`. On success it invalidates the read keys.
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.
9
23
  *
10
24
  * @example
11
25
  * ```tsx
12
- * const archiveNotification = httpsCallable(functions, 'archiveNotification');
26
+ * const enqueue = httpsCallable(functions, 'enqueueArchiveAll');
27
+ * const getStatus = httpsCallable(functions, 'getArchiveAllStatus');
13
28
  * const archiveAll = useArchiveAllNotifications({
14
29
  * userId: user.uid,
15
30
  * category: 'user',
16
- * archiveAllFn: () => archiveNotification({ category: 'user', scope: { kind: 'all' } }),
31
+ * enqueueArchiveAllFn: (category) => enqueue({ category }).then((r) => r.data),
32
+ * getArchiveAllStatusFn: (jobId) => getStatus({ jobId }).then((r) => r.data),
17
33
  * });
18
34
  *
19
35
  * archiveAll.mutate();
20
36
  * ```
21
37
  */
22
- export function useArchiveAllNotifications({ userId, category, archiveAllFn, invalidateKeys, }) {
38
+ export function useArchiveAllNotifications({ userId, category, enqueueArchiveAllFn, getArchiveAllStatusFn, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, maxPolls = DEFAULT_MAX_POLLS, invalidateKeys, }) {
23
39
  const queryClient = useQueryClient();
24
40
  const defaultInvalidateKeys = [
25
41
  ['notifications', 'active', category, userId],
@@ -27,37 +43,44 @@ export function useArchiveAllNotifications({ userId, category, archiveAllFn, inv
27
43
  ['notifications', 'history', category, userId],
28
44
  ];
29
45
  return useMutation({
30
- // I3: "Clear All" must actually clear ALL but it must also report HONESTLY when it could not.
31
- // The server bounds each call to a small page and returns `hasMore`; loop the adapter until the
32
- // active set is drained (bounded so a pathological tray can't run forever). The loop resolves on
33
- // an incomplete drain rather than throwing, so it returns an explicit `complete` flag the caller
34
- // gates side effects on (it is NOT a plain success — see ArchiveAllLoopResult).
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.
35
50
  mutationFn: async () => {
36
- const MAX_PASSES = 25; // bound: 25 × the server page cap — far beyond any real tray
37
- let archived = 0;
38
- let hasMore = true;
39
- let pass = 0;
40
- while (hasMore && pass < MAX_PASSES) {
41
- const r = await archiveAllFn();
42
- archived += r.archived;
43
- hasMore = r.hasMore;
44
- pass += 1;
45
- // No-progress guard: a pass that archived nothing yet still reports `hasMore` cannot make
46
- // progress on the next pass either (e.g. a generation-less head card the server can never
47
- // advance past) — looping would livelock to the pass bound. Stop now and report incomplete.
48
- if (r.archived === 0 && hasMore)
49
- break;
51
+ 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
+ let lastArchived = 0;
56
+ for (let poll = 0; poll < maxPolls; poll += 1) {
57
+ const snap = await getArchiveAllStatusFn(jobId);
58
+ lastArchived = snap.archived ?? 0;
59
+ if (snap.state === 'complete') {
60
+ return { status: 'complete', category, archived: lastArchived, complete: true };
61
+ }
62
+ if (snap.state === 'incomplete') {
63
+ return { status: 'incomplete', category, archived: lastArchived, complete: false };
64
+ }
65
+ if (snap.state === 'failed') {
66
+ return { status: 'failed', category, archived: lastArchived, complete: false, error: snap.error };
67
+ }
68
+ // Still in-progress — wait before the next poll unless this was the last allowed poll.
69
+ if (poll < maxPolls - 1)
70
+ await sleep(pollIntervalMs);
50
71
  }
51
- // `complete` is true ONLY when the active set was fully drained (`hasMore` is false). Hitting
52
- // the pass bound or the no-progress guard with `hasMore` still true returns an EXPLICIT
53
- // incomplete result, never a plain success.
54
- return { archived, hasMore, complete: !hasMore };
72
+ // Guard: the job never reached a terminal state within the poll budget report incomplete.
73
+ return { status: 'incomplete', category, archived: lastArchived, complete: false };
55
74
  },
56
- onSuccess: () => {
75
+ onSuccess: (result) => {
76
+ // Only a completed drain changed the active set; still invalidate on an incomplete drain since
77
+ // SOME cards were archived. A hard enqueue/poll failure rejects the mutation (onError), so this
78
+ // never runs for `failed`.
57
79
  const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;
58
80
  keysToInvalidate.forEach((key) => {
59
81
  queryClient.invalidateQueries({ queryKey: [...key], exact: false });
60
82
  });
83
+ void result;
61
84
  },
62
85
  });
63
86
  }
@@ -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;AAGpE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,0BAA0B,CAAC,EACzC,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,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,gGAAgG;QAChG,gGAAgG;QAChG,iGAAiG;QACjG,iGAAiG;QACjG,gFAAgF;QAChF,UAAU,EAAE,KAAK,IAAmC,EAAE;YACpD,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC,6DAA6D;YACpF,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,IAAI,OAAO,GAAG,IAAI,CAAC;YACnB,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,OAAO,OAAO,IAAI,IAAI,GAAG,UAAU,EAAE,CAAC;gBACpC,MAAM,CAAC,GAAG,MAAM,YAAY,EAAE,CAAC;gBAC/B,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC;gBACvB,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;gBACpB,IAAI,IAAI,CAAC,CAAC;gBACV,0FAA0F;gBAC1F,0FAA0F;gBAC1F,4FAA4F;gBAC5F,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,OAAO;oBAAE,MAAM;YACzC,CAAC;YACD,8FAA8F;YAC9F,wFAAwF;YACxF,4CAA4C;YAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC;QACD,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"}
1
+ {"version":3,"file":"useArchiveAllNotifications.js","sourceRoot":"","sources":["../../../src/react/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
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
+ export declare function useNotificationHistory({ config, userId, category, enabled, pageSize, staleTime, }: UseNotificationHistoryOptions): import("@ttt-productions/query-core/react").UseFirestorePaginatedResult<NotificationHistoryItem>;
17
+ //# sourceMappingURL=useNotificationHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotificationHistory.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,uBAAuB,EACvB,6BAA6B,EAC9B,MAAM,gBAAgB,CAAC;AAKxB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAc,EACd,QAA4B,EAC5B,SAA8B,GAC/B,EAAE,6BAA6B,oGA4B/B"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { useFirestorePaginated } from '@ttt-productions/query-core/react';
3
+ import { orderBy } from 'firebase/firestore';
4
+ const DEFAULT_PAGE_SIZE = 20;
5
+ const DEFAULT_STALE_TIME = 60_000;
6
+ /**
7
+ * Paginated read of ARCHIVED notification history (the two-tier system's history
8
+ * tier). Personal history lives under a per-user path
9
+ * (`userProfiles/{uid}/notificationHistory`) and shared/admin history under a
10
+ * top-level collection (`adminNotificationHistory`) — both resolved from the
11
+ * category's `historyPath`, so no `where` clause is needed either way. Rows are
12
+ * immutable and ordered newest-first by `archivedAt`.
13
+ *
14
+ * Each history doc is a wrapper (`archivedSnapshot` + archive metadata + a
15
+ * native-TTL `expireAt`); the `select` mapper flattens it into a
16
+ * {@link NotificationHistoryItem} so the read surface renders like the active
17
+ * list. Owner-only (user) / admin-only (admin) reads are enforced by Firestore
18
+ * rules — this hook performs no writes.
19
+ */
20
+ export function useNotificationHistory({ config, userId, category, enabled = true, pageSize = DEFAULT_PAGE_SIZE, staleTime = DEFAULT_STALE_TIME, }) {
21
+ const categoryConfig = config.categories[category];
22
+ if (!categoryConfig) {
23
+ throw new Error(`[notification-core] Unknown category: ${category}`);
24
+ }
25
+ const collectionPath = categoryConfig.historyPath(userId);
26
+ const constraints = [orderBy('archivedAt', 'desc')];
27
+ return useFirestorePaginated({
28
+ collectionPath,
29
+ queryKey: ['notifications', 'history', category, userId, { pageSize }],
30
+ constraints,
31
+ pageSize,
32
+ enabled: enabled && !!userId,
33
+ staleTime,
34
+ select: (data) => {
35
+ const snapshot = (data.archivedSnapshot ?? {});
36
+ return {
37
+ ...snapshot,
38
+ // The archive occurrence id is the stable history-doc key; expose
39
+ // archivedAt for ordering/display.
40
+ archiveOccurrenceId: data.id,
41
+ archivedAt: data.archivedAt ?? snapshot.updatedAt ?? 0,
42
+ };
43
+ },
44
+ });
45
+ }
46
+ //# sourceMappingURL=useNotificationHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotificationHistory.js","sourceRoot":"","sources":["../../../src/react/hooks/useNotificationHistory.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAwB,MAAM,oBAAoB,CAAC;AAOnE,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;;;;;;;;;GAaG;AACH,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,OAAO,qBAAqB,CAA0B;QACpD,cAAc;QACd,QAAQ,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC;QACtE,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,kEAAkE;gBAClE,mCAAmC;gBACnC,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;AACL,CAAC"}
@@ -1,3 +1,3 @@
1
- export { useActiveNotifications, useUnreadCount, useArchiveNotification, useArchiveAllNotifications, } from './hooks/index.js';
2
- export { NotificationList, NotificationEmptyState, NotificationUnreadBadge, } from './components/index.js';
1
+ export { useActiveNotifications, useNotificationHistory, useUnreadCount, useArchiveNotification, useArchiveAllNotifications, } from './hooks/index.js';
2
+ export { NotificationList, NotificationHistoryList, NotificationEmptyState, NotificationUnreadBadge, } from './components/index.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  // Hooks
3
- export { useActiveNotifications, useUnreadCount, useArchiveNotification, useArchiveAllNotifications, } from './hooks/index.js';
3
+ export { useActiveNotifications, useNotificationHistory, useUnreadCount, useArchiveNotification, useArchiveAllNotifications, } from './hooks/index.js';
4
4
  // Components
5
- export { NotificationList, NotificationEmptyState, NotificationUnreadBadge, } from './components/index.js';
5
+ export { NotificationList, NotificationHistoryList, NotificationEmptyState, NotificationUnreadBadge, } from './components/index.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,QAAQ;AACR,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,aAAa;AACb,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,QAAQ;AACR,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,aAAa;AACb,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -147,6 +147,28 @@ export interface UseActiveNotificationsOptions {
147
147
  pageSize?: number;
148
148
  refetchInterval?: number;
149
149
  }
150
+ /**
151
+ * A flattened archived-history item for the read surface. The history doc is a
152
+ * wrapper (`archivedSnapshot` + archive metadata + TTL); this shape lifts the
153
+ * snapshot's display fields to the top level so the history list renders exactly
154
+ * like the active list, plus `archivedAt` for ordering/display and
155
+ * `archiveOccurrenceId` as the stable React key.
156
+ */
157
+ export interface NotificationHistoryItem extends NotificationDoc {
158
+ /** The history doc id (stable key — the `archiveOccurrenceId`). */
159
+ archiveOccurrenceId: string;
160
+ /** Epoch ms the notification was archived (history docs order by this). */
161
+ archivedAt: number;
162
+ }
163
+ export interface UseNotificationHistoryOptions {
164
+ config: NotificationSystemConfig;
165
+ userId: string;
166
+ category: string;
167
+ enabled?: boolean;
168
+ pageSize?: number;
169
+ /** Read-freshness stale time in ms (archived rows are immutable; default 60s). */
170
+ staleTime?: number;
171
+ }
150
172
  export interface UseUnreadCountOptions {
151
173
  config: NotificationSystemConfig;
152
174
  userId: string;
@@ -168,42 +190,79 @@ export interface UseArchiveNotificationOptions {
168
190
  invalidateKeys?: readonly unknown[][];
169
191
  }
170
192
  /**
171
- * Result of ONE archive-all server call. `hasMore` is true when the server hit its per-invocation
172
- * cap and the caller's active set was NOT fully drained — `useArchiveAllNotifications` loops the
173
- * adapter until `hasMore` is false so "Clear All" actually clears all (I3).
193
+ * Terminal + transient states of a server-owned archive-all job, as surfaced to the UI.
174
194
  *
175
- * NOTE: the loop's aggregate result carries the same `archived`/`hasMore` shape PLUS a `complete`
176
- * flag (`ArchiveAllLoopResult`); a single server page does not know whether the WHOLE active set
177
- * was drained, so the per-call shape here intentionally has no `complete`.
195
+ * - `idle` — nothing enqueued yet (initial / reset state).
196
+ * - `enqueuing` the enqueue adapter is in flight (job not yet acknowledged).
197
+ * - `in-progress` the job is enqueued/running; the worker is still draining the category.
198
+ * - `complete` — TERMINAL: the worker fully drained the category's active set.
199
+ * - `incomplete` — TERMINAL: the job stopped without draining everything (e.g. hit its own
200
+ * bound / a non-advanceable card). The tray keeps a "some remain — try again"
201
+ * affordance, mirroring the old loop's incomplete result.
202
+ * - `failed` — TERMINAL: the enqueue call threw, or the job dead-lettered / reported failure.
178
203
  */
179
- export interface ArchiveAllResult {
180
- archived: number;
181
- hasMore: boolean;
204
+ export type ArchiveAllJobStatus = 'idle' | 'enqueuing' | 'in-progress' | 'complete' | 'incomplete' | 'failed';
205
+ /**
206
+ * Result of the INJECTED enqueue adapter — triggers exactly one server-owned archive-all job for
207
+ * the given category and returns immediately with the job's id (idempotent per user+category+request
208
+ * on the app side). The app wires this to its `httpsCallable(functions, 'enqueueArchiveAll')`
209
+ * (or equivalent); the generic package never names the callable.
210
+ */
211
+ export interface EnqueueArchiveAllResult {
212
+ /** Server job id the poller then polls via the status adapter. */
213
+ jobId: string;
214
+ }
215
+ /**
216
+ * Snapshot of a server-owned archive-all job, returned by the INJECTED status-query adapter.
217
+ * The poller maps `state` → {@link ArchiveAllJobStatus} and stops polling on any terminal state.
218
+ * `category` echoes the tab/category the job was enqueued for so the UI can assert per-tab scoping.
219
+ */
220
+ export interface ArchiveAllJobSnapshot {
221
+ /** Job lifecycle state as owned by the server worker. */
222
+ state: 'in-progress' | 'complete' | 'incomplete' | 'failed';
223
+ /** The notification category/tab this job is scoped to (must match the enqueue category). */
224
+ category: string;
225
+ /** Cards archived so far (monotonic); optional progress for the UI. */
226
+ archived?: number;
227
+ /** Human-readable failure reason when `state === 'failed'` (optional). */
228
+ error?: string;
182
229
  }
183
230
  /**
184
- * Aggregate result of the `useArchiveAllNotifications` continuation loop (I3). `complete` is the
185
- * load-bearing completion contract: it is `true` ONLY when the active set was fully drained
186
- * (`hasMore` reached `false`). It is `false` when the loop stopped early either the no-progress
187
- * guard tripped (a pass archived nothing yet still reported `hasMore`, e.g. a generation-less head
188
- * card that can't advance) or the per-mutation pass bound was hit with `hasMore` still `true`.
189
- * Callers MUST gate post-clear side effects (closing the tray, `onClearAll`) on `complete`, not on
190
- * mere resolution — the mutation resolves on an incomplete drain rather than throwing.
231
+ * Aggregate result the poller resolves with once the job reaches a terminal state.
232
+ * `complete` is the load-bearing completion contract (true ONLY when the whole category was
233
+ * drained), preserved from the old loop so callers gate side effects the same way.
191
234
  */
192
- export interface ArchiveAllLoopResult {
235
+ export interface ArchiveAllPollResult {
236
+ status: Extract<ArchiveAllJobStatus, 'complete' | 'incomplete' | 'failed'>;
237
+ category: string;
193
238
  archived: number;
194
- hasMore: boolean;
195
239
  complete: boolean;
240
+ error?: string;
196
241
  }
197
242
  export interface UseArchiveAllNotificationsOptions {
198
243
  userId: string;
199
244
  category: string;
200
245
  /**
201
- * App-supplied adapter that archives ONE bounded page of the caller's whole category — typically
202
- * an `httpsCallable(functions, 'archiveNotification')` invoked with the `{ kind: 'all' }` scope.
203
- * Returns `{ archived, hasMore }`; the hook re-invokes it while `hasMore` is true. No client
204
- * Firestore writes.
246
+ * App-supplied adapter that ENQUEUES one server-owned archive-all job scoped to `category` and
247
+ * returns its `jobId`. The `category` is passed so the app callable can carry the tab/source into
248
+ * the job (per-tab scoping is REQUIRED — the worker archives only that category's active cards).
249
+ * Typically `(category) => enqueueArchiveAll({ category })` over an `httpsCallable`.
250
+ */
251
+ enqueueArchiveAllFn: (category: string) => Promise<EnqueueArchiveAllResult>;
252
+ /**
253
+ * App-supplied adapter that returns the current {@link ArchiveAllJobSnapshot} for a job id — the
254
+ * hook polls it until the job reaches a terminal state. Typically
255
+ * `(jobId) => getArchiveAllStatus({ jobId })` over an `httpsCallable`, or a Firestore read of the
256
+ * job doc. No client Firestore writes.
257
+ */
258
+ getArchiveAllStatusFn: (jobId: string) => Promise<ArchiveAllJobSnapshot>;
259
+ /** Poll interval in ms while the job is in-progress (default 1500). */
260
+ pollIntervalMs?: number;
261
+ /**
262
+ * Max number of status polls before the poller gives up and resolves `incomplete` (guards a job
263
+ * that never reaches a terminal state — default 120, i.e. ~3 min at the default interval).
205
264
  */
206
- archiveAllFn: () => Promise<ArchiveAllResult>;
265
+ maxPolls?: number;
207
266
  invalidateKeys?: readonly unknown[][];
208
267
  }
209
268
  export interface NotificationListProps {
@@ -217,15 +276,35 @@ export interface NotificationListProps {
217
276
  */
218
277
  archiveFn: (notificationId: string) => Promise<unknown>;
219
278
  /**
220
- * Adapter that archives ONE bounded page of the caller's whole category (returns
221
- * `{ archived, hasMore }`). Passed straight through to `useArchiveAllNotifications`, which loops
222
- * it until fully drained; the app wires it to its callable.
279
+ * Adapter that ENQUEUES one server-owned archive-all job scoped to `category` and returns its
280
+ * `jobId`. Passed straight through to `useArchiveAllNotifications`; the app wires it to its
281
+ * enqueue callable. Per-tab scoping: the category flows through to the job.
282
+ */
283
+ enqueueArchiveAllFn: (category: string) => Promise<EnqueueArchiveAllResult>;
284
+ /**
285
+ * Adapter that returns the current status snapshot for an archive-all `jobId`. Passed straight
286
+ * through to `useArchiveAllNotifications`, which polls it until the job is terminal; the app wires
287
+ * it to its status callable / job-doc read.
223
288
  */
224
- archiveAllFn: () => Promise<ArchiveAllResult>;
289
+ getArchiveAllStatusFn: (jobId: string) => Promise<ArchiveAllJobSnapshot>;
225
290
  onClearAll?: () => void;
226
291
  refetchInterval?: number;
227
292
  emptyText?: string;
228
293
  }
294
+ export interface NotificationHistoryListProps {
295
+ config: NotificationSystemConfig;
296
+ userId: string;
297
+ category: string;
298
+ /**
299
+ * Optional click handler for an archived row (e.g. navigate to `targetPath`).
300
+ * Archived rows are read-only — there is no re-archive; the row does not mutate
301
+ * state on click. Omit to render non-interactive rows.
302
+ */
303
+ onNotificationClick?: (notification: NotificationHistoryItem) => void;
304
+ pageSize?: number;
305
+ staleTime?: number;
306
+ emptyText?: string;
307
+ }
229
308
  export interface NotificationEmptyStateProps {
230
309
  text?: string;
231
310
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/notification-core",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Shared notification system for TTT Productions apps — active/history two-tier architecture with dedup, batch processing, and themed UI components",
5
5
  "repository": {
6
6
  "type": "git",