@trycourier/courier-ui-inbox
Version:
Inbox components for the Courier web UI
229 lines (228 loc) • 8.19 kB
TypeScript
import { InboxMessage } from '@trycourier/courier-js';
import { CourierInboxFeed, InboxDataSet } from '../types/inbox-data-set';
import { CourierInboxDataStoreListener } from './datastore-listener';
/**
* Shared datastore for Inbox components.
*
* CourierInboxDatastore is a singleton. Use `CourierInboxDatastore.shared`
* to access the shared instance.
*
* @public
*/
export declare class CourierInboxDatastore {
private static readonly TAG;
private static readonly OPEN_BATCH_DELAY_MS;
private static readonly OPEN_BATCH_MAX_SIZE;
private static instance;
private _datasets;
private _listeners;
private _removeMessageEventListener?;
private _pendingOpenMessageIds;
private _openBatchTimer;
/**
* Global message store is a map of Message ID to Message for all messages
* that have been loaded.
*
* This acts as the source of truth to apply messages mutations to a message
* given its ID and propagate those mutations to individual datasets.
*/
private _globalMessages;
/** Access CourierInboxDatastore through {@link CourierInboxDatastore.shared} */
private constructor();
/**
* Instantiate the datastore with the feeds specified.
*
* Feeds are added to the datastore as datasets. Each feed has a respective
* dataset. Existing datasets will be cleared before the feeds specified are added.
*
* @param feeds - The feeds with which to instantiate the datastore
*/
registerFeeds(feeds: CourierInboxFeed[]): void;
private createDatasetsFromFilters;
/**
* Add a message to the datastore.
*
* The message will be added to any datasets for which it qualifies.
*
* @param message - The message to add
*/
addMessage(message: InboxMessage): void;
private updateDatasetsWithMessageChange;
/**
* Listen for real-time message updates from the Courier backend.
*
* If an existing WebSocket connection is open, it will be re-used. If not,
* a new connection will be opened.
*/
listenForUpdates(): Promise<void>;
/**
* Load unread counts for multiple tabs in a single GraphQL query.
* This populates tab badges without loading messages.
* @param tabIds - Array of tab IDs to load counts for
*/
loadUnreadCountsForTabs(tabIds: string[]): Promise<void>;
/**
* Add a datastore listener, whose callbacks will be called in response to various message events.
* @param listener - The listener instance to add
*/
addDataStoreListener(listener: CourierInboxDataStoreListener): void;
/**
* Remove a datastore listener.
* @param listener - The listener instance to remove
*/
removeDataStoreListener(listener: CourierInboxDataStoreListener): void;
/**
* Mark a message as read.
* @param message - The message to mark as read
*/
readMessage({ message }: {
message: InboxMessage;
}): Promise<void>;
/**
* Mark a message as unread.
* @param message - The message to mark as unread
*/
unreadMessage({ message }: {
message: InboxMessage;
}): Promise<void>;
/**
* Mark a message as opened.
*
* The local state is updated optimistically and the server call is
* batched: multiple opens arriving within a short window
* are collected and flushed as a single GraphQL request.
*
* @param message - The message to mark as opened
*/
openMessage({ message }: {
message: InboxMessage;
}): void;
private scheduleBatchOpen;
private flushBatchOpen;
/**
* Unarchive a message.
* @param message - The message to unarchive
*/
unarchiveMessage({ message }: {
message: InboxMessage;
}): Promise<void>;
/**
* Archive a message.
* @param message - The message to archive
*/
archiveMessage({ message }: {
message: InboxMessage;
}): Promise<void>;
/**
* Track a click event for a message.
* @param message - The message that was clicked
*/
clickMessage({ message }: {
message: InboxMessage;
}): Promise<void>;
/**
* Archive all messages for the specified dataset.
*/
archiveAllMessages(): Promise<void>;
/**
* Mark all messages read across all datasets.
*/
readAllMessages(): Promise<void>;
/**
* Archive all read messages for the specified dataset.
*/
archiveReadMessages(): Promise<void>;
/**
* Load datasets from the backend.
*
* Props:
* - canUseCache: If true and the dataset has already been loaded once, this will return the dataset from memory.
* - datasetIds: Optional: The set of dataset IDs to load. If unset, all known datasets will be loaded.
*
* @param props - Options to load datasets, see method documentation
*/
load(props?: {
canUseCache: boolean;
datasetIds?: string[];
}): Promise<void>;
private loadDatasets;
/**
* Sync messages from a dataset to the global message store.
* This is called after datasets load messages to ensure the global store has all messages.
*/
private syncDatasetMessagesToGlobalStore;
/**
* Fetch the next page of messages for the specified feed or datasetId.
*
* feedType is deprecated and will be removed in the next major release.
* Please migrate to pass the same identifier as datasetId.
* While both options are present, exactly one is required.
*
* @param props - Options to fetch the next page of messages, see method documetation
*/
fetchNextPageOfMessages(props: {
feedType?: string;
datasetId?: string;
}): Promise<InboxDataSet | null>;
/**
* Get the {@link InboxDataSet} representation of the dataset ID specified.
* @param datasetId - The dataset ID to get
*/
getDatasetById(datasetId: string): InboxDataSet | undefined;
/**
* Get datasets currently in the datastore.
* @returns A record mapping dataset IDs to their InboxDataSet representations
*/
getDatasets(): Record<string, InboxDataSet>;
/**
* Get the total unread count across all datasets.
*/
get totalUnreadCount(): number;
private fetchNextPageForDataset;
private handleMessageEvent;
/**
* Update all messages across all datasets from an InboxMessageEvent.
* This only handles InboxMessageEvents that do not specify a messageId
* and mutate all messages.
*
* Related: {@link CourierInboxDatastore.updateMessage}
*/
private updateAllMessages;
/**
* Update a single message across all datasets from an InboxMessageEvent.
* This only handles InboxMessageEvents that specify a messageId.
*
* Related: {@link CourierInboxDatastore.updateAllMessages}
*/
private updateMessage;
private clearDatasets;
private static getISONow;
/**
* Create a snapshot of all datasets and global messages for rollback purposes.
* This captures the current state of all messages and metadata.
*/
private createDatastoreSnapshot;
/**
* Restore all datasets and global messages from a snapshot, reverting any mutations.
* This is used for rollback when API calls or updates to downstream datasets fail.
*/
private restoreDatastoreSnapshot;
/**
* Execute an operation with automatic rollback on failure.
* Snapshots all datasets before the operation and restores them if the operation throws.
*
* Note: Errors are caught and logged, but not re-thrown to match the behavior
* for backwards compatibility with the legacy (inbox/archive) datastore implementation.
*
* Note: This method exists at the datastore level (rather than dataset) to handle
* errors from API calls.
*/
private executeWithRollback;
/**
* Get the shared instance of CourierInboxDatastore.
*
* CourierInboxDatastore is a singleton. Instance methods should be accessed
* through this `shared` static accessor.
*/
static get shared(): CourierInboxDatastore;
}