SuperAPI AI 工具集

feed-patterns

编程与开发

查看项目

更新于 10-16 06:06

技能介绍

用途

Feed Patterns 是一份关于 Amethyst 中信息流(feed)组合与数据访问层模式的技能说明。它用于以下场景:

  • 添加一个展示笔记列表的新界面。
  • 修改现有 feed 的过滤、排序或包含规则。
  • 排查为何静音、关注或书签变更后 feed 未更新。
  • 判断是扩展 ViewModel 还是编写新过滤器。
  • 理解 feed 在 Android 与桌面端之间的共享边界。

Amethyst 的 feed 抽象包含两部分:一个 FeedFilter 决定哪些笔记属于该列表,以及一个 FeedViewModel 将当前状态以响应式方式暴露给 UI。首页、个人资料、标签、书签、通知、私信等每一个可滚动列表,都是该模式的变体。

主要能力

共享(commons)的 ViewModel:

  • FeedViewModel.kt:抽象类 FeedViewModel(localFilter, cacheProvider)。持有 FeedContentState,订阅失效信号(来自 Account flows 与 LocalCacheFlow),重新运行过滤器,并向 UI 发出新的 FeedState。
  • ListChangeFeedViewModel.kt:用于成员频繁变化的 feed(如书签)。
  • ChatroomFeedViewModel.kt:私信会话 feed。
  • MarmotGroupFeedViewModel.kt:NIP-29 / marmot 群组 feed。
  • LiveStreamTopZappersViewModel.kt、SearchBarState.kt、ChatNewMessageState.kt:共享同一套基础设施的更窄的非 feed 状态。

共享的过滤器基类(commons):

  • FeedFilter.kt:抽象类 FeedFilter<T> : IFeedFilter<T>。提供 feed(): List<T>(针对缓存的同步查询)、feedKey(): String(用于缓存的标识)、limit() 与 loadTop()。
  • AdditiveFeedFilter.kt:抽象类 AdditiveFeedFilter<T> : FeedFilter<T>(), IAdditiveFeedFilter<T>。增加增量更新能力:updateListWith(oldList, newItems) 运行 applyFilter(newItems),并将接受的项嫁接到现有列表上(重新排序 + take(limit())),无需完整重算。
  • ChangesFlowFilter.kt:用粗略的“状态已变更”信号包装过滤器,让 ViewModel 知道需要重新查询。
  • FeedContentState.kt / FeedState.kt:UI 收集的响应式状态。

Android DAL(在 commons 之上的扩展):

  • AdditiveComplexFeedFilter.kt:抽象类 AdditiveComplexFeedFilter<T, U> : FeedFilter<T>()。类似 AdditiveFeedFilter,但传入项(Set<U>)与列表行(T)类型不同。
  • FilterByListParams.kt:许多过滤器共享的通用参数(顶部导航过滤、排除静音、since/until)。
  • DefaultFeedOrder.kt:Note、Event 与 Card 的标准比较器(createdAt 降序 + id 作为稳定分页的平局决胜)。
  • FeedFilters.kt / ChangesFlowFilter.kt:向后兼容的类型别名,重新导出 commons 类;不要在此添加逻辑。

具体过滤器(首页、标签、个人资料、书签、通知、社区等)位于 amethyst/.../ui/screen/loggedIn/*/ 下的功能 dal/ 子文件夹中,每个都继承 FeedFilter、AdditiveFeedFilter 或 AdditiveComplexFeedFilter。桌面端在 desktopApp/.../feeds/DesktopFeedFilters.kt 中有自己的实现。

使用方式

新增一个 Feed:

  1. 定义过滤器。继承 AdditiveFeedFilter<Note>(若无关增量则用普通 FeedFilter<Note>;若传入项与列表行类型不同则用 AdditiveComplexFeedFilter<T, U>)。实现:
    • feedKey():稳定标识(如标签名、账户公钥)。
    • feed():对 LocalCache / Account 状态进行同步扫描,产出有序列表。
    • limit():分页提示。
    • 若为增量式:实现 applyFilter(collection: Set<Note>): Set<Note> 与 sort(collection: Set<Note>): List<Note>。
  2. 选择或编写 ViewModel。若 feed 成员经常变动(书签、通知),继承 ListChangeFeedViewModel;否则用 FeedViewModel。
  3. 连接失效通知。ViewModel 必须观察正确的 Account flows 与 LocalCacheFlow,以便状态变化时重新查询。
  4. 渲染。在 composable 中收集 viewModel.feedState.feedContent,用 LazyColumn { items(..., key = { it.id }) { NoteCompose(it) } } 渲染。
  5. 订阅中继。多数 feed 还需要一个 Subscribable 来获取历史事件。参见 relay-client 技能。

过滤器共享(Android 与桌面端):

  • 过滤器基类(FeedFilter、AdditiveFeedFilter、ChangesFlowFilter)与 feed 状态(FeedContentState)位于 commons/.../ui/feeds/,共享。ViewModel 位于 commons/.../viewmodels/,共享。
  • 具体过滤器是各平台本地的:Android 的在 amethyst/.../ui/screen/loggedIn/*/dal/,桌面端的在 desktopApp/.../feeds/。amethyst/.../ui/dal/ 保留 Android 专属辅助(AdditiveComplexFeedFilter、FilterByListParams、DefaultFeedOrder)以及向后兼容类型别名。
  • 移植 feed 时,仅当两端需要完全相同的包含规则时才共享具体过滤器。

适用场景

适用于 Amethyst 项目中任何与信息流相关的开发任务,包括添加新列表界面、调整过滤与排序规则、排查 feed 更新问题,以及处理 Android 与桌面端的 feed 共享与移植。

限制与注意事项

  • 永远不要在 composable 中扫描 LocalCache。始终通过 FeedFilter + FeedViewModel,它在后台调度器上执行并会对失效通知做防抖。
  • feedKey() 用作缓存键。两个语义不同的 feed 必须产生不同的键,否则状态会交叉污染。
  • 增量更新必须与完整重算保持一致。若 applyFilter 接受了 feed() 不会包含的笔记,用户体验会发生偏移。
  • 分页不是免费的——应使用 limit() 与 FilterByListParams 中的 since/until,而不是裁剪一次巨大扫描。
  • 通知 feed 比较特殊——它会检查关注/静音状态(account.kind3FollowList.flow、account.hiddenUsers)与 LocalCache 删除记录,以隐藏被静音/删除的内容;应始终走 FilterByListParams 的排除路径,而不是事后过滤。

参考资料

  • references/feed-filter-composition.md:添加 feed 的分步说明。
  • references/viewmodel-base-classes.md:FeedViewModel 家族的继承图。

相关补充:account-state(数据存放位置)、relay-client(如何订阅)、compose-expert(如何渲染)。