AskaEth aeb37458e8 fix(chat): 分享落地必须等到连接就绪,超时不再等于发送失败
为什么超时不能等于发送失败
WS 还没连上时投递的分享(ACTION_SEND)此前走成了「发送失败,请重试」,内容还从待发队列里
永久消失。来处是「等连接」那句 withTimeoutOrNull 的返回值被丢掉了:超时与成功走同一条下游,
照样 take() 再 sendShared → deliver → ChatSocket.sendChat 回 false ⇒ ChatViewModel 落到
rollback(localId, "发送失败,请重试")。实机取证(2026-10-05 11:56):GET /ws/chat status=200
前后的窗口里投递分享,界面上是失败横幅 + 正文退回输入栏,而后端零请求(POST /api/v1/chat
一条都没有,也没有任何 error 帧)——那条横幅把「一次都没尝试发出去」说成了「发送失败」。
ShareIntake.take() 是原子出队且没有归还路径:那一次「其实一次都没发出去」的失败之后,内容
既不在 holder 里、也不在服务端,只剩回滚进输入栏的那一份(多图分享里除首条之外,连输入栏
都回不去)。重试资格因此一起丢了。所以判据只有一条:没连上就一个字节都别动——不 take()、
不发送、不产生任何 error 横幅。

为什么取走时机必须晚于连接就绪
短文本走 WS chat 帧,而分享冷启动必然撞上握手还没完成;帧发不发得出去完全取决于握手结果。
所以等待里不再有「到点就照发」这一档,只有「连上才发」:连接恢复之后由连接状态自己把这条
落地重新触发一遍。

改法
- 判据抽成纯函数 shareLanding(ui/chat/ShareLanding.kt,可单测):没有待发 ⇒ Nothing;已连接
  ⇒ Send(压过后两条,且返回的是单值动作,所以发送与告知不可能同时发生);未就绪且还没等够
  ⇒ KeepWaiting;未就绪、已等够、还没说过 ⇒ TellWaiting;已经说过 ⇒ KeepWaiting(不刷屏)。
- ChatScreen 的那个 effect 把连接状态并进键,连接后来就绪会自动重跑、自动补发;「不会重复
  发送」仍然由 ShareIntake.take() 的原子性兜住——哪怕某一次拿着已经过期的快照进来,take()
  给出的也只是队列里的下一条或 null。
- 等太久只做一件事:经既有的柔和通道 ChatViewModel.showShareNotice 说一句「还没连上,分享的
  内容会留着,连上后自动发出」(不是带 X 的 error 横幅),同一次等待只说一次。
- SHARE_CONNECT_TIMEOUT_MILLIS 改名 SHARE_WAIT_NOTICE_MILLIS:它不再是发送的时限,只是
  「等多久才值得说那一句」;「等够久」的计时只跟「队列里有没有东西」与「连上了没有」走,
  连接在 Connecting / Reconnecting 之间抖动不重置它。
- 普通发送路径(输入框、图片、附件)一行未动。

测试
- ShareLandingTest(新,7 条):未就绪在任何连接状态下都恒不 Send(不取、不发);未就绪且没
  等够恒 KeepWaiting(也不告知);等够了只说一次、说过了就不再刷屏;已连接恒 Send(压过告知);
  无待发恒 Nothing(任何连接状态下都不做事);文案必须说清「还没连上」与「自动发出」,且不含
  「失败」「重试」。

assembleDebug 与 testDebugUnitTest 全绿(1091 tests / 0 failures / 0 errors)。
2026-10-05 13:21:09 +08:00
S
Description
昔涟在安卓设备上的载体。
Apache-2.0
19 MiB
0 Stars 1 Watchers 0 Forks
2026-05-29 22:15:56 +08:00