聊天侧边栏体验优化
去 Modal + Reanimated 动画、NoSQL 会话读写、确认流图拓扑、SSE payload 补丁等 11 个子问题
1. 问题背景
神秘人 agent-chat 页通过右上角图标打开历史会话侧栏(列表 + 遮罩)。联调与真机反馈:打开/关闭不跟手、偶有顿挫,与主对话区的流畅度不一致;期望侧栏动画稳定 60fps、交互反馈明确。
2. 问题列表
2.1 打开侧栏瞬间卡顿、动画不平滑
根因:侧栏曾被包在 Modal(transparent + animationType="none")里;每次 visible=true 会走原生 Modal 挂载/层级切换,与后续 Animated + requestAnimationFrame 拼出来的打开动画叠在一起,主线程调度成本高。
采纳方案:去掉 Modal,在同屏内用 StyleSheet.absoluteFill + 高 zIndex/elevation 做全屏层;动画改由 react-native-reanimated 的 useSharedValue + withSpring/withTiming 驱动(UI 线程路径)。独立组件 AgentChatHistoryDrawer + 动画参数 drawerTokens.ts。
2.2 关闭路径不一致、部分操作「瞬关」无动画
根因:历史代码里 closeSidebar 仅存 setState,未与带动画的 dismissDrawer 统一。切换会话 / 新建会话若直接 setSidebarOpen(false),侧栏瞬时消失。
采纳方案:侧栏 closed / opening / open / closing 状态机(useReducer),禁止页面内再写 setSidebarOpen;关栏统一 requestClose() → CLOSE_START → 动画结束 → CLOSED。
2.3 历史列表首开展示时掉帧
根因:会话较多时,第一次打开侧栏 FlatList 布局+挂载与动画同帧竞争。
采纳方案:listAgentChatSessionsPage(常量 AGENT_CHAT_SESSION_PAGE_SIZE)+ 触底 onEndReached 追加;列表为 @shopify/flash-list;首屏同步前骨架屏。
2.4 拖动手势关栏与列表滚动抢事件
根因:若 Pan 包在整个侧栏面板外,纵向滑列表易与横向关栏手势冲突;FlatList 内部滚动与父级 Gesture.Pan 默认未声明同时识别关系。
采纳方案:仅「历史记录」标题栏包一层 GestureDetector,Pan.activeOffsetX / failOffsetY 约束激活条件;列表外包 NativeViewGestureHandler 并与 Pan simultaneousWithExternalGesture(ref)。Web 暂不启用手势(避免与浏览器滚动差异),仍依赖点遮罩/按钮。
2.5 Android 物理返回与 Modal 语义丢失
根因:移除 Modal 后,系统不再自动把返回键关联到 onRequestClose。表现为侧栏打开时按返回直接退出页面。
采纳方案:sidebarOpen 为真时注册 BackHandler,消费事件并调用 dismissDrawer()。
2.6 打开时缺少触感反馈
根因:纯反馈弱,用户心理模型上「打开抽屉」常与轻触感搭配。
采纳方案:openSidebar 内(非 Web)Haptics.impactAsync(Light)。
3. 小结
- 最大收益:去 Modal + Reanimated —— 减少层级切换与 JS/布局竞态,侧栏开闭主观顺滑度提升最明显。
- 一致性:状态机 +
requestClose(),避免瞬关与散落布尔位。 - 列表:分页 + FlashList + 骨架控制首屏与长列表成本。
- 手势与平台:整板 Pan + 列表 NativeView simultaneous;Android BackHandler 在抽屉组件内;Web 用 fixed 遮罩覆盖视口。