SuperAPI AI 工具集

ui-states-and-feedback

前端与建站

查看项目

更新于 10-18 18:02

技能介绍

用途

该技能用于规范 Expo / React Native 应用中数据屏幕(加载、列表、筛选类界面)的状态呈现与用户反馈方式。它把「屏幕当前显示什么」收敛为一个单一状态联合,并把提示、横幅、确认、撤销等反馈整理成一套有层级的规则,避免加载/错误双标志、默认空数组、刷新清屏、弹窗报结果等常见问题。

主要能力

  • 单一状态联合:每个数据屏幕在模型 hook 中通过 listScreenStateOf 把事实(items 或 undefined、fetching、paused、失败 kind、过滤器)转成一个 ListScreenState,视图只对该联合做 switch,不再维护 loading/error 标志对,也不会把查询数据默认成 []。
  • 区分四种「非内容」屏幕:空、筛选后为空、失败、离线各有自己的文案和下一步操作,并有字面量 testID <id>.state.<state>;失败文案取自失败 kind,而非 error.message。
  • 延迟且内容形状的加载占位:前 300 ms 不展示任何东西,之后显示与内容形状一致的高亮块,展示后至少保留 500 ms(DelayedPlaceholder、PLACEHOLDER_TIMING),不使用整屏 spinner。
  • 刷新不清屏:占位只来自「尚未加载任何内容」,后台重新拉取最多显示一行安静的更新提示;下拉刷新指示器只响应用户本人的下拉。
  • 重试保留现场:Try again 重新拉取,而筛选、搜索文本、已输入内容和滚动位置保存在查询之外。
  • 进行中动作反馈:useBusyAction 禁用控件、显示忙碌态并忽略第二次按压;动作失败以内联或 notice 呈现,不使用覆盖全屏的 modal spinner。
  • 反馈阶梯:内联(字段/行)→ notice(已完成动作或已恢复的失败)→ banner(持续状态,如离线)→ dialog(仅不可逆决策),禁止 Alert.alert。
  • 一次一个 notice:替换而非排队;结果 4 秒、带动作 8 秒,并遵循 Android 无障碍超时(getRecommendedTimeoutMillis);屏幕阅读器运行时带动作的 notice 保留 Dismiss。
  • 撤销优先于确认:可逆改动立即写入(软删除)并通过 runUndoable 提供 Undo;确认仅用于不可撤销操作,且需在契约中用 whyNotUndo 声明。
  • 确认写明后果:标题含数量(如「Delete 3 done tasks?」)、按钮用动词、Cancel 在前;confirm() 返回 'confirmed' | 'cancelled' | 'dismissed',仅 'confirmed' 执行。
  • 无障碍公告:notice 和 banner 作为 polite live region(TalkBack);iOS 上无 live region,需用 useAnnounce() 公告一次;iOS 上 notice 与确认通过 AboveModalScreens 覆盖窗口渲染,以显示在 modal 或表单页之上。
  • 屏幕状态契约与测试:contracts/screen-states.json 列出每个屏幕的视图、测试、状态和标记;每个声明状态都要在测试中渲染。

使用方式

  1. 阅读 references/states-and-the-switch.md,运行 node ${CLAUDE_SKILL_DIR}/scripts/check-ui-states.mjs . 查看现状。
  2. 复制 templates/src/ui/feedback/(含测试)、templates/src/hooks/use-reachability.ts(含测试)以及 templates/src/i18n/catalogs/ 的 catalog 键到每个语言。src/services/connectivity/ 来自 networking-and-server-state:仅当应用缺少时才复制模板,并让其 query manager 把 TanStack 的 onlineManager 接到该端口,否则离线状态不会出现。ui-component-library 默认排除 src/ui/feedback/,自定义排除目录时需保留 feedback/**。
  3. 在根 Stack 外层挂载唯一的 FeedbackProvider(位于 MotionConfig 和 i18n provider 之下,参考 examples/app-feedback.tsx),屏幕通过 useFeedback() 读取。
  4. 每个数据屏幕:在模型 hook 中构造事实并调用 listScreenStateOf(参考 examples/use-tasks-model.ts),把文案集中到一个纯函数(templates/src/features/tasks/tasks-state-copy.ts),用一个 switch 渲染并配合延迟占位(templates/src/features/tasks/components/tasks-state-switch.tsx、examples/tasks-screen.tsx)。下拉刷新读取用户下拉标志(围绕 refetch() 使用 useBusyAction),而非 isRefreshing。
  5. 反馈:结果走 notify(),可逆改动走 runUndoable,不可逆操作走 confirm(),进行中动作走 useBusyAction,离线横幅读取 useReachability。
  6. 契约:复制 templates/contracts/screen-states.json 及其 schema,列出每个加载/列表/筛选数据的屏幕和每个带 whyNotUndo 的确认。
  7. 测试:按模板测试渲染每个声明状态、notice 与确认结果;运行 npx jest src/ui/feedback src/features --ci 和 npm run typecheck。
  8. 再次运行检查脚本,修复每条 FAIL(规则 id 见 references/checker-rules.md)直到输出 RESULT: PASS。

适用场景

适用于 Expo / React Native 应用中会加载、列出或筛选服务端数据的数据屏幕,以及需要统一提示、离线横幅、确认与撤销交互的界面。检查器还有两种边界输出:尚无数据屏幕时提示 contracts/screen-states.json: none yet (...) 并通过;package.json 无 react-native 时输出 NOT APPLICABLE: react-native is not in package.json。

限制与注意

  • 本技能不替代数据层与组件库:ConnectivityPort、Toast/DialogCard/EmptyState 等分别由 networking-and-server-state、ui-component-library 等关联技能提供。
  • 同一时刻只保留一个 notice,不排队;旧消息不会延迟补发。
  • 反模式列表明示禁止:loading/error 双标志、query.data ?? [] 判断空态、isFetching 时返回 spinner、立即显示的居中 ActivityIndicator、默认 shimmer 循环占位、用 dialog 报结果、Alert.alert、离线用 toast、notice 排队、每次删除都「Are you sure?」、仅在 'cancelled' 时返回、只隐藏行的 Undo、仅靠 live region、refreshing={state.isRefreshing}、notice host 作为导航器普通兄弟节点或使用根 Modal。
  • 检查脚本输出不含 SKIP 行;iOS 上的 notice/确认显示与部分无障碍行为需在真机验证。
  • 具体收费、平台支持范围与效果数据,原文未提供。