跳到主要内容

引用回复

IMKit 支持引用回复功能,允许用户在聊天页面中回复彼此的消息。引用回复功能默认发送的消息包含引用消息内容对象 ReferenceMessage(类型标识:RC:ReferenceMsg)。

提示

从 26.3.0 开始,IMKit 针对引用消息进行功能升级。SDK 不再将回复内容封装为 RC:ReferenceMsg,而是在文本、图片、高清语音、文件、位置等实际回复消息结构上写入引用关系信息。该功能需要在 IMKit SDK 中配置开启后才能使用,默认仍然使用原引用消息逻辑。

局限

旧版引用回复功能目前有以下限制:

  • 仅支持文本消息、文件消息、图文消息、图片消息、引用消息的引用。
  • 引用深度仅支持一度,即只能引用回复原始消息。如果多重引用,只展示上一层被引消息内容。

用法

IMKit 会话页面默认已启用引用回复功能。用户在会话页面长按消息,在弹框里选择引用消息,即可引用该消息。在输入区添加消息内容后,SDK 默认会将输入内容与被引消息组合为 ReferenceMessage,并发送到会话中。

引用消息 V2

默认的引用回复会将输入内容与被引消息组合为 ReferenceMessage 发送。开启引用消息 V2 后,SDK 会在实际发送的消息对象上写入引用关系信息 QuoteInfo,并通过 Message.quoteInfo 标识该消息回复的是哪一条原始消息。原 RC:ReferenceMsg 仍然可以正常使用。

引用消息 V2 默认关闭。开启后,用户引用一条消息后,可以继续发送文本、图片、GIF、小视频、普通语音、高清语音、文件、位置、图文等多种类型的消息作为回复。

您可以通过 ConversationConfig 开启或关闭引用消息 V2。

TypeScript
let config = RongIM.getInstance().conversationService().getConversationConfig();

// 开启引用消息 V2
config.setQuoteV2Enable(true);
RongIM.getInstance().conversationService().setConversationConfig(config);

如需关闭引用消息 V2,传入 false 即可。关闭后,IMKit 仍使用旧版 ReferenceMessage 引用回复逻辑。

TypeScript
let config = RongIM.getInstance().conversationService().getConversationConfig();

// 关闭引用消息 V2,恢复旧版 ReferenceMessage 引用回复逻辑
config.setQuoteV2Enable(false);
RongIM.getInstance().conversationService().setConversationConfig(config);

可发送的回复消息类型由 setQuoteMessageTypeWhiteList 控制。默认包含:

消息类型类型标识
文本消息RC:TxtMsg
语音消息RC:VcMsg
高清语音消息RC:HQVCMsg
图片消息RC:ImgMsg
GIF 消息RC:GIFMsg
小视频消息RC:SightMsg
文件消息RC:FileMsg
位置消息RC:LBSMsg
图文消息RC:ImgTextMsg

如需限制可作为回复发送的消息类型,可以重新设置白名单。传入空数组时会重置为默认全量类型。

TypeScript
let config = RongIM.getInstance().conversationService().getConversationConfig();
config.setQuoteMessageTypeWhiteList([
"RC:TxtMsg",
"RC:ImgMsg",
"RC:FileMsg"
]);
RongIM.getInstance().conversationService().setConversationConfig(config);

引用自定义消息

IMKit 从 26.6.0 开始支持引用自定义消息。业务侧需要按实际场景完成以下配置:

  • 将自定义消息 objectName 加入可被引用的消息类型列表,使长按菜单可以展示引用消息入口。
  • 如需自定义被引用内容的展示,注册消息列表引用卡片和输入框上方引用预览的 Provider。
提示

setReferenceableMessageTypeList 控制的是“哪类消息可以被引用”,影响长按菜单入口。

自定义范围说明

开启引用消息 V2 后,回复消息仍使用实际消息类型对应的消息模板展示,引用关系保存在 Message.quoteInfo 中。IMKit 内置消息模板会根据 quoteInfo 展示引用卡片。

如果需要自定义回复消息的整体气泡样式,应按自定义消息模板方式处理,注册实际消息类型对应的 IMessageItemProvider。如果只需要自定义被引用内容在引用区域中的展示,可以注册下文的 ReferenceContentMessageItemProviderReferenceContentInputBarProvider

配置可被引用的自定义消息

SDK 初始化时默认填充现有支持被引用的内置消息类型,包括 RC:TxtMsgRC:ImgMsgRC:FileMsgRC:ImgTextMsgRC:ReferenceMsgRCE:ReferenceMsg,不包含业务自定义消息。您可以通过 ConversationConfig 增加自定义消息类型。

TypeScript
import { RongIM } from '@rongcloud/imkit';

const orderMessageObjectName = 'App:OrderMsg';

let config = RongIM.getInstance().conversationService().getConversationConfig();
let referenceableTypes = config.getReferenceableMessageTypeList();

if (!referenceableTypes.includes(orderMessageObjectName)) {
referenceableTypes.push(orderMessageObjectName);
}

config.setReferenceableMessageTypeList(referenceableTypes);
RongIM.getInstance().conversationService().setConversationConfig(config);

如果需要按业务条件控制长按菜单中的引用入口,可以设置 setIsShowReferenceMenuItem 回调。传入 null 时恢复 SDK 默认准入逻辑。该回调只控制引用入口是否展示,不改变发送中、发送失败、阅后即焚等 SDK 基础限制。

TypeScript
import { Message, RongIM } from '@rongcloud/imkit';

const orderMessageObjectName = 'App:OrderMsg';

let config = RongIM.getInstance().conversationService().getConversationConfig();

config.setIsShowReferenceMenuItem((message: Message): boolean => {
if (message.objectName === orderMessageObjectName) {
return true;
}
return config.isReferenceableMessageType(message.objectName);
});

RongIM.getInstance().conversationService().setConversationConfig(config);

接口定义如下:

TypeScript
setIsShowReferenceMenuItem(callback: ((message: Message) => boolean) | null): void;

自定义引用内容展示

引用自定义消息时,IMKit 提供两类 Provider 用于展示被引用内容:

Provider展示位置数据类型
ReferenceContentMessageItemProvider<T>消息列表中回复消息里的引用卡片ReferenceContentMessageItemData<T>
ReferenceContentInputBarProvider<T>输入框上方的引用预览区域ReferenceInputBarComponentData<T>

注册 Provider 时,SDK 会优先按 objectName 精确查找;未找到时,如果传入了被引用消息,会遍历已注册 Provider 并调用 isReferenceContentType 进行兜底匹配。

Provider 数据由 SDK 创建,业务侧通过 data.quotedMessage?.content 获取被引用消息内容。ReferenceContentMessageItemDataReferenceInputBarComponentData 不包含 quotedContentuserName 等派生字段。

提示
  • 未注册自定义引用内容 Provider 时,IMKit 会继续使用默认引用展示。
  • ReferenceContentMessageItemProvider 只处理消息列表中回复消息内的引用卡片展示,不控制输入框上方的引用预览区域。
  • ReferenceContentInputBarProvider 只处理用户选择引用消息后,输入框上方的引用预览区域。
  • Provider Builder 内部的点击、跳转、埋点等业务交互由业务侧自行实现;需要取消当前引用时,调用 ReferenceInputBarComponentData.action?.cancelReference()
TypeScript
export class ReferenceContentMessageItemData<T extends MessageContent | Object> {
context?: Context;
parentMessage?: Message;
quotedMessage?: Message;
}

export interface ReferenceContentMessageItemProvider<T extends MessageContent | Object> {
getWrapBuilder(): WrappedBuilder<[ReferenceContentMessageItemData<T>]>;

isReferenceContentType(content: MessageContent | Object): boolean;
}

export interface ReferenceInputBarAction {
cancelReference(): void;
}

export class ReferenceInputBarComponentData<T extends MessageContent | Object> {
context?: Context;
convId?: ConversationIdentifier;
quotedMessage?: Message;
action?: ReferenceInputBarAction;
inputAreaViewModel?: IInputAreaViewModel;
}

export interface ReferenceContentInputBarProvider<T extends MessageContent | Object> {
getWrapBuilder(): WrappedBuilder<[ReferenceInputBarComponentData<T>]>;

isReferenceContentType(content: MessageContent | Object): boolean;
}

注册引用内容 Provider

建议在应用初始化 IMKit 或打开会话页面前完成注册。

TypeScript
RongIM.getInstance()
.conversationService()
.addReferenceContentMessageItemProvider(
'App:OrderMsg',
new CustomOrderReferenceMessageItemProvider()
);

RongIM.getInstance()
.conversationService()
.addReferenceContentInputBarProvider(
'App:OrderMsg',
new CustomOrderReferenceInputBarProvider()
);

以下示例以 Demo 中的订单消息 CustomOrderMessage 为例,消息类型标识为 App:OrderMsg

TypeScript
import {
ReferenceContentInputBarProvider,
ReferenceContentMessageItemData,
ReferenceContentMessageItemProvider,
ReferenceInputBarComponentData,
RongIM
} from '@rongcloud/imkit';
import { MessageContent } from '@rongcloud/imlib';
import { CustomOrderMessage, CustomOrderMessageObjectName } from './message/CustomOrderMessage';

class CustomOrderReferenceMessageItemProvider implements ReferenceContentMessageItemProvider<CustomOrderMessage> {
getWrapBuilder(): WrappedBuilder<[ReferenceContentMessageItemData<CustomOrderMessage>]> {
return wrapBuilder(buildCustomOrderReferenceMessageItem);
}

isReferenceContentType(content: MessageContent | Object): boolean {
return content instanceof CustomOrderMessage;
}
}

class CustomOrderReferenceInputBarProvider implements ReferenceContentInputBarProvider<CustomOrderMessage> {
getWrapBuilder(): WrappedBuilder<[ReferenceInputBarComponentData<CustomOrderMessage>]> {
return wrapBuilder(buildCustomOrderReferenceInputBar);
}

isReferenceContentType(content: MessageContent | Object): boolean {
return content instanceof CustomOrderMessage;
}
}

@Builder
function buildCustomOrderReferenceMessageItem(data: ReferenceContentMessageItemData<CustomOrderMessage>) {
CustomOrderReferenceMessageItem({ data: data });
}

@Component
struct CustomOrderReferenceMessageItem {
@Prop data: ReferenceContentMessageItemData<CustomOrderMessage>;

private getOrder(): CustomOrderMessage | null {
if (this.data.quotedMessage?.content instanceof CustomOrderMessage) {
return this.data.quotedMessage.content as CustomOrderMessage;
}
return null;
}

build() {
Column({ space: 4 }) {
Text(`订单:${this.getOrder()?.name ?? ''}`)
.fontSize(14)
.fontColor($r('app.color.rc_color_111F2C'))
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis });
Text(`订单号:${this.getOrder()?.id ?? ''} ¥${this.getOrder()?.price ?? 0}`)
.fontSize(12)
.fontColor($r('app.color.rc_color_666666'))
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis });
}
.alignItems(HorizontalAlign.Start);
}
}

@Builder
function buildCustomOrderReferenceInputBar(data: ReferenceInputBarComponentData<CustomOrderMessage>) {
CustomOrderReferenceInputBar({ data: data });
}

@Component
struct CustomOrderReferenceInputBar {
@Prop data: ReferenceInputBarComponentData<CustomOrderMessage>;

private getOrder(): CustomOrderMessage | null {
if (this.data.quotedMessage?.content instanceof CustomOrderMessage) {
return this.data.quotedMessage.content as CustomOrderMessage;
}
return null;
}

build() {
Row() {
Column({ space: 4 }) {
Text('引用订单')
.fontSize(12)
.fontColor($r('app.color.rc_color_666666'));
Text(`${this.getOrder()?.name ?? ''} | ${this.getOrder()?.id ?? ''}`)
.fontSize(14)
.fontColor($r('app.color.rc_color_111F2C'))
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis });
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start);

Image($r('app.media.rc_chat_quote_cancel'))
.width(15)
.height(15)
.onClick(() => {
this.data.action?.cancelReference();
});
}
.width('100%')
.padding({ left: 15, right: 15, top: 10, bottom: 10 })
.backgroundColor($r('app.color.rc_color_F7F7F7'))
.alignItems(VerticalAlign.Center);
}
}

let conversationService = RongIM.getInstance().conversationService();
let config = conversationService.getConversationConfig();

config.setQuoteV2Enable(true);

let referenceableTypes = config.getReferenceableMessageTypeList();
if (!referenceableTypes.includes(CustomOrderMessageObjectName)) {
referenceableTypes.push(CustomOrderMessageObjectName);
}
config.setReferenceableMessageTypeList(referenceableTypes);

conversationService.addReferenceContentMessageItemProvider(
CustomOrderMessageObjectName,
new CustomOrderReferenceMessageItemProvider()
);
conversationService.addReferenceContentInputBarProvider(
CustomOrderMessageObjectName,
new CustomOrderReferenceInputBarProvider()
);

示例只表达接入链路。订单 UI、业务跳转和埋点由业务侧自行实现;SDK 负责挂载组件、绑定引用数据、默认兜底展示和提供 cancelReference()

不再需要自定义展示时,可以移除对应 Provider。以下示例中的 CustomOrderMessageObjectName 仍为订单消息的 objectName,即 App:OrderMsg

TypeScript
let conversationService = RongIM.getInstance().conversationService();
conversationService.removeReferenceContentMessageItemProvider(CustomOrderMessageObjectName);
conversationService.removeReferenceContentInputBarProvider(CustomOrderMessageObjectName);

相关接口

接口说明
ConversationConfiggetReferenceableMessageTypeList(): string[]获取允许进入引用流程的消息 objectName 列表。
ConversationConfigsetReferenceableMessageTypeList(objectNames: string[]): void设置允许进入引用流程的消息 objectName 列表。传空数组表示不允许任何消息进入引用流程。
ConversationConfigisReferenceableMessageType(objectName: string): boolean判断指定消息类型是否允许进入引用流程。
ConversationConfigsetIsShowReferenceMenuItem(callback: ((message: Message) => boolean) | null): void自定义长按消息时是否展示引用入口。传 null 恢复默认逻辑。
ConversationConfiggetIsShowReferenceMenuItem(): ((message: Message) => boolean) | null获取长按消息时是否展示引用入口的业务回调。
ConversationServiceaddReferenceContentMessageItemProvider(objectName: string, provider: ReferenceContentMessageItemProvider<object>): void注册消息列表引用卡片中的被引用内容 Provider。
ConversationServiceremoveReferenceContentMessageItemProvider(objectName: string): void移除消息列表引用卡片中的被引用内容 Provider。
ConversationServicegetReferenceContentMessageItemProvider(objectName: string, message?: Message): ReferenceContentMessageItemProvider<object> | null获取消息列表引用卡片中的被引用内容 Provider。
ConversationServiceaddReferenceContentInputBarProvider(objectName: string, provider: ReferenceContentInputBarProvider<object>): void注册输入框上方引用预览中的被引用内容 Provider。
ConversationServiceremoveReferenceContentInputBarProvider(objectName: string): void移除输入框上方引用预览中的被引用内容 Provider。
ConversationServicegetReferenceContentInputBarProvider(objectName: string, message?: Message): ReferenceContentInputBarProvider<object> | null获取输入框上方引用预览中的被引用内容 Provider。

关闭引用回复功能

IMKit 默认开启引用回复功能。如需关闭,可以通过 ConversationConfig.enableReference(false) 关闭引用入口。

TypeScript
let config = RongIM.getInstance().conversationService().getConversationConfig();
config.enableReference(false);
RongIM.getInstance().conversationService().setConversationConfig(config);

自定义旧版 ReferenceMessage 的 UI

默认引用回复使用 ReferenceMessageItemProvider 模板展示引用消息(RC:ReferenceMsg)。开启引用消息 V2 后,回复消息仍使用实际消息类型对应的模板展示,引用关系保存在 Message.quoteInfo 中,IMKit 内置消息模板会根据 quoteInfo 展示引用卡片。引用回复的点击行为与原消息类型的点击行为保持一致。

如果需要调整内置消息样式,需继承 BaseMessageItemProvider<ReferenceMessage> 自行实现消息展示模板类,详见自定义Provider

调用 addMessageItemProvider 的接口将该自定义模板提供给 SDK,objectNameReferenceMessageObjectName

TypeScript
import { ReferenceMessageObjectName, RongIM } from "@rongcloud/imkit";

// 注册自定义引用消息 provider 给 IMKit
RongIM.getInstance().conversationService().addMessageItemProvider(ReferenceMessageObjectName, new CustomReferenceMessageItemProvider())