Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Quoted replies

What users get

A quoted reply answers a specific message without leaving the conversation: the reply carries a copy of the message it answers. It works in channels and threads alike.

  • Quoting. Quote Reply in a message's menu shows the message above the composer's input and moves the focus to the input. The ✕ on the preview removes the quote.
  • The sent reply. The quoted message appears at the top of the reply's bubble, with its author, its text or a preview of its attachments.
  • Jumping. Clicking the quoted message jumps to the original in the list and highlights it, loading older messages when it isn't loaded.
  • Staying current. If the quoted message is edited while the user is writing, the preview updates. If it's deleted, the quote is removed.

How it works across the layers

The user wants to answer Ben's question from earlier in "Design":

  1. They pick Quote Reply on Ben's message. The action calls MessageComposer.setQuotedMessage(message) on the composer they type in: the channel's, or the thread's inside a thread.
  2. The composer keeps the message in its state as quotedMessage. QuotedMessagePreview reads it and shows it above the input.
  3. The user sends their reply. The composer adds quoted_message_id to the request, and a copy of the quoted message to the local message, so the reply shows the quote while it's sending.
  4. In the message list, QuotedMessage renders message.quoted_message. Clicking it calls MessagePaginator.jumpToMessage() on the list's message paginator.

While a message is quoted, commands of the moderation set are disabled in the composer. A draft keeps its quote, and restoring the draft brings it back. Removing the quote is MessageComposer.setQuotedMessage(null).

The composer's state and its parts are on Message composer, and the composer's previews on Message composer UI. How the list jumps to a message is on Jumping and highlighting.

Turning it on or off

Quoted replies are always available. Two things decide who can use them:

  • The quote-message capability. Without it, the message menu leaves out Quote Reply. The server derives it from the user's role and the channel type's settings, which include quotes.
  • The disableQuotedMessages prop of MessageList, which removes Quote Reply from that list. VirtualizedMessageList doesn't have it; remove the action from the action set there instead, as described on Actions.

Customizing

Quoting from your own control

Any component inside a Channel or Thread can quote a message through the composer:

import type { LocalMessage } from "stream-chat";
import { useMessageComposerController } from "stream-chat-react";

export const QuoteButton = ({ message }: { message: LocalMessage }) => {
  const composer = useMessageComposerController();

  return (
    <button onClick={() => composer.setQuotedMessage(message)} type="button">
      Quote
    </button>
  );
};

To put it in the message menu, add it to the message action set; see Actions.

Replacing the preview in the composer

QuotedMessagePreview is an override key. A replacement reads quotedMessage from the composer's state:

import type { MessageComposerState } from "stream-chat";
import {
  useMessageComposerController,
  useStateStore,
  WithComponents,
} from "stream-chat-react";

const quoteSelector = (state: MessageComposerState) => ({
  quotedMessage: state.quotedMessage,
});

const CompactQuotePreview = () => {
  const composer = useMessageComposerController();
  const { quotedMessage } = useStateStore(composer.state, quoteSelector);

  if (!quotedMessage) return null;

  return (
    <div className="compact-quote-preview">
      Replying to {quotedMessage.user?.name ?? "a message"}
      <button onClick={() => composer.setQuotedMessage(null)} type="button">
        Cancel
      </button>
    </div>
  );
};

export const App = () => (
  <WithComponents overrides={{ QuotedMessagePreview: CompactQuotePreview }}>
    {/* the rest of the app */}
  </WithComponents>
);

While the user edits a message, the composer shows EditedMessagePreview in its place.

Replacing the quote in a message

QuotedMessage renders the quote inside a sent message. A replacement reads the message from useMessageContext():

import {
  useMessageContext,
  useMessagePaginator,
  WithComponents,
} from "stream-chat-react";

const QuoteLine = () => {
  const { message } = useMessageContext();
  // Inside a thread, the original is in the thread's replies, which this list holds.
  const messagePaginator = useMessagePaginator();
  const quoted = message.quoted_message;

  if (!quoted) return null;

  return (
    <button
      className="quote-line"
      onClick={() => messagePaginator.jumpToMessage(quoted.id)}
      type="button"
    >
      {quoted.user?.name}: {quoted.text}
    </button>
  );
};

export const App = () => (
  <WithComponents overrides={{ QuotedMessage: QuoteLine }}>
    {/* the rest of the app */}
  </WithComponents>
);

The jump itself is explained on Jumping and highlighting.

Reference