引用回复
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。
let config = RongIM.getInstance().conversationService().getConversationConfig();
// 开启引用消息 V2
config.setQuoteV2Enable(true);
RongIM.getInstance().conversationService().setConversationConfig(config);
如需关闭引用消息 V2,传入 false 即可。关闭后,IMKit 仍使用旧版 ReferenceMessage 引用回复逻辑。
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 |
如需限制可作为回复发送的消息类型,可以重新设置白名单。传入空数组时会重置为默认全量类型。
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。如果只需要自定义被引用内容在引用区域中的展示,可以注册下文的 ReferenceContentMessageItemProvider 和 ReferenceContentInputBarProvider。
配置可被引用的自定义消息
SDK 初始化时默认填充现有支持被引用的内置消息类型,包括 RC:TxtMsg、RC:ImgMsg、RC:FileMsg、RC:ImgTextMsg、RC:ReferenceMsg、RCE:ReferenceMsg,不包含业务自定义消息。您可以通过 ConversationConfig 增加自定义消息类型。
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 基础限制。
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);
接口定义如下:
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 获取被引用消息内容。ReferenceContentMessageItemData 和 ReferenceInputBarComponentData 不包含 quotedContent、userName 等派生字段。
- 未注册自定义引用内容 Provider 时,IMKit 会继续使用默认引用展示。
ReferenceContentMessageItemProvider只处理消息列表中回复消息内的引用卡片展示,不控制输入框上方的引用预览区域。ReferenceContentInputBarProvider只处理用户选择引用消息后,输入框上方的引用预览区域。- Provider Builder 内部的点击、跳转、埋点等业务交互由业务侧自行实现;需要取消当前引用时,调用
ReferenceInputBarComponentData.action?.cancelReference()。
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 或打开会话页面前完成注册。
RongIM.getInstance()
.conversationService()
.addReferenceContentMessageItemProvider(
'App:OrderMsg',
new CustomOrderReferenceMessageItemProvider()
);
RongIM.getInstance()
.conversationService()
.addReferenceContentInputBarProvider(
'App:OrderMsg',
new CustomOrderReferenceInputBarProvider()
);
以下示例以 Demo 中的订单消息 CustomOrderMessage 为例,消息类型标识为 App:OrderMsg。
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。
let conversationService = RongIM.getInstance().conversationService();
conversationService.removeReferenceContentMessageItemProvider(CustomOrderMessageObjectName);
conversationService.removeReferenceContentInputBarProvider(CustomOrderMessageObjectName);
相关接口
| 类 | 接口 | 说明 |
|---|---|---|
ConversationConfig | getReferenceableMessageTypeList(): string[] | 获取允许进入引用流程的消息 objectName 列表。 |
ConversationConfig | setReferenceableMessageTypeList(objectNames: string[]): void | 设置允许进入引用流程的消息 objectName 列表。传空数组表示不允许任何消息进入引用流程。 |
ConversationConfig | isReferenceableMessageType(objectName: string): boolean | 判断指定消息类型是否允许进入引用流程。 |
ConversationConfig | setIsShowReferenceMenuItem(callback: ((message: Message) => boolean) | null): void | 自定义长按消息时是否展示引用入口。传 null 恢复默认逻辑。 |
ConversationConfig | getIsShowReferenceMenuItem(): ((message: Message) => boolean) | null | 获取长按消息时是否展示引用入口的业务回调。 |
ConversationService | addReferenceContentMessageItemProvider(objectName: string, provider: ReferenceContentMessageItemProvider<object>): void | 注册消息列表引用卡片中的被引用内容 Provider。 |
ConversationService | removeReferenceContentMessageItemProvider(objectName: string): void | 移除消息列表引用卡片中的被引用内容 Provider。 |
ConversationService | getReferenceContentMessageItemProvider(objectName: string, message?: Message): ReferenceContentMessageItemProvider<object> | null | 获取消息列表引用卡片中的被引用内容 Provider。 |
ConversationService | addReferenceContentInputBarProvider(objectName: string, provider: ReferenceContentInputBarProvider<object>): void | 注册输入框上方引用预览中的被引用内容 Provider。 |
ConversationService | removeReferenceContentInputBarProvider(objectName: string): void | 移除输入框上方引用预览中的被引用内容 Provider。 |
ConversationService | getReferenceContentInputBarProvider(objectName: string, message?: Message): ReferenceContentInputBarProvider<object> | null | 获取输入框上方引用预览中的被引用内容 Provider。 |
关闭引用回复功能
IMKit 默认开启 引用回复功能。如需关闭,可以通过 ConversationConfig.enableReference(false) 关闭引用入口。
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,objectName 传 ReferenceMessageObjectName。
import { ReferenceMessageObjectName, RongIM } from "@rongcloud/imkit";
// 注册自定义引用消息 provider 给 IMKit
RongIM.getInstance().conversationService().addMessageItemProvider(ReferenceMessageObjectName, new CustomReferenceMessageItemProvider())