可观察视图(client.views)
client.views 把「打开一个会话该显示什么」这件事收进核心:你打开一个视图,拿到 一份快照,之后核心持续把增量推给你。列表要不要重排、新消息插在哪、 撤回该改哪一条,都由核心算好。
这层此前只在 SDK 契约里,官网没有文档——但它是各端推荐的取数方式, web 与 iOS 的示例应用都已经走它,而不是自己在客户端合并消息。
为什么不是自己拉列表
不用视图的话,每端都要重写同一套逻辑:首屏拉多少、滚到顶再拉多少、 新消息插入后如何保持 seq 有序、撤回/编辑到达时改哪一条、切到后台再回来 要不要整表重来。这套逻辑写错的代价是消息顺序错乱或重复,而它在四个平台 上会被写四遍。
视图把它收敛成一次:打开 → 收增量 → 关闭。
四个方法
| 方法 | 作用 |
|---|---|
openTimeline | 打开某个会话的消息时间线,返回 viewId + 首屏快照 |
loadOlderTimeline | 该视图继续向上翻页(传 viewId,不是 seq) |
openConversationList | 打开会话列表视图 |
close | 关闭视图,停止推送增量 |
四个方法在契约里都标记为 stable。
打开一个会话
use flare_im_core_sdk::model::timeline::{
CloseViewRequest, OpenTimelineViewRequest, ViewSnapshot,
};
let opened = apis
.view_api
.open_timeline(OpenTimelineViewRequest {
conversation_id: cid.clone(),
message_limit: 100,
})
.await?;
// 首屏内容随打开动作一起返回,不必再单独拉一次
if let ViewSnapshot::Timeline(snapshot) = opened.snapshot {
render(&snapshot.messages);
}
// 离开会话时关掉,核心随即停止为它计算增量
apis.view_api
.close(CloseViewRequest { view_id: opened.view_id })
.await?;
TypeScript 侧同名:
const opened = await client.views.openTimeline({
conversationId,
messageLimit: 100,
})
render(opened.snapshot)
// 离开页面时
await client.views.close({ viewId: opened.viewId })
接住增量
打开之后,核心通过事件把更新推过来:
client.events.onViewUpdated((update) => {
if (update.viewId !== opened.viewId) return
if (update.kind === 'snapshot') {
// 整表替换:重连、跨设备同步等场景下核心判定增量不足以描述变化
replaceAll(update.snapshot)
} else {
// 增量:insert / update / …,按 seq 已排好序
applyDeltas(update.deltas)
}
})
两种 kind 的区别值得记住:delta 是常态,snapshot 是核心主动放弃增量 (例如断线重连后差距太大)。客户端两条路都要能走——只处理 delta 会在重连后 显示陈旧内容。
向上翻页
翻页传的是 viewId 而不是 seq —— 游标由核心持有,客户端不需要自己记 「上次翻到哪」,也就不会因为记错游标而漏消息或重复。
let older = apis
.view_api
.load_older_timeline(LoadOlderTimelineViewRequest {
view_id: opened.view_id.clone(),
message_limit: 50,
})
.await?;
会话列表
const list = await client.views.openConversationList({})
renderConversations(list.snapshot)
会话列表视图同样收增量:新消息到达时的重排、未读数变化、置顶与免打扰的影响, 都由核心算完再推给你。
两个容易踩的点
- 视图要关。
close之后核心才会停止为它计算增量;页面卸载时忘了关, 等于让核心一直为一个没人看的视图干活。 viewId不要跨会话复用。它绑定的是「这一次打开」,重新打开会拿到新的viewId,旧的那个已经失效。
相关
- Client SDK 使用指南:完整接入顺序,视图在其中的位置
- Client API 全表:
client.views的逐方法签名 - Client 模型索引:
ViewSnapshot/ViewUpdate等类型定义