笔记

聊天搜索跳回旧消息后,怎样继续翻页?

Web 聊天列表既要向上翻历史,也要从搜索结果跳到某条旧消息。两个操作都在同一个列表中发生,取数方式却不同。

项目把已获取的消息放在浏览器 IndexedDB 中,界面只展示其中一段,下面称为窗口。搜索把窗口移到很早的位置时,本地可能仍保留着最新消息,服务端同步也可能早已完成。

为什么不能用窗口末尾继续同步?

窗口回答“用户正在看哪里”,本地缓存回答“浏览器已拿到什么”,同步进度回答“服务端还有什么需要补”。把三者合成一个位置,搜索跳转就可能把同步起点一起拉回过去。

解决思路是分别维护阅读窗口和同步边界。项目按会话范围从本地取消息,切换窗口不直接改变整个会话的同步进度。

会话还带可选来源群,用来区分同一成员在不同群业务下的私聊。查询索引把会话、来源群和时间放在一起,避免先拿整个对象的数据,再让各调用方分别补过滤。正文在本地缓存中加密,外层索引字段仍可见,这也不能据此宣称消息已端到端加密。

先显示本地,再后台更新,是不是更快?

可以这样设计,用户不用等网络;代价是远端校准回来时,需要合并变化并保住当前滚动位置。另一种方式是先读本地,再等校准完成或失败,统一提交窗口。

项目当前使用后一种流程:先读本地分页,能构造历史请求时向服务端校准,新增数据写入本地后再读一次,最后更新窗口。远端失败则保留最初的本地结果。

因此当前加载会把 HTTP 耗时算进去,尚未做到本地结果立即显示。以后改成后台补齐,还要处理用户已滚动或切会话的情况,不能让迟到结果把列表顶走。

翻页用页码,还是用消息边界?

按页码取数比较直观,但聊天中会持续增加消息。按已知消息的边界继续取,更容易说明从哪里接上,也仍需要服务端定义返回顺序、边界是否包含和相同时间戳的处理。

项目用 beforeOriginId 取更早消息,用 afterOriginId 取更新消息。窗口合并时按消息 ID 去重,再按时间排序。去重能处理重叠,边界错误造成的遗漏却不能靠去重补回来。

当前是否还有下一页,是根据本地重读数量或远端返回数量是否等于 limit 来估算。最后一批恰好满页时,会再允许查一次,得到空结果才确认结束。要提前明确到头,需要服务端返回游标或结束信息。

搜索命中以后,为什么要重建窗口?

普通翻页是在窗口边缘追加,搜索则要让用户看到目标消息前后发生了什么。只把命中的单条塞进旧列表,位置和上下文都可能不连贯。

项目从本地读目标附近的上下文,默认大小为 10,替换当前窗口,再计算两个方向是否还能加载。目前这个入口只查本地,本地没有目标时,还不能自动去服务端补。

搜索后下面有更多消息,只表示用户正在看较旧位置,不代表会话同步落后。

返回上一页