Folo 移动端 v0.5.2 修复深度解读:登出鉴权残留、AI 摘要不可用状态与列表末尾已读边界

发布时间:2026/9/17 8:05:26
Folo 移动端 v0.5.2 修复深度解读:登出鉴权残留、AI 摘要不可用状态与列表末尾已读边界 Folo 移动端 v0.5.2 修复深度解读登出鉴权残留、AI 摘要不可用状态与列表末尾已读边界本文以 apps/mobile/changelog/0.5.2.md 为核心结合 follow 仓库对应版本的实际提交记录与源码逐条拆解 v0.5.2 的「No longer broken」三个修复项登出后的鉴权存储清理、AI 摘要不可用时的状态收敛以及条目列表末尾的已读页脚边界。读完你既能复现并验证这些 Bug也能理解 Folo 移动端在鉴权、摘要生成与滚动已读机制上的实现链路。Folofollow是一个开源的 AI RSS 阅读器项目其移动端Expo / React Native采用 monorepo 管理版本节奏紧凑v0.5.0 完成了 Expo SDK 55 升级、流式 TTS 与后台 OTA 能力v0.5.1 修复了 cookie 更新后的会话刷新而 v0.5.2 则集中收敛了三个稳定性问题。本文所有结论均可对照仓库中的真实提交与源码验证。一、版本定位与修复全景从仓库 git 历史看v0.5.2 的发布提交为3b336b926 release(mobile): release v0.5.2其与 v0.5.1 发布提交之间的代码变更中对应本次 changelog 的修复提交如下修复项涉及模块关键提交核心代码位置登出后遗留鉴权存储移动端鉴权与安全存储969a0770b fix(mobile): clear auth storage on sign outapps/mobile/src/lib/auth.ts、apps/mobile/src/lib/secure-store.tsAI 摘要不可用破坏摘要状态共享 store 的摘要同步服务174515b54 fix: handle unavailable ai summarypackages/internal/store/src/modules/summary/store.ts列表末尾最终条目 / 已读页脚问题滚动已读scroll-mark-read边界算法2fa23362callow final entries to scroll past、425e8610astabilize footer boundarypackages/internal/shared/src/scroll-mark-read.ts需要说明的是changelog 归并到移动端发布但第二、三项修复落在 monorepo 的共享包内store、shared桌面端与移动端共同受益这正体现了 follow 采用packages/internal/*抽取跨端逻辑的工程组织方式。二、修复一登出后不再残留移动端鉴权存储问题现象在 v0.5.2 之前用户在移动端执行登出后本地可能残留 cookie 与会话相关的鉴权数据下次启动或重新登录时可能出现会话状态错乱。结合本版本前后脉络v0.5.1 刚刚修复过“cookie 更新后的 session refresh”这类残留对登出→登录的完整闭环影响尤为明显。旧实现的隐患对比提交969a0770b的 diff旧的signOut实现大致为export const signOut async () { await authClient.signOut() safeSecureStore.removeItem(cookieKey) safeSecureStore.removeItem(sessionTokenKey) safeSecureStore.removeItem(sessionDataKey) // ... __DEV__ 下额外删除带 env profile 后缀的 key // ... 删除数据库文件后 reload }隐患有二fire-and-forget 删除safeSecureStore.removeItem内部对SecureStore.deleteItemAsync只做了void ... .catch(...)并未等待删除完成删除与 reload 竞态登出流程随后会调用FileSystem.deleteAsync(dbPath)并expo.reloadAppAsync(...)删除操作尚未落盘应用便已重载容易出现鉴权 key 被“留”在系统安全存储中的情况。新实现的修复链路新版本把“清理本地鉴权存储”抽成独立的、可等待完成的clearAuthStorage并保证即使远端登出失败也继续执行本地清理const clearAuthStorage async () { const keys [cookieKey, sessionTokenKey, sessionDataKey] if (__DEV__) { keys.push(${cookieKey}_${getEnvProfile()}) } await Promise.all(keys.map((key) safeSecureStore.removeItemAsync(key))) } export const signOut async () { try { await authClient.signOut() } catch (error) { console.warn([auth] Remote sign out failed: ${error.message}) } await clearAuthStorage() await userActions.removeCurrentUser() Navigation.rootNavigation.popToRoot() bumpAuthStateRevision() await refreshSessionQueries() const dbPath getDbPath() await FileSystem.deleteAsync(dbPath, { idempotent: true }) await expo.reloadAppAsync(User sign out) }关键改进点对应 apps/mobile/src/lib/auth.ts本地清理前置于重载await clearAuthStorage()先完成再执行后续 popToRoot、刷新查询、删除数据库、重载应用远端失败不阻断authClient.signOut()包在try/catch中仅告警不抛出本地凭据必然被清理避免“远端 4xx 导致本地登出卡住”开发环境 key 一并清理__DEV__下额外删除带getEnvProfile()后缀的 cookie key便于多环境调试切换删除幂等化FileSystem.deleteAsync(dbPath, { idempotent: true })防止重复登出时因文件不存在而抛错。配套的安全存储基础设施apps/mobile/src/lib/secure-store.ts 同步新增了removeItemAsync与内部辅助deleteSecureStoreItemconst deleteSecureStoreItem async (key: string) { try { await SecureStore.deleteItemAsync(key) } catch (error) { if (isSecureStoreUnavailable(error)) { forceFallback true warnFallback(removeItem, key, error) return } console.warn([auth-storage] SecureStore removeItem failed for ${key}: ...) } }该文件保留了forceFallback降级策略当系统expo-secure-store不可用例如设备不支持、权限异常时自动切换到普通Storage后端。removeItemAsync在降级场景下会同步先删Storage中的兜底值再决定是否继续删除 SecureStore保证两条路径都干净。验证方法开发环境__DEV__登出后检查 cookie/session key 是否全部消失或在登出瞬间杀掉进程确认重开应用不会恢复旧的登录态。三、修复二AI 摘要不可用时不再破坏摘要状态问题现象Folo 的 AI 摘要AI summary模块在服务端不可用、限流或返回空摘要时会让摘要的“生成状态”停留在异常值导致 UI 上摘要区域卡在生成中/错误态或复用已失效的旧摘要数据。根因空数据被当作异常抛出摘要同步服务位于共享 store 包SummarySyncService.generateSummarypackages/internal/store/src/modules/summary/store.ts。修复前的逻辑是.then((summary) { if (!summary.data) { throw new FollowAPIError(AI summary limit exceeded. Please try again later., 402) } // ... 写回 state.data、generatingStatus 等 return summary.data || })也就是说只要接口没有返回data无论原因是“真的没生成出内容”还是“服务端限流”都被统一抛出FollowAPIError(402)。抛错会走外层的.catch把该条目的generatingStatus置为失败态同时已生成的摘要状态content / readabilitySummary 等也没有被正确收敛。修复把“无内容”视为一次成功的空结果提交174515b54将“返回空”从异常路径改为正常的终止状态.then((summary) { const generatedSummary summary.data?.trim() ? summary.data : null if (!generatedSummary) { immerSet((state) { state.generatingStatus[statusID] SummaryGeneratingStatus.Success }) return null } immerSet((state) { // 仅在确有摘要内容时写回 data / readabilitySummary / lastAccessed state.data[entryId][actionLanguage] { summary: generatedSummary, ... } state.generatingStatus[statusID] SummaryGeneratingStatus.Success }) return generatedSummary })同时pendingPromises的类型从Promisestring放宽为Promisestring | null允许“生成成功但无内容”的返回值空的返回值不再触发FollowAPIErrorgeneratingStatus会置为SummaryGeneratingStatus.Success终态等待中的调用方拿到null后可自行决定 UI 表现如隐藏摘要区而不是让整条状态卡住或闪烁报错真正来自 API 的错误网络失败、超时等仍走.catch置为失败态两类情形得到区分。同一提交还新增了 99 行单元测试packages/internal/store/src/modules/summary/store.test.ts覆盖“空摘要→状态收敛为 Success”“有内容→正确写回 data”“异常→失败态”等分支。该 store 位于packages/internal/store移动端与桌面端共用因此这条修复也同步惠及桌面端阅读体验。关联阅读v0.5.0 曾“Increased AI summary font size for better readability”摘要 UI 的阅读优先级始终较高本次则从数据层保证其状态机不会被不可用的 AI 服务打断。四、修复三列表末尾的最终条目与已读页脚边界问题现象Folo 在滚动条目列表时会触发“滚动即已读”scroll mark-read——滚出视口的条目自动标记已读。但接近列表末尾时存在两类问题最终条目滚不“过去”最后几条内容被页脚/底部提示挤压无法完整滚动出可视区已读页脚触发不稳触底时的 mark-read footer已读标记的边界指示位置抖动或提前触发导致最后若干条目未被标记已读。共享算法层的边界修复滚动已读的边界算法集中在共享包 packages/internal/shared/src/scroll-mark-read.ts本版本由两个提交协同修复2fa23362c fix: allow final entries to scroll past允许最终条目滚过425e8610a fix: stabilize scroll mark-read footer boundary稳定页脚边界。算法层的关键改动包括引入末尾 padding 概念export const MIN_SCROLL_MARK_READ_END_PADDING 480 export const SCROLL_MARK_READ_END_INDICATOR_HEIGHT 1 // 根据视口高度计算需要预留的末尾内边距兜底为 480px export const getScrollMarkReadEndPadding (viewportHeight) { if (typeof viewportHeight ! number || !Number.isFinite(viewportHeight)) { return MIN_SCROLL_MARK_READ_END_PADDING } return Math.max(viewportHeight - SCROLL_MARK_READ_END_INDICATOR_HEIGHT, 0) } // 当列表有内容且没有下一页时才渲染末尾 spacer export const shouldRenderScrollMarkReadEndSpacer ({ entryCount, hasNextPage }) entryCount 0 !hasNextPage这里表达了修复思路的核心“判断列表是否触底”不再只看内容是否到达容器底部而是要考虑末尾 spacer/页脚的高度——只有为末尾内容预留足够滚动空间视口高度或最少 480px最后几条条目才能完整滚出并命中已读判定已读页脚也才会在稳定位置出现。同目录的测试 packages/internal/shared/src/scroll-mark-read.test.ts 在两个提交中各增加了数十行用例分别验证“末尾 padding 计算”“无下一页时渲染 spacer”“最终条目可滚动越过边界”等场景。渲染层同步适配从提交统计看桌面端渲染器的各列表形态grid.tsx、list.tsx、picture-masonry.tsx、EntryListFooter.tsx、EntryListContent*系列都针对新边界逻辑做了适配并新增了EntryListEndScrollSpacer与useScrollMarkReadEndPaddinghook 等基础设施。移动端条目列表apps/mobile/src/modules/entry-list/与桌面端共享同一套scroll-mark-read算法与 store 层的已读批量写入因此本次“列表末尾已读边界”修复对两端阅读列表同时生效——从仓库结构看这也是 Folo 把滚动已读纯逻辑放入packages/internal/shared的主要收益一个算法修复桌面、移动、网页ssr的阅读列表共同受益。五、如何在本仓库验证与复现单元测试回放仓库使用 vitest 工作区根目录 vitest.workspace.ts。摘要状态机的新增用例位于 packages/internal/store/src/modules/summary/store.test.ts滚动已读边界用例位于 packages/internal/shared/src/scroll-mark-read.test.ts在对应包内执行 vitest 即可覆盖本次第二、三项修复的行为分支源码比对登出清理链路可在 apps/mobile/src/lib/auth.ts 与 apps/mobile/src/lib/secure-store.ts 中追踪signOut → clearAuthStorage → removeItemAsync → deleteSecureStoreItem的完整调用链摘要在 packages/internal/store/src/modules/summary/store.ts 中查看SummaryGeneratingStatus的状态迁移移动端手工验证登出连续执行“登出→重开应用”确认不会恢复旧会话也不会残留调试环境专属的 cookie keyAI 摘要在 AI 服务不可用/返回空时打开条目摘要确认摘要区收敛为“无内容”而非永久生成中或报错滚动已读打开一个无后续分页的长列表确认末尾最后若干条目可完整滚出并被标记已读底部 footer 不再抖动。六、小结与延伸阅读v0.5.2 的三个修复分别体现了 Folo 移动端三个层面的工程质量取向登出清理强调“本地永远能回到干净状态”远端失败也不阻断AI 摘要强调“外部能力不可用时应优雅收敛为终态而非污染状态机”滚动已读强调“把跨端通用算法下沉到共享包一处修复全端生效”。如果想继续追踪后续演进可对照本目录相邻版本的移动端 changelog如 0.5.1、0.5.0其中 cookie 会话刷新、SDK 55 升级等背景与本版本修复环环相扣移动端发布元数据与文案的生成脚本位于 apps/mobile/scripts/apply-changelog.ts。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询