pixelize-design-library 3.0.0 → 3.1.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 (176) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/COMPONENT-GOTCHAS.md +37 -13
  3. package/DESIGN-GUIDE.md +4 -1
  4. package/dist/Components/NotificationBell/NotificationBellProps.d.ts +3 -3
  5. package/dist/Components/NotificationBell/NotificationItem.d.ts +1 -1
  6. package/dist/Components/NotificationBell/NotificationItem.js +1 -1
  7. package/dist/Components/NotificationBell/NotificationList.d.ts +1 -1
  8. package/dist/Components/NotificationBell/NotificationList.js +1 -1
  9. package/dist/Components/NotificationBell/NotificationPanel.d.ts +1 -1
  10. package/dist/Components/NotificationBell/NotificationPanel.js +1 -1
  11. package/dist/Components/NotificationCenter/center/CenterPanel.d.ts +15 -0
  12. package/dist/Components/NotificationCenter/center/CenterPanel.js +87 -0
  13. package/dist/Components/NotificationCenter/center/NotificationCenter.d.ts +4 -0
  14. package/dist/Components/NotificationCenter/center/NotificationCenter.js +130 -0
  15. package/dist/Components/NotificationCenter/center/NotificationCenterProps.d.ts +71 -0
  16. package/dist/Components/NotificationCenter/center/NotificationCenterProps.js +2 -0
  17. package/dist/Components/NotificationCenter/center/PanelRows.d.ts +10 -0
  18. package/dist/Components/NotificationCenter/center/PanelRows.js +35 -0
  19. package/dist/Components/NotificationCenter/center/PanelStates.d.ts +17 -0
  20. package/dist/Components/NotificationCenter/center/PanelStates.js +21 -0
  21. package/dist/Components/NotificationCenter/center/centerLabels.d.ts +4 -0
  22. package/dist/Components/NotificationCenter/center/centerLabels.js +38 -0
  23. package/dist/Components/NotificationCenter/center/useArrivals.d.ts +14 -0
  24. package/dist/Components/NotificationCenter/center/useArrivals.js +71 -0
  25. package/dist/Components/NotificationCenter/center/useIsSheet.d.ts +2 -0
  26. package/dist/Components/NotificationCenter/center/useIsSheet.js +20 -0
  27. package/dist/Components/NotificationCenter/core/NotificationCenterProvider.d.ts +17 -0
  28. package/dist/Components/NotificationCenter/core/NotificationCenterProvider.js +46 -0
  29. package/dist/Components/NotificationCenter/core/NotificationEngineProps.d.ts +182 -0
  30. package/dist/Components/NotificationCenter/core/NotificationEngineProps.js +2 -0
  31. package/dist/Components/NotificationCenter/core/NotificationSourceProps.d.ts +284 -0
  32. package/dist/Components/NotificationCenter/core/NotificationSourceProps.js +2 -0
  33. package/dist/Components/NotificationCenter/core/actionError.d.ts +4 -0
  34. package/dist/Components/NotificationCenter/core/actionError.js +45 -0
  35. package/dist/Components/NotificationCenter/core/format.d.ts +4 -0
  36. package/dist/Components/NotificationCenter/core/format.js +91 -0
  37. package/dist/Components/NotificationCenter/core/grouping.d.ts +14 -0
  38. package/dist/Components/NotificationCenter/core/grouping.js +102 -0
  39. package/dist/Components/NotificationCenter/core/labels.d.ts +6 -0
  40. package/dist/Components/NotificationCenter/core/labels.js +94 -0
  41. package/dist/Components/NotificationCenter/core/overlay.d.ts +109 -0
  42. package/dist/Components/NotificationCenter/core/overlay.js +281 -0
  43. package/dist/Components/NotificationCenter/core/useNotificationEngine.d.ts +13 -0
  44. package/dist/Components/NotificationCenter/core/useNotificationEngine.js +415 -0
  45. package/dist/Components/NotificationCenter/inbox/InboxList.d.ts +17 -0
  46. package/dist/Components/NotificationCenter/inbox/InboxList.js +25 -0
  47. package/dist/Components/NotificationCenter/inbox/InboxStates.d.ts +17 -0
  48. package/dist/Components/NotificationCenter/inbox/InboxStates.js +27 -0
  49. package/dist/Components/NotificationCenter/inbox/NotificationInbox.d.ts +4 -0
  50. package/dist/Components/NotificationCenter/inbox/NotificationInbox.js +434 -0
  51. package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.d.ts +106 -0
  52. package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.js +2 -0
  53. package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.d.ts +8 -0
  54. package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.js +63 -0
  55. package/dist/Components/NotificationCenter/inbox/inboxLabels.d.ts +4 -0
  56. package/dist/Components/NotificationCenter/inbox/inboxLabels.js +56 -0
  57. package/dist/Components/NotificationCenter/inbox/inboxModel.d.ts +17 -0
  58. package/dist/Components/NotificationCenter/inbox/inboxModel.js +47 -0
  59. package/dist/Components/NotificationCenter/inbox/useControllable.d.ts +1 -0
  60. package/dist/Components/NotificationCenter/inbox/useControllable.js +15 -0
  61. package/dist/Components/NotificationCenter/index.d.ts +8 -0
  62. package/dist/Components/NotificationCenter/index.js +26 -0
  63. package/dist/Components/NotificationCenter/preferences/NotificationPreferences.d.ts +4 -0
  64. package/dist/Components/NotificationCenter/preferences/NotificationPreferences.js +72 -0
  65. package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.d.ts +111 -0
  66. package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +2 -0
  67. package/dist/Components/NotificationCenter/preferences/PreferenceCells.d.ts +16 -0
  68. package/dist/Components/NotificationCenter/preferences/PreferenceCells.js +35 -0
  69. package/dist/Components/NotificationCenter/preferences/PreferenceGroups.d.ts +15 -0
  70. package/dist/Components/NotificationCenter/preferences/PreferenceGroups.js +26 -0
  71. package/dist/Components/NotificationCenter/preferences/preferencesLabels.d.ts +2 -0
  72. package/dist/Components/NotificationCenter/preferences/preferencesLabels.js +28 -0
  73. package/dist/Components/NotificationCenter/preferences/useAutosave.d.ts +8 -0
  74. package/dist/Components/NotificationCenter/preferences/useAutosave.js +41 -0
  75. package/dist/Components/NotificationCenter/row/NotificationDetail.d.ts +4 -0
  76. package/dist/Components/NotificationCenter/row/NotificationDetail.js +65 -0
  77. package/dist/Components/NotificationCenter/row/NotificationDetailProps.d.ts +15 -0
  78. package/dist/Components/NotificationCenter/row/NotificationDetailProps.js +2 -0
  79. package/dist/Components/NotificationCenter/row/NotificationRow.d.ts +3 -0
  80. package/dist/Components/NotificationCenter/row/NotificationRow.js +124 -0
  81. package/dist/Components/NotificationCenter/row/NotificationRowProps.d.ts +33 -0
  82. package/dist/Components/NotificationCenter/row/NotificationRowProps.js +2 -0
  83. package/dist/Components/NotificationCenter/row/ReasonForm.d.ts +14 -0
  84. package/dist/Components/NotificationCenter/row/ReasonForm.js +59 -0
  85. package/dist/Components/NotificationCenter/row/RowActions.d.ts +16 -0
  86. package/dist/Components/NotificationCenter/row/RowActions.js +36 -0
  87. package/dist/Components/NotificationCenter/row/RowQuickActions.d.ts +17 -0
  88. package/dist/Components/NotificationCenter/row/RowQuickActions.js +57 -0
  89. package/dist/Components/NotificationCenter/row/RowStatus.d.ts +10 -0
  90. package/dist/Components/NotificationCenter/row/RowStatus.js +42 -0
  91. package/dist/Components/NotificationCenter/row/category.d.ts +8 -0
  92. package/dist/Components/NotificationCenter/row/category.js +13 -0
  93. package/dist/Components/NotificationCenter/row/rowMenu.d.ts +14 -0
  94. package/dist/Components/NotificationCenter/row/rowMenu.js +37 -0
  95. package/dist/Components/NotificationCenter/row/rowParts.d.ts +40 -0
  96. package/dist/Components/NotificationCenter/row/rowParts.js +72 -0
  97. package/dist/Components/NotificationCenter/row/rowStyles.d.ts +11 -0
  98. package/dist/Components/NotificationCenter/row/rowStyles.js +25 -0
  99. package/dist/Components/SegmentedControl/segmentedInk.d.ts +4 -0
  100. package/dist/Components/SegmentedControl/segmentedInk.js +16 -2
  101. package/dist/Components/SideBar/SecondaryBar.js +2 -2
  102. package/dist/Components/SideBar/SideBar.js +18 -8
  103. package/dist/Components/SideBar/SideBarProps.d.ts +4 -2
  104. package/dist/Components/SideBar/components/MenuItemBox.d.ts +0 -2
  105. package/dist/Components/SideBar/components/MenuItemBox.js +2 -3
  106. package/dist/Components/SideBar/components/MenuItems.js +2 -2
  107. package/dist/Components/SideBar/components/PinnedSection.js +3 -4
  108. package/dist/Components/SideBar/sidebarScale.d.ts +0 -2
  109. package/dist/Components/SideBar/sidebarScale.js +0 -2
  110. package/dist/Components/Switch/Switch.d.ts +1 -1
  111. package/dist/Components/Switch/Switch.js +2 -2
  112. package/dist/Components/Switch/SwitchProps.d.ts +3 -0
  113. package/dist/Components/Toggle/TableToggle.js +4 -5
  114. package/dist/Components/Toggle/tableToggleInk.d.ts +7 -0
  115. package/dist/Components/Toggle/tableToggleInk.js +28 -0
  116. package/dist/esm/Components/NotificationBell/NotificationItem.js +1 -1
  117. package/dist/esm/Components/NotificationBell/NotificationList.js +1 -1
  118. package/dist/esm/Components/NotificationBell/NotificationPanel.js +1 -1
  119. package/dist/esm/Components/NotificationCenter/center/CenterPanel.js +81 -0
  120. package/dist/esm/Components/NotificationCenter/center/NotificationCenter.js +92 -0
  121. package/dist/esm/Components/NotificationCenter/center/NotificationCenterProps.js +1 -0
  122. package/dist/esm/Components/NotificationCenter/center/PanelRows.js +28 -0
  123. package/dist/esm/Components/NotificationCenter/center/PanelStates.js +11 -0
  124. package/dist/esm/Components/NotificationCenter/center/centerLabels.js +34 -0
  125. package/dist/esm/Components/NotificationCenter/center/useArrivals.js +67 -0
  126. package/dist/esm/Components/NotificationCenter/center/useIsSheet.js +16 -0
  127. package/dist/esm/Components/NotificationCenter/core/NotificationCenterProvider.js +35 -0
  128. package/dist/esm/Components/NotificationCenter/core/NotificationEngineProps.js +1 -0
  129. package/dist/esm/Components/NotificationCenter/core/NotificationSourceProps.js +1 -0
  130. package/dist/esm/Components/NotificationCenter/core/actionError.js +39 -0
  131. package/dist/esm/Components/NotificationCenter/core/format.js +87 -0
  132. package/dist/esm/Components/NotificationCenter/core/grouping.js +88 -0
  133. package/dist/esm/Components/NotificationCenter/core/labels.js +88 -0
  134. package/dist/esm/Components/NotificationCenter/core/overlay.js +274 -0
  135. package/dist/esm/Components/NotificationCenter/core/useNotificationEngine.js +408 -0
  136. package/dist/esm/Components/NotificationCenter/inbox/InboxList.js +18 -0
  137. package/dist/esm/Components/NotificationCenter/inbox/InboxStates.js +18 -0
  138. package/dist/esm/Components/NotificationCenter/inbox/NotificationInbox.js +396 -0
  139. package/dist/esm/Components/NotificationCenter/inbox/NotificationInboxProps.js +1 -0
  140. package/dist/esm/Components/NotificationCenter/inbox/ShortcutsDialog.js +23 -0
  141. package/dist/esm/Components/NotificationCenter/inbox/inboxLabels.js +52 -0
  142. package/dist/esm/Components/NotificationCenter/inbox/inboxModel.js +37 -0
  143. package/dist/esm/Components/NotificationCenter/inbox/useControllable.js +11 -0
  144. package/dist/esm/Components/NotificationCenter/index.js +8 -0
  145. package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferences.js +67 -0
  146. package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +1 -0
  147. package/dist/esm/Components/NotificationCenter/preferences/PreferenceCells.js +27 -0
  148. package/dist/esm/Components/NotificationCenter/preferences/PreferenceGroups.js +21 -0
  149. package/dist/esm/Components/NotificationCenter/preferences/preferencesLabels.js +24 -0
  150. package/dist/esm/Components/NotificationCenter/preferences/useAutosave.js +37 -0
  151. package/dist/esm/Components/NotificationCenter/row/NotificationDetail.js +60 -0
  152. package/dist/esm/Components/NotificationCenter/row/NotificationDetailProps.js +1 -0
  153. package/dist/esm/Components/NotificationCenter/row/NotificationRow.js +119 -0
  154. package/dist/esm/Components/NotificationCenter/row/NotificationRowProps.js +1 -0
  155. package/dist/esm/Components/NotificationCenter/row/ReasonForm.js +52 -0
  156. package/dist/esm/Components/NotificationCenter/row/RowActions.js +28 -0
  157. package/dist/esm/Components/NotificationCenter/row/RowQuickActions.js +49 -0
  158. package/dist/esm/Components/NotificationCenter/row/RowStatus.js +35 -0
  159. package/dist/esm/Components/NotificationCenter/row/category.js +9 -0
  160. package/dist/esm/Components/NotificationCenter/row/rowMenu.js +30 -0
  161. package/dist/esm/Components/NotificationCenter/row/rowParts.js +59 -0
  162. package/dist/esm/Components/NotificationCenter/row/rowStyles.js +19 -0
  163. package/dist/esm/Components/SegmentedControl/segmentedInk.js +15 -2
  164. package/dist/esm/Components/SideBar/SecondaryBar.js +3 -3
  165. package/dist/esm/Components/SideBar/SideBar.js +18 -8
  166. package/dist/esm/Components/SideBar/components/MenuItemBox.js +2 -3
  167. package/dist/esm/Components/SideBar/components/MenuItems.js +2 -2
  168. package/dist/esm/Components/SideBar/components/PinnedSection.js +3 -4
  169. package/dist/esm/Components/SideBar/sidebarScale.js +0 -2
  170. package/dist/esm/Components/Switch/Switch.js +2 -2
  171. package/dist/esm/Components/Toggle/TableToggle.js +4 -5
  172. package/dist/esm/Components/Toggle/tableToggleInk.js +24 -0
  173. package/dist/esm/index.js +1 -0
  174. package/dist/index.d.ts +8 -0
  175. package/dist/index.js +16 -3
  176. package/package.json +1 -1
@@ -0,0 +1,109 @@
1
+ import type { NotificationActionError, NotificationCategories, NotificationCounts, NotificationEntry, NotificationMarkAllScope } from "./NotificationSourceProps";
2
+ import type { LiveNotificationEntry, NotificationDecision, NotificationReasonRequest, NotificationRowStatus, NotificationZoneLists } from "./NotificationEngineProps";
3
+ export type ById<T> = {
4
+ [id: string]: T | undefined;
5
+ };
6
+ export type WindowPhase = "undo" | "sending" | "settled";
7
+ export type DecisionPhase = Exclude<NotificationRowStatus, "open">;
8
+ export interface DecisionRecord {
9
+ batch: string;
10
+ decision: NotificationDecision;
11
+ phase: DecisionPhase;
12
+ folded: boolean;
13
+ error?: NotificationActionError;
14
+ settledAt?: number;
15
+ }
16
+ export interface FlagRecord {
17
+ batch: string;
18
+ value: boolean;
19
+ phase: WindowPhase;
20
+ folded: boolean;
21
+ settledAt?: number;
22
+ previous?: FlagRecord;
23
+ }
24
+ export interface MarkAllRecord {
25
+ batch: string;
26
+ scope: NotificationMarkAllScope;
27
+ phase: WindowPhase;
28
+ settledAt?: number;
29
+ }
30
+ export interface DismissedRecord {
31
+ actionId: string;
32
+ settledAt?: number;
33
+ }
34
+ export interface NotificationOverlay {
35
+ decisions: ById<DecisionRecord>;
36
+ read: ById<FlagRecord>;
37
+ archived: ById<FlagRecord>;
38
+ markAll: MarkAllRecord[];
39
+ reasonRequests: ById<NotificationReasonRequest>;
40
+ dismissed: ById<DismissedRecord>;
41
+ epoch: number;
42
+ }
43
+ export declare const EMPTY_OVERLAY: NotificationOverlay;
44
+ export interface DecisionOutcome {
45
+ batch: string;
46
+ error?: NotificationActionError;
47
+ }
48
+ export type FlagKey = "read" | "archived";
49
+ export type OverlayAction = {
50
+ type: "decide";
51
+ batch: string;
52
+ ids: string[];
53
+ decision: NotificationDecision;
54
+ } | {
55
+ type: "retry";
56
+ batch: string;
57
+ ids: string[];
58
+ } | {
59
+ type: "fold";
60
+ batch: string;
61
+ } | {
62
+ type: "send";
63
+ batch: string;
64
+ } | {
65
+ type: "drop";
66
+ batch: string;
67
+ } | {
68
+ type: "settleDecisions";
69
+ outcomes: Record<string, DecisionOutcome>;
70
+ } | {
71
+ type: "dismiss";
72
+ id: string;
73
+ } | {
74
+ type: "clearReasonRequest";
75
+ id: string;
76
+ } | {
77
+ type: "flag";
78
+ key: FlagKey;
79
+ batch: string;
80
+ ids: string[];
81
+ value: boolean;
82
+ phase: "undo" | "sending";
83
+ } | {
84
+ type: "markAll";
85
+ batch: string;
86
+ scope: NotificationMarkAllScope;
87
+ ids: string[];
88
+ } | {
89
+ type: "settle";
90
+ batch: string;
91
+ } | {
92
+ type: "reconcile";
93
+ entries: NotificationEntry[];
94
+ } | {
95
+ type: "countsChanged";
96
+ };
97
+ export declare const overlayReducer: (state: NotificationOverlay, action: OverlayAction) => NotificationOverlay;
98
+ export declare const liveEntryFor: (entry: NotificationEntry, overlay: NotificationOverlay, categories: NotificationCategories) => LiveNotificationEntry;
99
+ export interface LiveLists {
100
+ lists: NotificationZoneLists;
101
+ byId: Map<string, LiveNotificationEntry>;
102
+ entries: NotificationEntry[];
103
+ }
104
+ export type LiveCache = WeakMap<NotificationEntry, {
105
+ categories: NotificationCategories;
106
+ live: LiveNotificationEntry;
107
+ }>;
108
+ export declare const buildLiveLists: (entries: NotificationEntry[], overlay: NotificationOverlay, categories: NotificationCategories, cache?: LiveCache) => LiveLists;
109
+ export declare const optimisticCounts: (live: LiveLists, overlay: NotificationOverlay, categories: NotificationCategories, server: NotificationCounts) => NotificationCounts;
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.optimisticCounts = exports.buildLiveLists = exports.liveEntryFor = exports.overlayReducer = exports.EMPTY_OVERLAY = void 0;
4
+ const grouping_1 = require("./grouping");
5
+ exports.EMPTY_OVERLAY = {
6
+ decisions: {},
7
+ read: {},
8
+ archived: {},
9
+ markAll: [],
10
+ reasonRequests: {},
11
+ dismissed: {},
12
+ epoch: 0,
13
+ };
14
+ const mapById = (record, fn) => {
15
+ let changed = false;
16
+ const next = {};
17
+ Object.keys(record).forEach((id) => {
18
+ const current = record[id];
19
+ if (current === undefined)
20
+ return;
21
+ const value = fn(current, id);
22
+ if (value !== current)
23
+ changed = true;
24
+ if (value !== undefined)
25
+ next[id] = value;
26
+ });
27
+ return changed ? next : record;
28
+ };
29
+ const hasAction = (entry, actionId) => Boolean(entry.actions && entry.actions.some((action) => action.id === actionId));
30
+ const foldsWithDecision = (phase) => phase === "undo" || phase === "sending" || phase === "resolved";
31
+ const withPrevious = (current) => current ? { ...current, previous: undefined } : undefined;
32
+ const setFlags = (record, ids, batch, value, phase) => {
33
+ const next = { ...record };
34
+ ids.forEach((id) => {
35
+ next[id] = { batch, value, phase, folded: false, previous: withPrevious(record[id]) };
36
+ });
37
+ return next;
38
+ };
39
+ const stepFlags = (record, batch, fn) => mapById(record, (rec) => (rec.batch === batch ? fn(rec) : rec));
40
+ const firstById = (entries) => {
41
+ const byId = new Map();
42
+ entries.forEach((entry) => {
43
+ if (!byId.has(entry.id))
44
+ byId.set(entry.id, entry);
45
+ });
46
+ return byId;
47
+ };
48
+ const reconcile = (state, entries) => {
49
+ const byId = firstById(entries);
50
+ const decisions = mapById(state.decisions, (rec, id) => {
51
+ const base = byId.get(id);
52
+ if (!base)
53
+ return undefined;
54
+ if ((rec.phase === "resolved" || rec.phase === "denied") && !hasAction(base, rec.decision.actionId))
55
+ return undefined;
56
+ return rec;
57
+ });
58
+ const dismissed = mapById(state.dismissed, (rec, id) => {
59
+ const base = byId.get(id);
60
+ return base && hasAction(base, rec.actionId) ? rec : undefined;
61
+ });
62
+ const settledFlag = (agrees) => (rec, id) => {
63
+ const base = byId.get(id);
64
+ if (!base)
65
+ return undefined;
66
+ return rec.phase === "settled" && agrees(base, rec) ? undefined : rec;
67
+ };
68
+ const read = mapById(state.read, settledFlag((base, rec) => base.isRead === rec.value));
69
+ const archived = mapById(state.archived, settledFlag((base, rec) => Boolean(base.archived) === rec.value));
70
+ const reasonRequests = mapById(state.reasonRequests, (req, id) => (byId.has(id) ? req : undefined));
71
+ const unchanged = decisions === state.decisions &&
72
+ dismissed === state.dismissed &&
73
+ read === state.read &&
74
+ archived === state.archived &&
75
+ reasonRequests === state.reasonRequests;
76
+ return unchanged ? state : { ...state, decisions, dismissed, read, archived, reasonRequests };
77
+ };
78
+ const settleDecisions = (state, outcomes) => {
79
+ const decisions = { ...state.decisions };
80
+ const reasonRequests = { ...state.reasonRequests };
81
+ Object.keys(outcomes).forEach((id) => {
82
+ const rec = decisions[id];
83
+ const outcome = outcomes[id];
84
+ if (!rec || rec.batch !== outcome.batch)
85
+ return;
86
+ const { error } = outcome;
87
+ if (!error) {
88
+ decisions[id] = { ...rec, phase: "resolved", folded: true, settledAt: state.epoch };
89
+ }
90
+ else if (error.kind === "reasonRequired") {
91
+ delete decisions[id];
92
+ reasonRequests[id] = { actionId: rec.decision.actionId, message: error.message };
93
+ }
94
+ else if (error.kind === "failed") {
95
+ decisions[id] = { ...rec, phase: "failed", folded: false, error };
96
+ }
97
+ else {
98
+ decisions[id] = { ...rec, phase: "denied", folded: false, error, settledAt: state.epoch };
99
+ }
100
+ });
101
+ return { ...state, decisions, reasonRequests };
102
+ };
103
+ const overlayReducer = (state, action) => {
104
+ switch (action.type) {
105
+ case "decide": {
106
+ const decisions = { ...state.decisions };
107
+ const reasonRequests = { ...state.reasonRequests };
108
+ action.ids.forEach((id) => {
109
+ decisions[id] = { batch: action.batch, decision: action.decision, phase: "undo", folded: false };
110
+ delete reasonRequests[id];
111
+ });
112
+ return { ...state, decisions, reasonRequests };
113
+ }
114
+ case "retry":
115
+ return {
116
+ ...state,
117
+ decisions: mapById(state.decisions, (rec, id) => action.ids.includes(id) && rec.phase === "failed"
118
+ ? { batch: action.batch, decision: rec.decision, phase: "sending", folded: false }
119
+ : rec),
120
+ };
121
+ case "fold":
122
+ return {
123
+ ...state,
124
+ decisions: mapById(state.decisions, (rec) => rec.batch === action.batch && foldsWithDecision(rec.phase) && !rec.folded ? { ...rec, folded: true } : rec),
125
+ archived: stepFlags(state.archived, action.batch, (rec) => (rec.value && !rec.folded ? { ...rec, folded: true } : rec)),
126
+ };
127
+ case "send": {
128
+ const toSending = (rec) => (rec.phase === "undo" ? { ...rec, phase: "sending" } : rec);
129
+ return {
130
+ ...state,
131
+ decisions: mapById(state.decisions, (rec) => rec.batch === action.batch && rec.phase === "undo" ? { ...rec, phase: "sending" } : rec),
132
+ read: stepFlags(state.read, action.batch, toSending),
133
+ archived: stepFlags(state.archived, action.batch, toSending),
134
+ markAll: state.markAll.map((rec) => (rec.batch === action.batch && rec.phase === "undo" ? { ...rec, phase: "sending" } : rec)),
135
+ };
136
+ }
137
+ case "drop":
138
+ return {
139
+ ...state,
140
+ decisions: mapById(state.decisions, (rec) => (rec.batch === action.batch ? undefined : rec)),
141
+ read: stepFlags(state.read, action.batch, (rec) => rec.previous),
142
+ archived: stepFlags(state.archived, action.batch, (rec) => rec.previous),
143
+ markAll: state.markAll.filter((rec) => rec.batch !== action.batch),
144
+ };
145
+ case "settleDecisions":
146
+ return settleDecisions(state, action.outcomes);
147
+ case "dismiss": {
148
+ const rec = state.decisions[action.id];
149
+ if (!rec || rec.phase !== "denied")
150
+ return state;
151
+ const decisions = { ...state.decisions };
152
+ delete decisions[action.id];
153
+ return {
154
+ ...state,
155
+ decisions,
156
+ dismissed: { ...state.dismissed, [action.id]: { actionId: rec.decision.actionId, settledAt: rec.settledAt } },
157
+ };
158
+ }
159
+ case "clearReasonRequest": {
160
+ if (!state.reasonRequests[action.id])
161
+ return state;
162
+ const reasonRequests = { ...state.reasonRequests };
163
+ delete reasonRequests[action.id];
164
+ return { ...state, reasonRequests };
165
+ }
166
+ case "flag": {
167
+ const next = setFlags(state[action.key], action.ids, action.batch, action.value, action.phase);
168
+ return action.key === "read" ? { ...state, read: next } : { ...state, archived: next };
169
+ }
170
+ case "markAll":
171
+ return {
172
+ ...state,
173
+ read: setFlags(state.read, action.ids, action.batch, true, "undo"),
174
+ markAll: [...state.markAll, { batch: action.batch, scope: action.scope, phase: "undo" }],
175
+ };
176
+ case "settle": {
177
+ const settle = (rec) => rec.phase === "sending" ? { ...rec, phase: "settled", settledAt: state.epoch } : rec;
178
+ return {
179
+ ...state,
180
+ read: stepFlags(state.read, action.batch, settle),
181
+ archived: stepFlags(state.archived, action.batch, settle),
182
+ markAll: state.markAll.map((rec) => rec.batch === action.batch && rec.phase === "sending" ? { ...rec, phase: "settled", settledAt: state.epoch } : rec),
183
+ };
184
+ }
185
+ case "reconcile":
186
+ return reconcile(state, action.entries);
187
+ case "countsChanged":
188
+ return { ...state, epoch: state.epoch + 1, markAll: state.markAll.filter((rec) => rec.phase !== "settled") };
189
+ }
190
+ };
191
+ exports.overlayReducer = overlayReducer;
192
+ const counts = (rec, epoch) => rec.settledAt === undefined || rec.settledAt >= epoch;
193
+ const resolveFlags = (entry, overlay, countingOnly) => {
194
+ const keep = (rec) => rec && (!countingOnly || counts(rec, overlay.epoch)) ? rec : undefined;
195
+ const read = keep(overlay.read[entry.id]);
196
+ const archive = keep(overlay.archived[entry.id]);
197
+ const shownArchive = countingOnly ? archive === null || archive === void 0 ? void 0 : archive.value : archive && (archive.value ? archive.folded : false);
198
+ return {
199
+ isRead: read && entry.isRead !== undefined ? read.value : entry.isRead,
200
+ archived: shownArchive === undefined ? Boolean(entry.archived) : shownArchive,
201
+ isArchiving: Boolean(!countingOnly && archive && archive.value && !archive.folded),
202
+ decision: keep(overlay.decisions[entry.id]),
203
+ isDismissed: keep(overlay.dismissed[entry.id]) !== undefined,
204
+ };
205
+ };
206
+ const isTouched = (id, overlay) => overlay.decisions[id] !== undefined ||
207
+ overlay.read[id] !== undefined ||
208
+ overlay.archived[id] !== undefined ||
209
+ overlay.dismissed[id] !== undefined ||
210
+ overlay.reasonRequests[id] !== undefined;
211
+ const pendingStatus = (status) => status === "open" || status === "failed";
212
+ const liveEntryFor = (entry, overlay, categories) => {
213
+ const flags = resolveFlags(entry, overlay, false);
214
+ const changed = flags.isRead !== entry.isRead || flags.archived !== Boolean(entry.archived);
215
+ const shown = changed ? { ...entry, isRead: flags.isRead, archived: flags.archived } : entry;
216
+ const zone = (0, grouping_1.zoneFor)(shown, categories);
217
+ const record = flags.decision;
218
+ const status = record ? record.phase : "open";
219
+ const state = {
220
+ zone,
221
+ status,
222
+ decision: record === null || record === void 0 ? void 0 : record.decision,
223
+ error: record === null || record === void 0 ? void 0 : record.error,
224
+ reasonRequest: overlay.reasonRequests[entry.id],
225
+ isHidden: flags.isArchiving || flags.isDismissed || Boolean(record && record.folded && foldsWithDecision(record.phase)),
226
+ isPending: zone === "needsYou" && !flags.isDismissed && pendingStatus(status),
227
+ };
228
+ return { entry: shown, state };
229
+ };
230
+ exports.liveEntryFor = liveEntryFor;
231
+ const contribution = (entry, overlay, categories) => {
232
+ const flags = overlay
233
+ ? resolveFlags(entry, overlay, true)
234
+ : { isRead: entry.isRead, archived: Boolean(entry.archived), isDismissed: false, decision: undefined };
235
+ const zone = (0, grouping_1.zoneFor)({ category: entry.category, isRead: flags.isRead, archived: flags.archived }, categories);
236
+ const status = flags.decision ? flags.decision.phase : "open";
237
+ return {
238
+ needsYou: zone === "needsYou" && !flags.isDismissed && pendingStatus(status) ? 1 : 0,
239
+ unread: zone === "activity" && flags.isRead === false ? 1 : 0,
240
+ };
241
+ };
242
+ const buildLiveLists = (entries, overlay, categories, cache) => {
243
+ const byId = new Map();
244
+ const unique = [];
245
+ const lists = { needsYou: [], activity: [], archived: [] };
246
+ entries.forEach((entry) => {
247
+ if (byId.has(entry.id))
248
+ return;
249
+ unique.push(entry);
250
+ const cached = cache === null || cache === void 0 ? void 0 : cache.get(entry);
251
+ const touched = isTouched(entry.id, overlay);
252
+ const live = !touched && cached && cached.categories === categories ? cached.live : (0, exports.liveEntryFor)(entry, overlay, categories);
253
+ if (!touched && cache)
254
+ cache.set(entry, { categories, live });
255
+ byId.set(entry.id, live);
256
+ lists[live.state.zone].push(live);
257
+ });
258
+ lists.needsYou.sort(grouping_1.byNewest);
259
+ lists.activity.sort(grouping_1.byNewest);
260
+ lists.archived.sort(grouping_1.byNewest);
261
+ return { lists, byId, entries: unique };
262
+ };
263
+ exports.buildLiveLists = buildLiveLists;
264
+ const isWholeActivity = (scope) => scope.zone === "activity" && !scope.category && !(scope.query && scope.query.trim());
265
+ const optimisticCounts = (live, overlay, categories, server) => {
266
+ let needsYou = server.needsYou;
267
+ let unread = server.unread;
268
+ live.entries.forEach((entry) => {
269
+ if (!isTouched(entry.id, overlay))
270
+ return;
271
+ const before = contribution(entry, null, categories);
272
+ const after = contribution(entry, overlay, categories);
273
+ needsYou += after.needsYou - before.needsYou;
274
+ unread += after.unread - before.unread;
275
+ });
276
+ if (overlay.markAll.some((rec) => isWholeActivity(rec.scope) && counts(rec, overlay.epoch))) {
277
+ unread = live.lists.activity.filter((item) => item.entry.isRead === false && !item.state.isHidden).length;
278
+ }
279
+ return { needsYou: Math.max(0, needsYou), unread: Math.max(0, unread) };
280
+ };
281
+ exports.optimisticCounts = optimisticCounts;
@@ -0,0 +1,13 @@
1
+ import type { NotificationSourceProps } from "./NotificationSourceProps";
2
+ import type { NotificationCenterValue } from "./NotificationEngineProps";
3
+ export declare const UNDO_WINDOW_MS = 6000;
4
+ export declare const DECISION_FOLD_MS = 900;
5
+ export declare const ARCHIVE_FOLD_MS = 220;
6
+ export declare const CLOCK_TICK_MS = 60000;
7
+ export type NotificationShared = Pick<NotificationCenterValue, "labels" | "format" | "now" | "categories" | "features" | "isOffline" | "appName" | "actions">;
8
+ export interface NotificationEngine {
9
+ value: NotificationCenterValue;
10
+ shared: NotificationShared;
11
+ announcement: string;
12
+ }
13
+ export declare const useNotificationEngine: (source: NotificationSourceProps) => NotificationEngine;