@ttt-productions/notification-core 0.2.1 → 0.2.2
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/dist/components/NotificationEmptyState.d.ts +6 -0
- package/dist/components/NotificationEmptyState.d.ts.map +1 -0
- package/dist/components/NotificationEmptyState.js +9 -0
- package/dist/components/NotificationEmptyState.js.map +1 -0
- package/dist/components/NotificationHistoryList.d.ts +7 -0
- package/dist/components/NotificationHistoryList.d.ts.map +1 -0
- package/dist/components/NotificationHistoryList.js +41 -0
- package/dist/components/NotificationHistoryList.js.map +1 -0
- package/dist/components/NotificationList.d.ts +6 -0
- package/dist/components/NotificationList.d.ts.map +1 -0
- package/dist/components/NotificationList.js +79 -0
- package/dist/components/NotificationList.js.map +1 -0
- package/dist/components/NotificationUnreadBadge.d.ts +8 -0
- package/dist/components/NotificationUnreadBadge.d.ts.map +1 -0
- package/dist/components/NotificationUnreadBadge.js +21 -0
- package/dist/components/NotificationUnreadBadge.js.map +1 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/relative-time.d.ts +5 -0
- package/dist/components/relative-time.d.ts.map +1 -0
- package/dist/components/relative-time.js +23 -0
- package/dist/components/relative-time.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -7
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -3
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useActiveNotifications.d.ts +7 -0
- package/dist/hooks/useActiveNotifications.d.ts.map +1 -0
- package/dist/hooks/useActiveNotifications.js +30 -0
- package/dist/hooks/useActiveNotifications.js.map +1 -0
- package/dist/hooks/useArchiveAllNotifications.d.ts +24 -0
- package/dist/hooks/useArchiveAllNotifications.d.ts.map +1 -0
- package/dist/hooks/useArchiveAllNotifications.js +87 -0
- package/dist/hooks/useArchiveAllNotifications.js.map +1 -0
- package/dist/hooks/useArchiveNotification.d.ts +23 -0
- package/dist/hooks/useArchiveNotification.d.ts.map +1 -0
- package/dist/hooks/useArchiveNotification.js +68 -0
- package/dist/hooks/useArchiveNotification.js.map +1 -0
- package/dist/hooks/useNotificationHistory.d.ts +16 -0
- package/dist/hooks/useNotificationHistory.d.ts.map +1 -0
- package/dist/hooks/useNotificationHistory.js +35 -0
- package/dist/hooks/useNotificationHistory.js.map +1 -0
- package/dist/hooks/useUnreadCount.d.ts +175 -15
- package/dist/hooks/useUnreadCount.d.ts.map +1 -1
- package/dist/hooks/useUnreadCount.js +27 -13
- package/dist/hooks/useUnreadCount.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/server/archiveNotificationHelper.d.ts +31 -0
- package/dist/server/archiveNotificationHelper.d.ts.map +1 -0
- package/dist/server/archiveNotificationHelper.js +83 -0
- package/dist/server/archiveNotificationHelper.js.map +1 -0
- package/dist/server/createNotificationHelper.d.ts +25 -0
- package/dist/server/createNotificationHelper.d.ts.map +1 -0
- package/dist/server/createNotificationHelper.js +118 -0
- package/dist/server/createNotificationHelper.js.map +1 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +5 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/processBatchHelper.d.ts +29 -0
- package/dist/server/processBatchHelper.d.ts.map +1 -0
- package/dist/server/processBatchHelper.js +158 -0
- package/dist/server/processBatchHelper.js.map +1 -0
- package/dist/server/types.d.ts +81 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +6 -0
- package/dist/server/types.js.map +1 -0
- package/dist/types.d.ts +159 -44
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/styles/notifications.css +206 -0
- package/dist/hooks/useMarkAllAsRead.d.ts +0 -30
- package/dist/hooks/useMarkAllAsRead.d.ts.map +0 -1
- package/dist/hooks/useMarkAllAsRead.js +0 -44
- package/dist/hooks/useMarkAllAsRead.js.map +0 -1
- package/dist/hooks/useMarkAsRead.d.ts +0 -30
- package/dist/hooks/useMarkAsRead.d.ts.map +0 -1
- package/dist/hooks/useMarkAsRead.js +0 -40
- package/dist/hooks/useMarkAsRead.js.map +0 -1
- package/dist/hooks/useNotifications.d.ts +0 -25
- package/dist/hooks/useNotifications.d.ts.map +0 -1
- package/dist/hooks/useNotifications.js +0 -31
- package/dist/hooks/useNotifications.js.map +0 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NotificationEmptyStateProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Empty state shown when there are no active notifications.
|
|
4
|
+
*/
|
|
5
|
+
export declare function NotificationEmptyState({ text, }: NotificationEmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=NotificationEmptyState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationEmptyState.d.ts","sourceRoot":"","sources":["../../src/components/NotificationEmptyState.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAE/D;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAA8B,GAC/B,EAAE,2BAA2B,2CAqB7B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Empty state shown when there are no active notifications.
|
|
5
|
+
*/
|
|
6
|
+
export function NotificationEmptyState({ text = "You're all caught up!", }) {
|
|
7
|
+
return (_jsxs("div", { className: "ntf-empty", children: [_jsxs("svg", { className: "ntf-empty-icon", xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }), _jsx("polyline", { points: "22 4 12 14.01 9 11.01" })] }), _jsx("p", { className: "ntf-empty-text", children: text })] }));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=NotificationEmptyState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationEmptyState.js","sourceRoot":"","sources":["../../src/components/NotificationEmptyState.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAIb;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,IAAI,GAAG,uBAAuB,GACF;IAC5B,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,eACE,SAAS,EAAC,gBAAgB,EAC1B,KAAK,EAAC,4BAA4B,EAClC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,eAAM,CAAC,EAAC,oCAAoC,GAAG,EAC/C,mBAAU,MAAM,EAAC,uBAAuB,GAAG,IACvC,EACN,YAAG,SAAS,EAAC,gBAAgB,YAAE,IAAI,GAAK,IACpC,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NotificationHistoryListProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Paginated list of archived notifications. Read-only (no clear all).
|
|
4
|
+
* Supports optional re-navigation on click.
|
|
5
|
+
*/
|
|
6
|
+
export declare function NotificationHistoryList({ config, userId, category, onNotificationClick, }: NotificationHistoryListProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=NotificationHistoryList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationHistoryList.d.ts","sourceRoot":"","sources":["../../src/components/NotificationHistoryList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAA0B,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAExF;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,GACpB,EAAE,4BAA4B,2CAwF9B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Badge, Button, Separator } from '@ttt-productions/ui-core';
|
|
4
|
+
import { useNotificationHistory } from '../hooks/useNotificationHistory.js';
|
|
5
|
+
import { formatRelativeTime } from './relative-time.js';
|
|
6
|
+
/**
|
|
7
|
+
* Paginated list of archived notifications. Read-only (no clear all).
|
|
8
|
+
* Supports optional re-navigation on click.
|
|
9
|
+
*/
|
|
10
|
+
export function NotificationHistoryList({ config, userId, category, onNotificationClick, }) {
|
|
11
|
+
const { data: history, isLoading, hasNextPage, nextPage, } = useNotificationHistory({
|
|
12
|
+
config,
|
|
13
|
+
userId,
|
|
14
|
+
category,
|
|
15
|
+
});
|
|
16
|
+
const getTypeIcon = (type) => {
|
|
17
|
+
const typeConfig = config.types[type];
|
|
18
|
+
return typeConfig?.icon ?? '🔔';
|
|
19
|
+
};
|
|
20
|
+
if (isLoading) {
|
|
21
|
+
return _jsx("div", { className: "ntf-loading", children: "Loading history..." });
|
|
22
|
+
}
|
|
23
|
+
if (!history || history.length === 0) {
|
|
24
|
+
return (_jsx("div", { className: "ntf-empty", children: _jsx("p", { className: "ntf-empty-text", children: "No notification history" }) }));
|
|
25
|
+
}
|
|
26
|
+
return (_jsxs("div", { className: "ntf-list", children: [_jsx("div", { className: "ntf-list-header", children: _jsx("span", { className: "ntf-list-header-title", children: "History" }) }), _jsx(Separator, {}), history.map((item) => {
|
|
27
|
+
const isClickable = !!onNotificationClick;
|
|
28
|
+
const className = isClickable
|
|
29
|
+
? 'ntf-history-item ntf-history-item--clickable'
|
|
30
|
+
: 'ntf-history-item';
|
|
31
|
+
return (_jsxs("div", { className: className, role: isClickable ? 'button' : undefined, tabIndex: isClickable ? 0 : undefined, onClick: isClickable ? () => onNotificationClick(item) : undefined, onKeyDown: isClickable
|
|
32
|
+
? (e) => {
|
|
33
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
onNotificationClick(item);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
: undefined, children: [_jsx("div", { className: "ntf-item-icon", children: getTypeIcon(item.type) }), _jsxs("div", { className: "ntf-item-content", children: [_jsx("div", { className: "ntf-item-title", children: item.title }), _jsx("div", { className: "ntf-item-message", children: item.message }), _jsx("div", { className: "ntf-item-timestamp", children: formatRelativeTime(item.archival.archivedAt) })] }), item.count > 1 && (_jsx("div", { className: "ntf-item-count", children: _jsxs(Badge, { variant: "secondary", children: ["\u00D7", item.count] }) }))] }, item.id));
|
|
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/components/NotificationHistoryList.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NotificationListProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scrollable list of active notifications with click-to-archive and clear-all.
|
|
4
|
+
*/
|
|
5
|
+
export declare function NotificationList({ config, userId, category, onNotificationClick, onClearAll, refetchInterval, device, emptyText, }: NotificationListProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=NotificationList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationList.d.ts","sourceRoot":"","sources":["../../src/components/NotificationList.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,MAAc,EACd,SAAS,GACV,EAAE,qBAAqB,2CA2IvB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
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, Separator } from '@ttt-productions/ui-core';
|
|
5
|
+
import { useActiveNotifications } from '../hooks/useActiveNotifications.js';
|
|
6
|
+
import { useArchiveNotification } from '../hooks/useArchiveNotification.js';
|
|
7
|
+
import { useArchiveAllNotifications } from '../hooks/useArchiveAllNotifications.js';
|
|
8
|
+
import { useUnreadCount } from '../hooks/useUnreadCount.js';
|
|
9
|
+
import { NotificationEmptyState } from './NotificationEmptyState.js';
|
|
10
|
+
import { formatRelativeTime } from './relative-time.js';
|
|
11
|
+
/**
|
|
12
|
+
* Scrollable list of active notifications with click-to-archive and clear-all.
|
|
13
|
+
*/
|
|
14
|
+
export function NotificationList({ config, userId, category, onNotificationClick, onClearAll, refetchInterval, device = 'web', emptyText, }) {
|
|
15
|
+
const { data: notifications, isLoading, hasNextPage, nextPage, } = useActiveNotifications({
|
|
16
|
+
config,
|
|
17
|
+
userId,
|
|
18
|
+
category,
|
|
19
|
+
refetchInterval,
|
|
20
|
+
});
|
|
21
|
+
const { count: unreadCount } = useUnreadCount({
|
|
22
|
+
config,
|
|
23
|
+
userId,
|
|
24
|
+
category,
|
|
25
|
+
refetchInterval,
|
|
26
|
+
});
|
|
27
|
+
const archiveMutation = useArchiveNotification({
|
|
28
|
+
config,
|
|
29
|
+
userId,
|
|
30
|
+
category,
|
|
31
|
+
});
|
|
32
|
+
const archiveAllMutation = useArchiveAllNotifications({
|
|
33
|
+
config,
|
|
34
|
+
userId,
|
|
35
|
+
category,
|
|
36
|
+
});
|
|
37
|
+
const handleNotificationClick = useCallback(async (notification) => {
|
|
38
|
+
try {
|
|
39
|
+
await archiveMutation.mutateAsync({
|
|
40
|
+
notificationId: notification.id,
|
|
41
|
+
archivalInfo: {
|
|
42
|
+
archivedBy: userId,
|
|
43
|
+
archivedAt: Date.now(),
|
|
44
|
+
device,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Still navigate even if archive fails
|
|
50
|
+
}
|
|
51
|
+
onNotificationClick(notification);
|
|
52
|
+
}, [archiveMutation, userId, device, onNotificationClick]);
|
|
53
|
+
const handleClearAll = useCallback(async () => {
|
|
54
|
+
try {
|
|
55
|
+
await archiveAllMutation.mutateAsync({
|
|
56
|
+
archivalInfo: {
|
|
57
|
+
archivedBy: userId,
|
|
58
|
+
archivedAt: Date.now(),
|
|
59
|
+
device,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Silently fail
|
|
65
|
+
}
|
|
66
|
+
onClearAll?.();
|
|
67
|
+
}, [archiveAllMutation, userId, device, onClearAll]);
|
|
68
|
+
const getTypeIcon = useCallback((type) => {
|
|
69
|
+
const typeConfig = config.types[type];
|
|
70
|
+
return typeConfig?.icon ?? '🔔';
|
|
71
|
+
}, [config]);
|
|
72
|
+
return (_jsxs("div", { className: "ntf-list", children: [_jsxs("div", { className: "ntf-list-header", children: [_jsx("span", { className: "ntf-list-header-title", children: "Notifications" }), unreadCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: handleClearAll, disabled: archiveAllMutation.isPending, children: archiveAllMutation.isPending ? 'Clearing...' : 'Clear All' }))] }), _jsx(Separator, {}), isLoading ? (_jsx("div", { className: "ntf-loading", children: "Loading..." })) : !notifications || notifications.length === 0 ? (_jsx(NotificationEmptyState, { text: emptyText })) : (_jsxs(_Fragment, { children: [notifications.map((notification) => (_jsxs("div", { className: "ntf-item", role: "button", tabIndex: 0, onClick: () => handleNotificationClick(notification), onKeyDown: (e) => {
|
|
73
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
handleNotificationClick(notification);
|
|
76
|
+
}
|
|
77
|
+
}, 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.updatedAt) })] }), notification.count > 1 && (_jsx("div", { className: "ntf-item-count", children: _jsxs(Badge, { variant: "secondary", children: ["\u00D7", notification.count] }) }))] }, notification.id))), hasNextPage && (_jsx("div", { className: "ntf-list-footer", children: _jsx(Button, { variant: "ghost", size: "sm", onClick: nextPage, children: "Load more" }) }))] }))] }));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=NotificationList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationList.js","sourceRoot":"","sources":["../../src/components/NotificationList.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NotificationUnreadBadgeProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Unread count badge (red circle with number).
|
|
4
|
+
* Renders nothing when count is 0.
|
|
5
|
+
* Apps wrap this around whatever trigger element they want.
|
|
6
|
+
*/
|
|
7
|
+
export declare function NotificationUnreadBadge({ config, userId, category, refetchInterval, }: NotificationUnreadBadgeProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
//# sourceMappingURL=NotificationUnreadBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationUnreadBadge.d.ts","sourceRoot":"","sources":["../../src/components/NotificationUnreadBadge.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,eAAe,GAChB,EAAE,4BAA4B,kDAe9B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useUnreadCount } from '../hooks/useUnreadCount.js';
|
|
4
|
+
/**
|
|
5
|
+
* Unread count badge (red circle with number).
|
|
6
|
+
* Renders nothing when count is 0.
|
|
7
|
+
* Apps wrap this around whatever trigger element they want.
|
|
8
|
+
*/
|
|
9
|
+
export function NotificationUnreadBadge({ config, userId, category, refetchInterval, }) {
|
|
10
|
+
const { count, hasMore } = useUnreadCount({
|
|
11
|
+
config,
|
|
12
|
+
userId,
|
|
13
|
+
category,
|
|
14
|
+
refetchInterval,
|
|
15
|
+
});
|
|
16
|
+
if (count === 0)
|
|
17
|
+
return null;
|
|
18
|
+
const displayCount = hasMore ? '99+' : count > 9 ? '9+' : String(count);
|
|
19
|
+
return (_jsx("span", { className: "ntf-badge", children: displayCount }));
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=NotificationUnreadBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationUnreadBadge.js","sourceRoot":"","sources":["../../src/components/NotificationUnreadBadge.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG5D;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,eAAe,GACc;IAC7B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;QACxC,MAAM;QACN,MAAM;QACN,QAAQ;QACR,eAAe;KAChB,CAAC,CAAC;IAEH,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAExE,OAAO,CACL,eAAM,SAAS,EAAC,WAAW,YAAE,YAAY,GAAQ,CAClD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { NotificationList } from './NotificationList.js';
|
|
2
|
+
export { NotificationEmptyState } from './NotificationEmptyState.js';
|
|
3
|
+
export { NotificationHistoryList } from './NotificationHistoryList.js';
|
|
4
|
+
export { NotificationUnreadBadge } from './NotificationUnreadBadge.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/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;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { NotificationList } from './NotificationList.js';
|
|
2
|
+
export { NotificationEmptyState } from './NotificationEmptyState.js';
|
|
3
|
+
export { NotificationHistoryList } from './NotificationHistoryList.js';
|
|
4
|
+
export { NotificationUnreadBadge } from './NotificationUnreadBadge.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/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;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relative-time.d.ts","sourceRoot":"","sources":["../../src/components/relative-time.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAc1D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format epoch ms to a relative time string (e.g. "2m ago", "3h ago", "1d ago").
|
|
3
|
+
*/
|
|
4
|
+
export function formatRelativeTime(epochMs) {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
const diffMs = now - epochMs;
|
|
7
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
8
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
9
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
10
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
11
|
+
if (diffSec < 60)
|
|
12
|
+
return 'just now';
|
|
13
|
+
if (diffMin < 60)
|
|
14
|
+
return `${diffMin}m ago`;
|
|
15
|
+
if (diffHr < 24)
|
|
16
|
+
return `${diffHr}h ago`;
|
|
17
|
+
if (diffDay < 7)
|
|
18
|
+
return `${diffDay}d ago`;
|
|
19
|
+
if (diffDay < 30)
|
|
20
|
+
return `${Math.floor(diffDay / 7)}w ago`;
|
|
21
|
+
return `${Math.floor(diffDay / 30)}mo ago`;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=relative-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relative-time.js","sourceRoot":"","sources":["../../src/components/relative-time.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAExC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,UAAU,CAAC;IACpC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3C,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,GAAG,MAAM,OAAO,CAAC;IACzC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC1C,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;IAC3D,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC;AAC7C,CAAC"}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { UseNotificationsOptions } from './useNotifications.js';
|
|
1
|
+
export { useActiveNotifications } from './useActiveNotifications.js';
|
|
3
2
|
export { useUnreadCount } from './useUnreadCount.js';
|
|
4
|
-
export
|
|
5
|
-
export {
|
|
6
|
-
export
|
|
7
|
-
export { useMarkAllAsRead } from './useMarkAllAsRead.js';
|
|
8
|
-
export type { UseMarkAllAsReadOptions } from './useMarkAllAsRead.js';
|
|
3
|
+
export { useArchiveNotification } from './useArchiveNotification.js';
|
|
4
|
+
export { useArchiveAllNotifications } from './useArchiveAllNotifications.js';
|
|
5
|
+
export { useNotificationHistory } from './useNotificationHistory.js';
|
|
9
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/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;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { useActiveNotifications } from './useActiveNotifications.js';
|
|
2
2
|
export { useUnreadCount } from './useUnreadCount.js';
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
3
|
+
export { useArchiveNotification } from './useArchiveNotification.js';
|
|
4
|
+
export { useArchiveAllNotifications } from './useArchiveAllNotifications.js';
|
|
5
|
+
export { useNotificationHistory } from './useNotificationHistory.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/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;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NotificationDoc, UseActiveNotificationsOptions } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Paginated fetch of active (unread) notifications.
|
|
4
|
+
* Uses polling (refetchInterval) instead of real-time subscriptions for cost control.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useActiveNotifications({ config, userId, category, enabled, pageSize, refetchInterval, }: UseActiveNotificationsOptions): import("@ttt-productions/query-core").UseFirestorePaginatedResult<NotificationDoc>;
|
|
7
|
+
//# sourceMappingURL=useActiveNotifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useActiveNotifications.d.ts","sourceRoot":"","sources":["../../src/hooks/useActiveNotifications.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAKlF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAc,EACd,QAA4B,EAC5B,eAA0C,GAC3C,EAAE,6BAA6B,sFAsB/B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useFirestorePaginated } from '@ttt-productions/query-core';
|
|
3
|
+
import { orderBy, where } from 'firebase/firestore';
|
|
4
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
5
|
+
const DEFAULT_REFETCH_INTERVAL = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Paginated fetch of active (unread) notifications.
|
|
8
|
+
* Uses polling (refetchInterval) instead of real-time subscriptions for cost control.
|
|
9
|
+
*/
|
|
10
|
+
export function useActiveNotifications({ config, userId, category, enabled = true, pageSize = DEFAULT_PAGE_SIZE, refetchInterval = DEFAULT_REFETCH_INTERVAL, }) {
|
|
11
|
+
const categoryConfig = config.categories[category];
|
|
12
|
+
if (!categoryConfig) {
|
|
13
|
+
throw new Error(`[notification-core] Unknown category: ${category}`);
|
|
14
|
+
}
|
|
15
|
+
const collectionPath = categoryConfig.activePath;
|
|
16
|
+
const isPersonal = categoryConfig.audienceType === 'personal';
|
|
17
|
+
const constraints = [
|
|
18
|
+
...(isPersonal ? [where('targetUserId', '==', userId)] : []),
|
|
19
|
+
orderBy('updatedAt', 'desc'),
|
|
20
|
+
];
|
|
21
|
+
return useFirestorePaginated({
|
|
22
|
+
collectionPath,
|
|
23
|
+
queryKey: ['notifications', 'active', category, userId, { pageSize }],
|
|
24
|
+
constraints,
|
|
25
|
+
pageSize,
|
|
26
|
+
enabled: enabled && !!userId,
|
|
27
|
+
staleTime: refetchInterval,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=useActiveNotifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useActiveNotifications.js","sourceRoot":"","sources":["../../src/hooks/useActiveNotifications.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAwB,MAAM,oBAAoB,CAAC;AAG1E,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAExC;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GAAG,IAAI,EACd,QAAQ,GAAG,iBAAiB,EAC5B,eAAe,GAAG,wBAAwB,GACZ;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,OAAO,qBAAqB,CAAkB;QAC5C,cAAc;QACd,QAAQ,EAAE,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC;QACrE,WAAW;QACX,QAAQ;QACR,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,MAAM;QAC5B,SAAS,EAAE,eAAe;KAC3B,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ArchivalInfo, UseArchiveAllNotificationsOptions } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Archive all active notifications in batches.
|
|
4
|
+
* Reads active docs in batches of 50, archives each batch, repeats until empty.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const archiveAll = useArchiveAllNotifications({
|
|
9
|
+
* config: TTT_NOTIFICATION_CONFIG,
|
|
10
|
+
* userId: user.uid,
|
|
11
|
+
* category: 'user',
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* archiveAll.mutate({
|
|
15
|
+
* archivalInfo: { archivedBy: user.uid, archivedAt: Date.now(), device: 'web' },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function useArchiveAllNotifications({ config, userId, category, invalidateKeys, }: UseArchiveAllNotificationsOptions): import("@tanstack/react-query").UseMutationResult<{
|
|
20
|
+
totalArchived: number;
|
|
21
|
+
}, Error, {
|
|
22
|
+
archivalInfo: ArchivalInfo;
|
|
23
|
+
}, unknown>;
|
|
24
|
+
//# sourceMappingURL=useArchiveAllNotifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArchiveAllNotifications.d.ts","sourceRoot":"","sources":["../../src/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAEV,YAAY,EACZ,iCAAiC,EAClC,MAAM,aAAa,CAAC;AAIrB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,cAAc,GACf,EAAE,iCAAiC;;;kBAehB,YAAY;YAkE/B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { collection, query, where, orderBy, limit, getDocs, doc, writeBatch, } from 'firebase/firestore';
|
|
4
|
+
import { useFirestoreDb } from '@ttt-productions/query-core';
|
|
5
|
+
const BATCH_SIZE = 50;
|
|
6
|
+
/**
|
|
7
|
+
* Archive all active notifications in batches.
|
|
8
|
+
* Reads active docs in batches of 50, archives each batch, repeats until empty.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const archiveAll = useArchiveAllNotifications({
|
|
13
|
+
* config: TTT_NOTIFICATION_CONFIG,
|
|
14
|
+
* userId: user.uid,
|
|
15
|
+
* category: 'user',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* archiveAll.mutate({
|
|
19
|
+
* archivalInfo: { archivedBy: user.uid, archivedAt: Date.now(), device: 'web' },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function useArchiveAllNotifications({ config, userId, category, invalidateKeys, }) {
|
|
24
|
+
const db = useFirestoreDb();
|
|
25
|
+
const queryClient = useQueryClient();
|
|
26
|
+
const categoryConfig = config.categories[category];
|
|
27
|
+
const defaultInvalidateKeys = [
|
|
28
|
+
['notifications', 'active', category, userId],
|
|
29
|
+
['notifications', 'unread-count', category, userId],
|
|
30
|
+
['notifications', 'history', category, userId],
|
|
31
|
+
];
|
|
32
|
+
return useMutation({
|
|
33
|
+
mutationFn: async ({ archivalInfo, }) => {
|
|
34
|
+
if (!categoryConfig) {
|
|
35
|
+
throw new Error(`[notification-core] Unknown category: ${category}`);
|
|
36
|
+
}
|
|
37
|
+
const activePath = categoryConfig.activePath;
|
|
38
|
+
const historyPath = categoryConfig.historyPath(userId);
|
|
39
|
+
const isPersonal = categoryConfig.audienceType === 'personal';
|
|
40
|
+
let totalArchived = 0;
|
|
41
|
+
let hasMore = true;
|
|
42
|
+
while (hasMore) {
|
|
43
|
+
const constraints = [
|
|
44
|
+
...(isPersonal ? [where('targetUserId', '==', userId)] : []),
|
|
45
|
+
orderBy('updatedAt', 'desc'),
|
|
46
|
+
limit(BATCH_SIZE),
|
|
47
|
+
];
|
|
48
|
+
const q = query(collection(db, activePath), ...constraints);
|
|
49
|
+
const snapshot = await getDocs(q);
|
|
50
|
+
if (snapshot.empty) {
|
|
51
|
+
hasMore = false;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
const batch = writeBatch(db);
|
|
55
|
+
snapshot.docs.forEach((docSnap) => {
|
|
56
|
+
const data = docSnap.data();
|
|
57
|
+
// Write to history
|
|
58
|
+
const historyRef = doc(db, historyPath, docSnap.id);
|
|
59
|
+
batch.set(historyRef, {
|
|
60
|
+
...data,
|
|
61
|
+
id: docSnap.id,
|
|
62
|
+
archival: archivalInfo,
|
|
63
|
+
...(categoryConfig.audienceType === 'shared'
|
|
64
|
+
? { handledBy: archivalInfo.archivedBy }
|
|
65
|
+
: {}),
|
|
66
|
+
});
|
|
67
|
+
// Delete from active
|
|
68
|
+
const activeRef = doc(db, activePath, docSnap.id);
|
|
69
|
+
batch.delete(activeRef);
|
|
70
|
+
});
|
|
71
|
+
await batch.commit();
|
|
72
|
+
totalArchived += snapshot.docs.length;
|
|
73
|
+
if (snapshot.docs.length < BATCH_SIZE) {
|
|
74
|
+
hasMore = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { totalArchived };
|
|
78
|
+
},
|
|
79
|
+
onSuccess: () => {
|
|
80
|
+
const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;
|
|
81
|
+
keysToInvalidate.forEach((key) => {
|
|
82
|
+
queryClient.invalidateQueries({ queryKey: [...key], exact: false });
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=useArchiveAllNotifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArchiveAllNotifications.js","sourceRoot":"","sources":["../../src/hooks/useArchiveAllNotifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ArchivalInfo, UseArchiveNotificationOptions } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Archive a single notification: read from active → write to history → delete from active.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const archive = useArchiveNotification({
|
|
8
|
+
* config: TTT_NOTIFICATION_CONFIG,
|
|
9
|
+
* userId: user.uid,
|
|
10
|
+
* category: 'user',
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* archive.mutate({
|
|
14
|
+
* notificationId: 'abc123',
|
|
15
|
+
* archivalInfo: { archivedBy: user.uid, archivedAt: Date.now(), device: 'web' },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function useArchiveNotification({ config, userId, category, invalidateKeys, }: UseArchiveNotificationOptions): import("@tanstack/react-query").UseMutationResult<void, Error, {
|
|
20
|
+
notificationId: string;
|
|
21
|
+
archivalInfo: ArchivalInfo;
|
|
22
|
+
}, unknown>;
|
|
23
|
+
//# sourceMappingURL=useArchiveNotification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArchiveNotification.d.ts","sourceRoot":"","sources":["../../src/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,YAAY,EACZ,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,cAAc,GACf,EAAE,6BAA6B;oBAgBV,MAAM;kBACR,YAAY;YA2C/B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { doc, getDoc, setDoc, deleteDoc } from 'firebase/firestore';
|
|
4
|
+
import { useFirestoreDb } from '@ttt-productions/query-core';
|
|
5
|
+
/**
|
|
6
|
+
* Archive a single notification: read from active → write to history → delete from active.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const archive = useArchiveNotification({
|
|
11
|
+
* config: TTT_NOTIFICATION_CONFIG,
|
|
12
|
+
* userId: user.uid,
|
|
13
|
+
* category: 'user',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* archive.mutate({
|
|
17
|
+
* notificationId: 'abc123',
|
|
18
|
+
* archivalInfo: { archivedBy: user.uid, archivedAt: Date.now(), device: 'web' },
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useArchiveNotification({ config, userId, category, invalidateKeys, }) {
|
|
23
|
+
const db = useFirestoreDb();
|
|
24
|
+
const queryClient = useQueryClient();
|
|
25
|
+
const categoryConfig = config.categories[category];
|
|
26
|
+
const defaultInvalidateKeys = [
|
|
27
|
+
['notifications', 'active', category, userId],
|
|
28
|
+
['notifications', 'unread-count', category, userId],
|
|
29
|
+
['notifications', 'history', category, userId],
|
|
30
|
+
];
|
|
31
|
+
return useMutation({
|
|
32
|
+
mutationFn: async ({ notificationId, archivalInfo, }) => {
|
|
33
|
+
if (!categoryConfig) {
|
|
34
|
+
throw new Error(`[notification-core] Unknown category: ${category}`);
|
|
35
|
+
}
|
|
36
|
+
const activePath = categoryConfig.activePath;
|
|
37
|
+
const historyPath = categoryConfig.historyPath(userId);
|
|
38
|
+
// Read active doc
|
|
39
|
+
const activeRef = doc(db, activePath, notificationId);
|
|
40
|
+
const activeSnap = await getDoc(activeRef);
|
|
41
|
+
if (!activeSnap.exists()) {
|
|
42
|
+
// Already archived or deleted — no-op
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const activeData = { id: activeSnap.id, ...activeSnap.data() };
|
|
46
|
+
// Build history doc
|
|
47
|
+
const historyDoc = {
|
|
48
|
+
...activeData,
|
|
49
|
+
archival: archivalInfo,
|
|
50
|
+
...(categoryConfig.audienceType === 'shared'
|
|
51
|
+
? { handledBy: archivalInfo.archivedBy }
|
|
52
|
+
: {}),
|
|
53
|
+
};
|
|
54
|
+
// Write to history
|
|
55
|
+
const historyRef = doc(db, historyPath, notificationId);
|
|
56
|
+
await setDoc(historyRef, historyDoc);
|
|
57
|
+
// Delete from active
|
|
58
|
+
await deleteDoc(activeRef);
|
|
59
|
+
},
|
|
60
|
+
onSuccess: () => {
|
|
61
|
+
const keysToInvalidate = invalidateKeys ?? defaultInvalidateKeys;
|
|
62
|
+
keysToInvalidate.forEach((key) => {
|
|
63
|
+
queryClient.invalidateQueries({ queryKey: [...key], exact: false });
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=useArchiveNotification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArchiveNotification.js","sourceRoot":"","sources":["../../src/hooks/useArchiveNotification.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAQ7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,cAAc,GACgB;IAC9B,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEnD,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,EAAE,EACjB,cAAc,EACd,YAAY,GAIb,EAAE,EAAE;YACH,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;YACvE,CAAC;YAED,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC;YAC7C,MAAM,WAAW,GAAG,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAEvD,kBAAkB;YAClB,MAAM,SAAS,GAAG,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;YACtD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAE3C,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;gBACzB,sCAAsC;gBACtC,OAAO;YACT,CAAC;YAED,MAAM,UAAU,GAAG,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,IAAI,EAAE,EAAqB,CAAC;YAElF,oBAAoB;YACpB,MAAM,UAAU,GAAuC;gBACrD,GAAG,UAAU;gBACb,QAAQ,EAAE,YAAY;gBACtB,GAAG,CAAC,cAAc,CAAC,YAAY,KAAK,QAAQ;oBAC1C,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,UAAU,EAAE;oBACxC,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;YAEF,mBAAmB;YACnB,MAAM,UAAU,GAAG,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;YACxD,MAAM,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAErC,qBAAqB;YACrB,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;QAC7B,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"}
|