UNPKG

@talkjs/core

Version:

Lets you connect to your TalkJS chat as a user and read, subscribe to, and update your chat data.

1,257 lines (1,223 loc) 155 kB
/** * A node in a {@link TextBlock} that renders its children as a clickable {@link https://talkjs.com/docs/Guides/JavaScript/Classic/Action_Buttons_Links/ | action button} which triggers a custom action. * * @remarks * By default, users do not have permission to send messages containing action buttons as they can be used maliciously to trick others into invoking custom actions. * For example, a user could send an "accept offer" action button, but disguise it as "view offer". * * @public */ export declare interface ActionButtonNode { readonly type: "actionButton"; /** * The name of the custom action to invoke when the button is clicked. */ readonly action: string; /** * The parameters to pass to the custom action when the button is clicked. */ readonly params: Record<string, string>; readonly children: TextNode[]; } /** * A node in a {@link TextBlock} that renders its children as a clickable {@link https://talkjs.com/docs/Guides/JavaScript/Classic/Action_Buttons_Links/ | action link} which triggers a custom action. * * @remarks * By default, users do not have permission to send messages containing `ActionLinkNode` as it can be used maliciously to trick others into invoking custom actions. * For example, a user could send an "accept offer" action link, but disguise it as a link to a website. * * @public */ export declare interface ActionLinkNode { readonly type: "actionLink"; /** * The name of the custom action to invoke when the link is clicked. */ readonly action: string; /** * The parameters to pass to the custom action when the link is clicked. */ readonly params: Record<string, string>; readonly children: TextNode[]; } /** * A FileBlock variant for an audio attachment, with additional audio-specific metadata. * * @remarks * You can identify this variant by checking for `subtype: "audio"`. * * The same file could be uploaded as either an audio block, or as a {@link VoiceBlock}. * The same data will be available either way, but they will be rendered differently in the UI. * * Includes metadata about the duration of the audio file in seconds, where available. * * Audio files that you upload with the TalkJS UI will include the duration as long as the sender's browser can preview the file. * Audio files that you upload with the REST API or {@link Session.uploadAudio} will include the duration if you specified it when uploading. * Audio files attached in a reply to an email notification will not include the duration. * * @public */ export declare interface AudioBlock { readonly type: "file"; readonly subtype: "audio"; /** * An encoded identifier for this file. Use in {@link SendFileBlock} to send this file in another message. */ readonly fileToken: FileToken; /** * The URL where you can fetch the file */ readonly url: string; /** * The size of the file in bytes */ readonly size: number; /** * The name of the audio file, including file extension */ readonly filename: string; /** * The duration of the audio in seconds, if known */ readonly duration?: number; } export declare interface AudioFileMetadata { /** * The name of the file including extension. */ filename: string; /** * The duration of the audio file in seconds, if known. */ duration?: number; } /** * A node in a {@link TextBlock} that renders `text` as a link (HTML `<a>`). * * @remarks * Used when user-typed text is turned into a link automatically. * * Unlike {@link LinkNode}, users do have permission to send AutoLinkNodes by default, because the `text` and `url` properties must match. * Specifically: * * - If `text` is an email, `url` must contain a `mailto:` link to the same email address * * - If `text` is a phone number, `url` must contain a `tel:` link to the same phone number * * - If `text` is a website, the domain name including subdomains must be the same in both `text` and `url`. * If `text` includes a protocol (such as `https`), path (/page), query string (?page=true), or url fragment (#title), they must be the same in `url`. * If `text` does not specify a protocol, `url` must use either `https` or `http`. * * This means that the following AutoLink is valid: * * ```json * { * type: "autoLink", * text: "talkjs.com" * url: "https://talkjs.com/docs/JavaScript_Data_API/Message_Content/#AutoLinkNode" * } * ``` * * That link will appear as `talkjs.com` and link you to the specific section of the documentation that explains how AutoLinkNodes work. * * These rules ensure that the user knows what link they are clicking, and prevents AutoLinkNode being used for phishing. * If you try to send a message containing an AutoLink that breaks these rules, the request will be rejected. * * @public */ export declare interface AutoLinkNode { readonly type: "autoLink"; /** * The URL to open when a user clicks this node. */ readonly url: string; /** * The text to display in the link. */ readonly text: string; } /** * A node in a {@link TextBlock} that adds indentation for a bullet-point list around its children (HTML `<ul>`). * * @remarks * Used when users send a bullet-point list by starting lines of their message with `-` or `*`. * * @public */ export declare interface BulletListNode { readonly type: "bulletList"; readonly children: TextNode[]; } /** * A node in a {@link TextBlock} that renders its children with a bullet-point (HTML `<li>`). * * @remarks * Used when users start a line of their message with `-` or `*`. * * @public */ export declare interface BulletPointNode { readonly type: "bulletPoint"; readonly children: TextNode[]; } /** * A node in a {@link TextBlock} that renders `text` in an inline code span (HTML `<code>`). * * @remarks * Used when a user types ```text```. * * @public */ export declare interface CodeSpanNode { readonly type: "codeSpan"; readonly text: string; } /** * Combines multiple simple filters into one, matching whenever any of the simple filters match (OR). * * @remarks * The first element must be the string "any", and the second element a list of simple filter objects. * * See {@link SimpleConversationFilter} for all available options in simple conversation filters. * * @example Only show conversations with messages, and empty conversations with the subject "cats!" * ```json * ["any", [ * { hasMessages: true }, * { subject: ["==", "cats!"] } * ]] * ``` * * @public */ export declare type CompoundFilter<T> = ["any", T[]]; /** * The content of a message is structured as a list of content blocks. * * @remarks * Currently, each message can only have one content block, but this will change in the future. * This will not be considered a breaking change, so your code should assume there can be multiple content blocks. * * These blocks are rendered in order, top-to-bottom. * * Currently the available Content Block types are: * * - `type: "text"` ({@link TextBlock}) * * - `type: "file"` ({@link FileBlock}) * * - `type: "location"` ({@link LocationBlock}) * * @public */ export declare type ContentBlock = TextBlock | FileBlock | LocationBlock; /** * The state of a conversation subscription when it is actively listening for changes * * @public */ export declare interface ConversationActiveState { readonly type: "active"; /** * The most recently received snapshot for the conversation, or `null` if you are not a participant in the conversation (including when the conversation does not exist). */ readonly latestSnapshot: ConversationSnapshot | null; } /** * Passed to `Session.subscribeConversations`, specifying which conversations should be returned in the subscription. * * @remarks * Either a {@link SimpleConversationFilter} or a {@link CompoundFilter} OR-ing multiple simple filters together. * * @example A simple conversation filter that only includes conversations with the subject "cats!" * ```json * { subject: ["==", "cats!"] } * ``` * * @example A compound conversation filter that includes conversations with messages, and empty conversations with the subject "cats!" * ```json * ["any", [ * { hasMessages: true }, * { subject: ["==", "cats!"] } * ]] * * @public */ export declare type ConversationFilter = SimpleConversationFilter | CompoundFilter<SimpleConversationFilter>; /** * The state of a conversation list subscription when it is actively listening for changes * * @public */ export declare interface ConversationListActiveState { readonly type: "active"; /** * The most recently received snapshot for the conversations */ readonly latestSnapshot: ConversationSnapshot[]; /** * True if `latestSnapshot` contains all conversations you are in. * Use {@link ConversationListSubscription.loadMore} to load more. */ readonly loadedAll: boolean; } /** * A subscription to your most recently active conversations. * * @remarks * Get a ConversationListSubscription by calling {@link Session.subscribeConversations}. * * The subscription is 'windowed'. Initially, this window contains the 20 most recent conversations. * Conversations are ordered by last activity. The last activity of a conversation is either `joinedAt` or `lastMessage.createdAt`, whichever is higher. * * The window will automatically expand to include any conversations you join, and any old conversations that receive new messages after subscribing. * * You can expand this window by calling {@link ConversationListSubscription.loadMore}, which extends the window further into the past. * * Remember to `.unsubscribe` the subscription once you are done with it. * * @public */ export declare interface ConversationListSubscription { /** * The current state of the subscription * * @remarks * An object with the following fields: * * `type` is one of "pending", "active", "unsubscribed", or "error". * * When `type` is "active", includes `latestSnapshot` and `loadedAll`. * * - `latestSnapshot: ConversationSnapshot[]` the current state of the conversations in the window * * - `loadedAll: boolean` true when `latestSnapshot` contains all the conversations you are in * * When `type` is "error", includes the `error` field. It is a JS `Error` object explaining what caused the subscription to be terminated. */ state: PendingState | ConversationListActiveState | UnsubscribedState | ErrorState; /** * Resolves when the subscription starts receiving updates from the server. * * @remarks * Wait for this promise if you want to perform some action as soon as the subscription is active. * * The promise rejects if the subscription is terminated before it connects. */ readonly connected: Promise<ConversationListActiveState>; /** * Resolves when the subscription permanently stops receiving updates from the server. * * @remarks * This is either because you unsubscribed or because the subscription encountered an unrecoverable error. */ readonly terminated: Promise<UnsubscribedState | ErrorState>; /** * Expand the window to include older conversations * * @remarks * The `count` parameter is relative to the current number of loaded conversations. * If you call `loadMore(5)` and then call `loadMore(10)` immediately afterwards (without awaiting the promise), * then only 10 additional conversations will be loaded, not 15. * * Avoid calling `.loadMore` in a loop until you have loaded all conversations. * This is usually unnecessary: any time a conversation receives a message, it appears at the start of the list of conversations. * If you do need to call loadMore in a loop, make sure you set a small upper bound (e.g. 100) on the number of conversations, where the loop will exit. * * @param count - The number of additional conversations to load. Must be between 1 and 30. Default 20. * @returns A promise that resolves once the additional conversations have loaded */ loadMore(count?: number): Promise<void>; /** * Unsubscribe from this resource and stop receiving updates. * * @remarks * If the subscription is already in the "unsubscribed" or "error" state, this is a no-op. */ unsubscribe(): void; } /** * References the current user's view of the conversation with a given conversation ID. * * @remarks * Used in all Data API operations affecting that conversation, such as fetching or updating conversation attributes. * Created via {@link Session.conversation|Session.conversation()}. * * Important note: ConversationRef is a reference to *the current user's view* of the conversation. * * If they are not a participant, "their view" of the conversation does not exist. * This means that {@link ConversationRef.get|ConversationRef.get()} will return null. * Additionally, it means that {@link ConversationRef.set|set()} and {@link ConversationRef.createIfNotExists|createIfNotExists()} will add the current user as a participant, in order to create "their view" of the conversation. * For more details, see the documentation for those methods. * * @public */ export declare interface ConversationRef { /** * The ID of the referenced conversation. * * @remarks * Immutable: if you want to reference a different conversation, get a new ConversationRef instead. */ readonly id: string; /** * Get a reference to a participant in this conversation * * @remarks * Note that `Participant` is not the same as `User`. * A `Participant` represents a user's settings related to a specific conversation. * * Calling this method multiple times with the same ID reuses the same {@link ParticipantRef} object. * * Calling {@link ConversationRef.createIfNotExists|ConversationRef.createIfNotExists} or {@link ConversationRef.set|ConversationRef.set} will automatically add the current user as a participant. * * @example To add "Alice" to the conversation "Cats" * ```ts * session.conversation("Cats").participant("Alice").createIfNotExists(); * ``` * * The user "Alice" must exist before you do this. * * @example To remove "Alice" from the conversation "Cats" * ```ts * session.conversation("Cats").participant("Alice").delete(); * ``` * * The user "Alice" will still exist after you do this. This deletes the participant, not the user. * * @param user - Specifies which participant in the conversation you want to reference. Either the user's ID, or a reference to that user. * @returns A {@link ParticipantRef} for that user's participation in this conversation * @throws If the user is not a UserRef or a non-empty string * @public */ participant(user: string | UserRef): ParticipantRef; /** * Get a reference to a message in this conversation * * @remarks * Use this if you need to fetch, delete, or edit a specific message in this conversation, and you know its message ID. * * Calling this method multiple times with the same ID reuses the same {@link MessageRef} object. * * To fetch the most recent messages in this conversation, use {@link ConversationRef.subscribeMessages} instead. * To send a message, use {@link ConversationRef.send}. * * @param id - The ID of the message that you want to reference * @returns A {@link MessageRef} for the message with that ID in this conversation * @throws If the id is not a string or is an empty string * @public */ message(id: string): MessageRef; /** * Fetches a snapshot of the conversation. * * @remarks * This contains all of the information related to the conversation and the current user's participation in the conversation. * * @returns A snapshot of the current user's view of the conversation, or null if the current user is not a participant (including if the conversation doesn't exist). */ get(): Promise<ConversationSnapshot | null>; /** * Sets properties of this conversation and the current user's participation. * * @remarks * If this conversation does not already exist, it will be created. * * If the user is not currently a participant in this conversation, they will be added. * * @returns A promise that resolves when the operation completes. * When client-side conversation syncing is disabled, you must already be a participant and you cannot set anything except the `notify` property. * Everything else requires client-side conversation syncing to be enabled, and will cause the promise to reject. */ set(params: SetConversationParams): Promise<void>; /** * Creates this conversation if it does not already exist. * Adds you as a participant in this conversation, if you are not already a participant. * * @remarks * If the conversation already exists or you are already a participant, this operation is still considered successful and the promise will still resolve. * * @returns A promise that resolves when the operation completes. The promise rejects if you are not already a participant and client-side conversation syncing is disabled. */ createIfNotExists(params?: CreateConversationParams): Promise<void>; /** * Marks the conversation as read. * * @returns A promise that resolves when the operation completes. The promise rejects if you are not a participant in the conversation. */ markAsRead(): Promise<void>; /** * Marks the conversation as unread. * * @returns A promise that resolves when the operation completes. The promise rejects if you are not a participant in the conversation. */ markAsUnread(): Promise<void>; /** * Sends a message in the conversation * * @example Send a simple message with markup (bold in this example) * ```ts * conversationRef.send("*Hello*"); * ``` * * @example Reply to a message and set custom message data * ```ts * conversationRef.send({ * text: "Agreed", * referencedMessageId: "...", * custom: { priority: "HIGH" } * }); * ``` * * @example Send pre-formatted text with {@link TextBlock} * ```ts * conversationRef.send({ * content: [{ * type: "text", * children: [{ * type: "bold", * children: ["Hello"] * }] * }] * }); * ``` * * @example Send a file with {@link SendFileBlock} * ```ts * // `file` is a File object from `<input type="file">` * const fileToken = await session.uploadImage( * file, { filename: file.name, width: 640, height: 480 } * ); * * conversationRef.send({ * content: [{ type: "file", fileToken }] * }); * ``` * * @example Send a location with {@link LocationBlock} * ```ts * // You can get the user's location with the browser's geolocation API * const [latitude, longitude] = [42.43, -83.99]; * conversationRef.send({ * content: [{ type: "location", latitude, longitude }] * }); * ``` * * @returns A promise that resolves with a reference to the newly created message. The promise will reject if you do not have permission to send the message. */ send(params: string | SendTextMessageParams | SendMessageParams): Promise<MessageRef>; /** * Subscribes to the messages in the conversation. * * @remarks * While the subscription is active, `onSnapshot` will be called whenever the message snapshots change. * This includes when a message is sent, edited, deleted, and when you load more messages. * It also includes when nested data changes, such as when `snapshot[0].referencedMessage.sender.name` changes. * `loadedAll` is true when `snapshot` contains all the messages in the conversation, and false if you could load more. * * The `snapshot` list is ordered chronologically with the most recent messages at the start. * When a new message is received, it will be added to the start of the list. * * The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist) * * Initially, you will be subscribed to the 30 most recent messages and any new messages. * Call `loadMore` to load additional older messages. This will trigger `onSnapshot`. * * Tip: If you only care about the most recent message in the conversation, use `ConversationRef.subscribe` or `Session.subscribeConversations`. * Then use the `ConversationSnapshot.lastMessage` property. This is easier to use and slightly more efficient. * * Remember to call `.unsubscribe` on the subscription once you are done with it. */ subscribeMessages(onSnapshot?: (snapshot: MessageSnapshot[] | null, loadedAll: boolean) => void): MessageSubscription; /** * Subscribes to the participants in the conversation. * * @remarks * While the subscription is active, `onSnapshot` will be called whenever the participant snapshots change. * This includes when someone joins or leaves, when their participant attributes are edited, and when you load more participants. * It also includes when nested data changes, such as when `snapshot[0].user.name` changes. * `loadedAll` is true when `snapshot` contains all the participants in the conversation, and false if you could load more. * * The `snapshot` list is ordered chronologically with the participants who joined most recently at the start. * When someone joins the conversation, they will be added to the start of the list. * * The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist) * * Initially, you will be subscribed to the 10 participants who joined most recently, and any new participants. * Call `loadMore` to load additional older participants. This will trigger `onSnapshot`. * * Remember to call `.unsubscribe` on the subscription once you are done with it. */ subscribeParticipants(onSnapshot?: (snapshot: ParticipantSnapshot[] | null, loadedAll: boolean) => void): ParticipantSubscription; /** * Subscribes to the conversation. * * @remarks * While the subscription is active, `onSnapshot` will be called when you join or leave the conversation, or when the snapshot changes. * This includes changes to nested data. As an extreme example, `onSnapshot` would be called when `snapshot.lastMessage.referencedMessage.sender.name` changes. * * The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist) * * Remember to call `.unsubscribe` on the subscription once you are done with it. */ subscribe(onSnapshot?: (snapshot: ConversationSnapshot | null) => void): ConversationSubscription; /** * Subscribes to the typing status of the conversation. * * @remarks * While the subscription is active, `onSnapshot` will be called when you join or leave the conversation, or when the snapshot changes. * This includes changes to the nested `UserSnapshot`s. If one of the people who is typing, changes their name, `onSnapshot` will be called. * You will not be notified when there are already "many" people typing, and another person starts typing, because the snapshot does not change. * * The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist) * * Remember to call `.unsubscribe` on the subscription once you are done with it. */ subscribeTyping(onSnapshot?: (snapshot: TypingSnapshot | null) => void): TypingSubscription; /** * Marks the current user as typing in this conversation for 10 seconds. * * @remarks * This means that other users will see a typing indicator in the UI, from the current user. * * The user will automatically stop typing after 10 seconds. You cannot manually mark a user as "not typing". * Users are also considered "not typing" when they send a message, even if that message was sent from a different tab or using the REST API. * * To keep the typing indicator visible for longer, call this function again to reset the 10s timer. * * @example Example implementation * ```ts * let lastMarkedAsTyping = 0; * * inputElement.addEventListener("change", event => { * const text = event.target.value; * * // Deleting your draft never counts as typing * if (text.length === 0) { * return; * } * * const now = Date.now(); * * // Call `markAsTyping` sparingly - not on every keystroke * // Only call if it has been at least 5 seconds since last time * if (now - lastMarkedAsTyping > 5000) { * lastMarkedAsTyping = now; * convRef.markAsTyping(); * } * }); * * // When you send a message, you are no longer considered typing * // So we need to send markAsTyping as soon as you type something * function onSendMessage() { * lastMarkedAsTyping = 0; * } * ``` */ markAsTyping(): Promise<void>; } /** * Search object returned by `TalkSession.searchConversations` functions. * Used to search for conversations the user is in. * @public */ export declare interface ConversationSearch { /** * Sets the query string for this search and starts searching for conversations whose `subject` or `custom.search` match the query. * * @remarks * This resets the search to its initial state, clearing all previous results. * Initially loads 3 results. Call {@link MessageSearch.loadMore} to load additional results. * * The query matches a conversation when, for each word in the query, that word appears in the conversation's subject or in `conversation.custom.search`. * This means one of the words in the string starts with the word from the query. * This is case-insensitive and punctuation at the start / end of words is ignored (in both the string and the query). * * A conversation titled `I like to look at rainbows`, will appear in searches for `rainbows`, `rain`, `look at`, `(look, i *LIKE* rain)`, and `look look look!`. * It will not appear in searches for `bows`, `rain-bows`, `look-at`, or `I like to look at rainbows too`. * * It is not possible to match both a conversation's subject and its `custom.search` field at the same time. * A conversation called `Kittens` with `custom.search: "where to buy food"` will not appear in a search for `kitten food`. * However, this only applies per-conversation. If there is one conversation with subject `Kittens` and a second conversation with `custom.search: "Kittens"`, then a search for `kittens` will return both conversations. * * You can optionally pass `filters.conversation` to restrict results to conversations that match a {@link ConversationFilter}. * Pass the same filter you use for a conversation list to keep search within that feed's scope. Omit it to search across all of the current user's conversations. * * @param query - The string to search for. * @param filters - Optional {@link ConversationSearchFilters}, e.g. `{ conversation }` to restrict results to conversations matching a {@link ConversationFilter}. * @returns A promise that resolves with the complete first page of results. The promise rejects if the search encounters an error. */ setQuery(query: string, filters?: ConversationSearchFilters): Promise<ConversationSearchState>; /** * Continues searching for more conversations. * * @remarks * You can only call `loadMore` when the search has a query set, and is idle. * Calling `loadMore` before calling `setQuery`, while the `setQuery` promise is still pending, or while another `loadMore` promise is pending, has no effect. * * @param limit - The number of results to load. `limit` should be between 1 and 50. Default 10. * @returns A promise that resolves once finished loading, containing all the results from this page and all previous pages. The promise rejects if the search encounters an error. */ loadMore(limit?: number): Promise<ConversationSearchState>; } /** * Filters for a conversation search, passed to {@link ConversationSearch.setQuery}. * * @public */ export declare interface ConversationSearchFilters { /** * Only return conversations that match this {@link ConversationFilter}. * * Pass the same filter you use for a conversation list to keep search within * that feed's scope. */ conversation?: ConversationFilter; } /** * The state of a conversation search in the current step. * * @public */ export declare interface ConversationSearchState { /** * The conversations that were found in the search. */ results: ConversationSnapshot[]; /** * Whether all results have been loaded. * * @remarks * Note: When true, this means that the search has reached the oldest matching conversation and no more conversations can be loaded. * However, it does not mean that `results` contains every matching conversation. * Any conversations that received messages since you called `setQuery`, may be missing from `results`. */ loadedAll: boolean; } /** * A snapshot of a conversation's attributes at a given moment in time. * * @remarks * Also includes information about the current user's view of that conversation, such as whether or not notifications are enabled. * * Snapshots are immutable and we try to reuse them when possible. You should only re-render your UI when `oldSnapshot !== newSnapshot`. * * @public */ export declare interface ConversationSnapshot { /** * The ID of the conversation */ readonly id: string; /** * Contains the conversation subject, or `null` if the conversation does not have a subject specified. */ readonly subject: string | null; /** * Contains the URL of a photo to represent the topic of the conversation or `null` if the conversation does not have a photo specified. */ readonly photoUrl: string | null; /** * One or more welcome messages that will be rendered at the start of this conversation as system messages. * * @remarks * Welcome messages are rendered in the UI as messages, but they are not real messages. * This means they do not appear when you list messages using the REST API or JS Data API, and you cannot reply or react to them. */ readonly welcomeMessages: string[]; /** * Custom metadata you have set on the conversation */ readonly custom: Record<string, string>; /** * The date that the conversation was created, as a unix timestamp in milliseconds. */ readonly createdAt: number; /** * The date that the current user joined the conversation, as a unix timestamp in milliseconds. */ readonly joinedAt: number; /** * The last message sent in this conversation, or null if no messages have been sent. */ readonly lastMessage: MessageSnapshot | null; /** * The number of messages in this conversation that the current user hasn't read. */ readonly unreadMessageCount: number; /** * The most recent date that the current user read the conversation. * * @remarks * This value is updated whenever you read a message in a chat UI, open an email notification, or mark the conversation as read using an API like {@link ConversationRef.markAsRead}. * * Any messages sent after this timestamp are unread messages. */ readonly readUntil: number; /** * Everyone in the conversation has read any messages sent on or before this date. * * @remarks * This is the minimum of all the participants' `readUntil` values. * Any messages sent on or before this timestamp should show a "read" indicator in the UI. * * This value will rarely change in very large conversations. * If just one person stops checking their messages, `everyoneReadUntil` will never update. */ readonly everyoneReadUntil: number; /** * Whether the conversation should be considered unread. * * @remarks * This can be true even when `unreadMessageCount` is zero, if the user has manually marked the conversation as unread. */ readonly isUnread: boolean; /** * The current user's permission level in this conversation. */ readonly access: "Read" | "ReadWrite"; /** * The current user's notification settings for this conversation. * * @remarks * `false` means no notifications, `true` means notifications for all messages, and `"MentionsOnly"` means that the user will only be notified when they are mentioned with an `@`. */ readonly notify: boolean | "MentionsOnly"; } /** * A subscription to a specific conversation. * * @remarks * Get a ConversationSubscription by calling {@link ConversationRef.subscribe} * * Remember to `.unsubscribe` the subscription once you are done with it. * * @public */ export declare interface ConversationSubscription { /** * The current state of the subscription * * @remarks * An object with the following fields: * * `type` is one of "pending", "active", "unsubscribed", or "error". * * When `type` is "active", includes `latestSnapshot: ConversationSnapshot | null`, the current state of the conversation. * `latestSnapshot` is `null` when you are not a participant or the conversation does not exist. * * When `type` is "error", includes the `error` field. It is a JS `Error` object explaining what caused the subscription to be terminated. */ state: PendingState | ConversationActiveState | UnsubscribedState | ErrorState; /** * Resolves when the subscription starts receiving updates from the server. */ readonly connected: Promise<ConversationActiveState>; /** * Resolves when the subscription permanently stops receiving updates from the server. * * @remarks * This is either because you unsubscribed or because the subscription encountered an unrecoverable error. */ readonly terminated: Promise<UnsubscribedState | ErrorState>; /** * Unsubscribe from this resource and stop receiving updates. * * @remarks * If the subscription is already in the "unsubscribed" or "error" state, this is a no-op. */ unsubscribe(): void; } /** * Parameters you can pass to {@link ConversationRef.createIfNotExists}. * * Properties that are `undefined` will be set to the default. * * @public */ export declare interface CreateConversationParams { /** * The conversation subject to display in the chat header. * Default = no subject, list participant names instead. */ subject?: string; /** * The URL for the conversation photo to display in the chat header. * Default = no photo, show a placeholder image. */ photoUrl?: string; /** * System messages which are sent at the beginning of a conversation. * Default = no messages. * * @remarks * Welcome messages are rendered in the UI as messages, but they are not real messages. * This means they do not appear when you list messages using the REST API or JS Data API, and you cannot reply or react to them. */ welcomeMessages?: string[]; /** * Custom metadata to set on the conversation. * Default = no custom metadata */ custom?: Record<string, string>; /** * Your access to the conversation. * Default = "ReadWrite" access. */ access?: "Read" | "ReadWrite"; /** * Your notification settings. * Default = `true` */ notify?: boolean | "MentionsOnly"; } /** * Parameters you can pass to {@link ParticipantRef.createIfNotExists}. * * @remarks * Properties that are `undefined` will be set to the default. * * @public */ export declare interface CreateParticipantParams { /** * The level of access the participant should have in the conversation. * Default = "ReadWrite" access. */ access?: "ReadWrite" | "Read"; /** * When the participant should be notified about new messages in this conversation. * Default = `true` * * @remarks * `false` means no notifications, `true` means notifications for all messages, and `"MentionsOnly"` means that the user will only be notified when they are mentioned with an `@`. */ notify?: boolean | "MentionsOnly"; } /** * Parameters you can pass to {@link UserRef.createIfNotExists}. * * @remarks * Properties that are `undefined` will be set to the default. * * @public */ export declare interface CreateUserParams { /** * The user's name which is displayed on the TalkJS UI */ name: string; /** * Custom metadata you have set on the user. * Default = no custom metadata */ custom?: Record<string, string>; /** * An {@link https://www.w3.org/International/articles/language-tags/ | IETF language tag}. * See the {@link https://talkjs.com/docs/Features/Language_Support/#localization | localization documentation} * Default = null, will use the locale set on the dashboard */ locale?: string; /** * An optional URL to a photo that is displayed as the user's avatar. * Default = no photo */ photoUrl?: string; /** * TalkJS supports multiple sets of settings, called "roles". These allow you to change the behavior of TalkJS for different users. * You have full control over which user gets which configuration. * Default = the `default` role */ role?: string; /** * The default message a person sees when starting a chat with this user. * Default = no welcome message * * @remarks * Welcome messages are rendered in the UI as messages, but they are not real messages. * This means they do not appear when you list messages using the REST API or JS Data API, and you cannot reply or react to them. * * Note: User welcome messages are only supported by the Classic SDKs, and in * the Components-based SDKs, this field is ignored. To show welcome messages * in the Components-based SDK, see * {@link https://talkjs.com/docs/Guides/React/Welcome_Messages/ | Welcome Messages}. * * @deprecated */ welcomeMessage?: string; /** * A single email address or an array of email addresses associated with the user. * Default = no email addresses */ email?: string | string[]; /** * A single phone number or an array of phone numbers associated with the user. * Default = no phone numbers */ phone?: string | string[]; /** * An object of push registration tokens to use when notifying this user. * * Keys in the object have the format `'provider:token_id'`, * where `provider` is either `"fcm"` for Android (Firebase Cloud Messaging), * or `"apns"` for iOS (Apple Push Notification Service). * * Default = no push registration tokens */ pushTokens?: Record<string, true>; } /** * A node in a {@link TextBlock} that is used for {@link https://talkjs.com/docs/Features/Messages/Emojis/#custom-emojis | custom emoji}. * * @public */ export declare interface CustomEmojiNode { readonly type: "customEmoji"; /** * The name (including colons at the start and end) of the custom emoji to show. */ readonly text: string; } /** * @public * A string or a two-element array that forms a predicate about a string field in a `custom` field. * * @remarks * Used in {@link MessagePredicate} and {@link ConversationPredicate}. * Allows all forms in {@link StringFilter} plus the following: * * - `"exists"` * * - `"!exists"` */ export declare type CustomFilter = Record<string, StringFilter | "exists" | "!exists">; /** * Parameters you can pass to {@link MessageRef.edit}. * * @remarks * Properties that are `undefined` will not be changed. * To clear / reset a property to the default, pass `null`. * * This is the more advanced method for editing a message. It gives you full control over the message content. * You can decide exactly how a text message should be formatted, edit an attachment, or even turn a text message into a location. * * @public */ export declare interface EditMessageParams { /** * Custom metadata to set on the message. * This value acts as a patch. Remove specific properties by setting them to `null`. * Default = no custom metadata */ custom?: Record<string, string | null> | null; /** * The new content for the message. * * @remarks * Any value provided here will overwrite the existing message content. * * By default users do not have permission to send {@link LinkNode}, {@link ActionLinkNode}, or {@link ActionButtonNode}, as they can be used to trick the recipient. */ content?: [SendContentBlock]; } /** * Parameters you can pass to {@link MessageRef.edit}. * * @remarks * Properties that are `undefined` will not be changed. * To clear / reset a property to the default, pass `null`. * * This is a simpler version of {@link EditMessageParams} that only supports setting the message content to text. * * @public */ export declare interface EditTextMessageParams { /** * Custom metadata to set on the message. * This value acts as a patch. Remove specific properties by setting them to `null`. * Default = no custom metadata */ custom?: Record<string, string | null> | null; /** * The new text to set in the message body. * * @remarks * This is parsed the same way as the text entered in the message field. For example, `*hi*` will appear as `hi` in bold. * * See the {@link https://talkjs.com/docs/Features/Messages/Formatting/ | message formatting documentation} for more details. */ text?: string; } /** * The state of a subscription after it encounters an unrecoverable error * * @public */ export declare interface ErrorState { readonly type: "error"; /** * The error that caused the subscription to be terminated */ readonly error: Error; } /** * The {@link TypingSnapshot} variant used when only a few people are typing. */ export declare interface FewTypingSnapshot { /** * Check this to differentiate between FewTypingSnapshot (`false`) and ManyTypingSnapshot (`true`). * * @remarks * When `false`, you can see the list of users who are typing in the `users` property. */ readonly many: false; /** * The users who are currently typing in this conversation. * * @remarks * The list is in chronological order, starting with the users who have been typing the longest. * The current user is never contained in the list, only other users. */ readonly users: UserSnapshot[]; } /** * A file attachment received in a message's content. * * @remarks * All `FileBlock` variants contain `url`, `size`, and `fileToken`. * Some file blocks have additional metadata, in which case they will have the `subtype` property set. * * Currently the available FileBlock subtypes are: * * - No `subtype` set ({@link GenericFileBlock}) * * - `subtype: "video"` ({@link VideoBlock}) * * - `subtype: "image"` ({@link ImageBlock}) * * - `subtype: "audio"` ({@link AudioBlock}) * * - `subtype: "voice"` ({@link VoiceBlock}) * * @public */ export declare type FileBlock = VideoBlock | ImageBlock | AudioBlock | VoiceBlock | GenericFileBlock; /** * A token representing a file uploaded to TalkJS. * * @remarks * You cannot create a FileToken yourself. Get a file token by uploading your file to TalkJS with {@link Session.uploadFile}, or one of the subtype-specific variants like {@link Session.uploadImage}. * Alternatively, take a file token from an existing {@link FileBlock} to re-send an attachment you received, without having to download and re-upload the file. * You can also upload files using the {@link https://talkjs.com/docs/REST_API/Messages/#1-upload-a-file| REST API}. * This system ensures that all files must be uploaded to TalkJS before being sent to users, limiting the risk of malware. * * Passed in {@link SendFileBlock} when you send a message containing a file attachment. * * We may change the FileToken format in the future. * Do not store old file tokens for future use, as these may stop working. * * @example Using a file input * ```ts * // From `<input type="file">` * const file: File = fileInputElement.files[0]; * const myFileToken = await session.uploadFile(file, { filename: file.name }); * * const block = { * type: 'file', * fileToken: myFileToken, * }; * session.conversation('example_conversation_id').send({ content: [block] }); * ``` * * @example Re-sending a file from a previous message * ```ts * session.conversation('example_conversation_id').send({ * content: previousMessageSnapshot.content * }); * ``` * * @public */ export declare type FileToken = string & { readonly __tag: Record<"TalkJS Encoded File Token", true>; }; /** * The most basic FileBlock variant, used whenever there is no additional metadata for a file. * * @remarks * Do not try to check for `subtype === undefined` directly, as this will break when we add new FileBlock variants in the future. * * Instead, treat GenericFileBlock as the default. For example: * * ```ts * if (block.subtype === "video") { * handleVideoBlock(block); * } else if (block.subtype === "image") { * handleImageBlock(block); * } else if (block.subtype === "audio") { * handleAudioBlock(block); * } else if (block.subtype === "voice") { * handleVoiceBlock(block); * } else { * handleGenericFileBlock(block); * } * ``` * * @public */ export declare interface GenericFileBlock { readonly type: "file"; /** * Never set for generic file blocks. */ readonly subtype?: never; /** * An encoded identifier for this file. Use in {@link SendFileBlock} to send this file in another message. */ readonly fileToken: FileToken; /** * The URL where you can fetch the file */ readonly url: string; /** * The size of the file in bytes */ readonly size: number; /** * The name of the file, including file extension */ readonly filename: string; } export declare interface GenericFileMetadata { /** * The name of the file including extension. */ filename: string; } /** * Returns a TalkSession for the specified App ID and User ID. * * @remarks * Backed by a registry, so calling this function twice with the same app and user returns the same session object both times. * A new session will be created if the old one encountered an error. */ export declare function getTalkSession(options: TalkSessionOptions): TalkSession; /** * A FileBlock variant for an image attachment, with additional image-specific metadata. * * @remarks * You can identify this variant by checking for `subtype: "image"`. * * Includes metadata about the height and width of the image in pixels, where available. * * Images that you upload with the TalkJS UI will include the image dimensions as long as the sender's browser can preview the file. * Images that you upload with the REST API or {@link Session.uploadImage} will include the dimensions if you specified them when uploading. * Image attached in a reply to an email notification will not include the dimensions. * * @public */ export declare interface ImageBlock { readonly type: "file"; readonly subtype: "image"; /** * An encoded identifier for this