@trycourier/courier-ui-inbox 1.2.3 → 2.0.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 (30) hide show
  1. package/dist/components/courier-inbox-feed-button.d.ts +28 -0
  2. package/dist/components/courier-inbox-header.d.ts +28 -11
  3. package/dist/components/courier-inbox-list-item.d.ts +4 -4
  4. package/dist/components/courier-inbox-list.d.ts +9 -4
  5. package/dist/components/courier-inbox-option-menu-item.d.ts +3 -0
  6. package/dist/components/courier-inbox-option-menu.d.ts +19 -12
  7. package/dist/components/courier-inbox-popup-menu.d.ts +57 -10
  8. package/dist/components/courier-inbox-tabs.d.ts +33 -0
  9. package/dist/components/courier-inbox.d.ts +121 -9
  10. package/dist/components/courier-unread-count-badge.d.ts +10 -5
  11. package/dist/datastore/__tests__/inbox-datastore.test.d.ts +1 -0
  12. package/dist/datastore/datatore-events.d.ts +61 -8
  13. package/dist/datastore/inbox-dataset.d.ts +110 -0
  14. package/dist/datastore/inbox-datastore.d.ts +217 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +3225 -1501
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/types/courier-inbox-theme.d.ts +64 -20
  21. package/dist/types/factories.d.ts +21 -8
  22. package/dist/types/inbox-data-set.d.ts +111 -2
  23. package/dist/types/inbox-defaults.d.ts +39 -0
  24. package/dist/types/snapshots.d.ts +6 -0
  25. package/dist/utils/utils.d.ts +7 -0
  26. package/package.json +3 -3
  27. package/dist/components/courier-inbox-header-title.d.ts +0 -23
  28. package/dist/datastore/datastore.d.ts +0 -114
  29. package/dist/types/feed-type.d.ts +0 -1
  30. /package/dist/datastore/__tests__/{datastore.test.d.ts → inbox-dataset.test.d.ts} +0 -0
@@ -1,12 +1,65 @@
1
- import { CourierInboxFeedType } from '../types/feed-type';
2
1
  import { InboxDataSet } from '../types/inbox-data-set';
3
2
  import { InboxMessage } from '@trycourier/courier-js';
3
+ /**
4
+ * Event callbacks which may be fully or partially implemented to be called when
5
+ * {@link CourierInboxDatastore} has updates.
6
+ *
7
+ * @public
8
+ */
4
9
  export declare class CourierInboxDatastoreEvents {
5
- onDataSetChange?(_: InboxDataSet, __: CourierInboxFeedType): void;
6
- onPageAdded?(_: InboxDataSet, __: CourierInboxFeedType): void;
7
- onUnreadCountChange?(_: number): void;
8
- onMessageAdd?(_: InboxMessage, __: number, ___: CourierInboxFeedType): void;
9
- onMessageRemove?(_: InboxMessage, __: number, ___: CourierInboxFeedType): void;
10
- onMessageUpdate?(_: InboxMessage, __: number, ___: CourierInboxFeedType): void;
11
- onError?(_: Error): void;
10
+ /**
11
+ * Called when the dataset changes.
12
+ * @public
13
+ * @param _dataset - The updated inbox dataset
14
+ */
15
+ onDataSetChange?(_dataset: InboxDataSet): void;
16
+ /**
17
+ * Called when a new page is added to the dataset.
18
+ * @public
19
+ * @param _dataset - The updated inbox dataset with the new page
20
+ */
21
+ onPageAdded?(_dataset: InboxDataSet): void;
22
+ /**
23
+ * Called when the unread count changes.
24
+ * @public
25
+ * @param _unreadCount - The new unread count
26
+ * @param _datasetId - The dataset ID that was updated
27
+ */
28
+ onUnreadCountChange?(_unreadCount: number, _datasetId: string): void;
29
+ /**
30
+ * Called when the total unread count across all datasets changes.
31
+ * @public
32
+ * @param _totalUnreadCount - The new total unread count
33
+ */
34
+ onTotalUnreadCountChange?(_totalUnreadCount: number): void;
35
+ /**
36
+ * Called when a new message is added.
37
+ * @public
38
+ * @param _message - The added InboxMessage
39
+ * @param _index - The index where the message was added
40
+ * @param _datasetId - The dataset ID that was updated
41
+ */
42
+ onMessageAdd?(_message: InboxMessage, _index: number, _datasetId: string): void;
43
+ /**
44
+ * Called when a message is removed.
45
+ * @public
46
+ * @param _message - The InboxMessage that was removed
47
+ * @param _index - The index from which the message was removed
48
+ * @param _datasetId - The dataset ID that was updated
49
+ */
50
+ onMessageRemove?(_message: InboxMessage, _index: number, _datasetId: string): void;
51
+ /**
52
+ * Called when a message is updated.
53
+ * @public
54
+ * @param _message - The updated InboxMessage
55
+ * @param _index - The index where the message was updated
56
+ * @param _datasetId - The dataset ID that was updated
57
+ */
58
+ onMessageUpdate?(_message: InboxMessage, _index: number, _datasetId: string): void;
59
+ /**
60
+ * Called when an error occurs in the data store.
61
+ * @public
62
+ * @param _error - The error object
63
+ */
64
+ onError?(_error: Error): void;
12
65
  }
@@ -0,0 +1,110 @@
1
+ import { InboxMessage } from '@trycourier/courier-js';
2
+ import { CourierInboxDatasetFilter, InboxDataSet } from '../types/inbox-data-set';
3
+ import { CourierGetInboxMessagesQueryFilter } from '@trycourier/courier-js/dist/types/inbox';
4
+ import { CourierInboxDataStoreListener } from './datastore-listener';
5
+ export declare class CourierInboxDataset {
6
+ /** The unique ID for this dataset, provided by the consumer to later identify this set of messages. */
7
+ private _id;
8
+ /** The set of messages in this dataset. */
9
+ private _messages;
10
+ /**
11
+ * True if the first fetch of messages has completed successfully.
12
+ *
13
+ * This marker is used to distinguish if _messages can be returned when cached messages
14
+ * are acceptable, since an empty array of messages could indicate they weren't
15
+ * ever fetched or that they were fetched but there are currently none in the dataset.
16
+ */
17
+ private _firstFetchComplete;
18
+ /** True if the fetched dataset sets hasNextPage to true. */
19
+ private _hasNextPage;
20
+ /**
21
+ * The pagination cursor to pass to subsequent fetch requests
22
+ * or null if this is the first request or a response has indicated
23
+ * there is no next page.
24
+ */
25
+ private _lastPaginationCursor?;
26
+ private readonly _filter;
27
+ private readonly _datastoreListeners;
28
+ /**
29
+ * The total unread count loaded before messages are fetched.
30
+ * Used to show unread badge counts on tabs before the user clicks into them.
31
+ *
32
+ * Total unread count is maintained manually (rather than derived from _messages) because:
33
+ *
34
+ * 1. We load unread counts for all tabs in view before their messages are loaded.
35
+ * 2. The set of loaded messages may not fully reflect the unread count for a tab.
36
+ * Messages are paginated, so unread messages may be present on the server but
37
+ * but not on the client.
38
+ */
39
+ private _totalUnreadCount;
40
+ constructor(id: string, filter: CourierInboxDatasetFilter);
41
+ /** Get the current total unread count. */
42
+ get totalUnreadCount(): number;
43
+ /** Private setter for unread count. */
44
+ private set totalUnreadCount(value);
45
+ /**
46
+ * Set the unread count explicitly.
47
+ * Used for batch loading unread counts for all datasets before messages are fetched.
48
+ */
49
+ setUnreadCount(count: number): void;
50
+ /**
51
+ * Get the filter configuration for this dataset.
52
+ * Used for batch loading unread counts.
53
+ */
54
+ getFilter(): CourierGetInboxMessagesQueryFilter;
55
+ /**
56
+ * Add a message to the dataset if it qualifies based on the dataset's filters.
57
+ *
58
+ * @param message the message to add
59
+ * @returns true if the message was added, otherwise false
60
+ */
61
+ addMessage(message: InboxMessage, insertIndex?: number): boolean;
62
+ /**
63
+ * Update the messages and unread count for the dataset based on a change in a message.
64
+ *
65
+ * Based on a message's change (unread -> read, archived -> unarchived, etc) this method
66
+ * inserts, updates, removes, or excludes it from the dataset. Given the before/existing
67
+ * and after states, it updates the unread count.
68
+ *
69
+ * The before state identifies messages that would qualify for the dataset
70
+ * before the dataset (or a particular message in the dataset) has been loaded.
71
+ * These messages may not be explicitly removed from the dataset since they aren't
72
+ * yet present, but may have an effect on the unread count.
73
+ *
74
+ * @param beforeMessage the message before the change
75
+ * @param afterMessage the message after the change
76
+ * @returns true if afterMessage qualifies for the dataset and was inserted or updated, false if the message was removed
77
+ */
78
+ updateWithMessageChange(beforeMessage: InboxMessage, afterMessage: InboxMessage): boolean;
79
+ private calculateUnreadChange;
80
+ /**
81
+ * Remove the specified message from this dataset, if it's present.
82
+ *
83
+ * @param message the message to remove from this dataset
84
+ * @returns true if the message was removed, else false
85
+ */
86
+ removeMessage(message: InboxMessage): boolean;
87
+ getMessage(messageId: string): InboxMessage | undefined;
88
+ loadDataset(canUseCache: boolean): Promise<void>;
89
+ fetchNextPageOfMessages(): Promise<InboxDataSet | null>;
90
+ addDatastoreListener(listener: CourierInboxDataStoreListener): void;
91
+ removeDatastoreListener(listener: CourierInboxDataStoreListener): void;
92
+ toInboxDataset(): InboxDataSet;
93
+ private fetchMessages;
94
+ private indexOfMessage;
95
+ /**
96
+ * Find the insert index for a new message in a data set
97
+ * @param newMessage - The new message to insert
98
+ * @param dataSet - The data set to insert the message into
99
+ * @returns The index to insert the message at
100
+ */
101
+ private findInsertIndex;
102
+ private messageQualifiesForDataset;
103
+ /**
104
+ * Restore this dataset from a snapshot.
105
+ *
106
+ * Note: _firstFetchComplete does not need to be restored
107
+ * as it indicates specific lifecycle stages for the dataset.
108
+ */
109
+ restoreFromSnapshot(snapshot: InboxDataSet): void;
110
+ }
@@ -0,0 +1,217 @@
1
+ import { InboxMessage } from '@trycourier/courier-js';
2
+ import { CourierInboxFeed, InboxDataSet } from '../types/inbox-data-set';
3
+ import { CourierInboxDataStoreListener } from './datastore-listener';
4
+ /**
5
+ * Shared datastore for Inbox components.
6
+ *
7
+ * CourierInboxDatastore is a singleton. Use `CourierInboxDatastore.shared`
8
+ * to access the shared instance.
9
+ *
10
+ * @public
11
+ */
12
+ export declare class CourierInboxDatastore {
13
+ private static readonly TAG;
14
+ private static instance;
15
+ private _datasets;
16
+ private _listeners;
17
+ private _removeMessageEventListener?;
18
+ /**
19
+ * Global message store is a map of Message ID to Message for all messages
20
+ * that have been loaded.
21
+ *
22
+ * This acts as the source of truth to apply messages mutations to a message
23
+ * given its ID and propagate those mutations to individual datasets.
24
+ */
25
+ private _globalMessages;
26
+ /** Access CourierInboxDatastore through {@link CourierInboxDatastore.shared} */
27
+ private constructor();
28
+ /**
29
+ * Instantiate the datastore with the feeds specified.
30
+ *
31
+ * Feeds are added to the datastore as datasets. Each feed has a respective
32
+ * dataset. Existing datasets will be cleared before the feeds specified are added.
33
+ *
34
+ * @param feeds - The feeds with which to instantiate the datastore
35
+ */
36
+ registerFeeds(feeds: CourierInboxFeed[]): void;
37
+ private createDatasetsFromFilters;
38
+ /**
39
+ * Add a message to the datastore.
40
+ *
41
+ * The message will be added to any datasets for which it qualifies.
42
+ *
43
+ * @param message - The message to add
44
+ */
45
+ addMessage(message: InboxMessage): void;
46
+ private updateDatasetsWithMessageChange;
47
+ /**
48
+ * Listen for real-time message updates from the Courier backend.
49
+ *
50
+ * If an existing WebSocket connection is open, it will be re-used. If not,
51
+ * a new connection will be opened.
52
+ */
53
+ listenForUpdates(): Promise<void>;
54
+ /**
55
+ * Load unread counts for multiple tabs in a single GraphQL query.
56
+ * This populates tab badges without loading messages.
57
+ * @param tabIds - Array of tab IDs to load counts for
58
+ */
59
+ loadUnreadCountsForTabs(tabIds: string[]): Promise<void>;
60
+ /**
61
+ * Add a datastore listener, whose callbacks will be called in response to various message events.
62
+ * @param listener - The listener instance to add
63
+ */
64
+ addDataStoreListener(listener: CourierInboxDataStoreListener): void;
65
+ /**
66
+ * Remove a datastore listener.
67
+ * @param listener - The listener instance to remove
68
+ */
69
+ removeDataStoreListener(listener: CourierInboxDataStoreListener): void;
70
+ /**
71
+ * Mark a message as read.
72
+ * @param message - The message to mark as read
73
+ */
74
+ readMessage({ message }: {
75
+ message: InboxMessage;
76
+ }): Promise<void>;
77
+ /**
78
+ * Mark a message as unread.
79
+ * @param message - The message to mark as unread
80
+ */
81
+ unreadMessage({ message }: {
82
+ message: InboxMessage;
83
+ }): Promise<void>;
84
+ /**
85
+ * Mark a message as opened.
86
+ * @param message - The message to mark as opened
87
+ */
88
+ openMessage({ message }: {
89
+ message: InboxMessage;
90
+ }): Promise<void>;
91
+ /**
92
+ * Unarchive a message.
93
+ * @param message - The message to unarchive
94
+ */
95
+ unarchiveMessage({ message }: {
96
+ message: InboxMessage;
97
+ }): Promise<void>;
98
+ /**
99
+ * Archive a message.
100
+ * @param message - The message to archive
101
+ */
102
+ archiveMessage({ message }: {
103
+ message: InboxMessage;
104
+ }): Promise<void>;
105
+ /**
106
+ * Track a click event for a message.
107
+ * @param message - The message that was clicked
108
+ */
109
+ clickMessage({ message }: {
110
+ message: InboxMessage;
111
+ }): Promise<void>;
112
+ /**
113
+ * Archive all messages for the specified dataset.
114
+ */
115
+ archiveAllMessages(): Promise<void>;
116
+ /**
117
+ * Mark all messages read across all datasets.
118
+ */
119
+ readAllMessages(): Promise<void>;
120
+ /**
121
+ * Archive all read messages for the specified dataset.
122
+ */
123
+ archiveReadMessages(): Promise<void>;
124
+ /**
125
+ * Load datasets from the backend.
126
+ *
127
+ * Props:
128
+ * - canUseCache: If true and the dataset has already been loaded once, this will return the dataset from memory.
129
+ * - datasetIds: Optional: The set of dataset IDs to load. If unset, all known datasets will be loaded.
130
+ *
131
+ * @param props - Options to load datasets, see method documentation
132
+ */
133
+ load(props?: {
134
+ canUseCache: boolean;
135
+ datasetIds?: string[];
136
+ }): Promise<void>;
137
+ private loadDatasets;
138
+ /**
139
+ * Sync messages from a dataset to the global message store.
140
+ * This is called after datasets load messages to ensure the global store has all messages.
141
+ */
142
+ private syncDatasetMessagesToGlobalStore;
143
+ /**
144
+ * Fetch the next page of messages for the specified feed or datasetId.
145
+ *
146
+ * feedType is deprecated and will be removed in the next major release.
147
+ * Please migrate to pass the same identifier as datasetId.
148
+ * While both options are present, exactly one is required.
149
+ *
150
+ * @param props - Options to fetch the next page of messages, see method documetation
151
+ */
152
+ fetchNextPageOfMessages(props: {
153
+ feedType?: string;
154
+ datasetId?: string;
155
+ }): Promise<InboxDataSet | null>;
156
+ /**
157
+ * Get the {@link InboxDataSet} representation of the dataset ID specified.
158
+ * @param datasetId - The dataset ID to get
159
+ */
160
+ getDatasetById(datasetId: string): InboxDataSet | undefined;
161
+ /**
162
+ * Get datasets currently in the datastore.
163
+ * @returns A record mapping dataset IDs to their InboxDataSet representations
164
+ */
165
+ getDatasets(): Record<string, InboxDataSet>;
166
+ /**
167
+ * Get the total unread count across all datasets.
168
+ */
169
+ get totalUnreadCount(): number;
170
+ private fetchNextPageForDataset;
171
+ private handleMessageEvent;
172
+ /**
173
+ * Update all messages across all datasets from an InboxMessageEvent.
174
+ * This only handles InboxMessageEvents that do not specify a messageId
175
+ * and mutate all messages.
176
+ *
177
+ * Related: {@link CourierInboxDatastore.updateMessage}
178
+ */
179
+ private updateAllMessages;
180
+ /**
181
+ * Update a single message across all datasets from an InboxMessageEvent.
182
+ * This only handles InboxMessageEvents that specify a messageId.
183
+ *
184
+ * Related: {@link CourierInboxDatastore.updateAllMessages}
185
+ */
186
+ private updateMessage;
187
+ private clearDatasets;
188
+ private static getISONow;
189
+ /**
190
+ * Create a snapshot of all datasets and global messages for rollback purposes.
191
+ * This captures the current state of all messages and metadata.
192
+ */
193
+ private createDatastoreSnapshot;
194
+ /**
195
+ * Restore all datasets and global messages from a snapshot, reverting any mutations.
196
+ * This is used for rollback when API calls or updates to downstream datasets fail.
197
+ */
198
+ private restoreDatastoreSnapshot;
199
+ /**
200
+ * Execute an operation with automatic rollback on failure.
201
+ * Snapshots all datasets before the operation and restores them if the operation throws.
202
+ *
203
+ * Note: Errors are caught and logged, but not re-thrown to match the behavior
204
+ * for backwards compatibility with the legacy (inbox/archive) datastore implementation.
205
+ *
206
+ * Note: This method exists at the datastore level (rather than dataset) to handle
207
+ * errors from API calls.
208
+ */
209
+ private executeWithRollback;
210
+ /**
211
+ * Get the shared instance of CourierInboxDatastore.
212
+ *
213
+ * CourierInboxDatastore is a singleton. Instance methods should be accessed
214
+ * through this `shared` static accessor.
215
+ */
216
+ static get shared(): CourierInboxDatastore;
217
+ }
package/dist/index.d.ts CHANGED
@@ -5,11 +5,11 @@ export * from './components/courier-inbox-list-item';
5
5
  export * from './components/courier-inbox-popup-menu';
6
6
  export * from './utils/extensions';
7
7
  export * from './types/factories';
8
- export * from './types/feed-type';
9
8
  export * from './types/courier-inbox-theme';
10
9
  export * from './types/courier-inbox-theme-manager';
11
10
  export * from './types/inbox-data-set';
12
- export * from './datastore/datastore';
11
+ export * from './types/inbox-defaults';
12
+ export * from './datastore/inbox-datastore';
13
13
  export * from './datastore/datastore-listener';
14
14
  export * from './datastore/datatore-events';
15
15
  export { Courier };