Repository navigation
rss feed support - #15
Conversation
📝 WalkthroughWalkthroughThis PR implements an official blog RSS feed integration for Hagicode Desktop, adding a singleton RSSFeedManager for fetching and caching RSS content, Redux state management for the renderer, a BlogFeedCard UI component, IPC handlers bridging main and renderer processes, type definitions, and internationalization support for displaying blog posts in the app. Changes
Sequence DiagramssequenceDiagram
participant Renderer as Renderer Process
participant IPC as IPC Channel
participant Main as Main Process
participant Fetch as HTTP/Axios
participant Cache as Electron Store
participant Parser as RSS Parser
Renderer->>IPC: rss-get-feed-items()
IPC->>Main: Receive request
alt Cache valid
Main->>Cache: Read cached items
Cache-->>Main: Return cached items
else Cache invalid or missing
Main->>Fetch: Fetch RSS XML from feed URL
Fetch-->>Main: RSS XML response
Main->>Parser: Parse XML
Parser-->>Main: RSSFeedItem[] objects
Main->>Cache: Store items + lastUpdate
Cache-->>Main: Cache stored
end
Main-->>IPC: Return RSSFeedItem[]
IPC-->>Renderer: Feed items received
sequenceDiagram
participant User as User/UI
participant Component as BlogFeedCard
participant Store as Redux Store
participant Saga as RSS Saga
participant IPC as IPC/electronAPI
participant Main as Main Process
User->>Component: Page mount or refresh click
Component->>Store: Dispatch fetchFeedItemsAction
Store->>Saga: Watch FETCH_FEED_ITEMS action
Saga->>Saga: Set loading = true
Saga->>IPC: Call electronAPI.rss.getFeedItems()
IPC->>Main: Invoke 'rss-get-feed-items'
Main-->>IPC: Return RSSFeedItem[] and lastUpdate
IPC-->>Saga: Data received
Saga->>Store: Dispatch setItems, setLoading(false), setLastUpdate
Store-->>Component: Update state
Component->>Component: Render feed items, clear loading state
User-->>Component: View blog posts
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/proposal.md`:
- Around line 3-60: Update the proposal headings to match the standard template
by replacing or adding the sections: "Why" (move Background & motivation content
here), "What Changes" (move Scope and high-level change list here), "UI Design
Changes" (extract and expand any UI/UX details like
BlogFeedCard/SystemManagementView integration), "Code Flow Changes" (move
Technical Design items like rss-feed-manager.ts, IPC channels, rssFeedSlice/saga
and persistence/refresh logic here), and "Impact" (list rollout, performance,
privacy and maintenance impacts); ensure each section contains the existing
relevant text from Overview/Background/Scope/Technical Design and rename
headings accordingly while keeping the RSS URL, change ID and metadata intact.
- Around line 64-110: The fenced ASCII diagram in proposal.md lacks a language
tag, triggering markdownlint MD040; update the opening fence for the diagram
(the triple-backtick block that begins the ASCII diagram containing "渲染进程
(Renderer)" and the box-drawing characters) to include a language identifier
such as text (e.g., change ``` to ```text) so the fenced code block is properly
annotated.
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/tasks.md`:
- Around line 7-30: The markdown file contains multiple fenced code blocks
without language identifiers (e.g., the task list blocks in
openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/tasks.md);
update every triple-backtick fence used for the phase/task diagrams and the UI
ASCII art blocks to include a language tag (for these cases use "text" or
"plain") so markdownlint warnings are resolved—ensure you update all occurrences
(including the additional range noted at lines 240-259) by replacing ``` with
```text (or ```plain) at each opening fence and keeping the matching closing
fences unchanged.
- Around line 49-381: Update the checklist items in this tasks.md to mark
completed work as done by changing "- [ ]" to "- [x]" for tasks that have been
fully implemented and verified (e.g., the T1.2, T2.1-T2.3, T3.1-T3.2, T4.1-T4.3,
T5.1-T5.3 entries and their sub-items); ensure you only flip boxes after
confirming implementation and tests, update all instances under
openspec/changes/*/tasks.md, and include a brief commit message like "mark RSS
feature tasks completed" when committing the changes.
In `@package.json`:
- Line 21: The dev script uses the POSIX-only rm -rf which breaks on Windows;
update the "dev" npm script to use a cross-platform deletion tool (e.g., replace
rm -rf dist with rimraf dist or use shx rm -rf dist) and add the corresponding
devDependency (e.g., rimraf) so npm run dev works on Windows, keeping the rest
of the concurrently command and the referenced scripts ("dev:renderer",
"dev:main", "dev:preload") unchanged.
In `@src/renderer/components/BlogFeedCard.tsx`:
- Around line 176-180: The article tile uses a clickable div which is not
keyboard-accessible; update the BlogFeedCard so the interactive container uses a
semantic element (prefer an <a> or <button>) or at minimum adds role, tabIndex
and key event handling; replace the clickable <div> that calls
handleArticleClick(item.link) with a semantic element (e.g., an anchor linking
to item.link or a button that calls handleArticleClick) and ensure focus
styles/hover classes are preserved and Enter/Space key activation is supported
if you choose a non-semantic element; reference the interactive container around
handleArticleClick and the item.link usage when making the change.
🧹 Nitpick comments (1)
src/main/types/rss-types.ts (1)
9-22: Consider sharingRSSFeedItemacross main/renderer to avoid drift.
The same interface exists insrc/renderer/store/slices/rssFeedSlice.ts; a shared type module would reduce duplication.
| ## 概述 | ||
|
|
||
| 在 Hagicode Desktop 应用主页集成 Hagicode 官方博客的 RSS 订阅源,允许用户直接在应用内查看最新的官方动态、技术文章和产品更新公告。 | ||
|
|
||
| **变更 ID**: `official-blog-rss-feed-integration` | ||
| **状态**: ExecutionCompleted | ||
| **目标版本**: 待定 | ||
|
|
||
| ## 背景与动机 | ||
|
|
||
| ### 当前状态 | ||
|
|
||
| Hagicode Desktop 是一个基于 Electron 的跨平台桌面应用,提供 Web 服务管理、包管理等功能。目前主页缺少官方博客内容的展示,用户需要: | ||
|
|
||
| - 手动访问外部网站获取 Hagicode 最新动态 | ||
| - 无法及时了解产品更新和功能发布公告 | ||
| - 缺少快速访问官方文档和教程资源的入口 | ||
|
|
||
| ### 目标 | ||
|
|
||
| 通过集成官方博客 RSS 订阅源(`https://docs.hagicode.com/blog/rss.xml`),在应用主页提供: | ||
| - 最新 5 条博客文章的快速预览 | ||
| - 自动刷新机制,确保内容及时更新 | ||
| - 一键跳转至完整文章 | ||
|
|
||
| ## 范围 | ||
|
|
||
| ### 包含内容 | ||
|
|
||
| 1. **主进程 RSS 管理器** | ||
| - 创建 `rss-feed-manager.ts` 单例管理器 | ||
| - 实现从 `https://docs.hagicode.com/blog/rss.xml` 获取和解析 RSS 数据 | ||
| - 使用 electron-store 持久化缓存 RSS 数据 | ||
| - 实现每 24 小时自动刷新机制 | ||
|
|
||
| 2. **IPC 通信层** | ||
| - `rss-get-feed-items` - 获取 RSS 文章列表 | ||
| - `rss-refresh-feed` - 手动触发刷新 | ||
| - `rss-get-last-update` - 获取上次更新时间 | ||
|
|
||
| 3. **渲染进程状态管理** | ||
| - 创建 `rssFeedSlice.ts` 管理 RSS 状态 | ||
| - 创建 `rssFeedSaga.ts` 处理异步操作 | ||
|
|
||
| 4. **UI 组件** | ||
| - 创建 `BlogFeedCard.tsx` 展示博客文章列表 | ||
| - 集成到主页(SystemManagementView.tsx) | ||
|
|
||
| 5. **国际化支持** | ||
| - 中英文翻译文件更新 | ||
|
|
||
| ### 排除内容 | ||
|
|
||
| - RSS 订阅源配置界面(仅使用固定的官方博客地址) | ||
| - 离线模式下的缓存阅读(仅缓存数据用于刷新间隔) | ||
| - 博客内容的全文展示(仅摘要和跳转链接) | ||
|
|
||
| ## 技术设计 |
There was a problem hiding this comment.
Add the standard proposal sections (Why / What Changes / UI Design Changes / Code Flow Changes / Impact).
Please add or rename headings to match the required template; the current structure uses Overview/Background/Scope/Technical Design instead.
Based on learnings: Structure proposal.md with Why, What Changes, UI Design Changes (if applicable), Code Flow Changes (if applicable), and Impact sections.
🤖 Prompt for AI Agents
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/proposal.md`
around lines 3 - 60, Update the proposal headings to match the standard template
by replacing or adding the sections: "Why" (move Background & motivation content
here), "What Changes" (move Scope and high-level change list here), "UI Design
Changes" (extract and expand any UI/UX details like
BlogFeedCard/SystemManagementView integration), "Code Flow Changes" (move
Technical Design items like rss-feed-manager.ts, IPC channels, rssFeedSlice/saga
and persistence/refresh logic here), and "Impact" (list rollout, performance,
privacy and maintenance impacts); ensure each section contains the existing
relevant text from Overview/Background/Scope/Technical Design and rename
headings accordingly while keeping the RSS URL, change ID and metadata intact.
| ``` | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ 渲染进程 (Renderer) │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ ┌─────────────────┐ ┌─────────────────┐ │ | ||
| │ │ BlogFeedCard │◄────│ rssFeedSlice │ │ | ||
| │ │ (UI 组件) │ │ (Redux 状态) │ │ | ||
| │ └─────────────────┘ └─────────────────┘ │ | ||
| │ ▲ ▲ │ | ||
| │ │ │ │ | ||
| │ └───────┬───────────────┘ │ | ||
| │ │ │ | ||
| │ ┌────────▼────────┐ │ | ||
| │ │ rssFeedSaga │ │ | ||
| │ │ (异步处理) │ │ | ||
| │ └────────┬────────┘ │ | ||
| └──────────────────┼───────────────────────────────────────────┘ | ||
| │ IPC 通信 | ||
| ▼ | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ 主进程 (Main) │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ ┌──────────────────────────────────────────────────────┐ │ | ||
| │ │ rss-feed-manager.ts (单例) │ │ | ||
| │ │ ┌─────────────────────────────────────────────────┐ │ │ | ||
| │ │ │ • fetchRSSFeed() - 获取 RSS 数据 │ │ │ | ||
| │ │ │ • parseRSSXML() - 解析 XML │ │ │ | ||
| │ │ │ • getCachedFeed() - 获取缓存数据 │ │ │ | ||
| │ │ │ • refreshFeed() - 刷新 RSS 数据 │ │ │ | ||
| │ │ │ • startAutoRefresh() - 启动定时刷新 │ │ │ | ||
| │ │ └─────────────────────────────────────────────────┘ │ │ | ||
| │ └──────────────────────────────────────────────────────┘ │ | ||
| │ │ │ | ||
| │ ▼ │ | ||
| │ ┌──────────────────────────────────────────────────────┐ │ | ||
| │ │ electron-store (持久化) │ │ | ||
| │ │ • rssFeed.items - 文章列表 │ │ | ||
| │ │ • rssFeed.lastUpdate - 最后更新时间 │ │ | ||
| │ └──────────────────────────────────────────────────────┘ │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
| │ | ||
| ▼ | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ 外部数据源 │ | ||
| │ https://docs.hagicode.com/blog/rss.xml │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
| ``` |
There was a problem hiding this comment.
Specify a language for the fenced diagram block.
markdownlint MD040 requires a language on fenced code blocks.
📝 Suggested fix
-```
+```text
┌─────────────────────────────────────────────────────────────┐
│ 渲染进程 (Renderer) │
...
└─────────────────────────────────────────────────────────────┘
-```
+```📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| ┌─────────────────────────────────────────────────────────────┐ | |
| │ 渲染进程 (Renderer) │ | |
| ├─────────────────────────────────────────────────────────────┤ | |
| │ ┌─────────────────┐ ┌─────────────────┐ │ | |
| │ │ BlogFeedCard │◄────│ rssFeedSlice │ │ | |
| │ │ (UI 组件) │ │ (Redux 状态) │ │ | |
| │ └─────────────────┘ └─────────────────┘ │ | |
| │ ▲ ▲ │ | |
| │ │ │ │ | |
| │ └───────┬───────────────┘ │ | |
| │ │ │ | |
| │ ┌────────▼────────┐ │ | |
| │ │ rssFeedSaga │ │ | |
| │ │ (异步处理) │ │ | |
| │ └────────┬────────┘ │ | |
| └──────────────────┼───────────────────────────────────────────┘ | |
| │ IPC 通信 | |
| ▼ | |
| ┌─────────────────────────────────────────────────────────────┐ | |
| │ 主进程 (Main) │ | |
| ├─────────────────────────────────────────────────────────────┤ | |
| │ ┌──────────────────────────────────────────────────────┐ │ | |
| │ │ rss-feed-manager.ts (单例) │ │ | |
| │ │ ┌─────────────────────────────────────────────────┐ │ │ | |
| │ │ │ • fetchRSSFeed() - 获取 RSS 数据 │ │ │ | |
| │ │ │ • parseRSSXML() - 解析 XML │ │ │ | |
| │ │ │ • getCachedFeed() - 获取缓存数据 │ │ │ | |
| │ │ │ • refreshFeed() - 刷新 RSS 数据 │ │ │ | |
| │ │ │ • startAutoRefresh() - 启动定时刷新 │ │ │ | |
| │ │ └─────────────────────────────────────────────────┘ │ │ | |
| │ └──────────────────────────────────────────────────────┘ │ | |
| │ │ │ | |
| │ ▼ │ | |
| │ ┌──────────────────────────────────────────────────────┐ │ | |
| │ │ electron-store (持久化) │ │ | |
| │ │ • rssFeed.items - 文章列表 │ │ | |
| │ │ • rssFeed.lastUpdate - 最后更新时间 │ │ | |
| │ └──────────────────────────────────────────────────────┘ │ | |
| └─────────────────────────────────────────────────────────────┘ | |
| │ | |
| ▼ | |
| ┌─────────────────────────────────────────────────────────────┐ | |
| │ 外部数据源 │ | |
| │ https://docs.hagicode.com/blog/rss.xml │ | |
| └─────────────────────────────────────────────────────────────┘ | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 64-64: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/proposal.md`
around lines 64 - 110, The fenced ASCII diagram in proposal.md lacks a language
tag, triggering markdownlint MD040; update the opening fence for the diagram
(the triple-backtick block that begins the ASCII diagram containing "渲染进程
(Renderer)" and the box-drawing characters) to include a language identifier
such as text (e.g., change ``` to ```text) so the fenced code block is properly
annotated.
| ``` | ||
| Phase 1: 准备工作 | ||
| ├── T1.1 安装 rss-parser 依赖 | ||
| └── T1.2 定义 TypeScript 类型 | ||
|
|
||
| Phase 2: 主进程开发 | ||
| ├── T2.1 创建 RSS Feed Manager | ||
| ├── T2.2 实现 IPC 通道注册 | ||
| └── T2.3 配置预加载脚本 | ||
|
|
||
| Phase 3: 渲染进程状态管理 | ||
| ├── T3.1 创建 rssFeedSlice | ||
| └── T3.2 创建 rssFeedSaga | ||
|
|
||
| Phase 4: UI 组件开发 | ||
| ├── T4.1 创建 BlogFeedCard 组件 | ||
| ├── T4.2 集成到主页 | ||
| └── T4.3 添加国际化翻译 | ||
|
|
||
| Phase 5: 测试与验证 | ||
| ├── T5.1 功能测试 | ||
| ├── T5.2 错误处理测试 | ||
| └── T5.3 国际化验证 | ||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks.
markdownlint warns when code blocks lack a language tag.
✍️ Suggested fix
-```
+```text
Phase 1: 准备工作
├── T1.1 安装 rss-parser 依赖
└── T1.2 定义 TypeScript 类型
@@
-```
+```
@@
-```
+```text
┌─────────────────────────────────────────┐
│ 📰 Hagicode 博客 [刷新] │
@@
-```
+```Also applies to: 240-259
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/tasks.md`
around lines 7 - 30, The markdown file contains multiple fenced code blocks
without language identifiers (e.g., the task list blocks in
openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/tasks.md);
update every triple-backtick fence used for the phase/task diagrams and the UI
ASCII art blocks to include a language tag (for these cases use "text" or
"plain") so markdownlint warnings are resolved—ensure you update all occurrences
(including the additional range noted at lines 240-259) by replacing ``` with
```text (or ```plain) at each opening fence and keeping the matching closing
fences unchanged.
| **验收标准**: | ||
| - [ ] `rss-parser` 已添加到 package.json dependencies | ||
| - [ ] `npm install` 无错误完成 | ||
|
|
||
| --- | ||
|
|
||
| ### T1.2 定义 TypeScript 类型 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 10 分钟 | ||
| **依赖**: T1.1 | ||
|
|
||
| **描述**: | ||
| 定义 RSS 相关的 TypeScript 接口和类型。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 创建文件 `src/main/types/rss-types.ts` | ||
| 2. 定义 `RSSFeedItem` 接口 | ||
| 3. 定义 `RSSFeedCache` 接口 | ||
|
|
||
| **代码位置**: `src/main/types/rss-types.ts` (新建) | ||
|
|
||
| **验收标准**: | ||
| - [ ] 类型定义文件已创建 | ||
| - [ ] 类型包含所有必需字段 | ||
| - [ ] 类型导出正确 | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 2: 主进程开发 | ||
|
|
||
| ### T2.1 创建 RSS Feed Manager | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 60 分钟 | ||
| **依赖**: T1.2 | ||
|
|
||
| **描述**: | ||
| 创建单例模式的 RSS Feed Manager,负责获取、解析和缓存 RSS 数据。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 创建 `src/main/rss-feed-manager.ts` | ||
| 2. 实现单例模式(参考现有管理器如 `web-service-manager.ts`) | ||
| 3. 实现 `fetchRSSFeed()` 方法 - 使用 axios 获取 RSS 数据 | ||
| 4. 实现 `parseRSSXML()` 方法 - 使用 rss-parser 解析 XML | ||
| 5. 实现 `getCachedFeed()` 方法 - 从 electron-store 读取缓存 | ||
| 6. 实现 `refreshFeed()` 方法 - 获取并更新缓存 | ||
| 7. 实现 `startAutoRefresh()` 方法 - 设置 24 小时定时刷新 | ||
| 8. 实现 `getLastUpdateTime()` 方法 - 返回最后更新时间 | ||
|
|
||
| **代码位置**: `src/main/rss-feed-manager.ts` (新建) | ||
|
|
||
| **验收标准**: | ||
| - [ ] 单例模式正确实现 | ||
| - [ ] 成功从 `https://docs.hagicode.com/blog/rss.xml` 获取数据 | ||
| - [ ] RSS XML 正确解析为 `RSSFeedItem[]` | ||
| - [ ] 数据正确缓存到 electron-store | ||
| - [ ] 自动刷新定时器正确设置 | ||
|
|
||
| --- | ||
|
|
||
| ### T2.2 实现 IPC 通道注册 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 30 分钟 | ||
| **依赖**: T2.1 | ||
|
|
||
| **描述**: | ||
| 在主进程注册 RSS 相关的 IPC 处理程序。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 编辑 `src/main/index.ts` | ||
| 2. 导入 `RssFeedManager` | ||
| 3. 获取管理器实例 | ||
| 4. 注册 `rss-get-feed-items` 处理程序 | ||
| 5. 注册 `rss-refresh-feed` 处理程序 | ||
| 6. 注册 `rss-get-last-update` 处理程序 | ||
|
|
||
| **代码位置**: `src/main/index.ts` (修改) | ||
|
|
||
| **验收标准**: | ||
| - [ ] 所有 IPC 通道已注册 | ||
| - [ ] 通道正确调用管理器方法 | ||
| - [ ] 错误处理已实现 | ||
|
|
||
| --- | ||
|
|
||
| ### T2.3 配置预加载脚本 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 20 分钟 | ||
| **依赖**: T2.2 | ||
|
|
||
| **描述**: | ||
| 在预加载脚本中暴露 RSS API 给渲染进程。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 编辑 `src/preload/index.ts` | ||
| 2. 在 `electronAPI` 对象中添加 RSS 方法 | ||
| 3. 添加类型定义(如需要) | ||
|
|
||
| **代码位置**: `src/preload/index.ts` (修改) | ||
|
|
||
| **验收标准**: | ||
| - [ ] RSS API 已暴露给渲染进程 | ||
| - [ ] 类型定义正确 | ||
| - [ ] 预加载脚本编译无错误 | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 3: 渲染进程状态管理 | ||
|
|
||
| ### T3.1 创建 rssFeedSlice | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 30 分钟 | ||
| **依赖**: T2.3 | ||
|
|
||
| **描述**: | ||
| 创建 Redux Toolkit slice 管理 RSS 状态。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 创建 `src/renderer/store/slices/rssFeedSlice.ts` | ||
| 2. 定义状态接口:`items`, `loading`, `error`, `lastUpdate` | ||
| 3. 实现 `setItems` reducer | ||
| 4. 实现 `setLoading` reducer | ||
| 5. 实现 `setError` reducer | ||
| 6. 实现 `setLastUpdate` reducer | ||
|
|
||
| **代码位置**: `src/renderer/store/slices/rssFeedSlice.ts` (新建) | ||
|
|
||
| **验收标准**: | ||
| - [ ] Slice 正确导出 | ||
| - [ ] Reducers 正确实现 | ||
| - [ ] Actions 正确导出 | ||
| - [ ] Slice 已添加到 store 配置 | ||
|
|
||
| --- | ||
|
|
||
| ### T3.2 创建 rssFeedSaga | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 40 分钟 | ||
| **依赖**: T3.1 | ||
|
|
||
| **描述**: | ||
| 创建 Redux Saga 处理 RSS 相关的异步操作。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 创建 `src/renderer/store/sagas/rssFeedSaga.ts` | ||
| 2. 实现 `fetchFeedItemsSaga` - 获取文章列表 | ||
| 3. 实现 `refreshFeedSaga` - 刷新 RSS 数据 | ||
| 4. 实现 `fetchLastUpdateSaga` - 获取最后更新时间 | ||
| 5. 注册 saga 监听器 | ||
|
|
||
| **代码位置**: `src/renderer/store/sagas/rssFeedSaga.ts` (新建) | ||
|
|
||
| **验收标准**: | ||
| - [ ] Saga 正确监听 actions | ||
| - [ ] IPC 调用正确实现 | ||
| - [ ] 状态更新正确 | ||
| - [ ] 错误处理已实现 | ||
| - [ ] Saga 已添加到 root saga | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 4: UI 组件开发 | ||
|
|
||
| ### T4.1 创建 BlogFeedCard 组件 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 60 分钟 | ||
| **依赖**: T3.2 | ||
|
|
||
| **描述**: | ||
| 创建博客文章展示卡片组件。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 创建 `src/renderer/components/BlogFeedCard.tsx` | ||
| 2. 使用 shadcn/ui Card 组件作为容器 | ||
| 3. 实现文章列表渲染(最多 5 条) | ||
| 4. 实现每条文章的显示:标题、日期、摘要 | ||
| 5. 添加刷新按钮 | ||
| 6. 实现加载状态 UI | ||
| 7. 实现错误状态 UI | ||
| 8. 实现点击跳转功能 | ||
| 9. 应用国际化翻译 | ||
|
|
||
| **代码位置**: `src/renderer/components/BlogFeedCard.tsx` (新建) | ||
|
|
||
| **UI 设计参考**: | ||
| ``` | ||
| ┌─────────────────────────────────────────┐ | ||
| │ 📰 Hagicode 博客 [刷新] │ | ||
| ├─────────────────────────────────────────┤ | ||
| │ ◦ 产品发布:Hagicode v2.0 正式发布 │ | ||
| │ 2025-01-15 │ | ||
| ├─────────────────────────────────────────┤ | ||
| │ ◦ 教程:如何配置自定义包源 │ | ||
| │ 2025-01-10 │ | ||
| ├─────────────────────────────────────────┤ | ||
| │ ◦ 更新日志:版本 v1.9.5 修复内容 │ | ||
| │ 2025-01-05 │ | ||
| ├─────────────────────────────────────────┤ | ||
| │ ◦ 技术文章:Electron 应用性能优化实践 │ | ||
| │ 2024-12-28 │ | ||
| ├─────────────────────────────────────────┤ | ||
| │ ◦ 公告:服务器维护通知 │ | ||
| │ 2024-12-20 │ | ||
| └─────────────────────────────────────────┘ | ||
| ``` | ||
|
|
||
| **验收标准**: | ||
| - [ ] 组件正确渲染 | ||
| - [ ] 显示最多 5 条文章 | ||
| - [ ] 加载状态显示正确 | ||
| - [ ] 错误状态显示正确 | ||
| - [ ] 刷新按钮功能正常 | ||
| - [ ] 点击文章正确跳转 | ||
| - [ ] 国际化文本显示正确 | ||
|
|
||
| --- | ||
|
|
||
| ### T4.2 集成到主页 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 15 分钟 | ||
| **依赖**: T4.1 | ||
|
|
||
| **描述**: | ||
| 将 BlogFeedCard 组件集成到应用主页。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 编辑 `src/renderer/components/SystemManagementView.tsx` | ||
| 2. 导入 `BlogFeedCard` 组件 | ||
| 3. 导入 `useAppDispatch` 和 `useAppSelector` | ||
| 4. 在组件挂载时 dispatch 获取 RSS 数据的 action | ||
| 5. 在适当位置添加 `BlogFeedCard` 组件 | ||
|
|
||
| **代码位置**: `src/renderer/components/SystemManagementView.tsx` (修改) | ||
|
|
||
| **验收标准**: | ||
| - [ ] BlogFeedCard 已添加到主页 | ||
| - [ ] 组件在挂载时自动加载数据 | ||
| - [ ] 布局正确,不影响其他组件 | ||
|
|
||
| --- | ||
|
|
||
| ### T4.3 添加国际化翻译 | ||
|
|
||
| **优先级**: 中 | ||
| **预计工作量**: 15 分钟 | ||
| **依赖**: T4.1 | ||
|
|
||
| **描述**: | ||
| 添加中英文翻译支持。 | ||
|
|
||
| **执行步骤**: | ||
| 1. 编辑 `src/renderer/i18n/locales/zh-CN/components.json` | ||
| 2. 添加以下翻译键: | ||
| - `blogFeed.title` - "Hagicode 博客" | ||
| - `blogFeed.refresh` - "刷新" | ||
| - `blogFeed.loading` - "加载中..." | ||
| - `blogFeed.error` - "加载失败,请稍后重试" | ||
| - `blogFeed.noArticles` - "暂无文章" | ||
| - `blogFeed.lastUpdate` - "最后更新:{date}" | ||
|
|
||
| 3. 编辑 `src/renderer/i18n/locales/en-US/components.json` | ||
| 4. 添加对应的英文翻译 | ||
|
|
||
| **代码位置**: | ||
| - `src/renderer/i18n/locales/zh-CN/components.json` (修改) | ||
| - `src/renderer/i18n/locales/en-US/components.json` (修改) | ||
|
|
||
| **验收标准**: | ||
| - [ ] 中英文翻译已添加 | ||
| - [ ] 组件正确使用翻译键 | ||
| - [ ] 语言切换时翻译正确更新 | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 5: 测试与验证 | ||
|
|
||
| ### T5.1 功能测试 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 30 分钟 | ||
| **依赖**: T4.2 | ||
|
|
||
| **描述**: | ||
| 验证核心功能正常工作。 | ||
|
|
||
| **测试用例**: | ||
| - [ ] 应用启动后自动加载 RSS 数据 | ||
| - [ ] 博客文章正确显示(标题、日期、摘要) | ||
| - [ ] 最多显示 5 条文章 | ||
| - [ ] 点击文章在浏览器中打开 | ||
| - [ ] 刷新按钮能手动更新数据 | ||
| - [ ] 最后更新时间正确显示 | ||
|
|
||
| --- | ||
|
|
||
| ### T5.2 错误处理测试 | ||
|
|
||
| **优先级**: 高 | ||
| **预计工作量**: 20 分钟 | ||
| **依赖**: T5.1 | ||
|
|
||
| **描述**: | ||
| 验证错误场景的处理。 | ||
|
|
||
| **测试用例**: | ||
| - [ ] 网络断开时显示错误提示 | ||
| - [ ] RSS 源不可用时显示友好消息 | ||
| - [ ] RSS 格式错误时有降级处理 | ||
| - [ ] 加载状态正确显示 | ||
|
|
||
| --- | ||
|
|
||
| ### T5.3 国际化验证 | ||
|
|
||
| **优先级**: 中 | ||
| **预计工作量**: 10 分钟 | ||
| **依赖**: T5.1 | ||
|
|
||
| **描述**: | ||
| 验证国际化功能。 | ||
|
|
||
| **测试用例**: | ||
| - [ ] 中文界面翻译正确 | ||
| - [ ] 英文界面翻译正确 | ||
| - [ ] 语言切换时组件正确更新 | ||
|
|
There was a problem hiding this comment.
Mark completed tasks with - [x] once verified.
Many items appear implemented in this PR but remain unchecked.
Based on learnings: Applies to openspec/changes/*/tasks.md : Update every task in tasks.md to - [x] after all implementation work is complete and confirmed.
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 240-240: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In
`@openspec/changes/archive/2025-02-10-official-blog-rss-feed-integration/tasks.md`
around lines 49 - 381, Update the checklist items in this tasks.md to mark
completed work as done by changing "- [ ]" to "- [x]" for tasks that have been
fully implemented and verified (e.g., the T1.2, T2.1-T2.3, T3.1-T3.2, T4.1-T4.3,
T5.1-T5.3 entries and their sub-items); ensure you only flip boxes after
confirming implementation and tests, update all instances under
openspec/changes/*/tasks.md, and include a brief commit message like "mark RSS
feature tasks completed" when committing the changes.
| "dev:main": "tsc --watch", | ||
| "dev:preload": "vite build --config vite.preload.config.ts --watch", | ||
| "dev": " npm run predev && rm -rf dist && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", | ||
| "dev": "rm -rf dist && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", |
There was a problem hiding this comment.
Make the dev script cross‑platform (Windows-safe).
rm -rf will fail on Windows shells; use a Node-based delete instead.
🔧 Suggested change
- "dev": "rm -rf dist && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"",
+ "dev": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "dev": "rm -rf dist && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", | |
| "dev": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", |
🤖 Prompt for AI Agents
In `@package.json` at line 21, The dev script uses the POSIX-only rm -rf which
breaks on Windows; update the "dev" npm script to use a cross-platform deletion
tool (e.g., replace rm -rf dist with rimraf dist or use shx rm -rf dist) and add
the corresponding devDependency (e.g., rimraf) so npm run dev works on Windows,
keeping the rest of the concurrently command and the referenced scripts
("dev:renderer", "dev:main", "dev:preload") unchanged.
| > | ||
| <div | ||
| onClick={() => handleArticleClick(item.link)} | ||
| className="p-3 rounded-lg border border-border/50 hover:border-primary/50 hover:bg-accent/5 transition-all cursor-pointer" | ||
| > |
There was a problem hiding this comment.
Make article tiles keyboard-accessible.
The clickable <div> only supports mouse input, so keyboard users can’t open articles. Prefer a semantic <button>/<a> (or add role/tabIndex/keydown handling).
✅ Suggested fix (semantic button)
- <div
- onClick={() => handleArticleClick(item.link)}
- className="p-3 rounded-lg border border-border/50 hover:border-primary/50 hover:bg-accent/5 transition-all cursor-pointer"
- >
+ <button
+ type="button"
+ onClick={() => handleArticleClick(item.link)}
+ className="w-full text-left p-3 rounded-lg border border-border/50 hover:border-primary/50 hover:bg-accent/5 transition-all cursor-pointer"
+ >
<div className="flex items-start justify-between gap-3">
<div className="flex-1 min-w-0">
<h4 className="text-sm font-medium text-foreground group-hover:text-primary transition-colors line-clamp-2">
{item.title}
</h4>
@@
</div>
- </div>
+ </button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| > | |
| <div | |
| onClick={() => handleArticleClick(item.link)} | |
| className="p-3 rounded-lg border border-border/50 hover:border-primary/50 hover:bg-accent/5 transition-all cursor-pointer" | |
| > | |
| > | |
| <button | |
| type="button" | |
| onClick={() => handleArticleClick(item.link)} | |
| className="w-full text-left p-3 rounded-lg border border-border/50 hover:border-primary/50 hover:bg-accent/5 transition-all cursor-pointer" | |
| > |
🤖 Prompt for AI Agents
In `@src/renderer/components/BlogFeedCard.tsx` around lines 176 - 180, The article
tile uses a clickable div which is not keyboard-accessible; update the
BlogFeedCard so the interactive container uses a semantic element (prefer an <a>
or <button>) or at minimum adds role, tabIndex and key event handling; replace
the clickable <div> that calls handleArticleClick(item.link) with a semantic
element (e.g., an anchor linking to item.link or a button that calls
handleArticleClick) and ensure focus styles/hover classes are preserved and
Enter/Space key activation is supported if you choose a non-semantic element;
reference the interactive container around handleArticleClick and the item.link
usage when making the change.
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added official blog RSS feed integration displaying the latest blog posts within the app * Automatic feed refresh every 24 hours with manual refresh capability * Offline caching for continuous access to blog posts * Multi-language support for the blog feed interface (English and Chinese) <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary by CodeRabbit