@talkjs/core
Version:
Lets you connect to your TalkJS chat as a user and read, subscribe to, and update your chat data.
1,370 lines (1,332 loc) • 95.4 kB
TypeScript
/**
* A node in a {@link TextBlock} that renders its children as a clickable {@link https://talkjs.com/docs/Features/Customizations/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 {
type: "actionButton";
/**
* The name of the custom action to invoke when the button is clicked.
*/
action: string;
/**
* The parameters to pass to the custom action when the button is clicked.
*/
params: Record<string, string>;
children: TextNode[];
}
/**
* A node in a {@link TextBlock} that renders its children as a clickable {@link https://talkjs.com/docs/Features/Customizations/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 {
type: "actionLink";
/**
* The name of the custom action to invoke when the link is clicked.
*/
action: string;
/**
* The parameters to pass to the custom action when the link is clicked.
*/
params: Record<string, string>;
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 {
type: "file";
subtype: "audio";
/**
* An encoded identifier for this file. Use in {@link SendFileBlock} to send this file in another message.
*/
fileToken: FileToken;
/**
* The URL where you can fetch the file
*/
url: string;
/**
* The size of the file in bytes
*/
size: number;
/**
* The name of the audio file, including file extension
*/
filename: string;
/**
* The duration of the audio in seconds, if known
*/
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/Reference/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 {
type: "autoLink";
/**
* The URL to open when a user clicks this node.
*/
url: string;
/**
* The text to display in the link.
*/
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 {
type: "bulletList";
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 {
type: "bulletPoint";
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 {
type: "codeSpan";
text: string;
}
/**
* 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 {
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).
*/
latestSnapshot: ConversationSnapshot | null;
}
/**
* The state of a conversation list subscription when it is actively listening for changes
*
* @public
*/
export declare interface ConversationListActiveState {
type: "active";
/**
* The most recently received snapshot for the conversations
*/
latestSnapshot: ConversationSnapshot[];
/**
* True if `latestSnapshot` contains all conversations you are in.
* Use {@link ConversationListSubscription.loadMore} to load more.
*/
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.
*
* @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.
*/
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.
*/
terminated: Promise<UnsubscribedState | ErrorState>;
/**
* Expand the window to include older conversations
*
* @remarks
* Calling `loadMore` multiple times in parallel will still only load one page of conversations.
*
* @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 conversation with a given conversation ID, from the perspective of the current user.
*
* @remarks
* Used in all Data API operations affecting that conversation, such as fetching or updating conversation attributes.
* Created via {@link Session.conversation|Session.conversation()}.
*
* @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 {@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 {@linkcode ParticipantRef} for that user's participation in this conversation
* @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.
* To fetch the most recent messages in this conversation, use {@link ConversationRef.subscribeMessages} instead.
*
* @param id - The ID of the user that you want to reference
* @returns A {@linkcode UserRef} for the user with that ID
* @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 your participation in it.
*
* @remarks
* The conversation is created if a conversation with this ID doesn't already exist.
* You are added as a participant if you are not already a participant in the conversation.
*
* @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
* Initially, you will be subscribed to the 30 most recent messages and any new messages.
* Call `loadMore` to load additional older messages.
*
* Whenever `Subscription.state.type` is "active" and a message is sent, edited, deleted, or you load more messages, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated.
* `loadedAll` is true when the snapshot contains all the messages in the conversation.
*
* The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist)
*/
subscribeMessages(onSnapshot?: (snapshot: MessageSnapshot[] | null, loadedAll: boolean) => void): MessageSubscription;
/**
* Subscribes to the participants in the conversation.
*
* @remarks
* Initially, you will be subscribed to the 10 participants who joined most recently, and any new participants.
* Call `loadMore` to load additional older participants.
*
* Whenever `Subscription.state.type` is "active" and a participant is created, edited, deleted, or you load more participants, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated.
* `loadedAll` is true when the snapshot contains all the participants in the conversation.
*
* The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist)
*/
subscribeParticipants(onSnapshot?: (snapshot: ParticipantSnapshot[] | null, loadedAll: boolean) => void): ParticipantSubscription;
/**
* Subscribes to the conversation.
*
* @remarks
* Whenever `Subscription.state.type` is "active" and something about the conversation changes, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated.
* 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)
*/
subscribe(onSnapshot?: (snapshot: ConversationSnapshot | null) => void): ConversationSubscription;
/**
* Subscribes to the typing status of the conversation.
*
* @remarks
* Whenever `Subscription.state.type` is "active" and the typing status changes, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated.
* This includes changes to nested data, such as when a user who is typing changes their name.
*
* The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist)
*
* Note that if there are "many" people typing and another person starts to type, `onSnapshot` will not be called.
* This is because your existing {@link ManyTypingSnapshot} is still valid and did not change when the new person started to type.
*/
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();
*
* // Don't mark as typing more than once every 5s
* 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>;
}
/**
* 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
*/
id: string;
/**
* Contains the conversation subject, or `null` if the conversation does not have a subject specified.
*/
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.
*/
photoUrl: string | null;
/**
* One or more welcome messages that will display to the user as a SystemMessage
*/
welcomeMessages: string[];
/**
* Custom metadata you have set on the conversation
*/
custom: Record<string, string>;
/**
* The date that the conversation was created, as a unix timestamp in milliseconds.
*/
createdAt: number;
/**
* The date that the current user joined the conversation, as a unix timestamp in milliseconds.
*/
joinedAt: number;
/**
* The last message sent in this conversation, or null if no messages have been sent.
*/
lastMessage: MessageSnapshot | null;
/**
* The number of messages in this conversation that the current user hasn't read.
*/
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.
*/
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.
*/
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.
*/
isUnread: boolean;
/**
* The current user's permission level in this conversation.
*/
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 `@`.
*/
notify: boolean | "MentionsOnly";
}
/**
* A subscription to a specific conversation.
*
* @remarks
* Get a ConversationSubscription by calling {@link ConversationRef.subscribe}
*
* @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.
*/
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.
*/
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.
*/
welcomeMessages?: string[];
/**
* Custom metadata you have set on the conversation.
* This value acts as a patch. Remove specific properties by setting them to `null`.
* 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.html | localization documentation}
* Default = the locale selected 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
*/
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/Message_Features/Emoji_Reactions/#custom-emojis | custom emoji}.
*
* @public
*/
export declare interface CustomEmojiNode {
type: "customEmoji";
/**
* The name of the custom emoji to show.
*/
text: string;
}
/**
* 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 you have 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 you have 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/Message_Features/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 {
type: "error";
/**
* The error that caused the subscription to be terminated
*/
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.
*/
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.
*/
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/Reference/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 & {
__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 {
type: "file";
/**
* Never set for generic file blocks.
*/
subtype?: undefined;
/**
* An encoded identifier for this file. Use in {@link SendFileBlock} to send this file in another message.
*/
fileToken: FileToken;
/**
* The URL where you can fetch the file
*/
url: string;
/**
* The size of the file in bytes
*/
size: number;
/**
* The name of the file, including file extension
*/
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 or got garbage collected.
*
* The `token` and `tokenFetcher` properties are ignored if there is already a session for that user in the registry.
*/
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 {
type: "file";
subtype: "image";
/**
* An encoded identifier for this file. Use in {@link SendFileBlock} to send this image in another message.
*/
fileToken: FileToken;
/**
* The URL where you can fetch the file.
*/
url: string;
/**
* The size of the file in bytes.
*/
size: number;
/**
* The name of the image file, including file extension.
*/
filename: string;
/**
* The width of the image in pixels, if known.
*/
width?: number;
/**
* The height of the image in pixels, if known.
*/
height?: number;
}
export declare interface ImageFileMetadata {
/**
* The name of the file including extension.
*/
filename: string;
/**
* The width of the image in pixels, if known.
*/
width?: number;
/**
* The height of the image in pixels, if known.
*/
height?: number;
}
/**
* A node in a {@link TextBlock} that renders its children as a clickable link (HTML `<a>`).
*
* @remarks
* By default, users do not have permission to send messages containing `LinkNode` as it can be used to maliciously hide the true destination of a link.
*
* @public
*/
export declare interface LinkNode {
type: "link";
/**
* The URL to open when the node is clicked.
*/
url: string;
children: TextNode[];
}
/**
* A block showing a location in the world, typically because a user shared their location in the chat.
*
* @remarks
* In the TalkJS UI, location blocks are rendered as a link to Google Maps, with the map pin showing at the specified coordinate.
* A thumbnail shows the surrounding area on the map.
*
* @public
*/
export declare interface LocationBlock {
type: "location";
/**
* The north-south coordinate of the location.
*
* @remarks
* Usually listed first in a pair of coordinates.
*
* Must be a number between -90 and 90
*/
latitude: number;
/**
* The east-west coordinate of the location.
*
* @remarks
* Usually listed second in a pair of coordinates.
*
* Must be a number between -180 and 180
*/
longitude: number;
}
/**
* The {@link TypingSnapshot} variant used when many people are typing.
*/
export declare interface ManyTypingSnapshot {
/**
* Check this to differentiate between FewTypingSnapshot (`false`) and ManyTypingSnapshot (`true`).
*
* @remarks
* When `true`, you do not receive a list of users who are typing.
* You should show a message like "several people are typing" instead.
*/
many: true;
}
/**
* A node in a {@link TextBlock} that renders its children with a specific style.
*
* @public
*/
export declare interface MarkupNode {
/**
* The kind of formatting to apply when rendering the children
*
* - `type: "bold"` is used when users type `*text*` and is rendered with HTML `<strong>`
*
* - `type: "italic"` is used when users type `_text_` and is rendered with HTML `<em>`
*
* - `type: "strikethrough"` is used when users type `~text~` and is rendered with HTML `<s>`
*/
type: "bold" | "italic" | "strikethrough";
children: TextNode[];
}
/**
* A node in a {@link TextBlock} that is used when a user is {@link https://talkjs.com/docs/Features/Message_Features/Mentions/ | mentioned}.
*
* @remarks
* Used when a user types `@name` and selects the user they want to mention.
*
* @public
*/
export declare interface MentionNode {
type: "mention";
/**
* The ID of the user who is mentioned.
*/
id: string;
/**
* The name of the user who is mentioned.
*/
text: string;
}
/**
* The state of a messages subscription when it is actively listening for changes
*
* @public
*/
export declare interface MessageActiveState {
type: "active";
/**
* The most recently received snapshot for the user, or `null` if the user does not exist yet.
*/
latestSnapshot: MessageSnapshot[] | null;
/**
* True if `latestSnapshot` contains all messages in the conversation.
* Use {@link MessageSubscription.loadMore} to load more.
*/
loadedAll: boolean;
}
/**
* References the message with a given message ID.
*
* @remarks
* Used in all Data API operations affecting that message, such as fetching or editing the message attributes, or deleting the message.
* Created via {@link ConversationRef.message} and {@link ConversationRef.send}.
*
* @public
*/
export declare interface MessageRef {
/**
* The ID of the referenced message.
*
* @remarks
* Immutable: if you want to reference a different message, get a new MessageRef instead.
*/
readonly id: string;
/**
* The ID of the conversation that the referenced message belongs to.
*
* @remarks
* Immutable: if you want to reference a message from a different conversation, get a new MessageRef from that conversation.
*/
readonly conversationId: string;
/**
* Fetches a snapshot of the message.
*
* @remarks
* Supports {@link https://talkjs.com/docs/Reference/JavaScript_Data_API/Performance/#cached-fetch | Cached Fetch}
*
* @returns A snapshot of the message's attributes, or null if the message doesn't exist, the conversation doesn't exist, or you're not a participant in the conversation.
*/
get(): Promise<MessageSnapshot | null>;
/**
* Edits this message.
*
* @returns A promise that resolves when the operation completes. The promise will reject if the request is invalid, the message doesn't exist, or you do not have permission to edit that message.
*/
edit(params: string | EditTextMessageParams | EditMessageParams): Promise<void>;
/**
* Deletes this message, or does nothing if they are already not a participant.
*
* @remarks
* Deleting a nonexistent message is treated as success, and the promise will resolve.
*
* @returns A promise that resolves when the operation completes. This promise will reject if you are not a participant in the conversation or if your role does not give you permission to delete this message.
*/
delete(): Promise<void>;
}
/**
* A snapshot of a message's attributes at a given moment in time.
*
* @remarks
* Automatically expanded to include a snapshot of the user that sent the message, and a snapshot of the referenced message, if this message is a reply.
*
* 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 MessageSnapshot {
/**
* The unique ID that is used to identify the message in TalkJS
*/
id: string;
/**
* Whether this message was "from a user" or a general system message without a specific sender.
*
* The `sender` property is always present for "UserMessage" messages and never present for "SystemMessage" messages.
*/
type: "UserMessage" | "SystemMessage";
/**
* A snapshot of the user who sent the message, or null if it is a system message.
* The user's attributes may have been updated since they sent the message, in which case this snapshot contains the updated data.
* It is not a historical snapshot.
*/
sender: UserSnapshot | null;
/**
* Custom metadata you have set on the message
*/
custom: Record<string, string>;
/**
* Time at which the message was sent, as a unix timestamp in milliseconds
*/
createdAt: number;
/**
* Time at which the message was last edited, as a unix timestamp in milliseconds.
* `null` if the message has never been edited.
*/
editedAt: number | null;
/**
* A snapshot of the message that this message is a reply to, or null if this message is not a reply.
*
* @remarks
* Only UserMessages can reference other messages.
* The referenced message snapshot does not have a `referencedMessage` field.
* Instead, it has `referencedMessageId`.
* This prevents TalkJS fetching an unlimited number of messages in a long chain of replies.
*/
referencedMessage: ReferencedMessageSnapshot | null;
/**
* Where this message originated from:
*
* - "web" = Message sent via the UI or via {@link ConversationBuilder.sendMessage}
*
* - "rest" = Message sent via the REST API's "send message" endpoint or {@link ConversationRef.send}
*
* - "import" = Message sent via the REST API's "import messages" endpoint
*
* - "email" = Message sent by replying to an email notification
*/
origin: "web" | "rest" | "import" | "email";
/**
* The contents of the message, as a plain text string without any formatting or attachments.
* Useful for showing in a conversation list or in notifications.
*/
plaintext: string;
/**
* The main body of the message, as a list of blocks that are rendered top-to-bottom.
*/
content: ContentBlock[];
}
/**
* A subscription to the messages in a specific conversation.
*
* @remarks
* Get a MessageSubscription by calling {@link ConversationRef.subscribeMessages}
*
* The subscription is 'windowed'. It includes all messages since a certain point in time.
* By default, you subscribe to the 30 most recent messages, and any new messages that are sent after you subscribe.
*
* You can expand this window by calling {@link MessageSubscription.loadMore}, which extends the window further into the past.
*
* @public
*/
export declare interface MessageSubscription {
/**
* 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: MessageSnapshot[] | null` the current state of the messages in the window, or null if you're not a participant in the conversation
*
* - `loadedAll: boolean` true when `latestSnapshot` contains all the messages in the conversation
*
* When `type` is "error", includes the `error` field. It is a JS `Error` object explaining what caused the subscription to be terminated.
*/
state: Pendi