跳至正文

可观察视图(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,旧的那个已经失效。

相关