news 2026/10/15 16:39:04

NewsBlur 已保存故事视图(Saved Stories):按站点与文件夹组织、检索你的收藏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NewsBlur 已保存故事视图(Saved Stories):按站点与文件夹组织、检索你的收藏
  • 后端
  • 前端
  • 社交
  • 人工智能

【免费下载链接】NewsBlur

NewsBlur is a personal news reader that brings people together to talk about the world. A new sound of an old instrument.

项目地址:https://gitcode.com/gh_mirrors/ne/NewsBlur
点击查看免费下载

NewsBlur 在推出 Saved Story Tagging(已保存故事标签)之后,为每个已保存故事补充了天然的第二个分类维度——来源站点(site),并上线了专门的 Saved Stories 视图:你可以按站点浏览已保存的故事,点击任一包含站点的文件夹即可聚合查看该文件夹内所有来源的收藏,还能借助全文搜索在所有已保存故事中检索。本文结合 NewsBlur 当前开源仓库的前后端实现,从使用方式、接口参数到 MongoDB 数据模型逐层拆解这一功能,帮助你完全掌握并复用这套"个人剪藏库"的构建思路。

功能背景:为什么需要"按站点"这个分类维度

2014 年初,NewsBlur 上线了 Saved Story Tagging(已保存故事标签),允许用户为收藏的故事打上自定义标签。在标签体系落地之后,官方博客指出一个客观事实:每个已保存故事都存在一个固有的、无需用户额外维护的分类维度——它来自哪个站点(site)。与其只允许用户通过站点去"筛选"已保存故事,不如直接构建一个专门的视图,让用户按站点浏览自己的收藏。

这个思路在功能页 templates/static/feature_saved_stories.xhtml 中被概括为一句定位:Your personal clipping library, searchable and shareable(一个可搜索、可分享的个人剪藏库)。功能上对应四条核心能力:

  1. 一键(或快捷键)保存任意故事;
  2. 为已保存故事添加自定义标签,按主题组织;
  3. 在侧边栏按标签浏览已保存故事;
  4. 在所有已保存故事与标签上进行搜索。

按站点视图正是在这套"标签库"之外,用站点作为另一套自动形成的索引。

核心功能一:按站点浏览已保存故事

在 Web 阅读器侧边栏中,Saved Stories(已保存故事)以虚拟文件夹的形式存在,点击即可进入/saved视图。前端路由逻辑位于 media/js/newsblur/reader/reader.js 的open_starred_stories:

  • 无标签参数时,URL 导航到/saved(this.active_feed = 'starred',侧边栏NB-starred-header被选中);
  • 点击某个标签时,URL 变为/saved/<tag_slug>,例如/saved/linux;
  • 对应路由在 newsblur_web/urls.py 中声明:^saved/(?P<tag_name>\d+)?与^saved/?都落到reader_views.index。

侧边栏的展开、折叠由 media/js/newsblur/views/sidebar.js 中的open_starred_stories/collapse_starred_stories事件驱动,并把starred作为一个可折叠的虚拟文件夹(NB-starred-folder)纳入Preferences.collapsed_folders管理。

按站点查看的实现依据:后端统计模型 apps/rss_feeds/models.py 中的MStarredStoryCounts同时记录了三种维度——tag(标签)、feed_id(站点)、is_highlights(高亮),其__unicode__分别输出Tag: xxx (n)、Feed: xxx (n)、Highlights: xxx (n)。starred_counts接口(apps/reader/views.py)把统计结果连同总量一起返回给前端;侧边栏据此渲染出按站点与按标签分组的收藏目录树,点击站点即可进入该站点维度下的已保存故事列表。

核心功能二:文件夹聚合——一个文件夹,看遍全部站点

新视图最有价值的一点在于它同样适用于文件夹(Folder):点击包含多个站点的文件夹,即可看到该文件夹内所有站点的已保存故事,相当于对收藏按你自己的信息架构(订阅文件夹划分)做了一次自动聚合。

从实现看,这套聚合能力建立在两个基础上:

  • 站点维度统计已就绪:MStarredStoryCounts为每个feed_id单独维护计数,文件夹天然拥有其下站点的集合,前端选中文件夹时即可把站点点位展开为聚合视图;
  • 故事数据按站点可追溯:MStarredStory中保存了story_feed_id(故事所属站点)字段,后端查询时按user_id+ 排序/过滤条件取出故事,再通过story_feed_id回填订阅信息。

这与 NewsBlur 中"River of Stories"按文件夹聚合的思路一致:文件夹是用户的订阅分组,Saved Stories 视图复用这套分组,让收藏自动归位到对应的信息架构中,无需用户手工整理。

核心功能三:全文搜索——检索所有已保存故事

在按站点视图发布之前的一个月,NewsBlur 刚刚上线了全文搜索能力;按站点视图直接接入了这套搜索,使得所有已保存故事(无论按站点还是按标签浏览)都可以被直接检索。

后端搜索入口是 apps/reader/views.py 的load_starred_stories(前端通过/reader/starred_stories调用,见 media/js/newsblur/common/assetmodel.js)。当请求携带query参数时,走MStarredStory.find_stories分支(apps/rss_feeds/models.py):

  • 匹配范围:story_title、story_author_name、story_tags三个字段的icontains模糊匹配;
  • 可叠加过滤:tag(仅匹配含该标签的故事)、date_filter_start/date_filter_end(按starred_date时间段过滤);
  • 排序与分页:order(newest/oldest)决定-starred_date方向,offset/limit控制分页;
  • 权限限制:搜索分支要求user.profile.is_premium,否则返回空结果并提示"You must be a premium subscriber to search."(非 Premium 用户不能使用已保存故事搜索)。

load_starred_stories支持的完整查询参数如下(这些参数同样支撑了按标签、按高亮、按日期等浏览模式):

参数含义默认值 / 取值
offset起始偏移0
limit每页条数10
page页码(存在时offset = limit * (page-1))0
query搜索关键词空(不搜索)
order排序方向newest/oldest
read_filter已读/未读过滤all
date_filter_start/date_filter_end保存日期区间(Premium Archive 功能,非 Archive 用户会被置空)null
tag按标签过滤(Premium)空
highlights仅看含高亮片段的故事(Premium)false
h/h[]指定故事哈希批量取回(最多 100 条)空
vAPI 版本1(前端传2)

值得一提的是,当前仓库实现中搜索走的是 MongoDB 文本匹配(SearchStarredStory的 Elasticsearch 版调用在 apps/reader/views.py 被注释),这正好印证了博客原文"full text search"所描述的体验:搜标题、作者、故事标签,全部命中已保存故事库。

数据模型:已保存故事如何存储

按站点视图与搜索能力的底层是 apps/rss_feeds/models.py 中的MStarredStory(MongoDB 集合starred_stories)。关键字段:

字段说明
user_id所属用户(与story_guid构成唯一约束)
starred_date/starred_updated保存时间 / 最近更新时间
story_feed_id故事来源站点 ID(按站点视图的核心依据)
story_date故事原始发布时间
story_title/story_content/story_content_z标题、正文(正文在save()时 zlib 压缩进*_z字段)
story_author_name/story_permalink/story_guid作者、原文链接、全局唯一 GUID
story_hashfeed_id:story_hash形式的故事哈希,由feed_guid_hash计算并覆写
story_tags故事自带的文章标签(来自 RSS)
user_tags用户自定义标签(Saved Story Tagging 的落点,最大 128 字符/个)
user_notes用户笔记
highlights高亮片段(Premium 特性)
image_urls配图 URL 列表

模型自带四个索引组合:(user_id, -starred_date)(按保存时间倒序浏览)、(user_id, story_feed_id)(按站点聚合)、(user_id, story_hash)(按哈希去重/取回)、story_feed_id(站点维度计数)。story_feed_id索引的存在正是按站点视图能够在 MongoDB 侧高效分组的直接证据。

标签系统:按标签浏览、重命名与 RSS 导出

按站点视图上线于 2014 年,而标签(Tag)是它的前置功能。标签体系在仓库中同样完整保留:

  • 按标签浏览:前端open_starred_stories检测options.tag,在starred_feeds集合中按tag_slug()或tag匹配模型(media/js/newsblur/reader/reader.js),后端则用user_tags__contains=tag过滤(Premium 限定,见 apps/reader/views.py)。
  • 标签重命名/删除:rename_starred_tag与delete_starred_tag(apps/reader/views.py)分别完成"全库标签改名"与"移除标签(故事保留)",路由注册于 apps/reader/urls.py。
  • 测试佐证:apps/reader/tests.py 的Test_RenameStarredTag用例验证了重命名会同步更新所有故事、合并已有同名标签的计数、且不影响其余标签——这是标签数据一致性的直接保障。
  • RSS 导出:每个标签(以及全部已保存故事)都可以生成独立的 Atom/RSS feed。starred_stories_rss_feed_tag(apps/reader/views.py)以user_id + secret_token + tag_slug为寻址方式生成Saved Stories - <tag>标题的订阅源;MStarredStoryCounts.rss_url属性(apps/rss_feeds/models.py)拼接出形如/reader/starred_rss/<user_id>/<secret_token>/<slug>的订阅地址。这让收藏可以订阅到其他阅读器、或通过 IFTTT/Zapier 等工具继续流转——功能页 FAQ 中明确写着"Each tag gets its own RSS feed"。

前端交互链路:从点击到渲染

一次"打开已保存故事"的完整调用链为:

  1. 侧边栏点击 Starred 头部或标签 →sidebar.js的open_starred_stories;
  2. →reader.js的open_starred_stories(options):设置active_feed、flags['starred_tag']、URL 导航,并决定 story view 布局(feed/page);
  3. →assetmodel.js的fetch_starred_stories(page, tag, highlights, ...):组装GET /reader/starred_stories请求,携带query(来自全局搜索框)、order(视图设置)、tag、highlights、date_filter_*、v:2;
  4. → 后端load_starred_stories返回stories(经Feed.format_stories格式化)+starred_counts等元数据;
  5. 前端把结果渲染进故事标题栏/故事视图,同时侧边栏用MStarredStoryCounts.user_counts的结果(apps/rss_feeds/models.py)刷新标签与站点的计数徽标。

移动端规划

博客原文最后预告:按站点视图与已保存故事搜索能力将逐步移植到 iOS 与 Android 客户端。当前仓库中移动端仍在持续演进,Web 端这套"站点 + 文件夹 + 标签 + 搜索"的组织模型(MStarredStory统一数据源 +starred_counts统计接口)正是多端共用同一套后端接口的基础——这也是 NewsBlur 各端数据同步的前提。

总结

NewsBlur 的 Saved Stories 视图给出了一个非常实用的收藏组织范式:用系统自动维护的站点维度 + 用户自定义的标签维度,叠加全文搜索与 RSS 导出,把"以后再看"的收藏变成可浏览、可检索、可分享的个人知识库。其技术落点在当前仓库中清晰可循——MongoDB 侧的MStarredStory与MStarredStoryCounts双模型(apps/rss_feeds/models.py)承担存储与聚合,load_starred_stories/starred_counts(apps/reader/views.py)承担查询与统计,前端reader.js/sidebar.js/assetmodel.js完成交互闭环。如果你想自建类似的"剪藏 + 站点分组 + 搜索"系统,这套按feed_id建立二级索引、以标签与站点双维度聚合、并为聚合结果提供订阅导出的方案,值得直接参考。

  • 后端
  • 前端
  • 社交
  • 人工智能

【免费下载链接】NewsBlur

NewsBlur is a personal news reader that brings people together to talk about the world. A new sound of an old instrument.

项目地址:https://gitcode.com/gh_mirrors/ne/NewsBlur
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:37:40

工业级多源融合定位系统:IMU主干+GNSS校准+激光约束

1. 项目概述&#xff1a;这不是登月任务&#xff0c;而是一套高精度工业级实时定位与建图系统“Apollo-11”这个名称一出现&#xff0c;很多人第一反应是1969年的载人登月——但今天我们要聊的&#xff0c;是某自动驾驶实验室内部代号为“Apollo-11”的一套嵌入式多源融合定位系…

作者头像 李华
网站建设 2026/10/15 16:37:29

Delphi AlphaControls 14.24 懒人包:VCL 皮肤机制与 WebBrowser 皮肤激活实战

简介&#xff1a;AlphaControls 14.24 懒人包面向使用 Delphi 与 CBuilder 的界面开发者&#xff0c;尤其适合需要在项目中快速集成皮肤控件、又不想被商业皮肤提示打扰的中高级程序员。该版本已包含全部组件&#xff0c;并激活 WebBrowser 皮肤&#xff0c;同时去除商业皮肤提…

作者头像 李华
网站建设 2026/10/15 16:36:46

5分钟拉伸运动

人机协作&#xff0c;仅供参考。非专业建议&#xff0c;建议咨询专业人士。这套拉伸运动只需5分钟&#xff0c;适合工作间隙或运动前后进行&#xff0c;能活动关节、放松肌肉。两臂左右拉伸&#xff1a;两臂向左右两侧伸展&#xff0c;左右各做30下&#xff0c;充分打开肩胸。两…

作者头像 李华
网站建设 2026/10/15 16:36:33

WebCrack:Python后台弱口令批量检测工具实战指南

简介&#xff1a;WebCrack 是一款面向 Web 安全测试人员与渗透测试学习者的后台弱口令、万能密码批量检测工具&#xff0c;导入后台地址即可自动化检测&#xff0c;适合具备一定 Python 基础、需要批量排查登录入口安全性的从业者使用。资源包共 13 个文件&#xff0c;以 8 个 …

作者头像 李华
网站建设 2026/10/15 16:36:30

STM32C8T6+OLED波形显示:ADC采样与DMA绘图实战

简介&#xff1a;一款基于STM32C8T6的OLED波形显示工程包&#xff0c;别名“mini版示波器”&#xff0c;面向嵌入式初学者与电子爱好者&#xff0c;解决如何利用低成本MCU驱动OLED并实时绘制波形的问题。项目同时支持0.96寸与1.3寸屏幕&#xff0c;覆盖ADC采样、信号滤波/峰值检…

作者头像 李华