Folo 移动端 v0.1.9 更新全解读:隐藏已读订阅、Discover Trending 源与 AI 摘要默认开启
本文以 apps/mobile/changelog/0.1.9.md 为骨架,结合 Folo 移动端仓库源码(apps/mobile 与 packages/internal/shared)逐项剖析本次发布的新功能、体验改进与缺陷修复,帮助读者理解每个变更背后的实现机制、对应设置入口与实际影响。读完你不仅能掌握 v0.1.9 的全部更新点,还能顺着源码定位到每个功能的实现文件与参数默认值,便于二次开发或参与贡献。
一、v0.1.9 更新总览
v0.1.9 是 Folo(AI RSS Reader)移动端的增量版本,变更集中在订阅列表的信息噪音治理、发现(Discover)页面的内容发现能力、AI 能力的默认体验三个方向,并修复了一批列表与视图层面的缺陷。按 changelog 原文分类可归纳为:
| 分类 | 内容 |
|---|---|
| Shiny new things(新特性) | 新增“隐藏无未读条目订阅”选项;Trending Feeds 出现在 Discover 页与新手引导流程中 |
| Improvements(体验改进) | RSSHub 源异常时错误提示更清晰;AI 摘要默认开启;图标加载失败时展示占位图标;精简登录/注册流程 |
| No longer broken(缺陷修复) | 点头像导航不正确;视频视图下无图条目显示异常;部分订阅不出现在搜索结果;图片/视频视图未读指示器显示错误 |
下文将逐项对照源码展开说明。仓库同时维护了完整的历史变更记录目录(apps/mobile/changelog),如需回溯此前行为可对照阅读相邻版本记录。
二、新特性:隐藏“无未读条目”的订阅
2.1 设置入口与开关语义
v0.1.9 在 Settings → General → Subscriptions(订阅) 分组下新增了一个“Hide Read”(隐藏无未读订阅)开关,对应的设置键为 hideAllReadSubscriptions。设置界面实现在 apps/mobile/src/modules/settings/routes/General.tsx,它与其他订阅行为开关(如 autoGroup 自动分组、hidePrivateSubscriptionsInTimeline 在时间线隐藏私密订阅)并列放置在 Subscriptions 分组卡片内:
- 开关键:
hideAllReadSubscriptions,由 General.tsx 中setGeneralSetting("hideAllReadSubscriptions", value)写入; - 对应文案 i18n 键:
general.hide_all_read_subscriptions.label/.description(见 locales 等语言资源)。
2.2 源码视角:开关如何真正生效
值得注意的一点是:在 Folo 的实现中,该开关并不是“独立生效”的,而是与 unreadOnly(启动/默认只显示未读)配合使用。核心逻辑位于 apps/mobile/src/atoms/settings/general.ts:
export function useHideAllReadSubscriptions() {
const hideAllReadSubscriptions = useGeneralSettingKey("hideAllReadSubscriptions")
const unreadOnly = useGeneralSettingKey("unreadOnly")
return hideAllReadSubscriptions && unreadOnly
}
export function getHideAllReadSubscriptions() {
const { hideAllReadSubscriptions, unreadOnly } = getGeneralSettings()
return hideAllReadSubscriptions && unreadOnly
}
也就是说,只有同时开启“隐藏无未读订阅”与“仅显示未读”(unreadOnly)时,该功能才会真正启用(hook 返回 hideAllReadSubscriptions && unreadOnly)。这可以理解为一种安全护栏:如果你并不处于“未读优先”模式,列表不会被误过滤。
该判定结果被多处以“是否同时排除私密/已读订阅”的入参形式使用,例如:
- 订阅管理列表过滤:apps/mobile/src/modules/subscription/SubscriptionLists.tsx 与 apps/mobile/src/modules/subscription/UnGroupedList.tsx 均通过
useHideAllReadSubscriptions()过滤“全部已读”的订阅分组/订阅项; - “全部标记为已读”对话框在完成后是否需要刷新/剔除:apps/mobile/src/modules/dialogs/MarkAllAsReadDialog.tsx 读取
getHideAllReadSubscriptions()作为excludePrivate参数; - 时间线底部“全部已读”操作及上下文菜单同样会携带该判定:apps/mobile/src/modules/entry-list/EntryListFooter.tsx、apps/mobile/src/modules/context-menu/entry.tsx、apps/mobile/src/modules/screen/TimelineViewSelectorContextMenu.tsx。
2.3 默认值与数据流
GeneralSettings 的类型与默认值统一收敛在共享包 packages/internal/shared/src/settings/defaults.ts 中:
// subscription
autoGroup: true,
hideAllReadSubscriptions: false,
hidePrivateSubscriptionsInTimeline: false,
可以看到 v0.1.9 新增的 hideAllReadSubscriptions 默认值为 false,即默认不隐藏,需要用户在设置中主动开启(为避免默认行为突变)。设置写入路径遵循移动端统一的 createSettingAtom("general", …) 模式:由 general.ts 创建 general 设置原子,再被设置页与各业务模块订阅,形成“UI 开关 → 设置原子 → 订阅列表/时间线过滤”的完整链路。
三、新特性:Trending Feeds 进入 Discover 与新手引导
3.1 功能位置
v0.1.9 将 Trending(热门源) 区块同时接入两处:
- Discover(发现)页面:作为独立区块渲染,见 apps/mobile/src/modules/discover/DiscoverContent.tsx,其内部直接引入
<Trending />; - 新手引导(onboarding)的兴趣选择步骤:apps/mobile/src/modules/onboarding/step-interests.tsx 在步骤底部同样渲染
<Trending className="mb-4 w-full" />,让新用户在订阅兴趣推荐之外,可以顺手一键关注社区热门源。
3.2 实现剖析:数据来源与渲染
热门源的 UI 实现在 apps/mobile/src/modules/discover/Trending.tsx,关键点如下:
- 数据请求:组件内通过
useQuery发起,queryKey 为["trending", "feeds", discoverLanguage],并使用meta: { persist: true }让结果可被本地持久化缓存;请求函数是fetchFeedTrending({ lang, limit: 20 }); - 接口封装:apps/mobile/src/modules/discover/api.ts 中
fetchFeedTrending调用后端 APIfollowClient.api.trending.getFeeds({ language, view, limit }),即网络请求由服务端聚合; - 语言映射:读取 UI 设置
discoverLanguage(默认"all",见 defaults.ts),并对法语做了"fra" → "eng"的兜底(当前热门源暂未覆盖法语);语言参数为"all"时不下发lang; - 排序徽标与订阅数:列表对前 3 名使用 accent 高亮圆形排名徽标(第 1 名实心、第 2/3 名按透明度递减),每行右侧展示
item.analytics.subscriptionCount(经formatNumber格式化)作为热度佐证; - 筛选项:区块头部右侧有筛选按钮(Filter 图标),点击弹出
DiscoverSettingsScreen,用于切换 Discover 语言偏好。
3.3 对新手引导的意义
引导页本身维护了数套内置推荐订阅 JSON(如 apps/mobile/src/modules/onboarding/feeds.json、feeds-english.json,内含大量 rsshub:// 协议源)。Trending 区块的加入等于在“按分类挑订阅”之外,提供了一条基于实时订阅统计的发现路径,降低了新用户冷启动时“不知道订阅什么”的门槛。
四、体验改进:错误信息、AI 默认值与图标兜底
4.1 RSSHub 订阅失败的提示更清晰
RSSHub 是 Folo 获取长尾内容源的重要协议(引导内置源大量使用 rsshub://youtube/...、rsshub://hackernews 等,Discover 页的“Popular”推荐也基于 RSSHub 分类),因此其源异常时给出可读的错误文案对排障至关重要。v0.1.9 的变更目标是:当某个 RSSHub 源抓取失败时,界面提示不再只有笼统的“订阅失败”,而是带上具体的失败原因/链路阶段,方便用户区分“路由不存在、站点反爬、网络超时”等情形。相关订阅与发现逻辑集中在 apps/mobile/src/modules/discover(如 Recommendations.tsx、Category.tsx)中,其渲染依赖 @follow/constants 提供的 RSSHubCategories 与 @follow/models/rsshub 中的路由声明类型。
4.2 AI 摘要改为默认开启
此前移动端的 AI 摘要开关默认是关闭的,用户需要手动在 General → Action 中打开“Summary”;v0.1.9 将其改为默认开启。这一变更的证据在共享默认配置 packages/internal/shared/src/settings/defaults.ts:summary: true。
export const defaultGeneralSettings: GeneralSettings = {
translation: false,
translationMode: "bilingual",
summary: true,
actionLanguage: "default",
...
}
设置页开关见 General.tsx(general-ai-summary-switch)。需要说明两点:
- 该默认值仅作用于新安装/未初始化过设置的设备;升级用户如曾在旧版本手动关闭过 AI 摘要,其本地设置会保留
false,不会被强制改回(设置原子只在首次初始化时写入默认值,参见 general.ts 的initializeDefaultSettings流程); - 实际能否生成摘要仍取决于当前账号的 AI 能力/配额,默认开启只代表“请求摘要的行为默认启用”。
4.3 图标加载失败的占位兜底
若某个 feed 的站点图标(favicon)加载失败(网络、源站 404 等),列表将不再出现破图/空白,而是回退显示占位图标。此类“图标解析 → 加载 → 失败回退”的处理在移动端订阅与条目卡片中被广泛复用(相关基础组件位于 apps/mobile/src/components),占位逻辑与空态展示样式可在仓库内结合具体组件验证。
4.4 精简登录/注册流程
登录与注册链路被重新梳理,减少中间跳转与不必要的确认步骤。移动端认证相关逻辑可参见仓库中浏览器/设备登录引导的实现(cli 侧对应 apps/cli/src/browser-login.ts,移动端账号引导逻辑位于 e2e 支持脚本 apps/mobile/e2e 相关 support 及 src 中认证模块)。
五、缺陷修复逐个拆解
v0.1.9 修复了 4 个用户可感知的问题,逐一说明其场景与验证方式:
- 点击头像的导航不正确:在部分页面(如帖子/条目头部)点击用户头像无法进入正确的用户主页或列表。修复后头像点击按预期触发对应导航。
- 视频视图下无图条目显示异常:条目没有配图时,在“视频/图文视图”渲染会出现占位错位、高度异常等。修复使无图条目在视频视图中按纯文本卡片正确布局。
- 部分订阅不出现在搜索结果中:此前搜索订阅源时个别已订阅/合法源匹配不到(结果集会遗漏)。修复涉及搜索结果构建逻辑(可结合 apps/mobile/src/modules/discover/search-tabs 下的
search-feed-items.ts及其测试search-feed-items.test.ts理解搜索条目的组装与去重)。 - 图片/视频两种视图下未读指示器显示错误:未读红点/计数在 picture 视图与 video 视图的条目上出现错显,修复统一了两种视图消费未读状态的方式。
这些修复点与订阅列表过滤(第二节)、发现页内容(第三节)共同属于“视图状态正确性”范畴。由于 changelog 中每个修复只附了 commit 引用,具体到行级实现可以在各视图组件(apps/mobile/src/modules/entry-list 与 view 相关组件)中按需继续排查。
六、如何在仓库中验证 v0.1.9 的行为
如果你是开发者或希望复现验证上述行为,可以从以下几处入手:
- 对照默认配置:所有新增设置键的默认值、注释分组都在 packages/internal/shared/src/settings/defaults.ts,类型定义在 packages/internal/shared/src/settings/interface.ts;
- 跟踪设置页 UI:apps/mobile/src/modules/settings/routes/General.tsx 是 General 设置页的完整实现(Language、Action、TTS、Subscriptions、Timeline、Mark as Read、Content 全部分组);
- 运行移动端测试:仓库使用 Vitest 组织测试(vitest.workspace.ts),可在 apps/mobile 目录下按既有测试约定运行与搜索相关的单测(如 search-tabs 下对搜索条目聚合逻辑的测试);
- 查看移动端 i18n 文案:设置项的中英日法繁体文案分别位于 locales/mobile/default 各语言 JSON,便于核对开关的显示名与描述。
七、小结
v0.1.9 是 Folo 移动端在“订阅管理效率 + 内容发现 + AI 默认体验”上的一次集中迭代:新增的“隐藏无未读订阅”与 unreadOnly 联动设计体现对“未读优先”工作流的尊重;Trending 区块被同时注入 Discover 与引导流程,打通了新用户冷启动的热门源推荐;而 AI 摘要默认开启、RSSHub 错误信息优化则降低了普通用户理解与使用门槛。若想继续追踪后续版本演进(如热门源的分组/视图支持、摘要触发策略调整等),可对照 apps/mobile/changelog 目录下相邻版本记录,并结合 defaults.ts 观察设置默认值的演进轨迹。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00