From 663d532f7e58f7f3d676362c69cc026a0b47519b Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 20:08:58 +0800 Subject: [PATCH 1/9] Load more content automatically in the card view --- src/components/feeds/cards-feed.tsx | 66 +++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/components/feeds/cards-feed.tsx b/src/components/feeds/cards-feed.tsx index 9e9241f5..c5b2a3ad 100644 --- a/src/components/feeds/cards-feed.tsx +++ b/src/components/feeds/cards-feed.tsx @@ -9,6 +9,7 @@ import { List, AnimationClassNames } from "@fluentui/react" class CardsFeed extends React.Component { observer: ResizeObserver state = { width: window.innerWidth, height: window.innerHeight } + pollTimer: NodeJS.Timeout | null = null updateWindowSize = (entries: ResizeObserverEntry[]) => { if (entries) { @@ -25,9 +26,42 @@ class CardsFeed extends React.Component { }) this.observer = new ResizeObserver(this.updateWindowSize) this.observer.observe(document.querySelector(".main")) + this.bindScrollEvent() } + + componentDidUpdate(prevProps: FeedProps) { + this.bindScrollEvent() + } + componentWillUnmount() { this.observer.disconnect() + this.unbindScrollEvent() + } + + bindScrollEvent = () => { + this.unbindScrollEvent() + this.pollTimer = setInterval(() => { + const container = document.getElementById("refocus") + if (container) { + container.addEventListener( + "scroll", + this.handleAutoLoadByScroll + ) + clearInterval(this.pollTimer!) + this.pollTimer = null + } + }, 100) + } + + unbindScrollEvent = () => { + if (this.pollTimer) { + clearInterval(this.pollTimer) + this.pollTimer = null + } + const container = document.getElementById("refocus") + if (container) { + container.removeEventListener("scroll", this.handleAutoLoadByScroll) + } } getItemCountForPage = () => { @@ -67,15 +101,41 @@ class CardsFeed extends React.Component { if (el.id === "load-more") { const container = document.getElementById("refocus") const result = + container && container.scrollTop > - container.scrollHeight - 2 * container.offsetHeight - if (!result) container.scrollTop += 100 - return result + container.scrollHeight - 2 * container.offsetHeight + if (container && !result) container.scrollTop += 100 + return result || false } else { return true } } + autoTriggerLoadMore = () => { + const loadMoreBtn = document.getElementById( + "load-more" + ) as HTMLButtonElement + if ( + loadMoreBtn && + !loadMoreBtn.disabled && + !this.props.feed.allLoaded + ) { + loadMoreBtn.click() + } + } + + // 滚动触底自动加载 + handleAutoLoadByScroll = () => { + const container = document.getElementById("refocus") + if (!container) return + const isBottom = + container.scrollHeight - + container.scrollTop - + container.clientHeight < + 200 + if (isBottom) this.autoTriggerLoadMore() + } + render() { return ( this.props.feed.loaded && ( From 89ee5934eac41dbe5afbce6047dace83b94ba99b Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 20:51:07 +0800 Subject: [PATCH 2/9] add subscription counter --- src/components/menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/menu.tsx b/src/components/menu.tsx index a95532a2..57cc83bf 100644 --- a/src/components/menu.tsx +++ b/src/components/menu.tsx @@ -62,7 +62,7 @@ export class Menu extends React.Component { ], }, { - name: intl.get("menu.subscriptions"), + name: intl.get("menu.subscriptions") + (` (${this.props.groups.length})`), links: this.props.groups .filter(g => g.sids.length > 0) .map(g => { From 64cfdd26ff1d488a301e1f32fdc9520902638465 Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 20:53:08 +0800 Subject: [PATCH 3/9] fix: prevent source duplication --- src/components/settings/sources.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/settings/sources.tsx b/src/components/settings/sources.tsx index 2440a35f..6b7a47ec 100644 --- a/src/components/settings/sources.tsx +++ b/src/components/settings/sources.tsx @@ -204,7 +204,8 @@ class SourcesTab extends React.Component { addSource = (event: React.FormEvent) => { event.preventDefault() - let trimmed = this.state.newUrl.trim() + // Remove whitespace and trailing slash + const trimmed = this.state.newUrl.trim().replace(/\/$/, '') if (urlTest(trimmed)) this.props.addSource(trimmed) } From 27833639d84b0285074304973f9bbaba75eab953 Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 20:57:47 +0800 Subject: [PATCH 4/9] feat: support fetch favicon from origin URL --- src/scripts/models/source.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/scripts/models/source.ts b/src/scripts/models/source.ts index 9c858a23..ee0f7e44 100644 --- a/src/scripts/models/source.ts +++ b/src/scripts/models/source.ts @@ -37,6 +37,7 @@ export const enum SourceTextDirection { export class RSSSource { sid: number url: string + originUrl?: string iconurl?: string name: string openTarget: SourceOpenTarget @@ -60,6 +61,7 @@ export class RSSSource { static async fetchMetaData(source: RSSSource) { let feed = await parseRSS(source.url) + source.originUrl = feed.link if (!source.name) { if (feed.title) source.name = feed.title.trim() source.name = source.name || intl.get("sources.untitled") @@ -426,8 +428,8 @@ export function updateFavicon( sids = sids.filter(sid => sid in initSources) } const promises = sids.map(async sid => { - const url = initSources[sid].url - let favicon = (await fetchFavicon(url)) || "" + const { originUrl, url }= initSources[sid] + let favicon = (await fetchFavicon(originUrl || url)) || "" const source = getState().sources[sid] if ( source && From b1d0ce316eb139bc2990440cf3f916ed299ed2ea Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 22:10:45 +0800 Subject: [PATCH 5/9] Add the package command for win x64 --- package-lock.json | 302 ++++++++++++++++++++++++++++++++++++++-------- package.json | 4 + 2 files changed, 258 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05bec446..3625cc24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "version": "1.1.4", "license": "BSD-3-Clause", "dependencies": { + "@formatjs/icu-messageformat-parser": "^3.2.0", + "@formatjs/intl-numberformat": "^9.1.0", "node-polyfill-webpack-plugin": "^2.0.1" }, "devDependencies": { + "@electron/osx-sign": "^2.3.0", "@fluentui/react": "^7.126.2", "@types/lovefield": "^2.1.3", "@types/nedb": "^1.8.9", @@ -110,6 +113,26 @@ "semver": "bin/semver.js" } }, + "node_modules/@electron/osx-sign": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-2.3.0.tgz", + "integrity": "sha512-qh/BkWUHITP2W1grtCWn8Pm4EML3q1Rfo2tnd/KHo+f1le5gAYotBc6yEfK6X2VHyN21mPZ3CjvOiYOwjimBlA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.3.4", + "isbinaryfile": "^4.0.8", + "plist": "^3.0.5", + "semver": "^7.7.1" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.mjs", + "electron-osx-sign": "bin/electron-osx-sign.mjs" + }, + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/@electron/universal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", @@ -220,6 +243,106 @@ "tslib": "^1.10.0" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@formatjs/ecma402-abstract/-/ecma402-abstract-3.0.6.tgz", + "integrity": "sha512-fIOujvbua2wbKk8et+ftvUvRiW8RQDAwix7ZZNjq7GbCh0npeqsLpRyBmDQ2w3tLT9PwcBwDepfJukByz8nbSA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "3.0.1", + "@formatjs/intl-localematcher": "0.7.3", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@formatjs/fast-memoize": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@formatjs/fast-memoize/-/fast-memoize-3.0.1.tgz", + "integrity": "sha512-kzk635kEmsxrrEWQXY7uKRocFCVXR4es5OQqcqCGg2NPtQztG/OBkE9THHu6UOTxpfyIkZhh6DjPBZGRp7y3og==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.2.0.tgz", + "integrity": "sha512-igWyeCLk6QzBQ4UXdAaMXxJ9Yhc7PC57Xjy9swGrGgqIQl0NlNp9ZBwifL1BHfqB9HBJxUbyc4jHr4wDcbHmnw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "3.0.6", + "@formatjs/icu-skeleton-parser": "2.0.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.0.6.tgz", + "integrity": "sha512-ukF8ZQc4KEwgXOxhyg8ZvGjfNjvsxPkO5H+2uqeT/GFBqj/72tvfLHxsSxz/DVJkX1y4Bh95hhORbjfVDuNx8g==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "3.0.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.7.3", + "resolved": "https://registry.npmmirror.com/@formatjs/intl-localematcher/-/intl-localematcher-0.7.3.tgz", + "integrity": "sha512-NaeABectKdTCOnlH9VFGmMS3K0JuR7Soc2t5R2MCkBrM3H/hlKVYh0XSrcjjPkbjIdrF7L/Bzx9JtGuVaSfYlA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "3.0.1", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@formatjs/intl-numberformat": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/@formatjs/intl-numberformat/-/intl-numberformat-9.1.0.tgz", + "integrity": "sha512-UxII2Wm7Ck8Mvzz6QszGn70uVQ4vcj5FO9ONj/TPOyQ/biLg1Zu8+PAhiH4NM/4YrDFfTz6v/Cl0MKYBbZf89w==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "3.0.6", + "@formatjs/intl-localematcher": "0.7.3", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-numberformat/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@formatjs/intl-unified-numberformat": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz", @@ -2193,6 +2316,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -5877,13 +6006,11 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5898,24 +6025,6 @@ "dev": true, "optional": true }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -6999,6 +7108,18 @@ } } }, + "@electron/osx-sign": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-2.3.0.tgz", + "integrity": "sha512-qh/BkWUHITP2W1grtCWn8Pm4EML3q1Rfo2tnd/KHo+f1le5gAYotBc6yEfK6X2VHyN21mPZ3CjvOiYOwjimBlA==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "isbinaryfile": "^4.0.8", + "plist": "^3.0.5", + "semver": "^7.7.1" + } + }, "@electron/universal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", @@ -7100,6 +7221,106 @@ "tslib": "^1.10.0" } }, + "@formatjs/ecma402-abstract": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@formatjs/ecma402-abstract/-/ecma402-abstract-3.0.6.tgz", + "integrity": "sha512-fIOujvbua2wbKk8et+ftvUvRiW8RQDAwix7ZZNjq7GbCh0npeqsLpRyBmDQ2w3tLT9PwcBwDepfJukByz8nbSA==", + "requires": { + "@formatjs/fast-memoize": "3.0.1", + "@formatjs/intl-localematcher": "0.7.3", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@formatjs/fast-memoize": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@formatjs/fast-memoize/-/fast-memoize-3.0.1.tgz", + "integrity": "sha512-kzk635kEmsxrrEWQXY7uKRocFCVXR4es5OQqcqCGg2NPtQztG/OBkE9THHu6UOTxpfyIkZhh6DjPBZGRp7y3og==", + "requires": { + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.2.0.tgz", + "integrity": "sha512-igWyeCLk6QzBQ4UXdAaMXxJ9Yhc7PC57Xjy9swGrGgqIQl0NlNp9ZBwifL1BHfqB9HBJxUbyc4jHr4wDcbHmnw==", + "requires": { + "@formatjs/ecma402-abstract": "3.0.6", + "@formatjs/icu-skeleton-parser": "2.0.6", + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.0.6.tgz", + "integrity": "sha512-ukF8ZQc4KEwgXOxhyg8ZvGjfNjvsxPkO5H+2uqeT/GFBqj/72tvfLHxsSxz/DVJkX1y4Bh95hhORbjfVDuNx8g==", + "requires": { + "@formatjs/ecma402-abstract": "3.0.6", + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@formatjs/intl-localematcher": { + "version": "0.7.3", + "resolved": "https://registry.npmmirror.com/@formatjs/intl-localematcher/-/intl-localematcher-0.7.3.tgz", + "integrity": "sha512-NaeABectKdTCOnlH9VFGmMS3K0JuR7Soc2t5R2MCkBrM3H/hlKVYh0XSrcjjPkbjIdrF7L/Bzx9JtGuVaSfYlA==", + "requires": { + "@formatjs/fast-memoize": "3.0.1", + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@formatjs/intl-numberformat": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/@formatjs/intl-numberformat/-/intl-numberformat-9.1.0.tgz", + "integrity": "sha512-UxII2Wm7Ck8Mvzz6QszGn70uVQ4vcj5FO9ONj/TPOyQ/biLg1Zu8+PAhiH4NM/4YrDFfTz6v/Cl0MKYBbZf89w==", + "requires": { + "@formatjs/ecma402-abstract": "3.0.6", + "@formatjs/intl-localematcher": "0.7.3", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, "@formatjs/intl-unified-numberformat": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz", @@ -8694,6 +8915,11 @@ "ms": "2.1.2" } }, + "decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==" + }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -11552,30 +11778,10 @@ } }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } + "version": "7.7.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true }, "semver-compare": { "version": "1.0.0", diff --git a/package.json b/package.json index fed52dc8..47fa4aa2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start": "npm run build && npm run electron", "format": "prettier --write .", "package-win": "electron-builder -w appx:x64 && electron-builder -w appx:ia32 && electron-builder -w appx:arm64", + "package-win-x64": "electron-builder -w appx:x64", "package-win-ci": "electron-builder -w --x64 -p never && electron-builder -w --ia32 -p never", "package-mac": "electron-builder --mac --x64", "package-mas": "bash build/resignAndPackage.sh", @@ -19,6 +20,7 @@ "license": "BSD-3-Clause", "repository": "github:yang991178/fluent-reader", "devDependencies": { + "@electron/osx-sign": "^2.3.0", "@fluentui/react": "^7.126.2", "@types/lovefield": "^2.1.3", "@types/nedb": "^1.8.9", @@ -52,6 +54,8 @@ "webpack-cli": "^5.1.4" }, "dependencies": { + "@formatjs/icu-messageformat-parser": "^3.2.0", + "@formatjs/intl-numberformat": "^9.1.0", "node-polyfill-webpack-plugin": "^2.0.1" } } From 086bd5c6728c5e0e4a5977e56fe966f09b1649ea Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 22:11:25 +0800 Subject: [PATCH 6/9] Add a shortcut key that directly marks all as read --- src/components/nav.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/nav.tsx b/src/components/nav.tsx index dcb4c8f7..b54ea0e5 100644 --- a/src/components/nav.tsx +++ b/src/components/nav.tsx @@ -5,6 +5,8 @@ import { AppState } from "../scripts/models/app" import { ProgressIndicator, IObjectWithKey } from "@fluentui/react" import { getWindowBreakpoint } from "../scripts/utils" import { WindowStateListenerType } from "../schema-types" +import { markAllRead } from "../scripts/models/item" +import { rootStore } from "../scripts/reducer" type NavProps = { state: AppState @@ -66,6 +68,9 @@ class Nav extends React.Component { case "F2": this.props.search() break + case "F4": + rootStore.dispatch(markAllRead()) + break case "F5": this.fetch() break From 6641e4d9783e4fbfcea1f23ebd2bddfd6339bcec Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 22:31:45 +0800 Subject: [PATCH 7/9] Update the source url --- src/components/settings/about.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/settings/about.tsx b/src/components/settings/about.tsx index 8eeb1290..5760ef70 100644 --- a/src/components/settings/about.tsx +++ b/src/components/settings/about.tsx @@ -22,7 +22,7 @@ class AboutTab extends React.Component { window.utils.openExternal( - "https://github.com/yang991178/fluent-reader/wiki/Support#keyboard-shortcuts" + "https://github.com/HaleShaw/fluent-reader/wiki/Support#keyboard-shortcuts" ) }> {intl.get("settings.shortcuts")} @@ -32,7 +32,7 @@ class AboutTab extends React.Component { window.utils.openExternal( - "https://github.com/yang991178/fluent-reader" + "https://github.com/HaleShaw/fluent-reader" ) }> {intl.get("settings.openSource")} @@ -42,7 +42,7 @@ class AboutTab extends React.Component { window.utils.openExternal( - "https://github.com/yang991178/fluent-reader/issues" + "https://github.com/HaleShaw/fluent-reader/issues" ) }> {intl.get("settings.feedback")} From 5c1f4c313c71c78bf299d5b4f4bc3c61c99fdf87 Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 22:37:10 +0800 Subject: [PATCH 8/9] update the readme --- README.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b1b7c869..c44743b4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@

- +

Fluent Reader

A modern desktop RSS reader

- - - + + +


## Download -For Windows 10 users, the recommended way of installation is through [Microsoft Store](https://www.microsoft.com/store/apps/9P71FC94LRH8?cid=github). -This enables auto-update and experimental ARM64 support. +For Windows 10 users, the recommended way of installation is through [Microsoft Store](https://www.microsoft.com/store/apps/9P71FC94LRH8?cid=github). +This enables auto-update and experimental ARM64 support. macOS users can also get Fluent Reader from the [Mac App Store](https://apps.apple.com/app/id1520907427). -If you are using Linux or an older version of Windows, you can [get Fluent Reader from GitHub releases](https://github.com/yang991178/fluent-reader/releases). +If you are using Linux or an older version of Windows, you can [get Fluent Reader from GitHub releases](https://github.com/HaleShaw/fluent-reader/releases). ### Mobile App @@ -25,7 +25,7 @@ The repo of the mobile version of this app [can be found here](https://github.co ## Features

- +

- A modern UI inspired by Fluent Design System with full dark mode support. @@ -35,24 +35,25 @@ The repo of the mobile version of this app [can be found here](https://github.co - Read the full content with the built-in article view or load webpages by default. - Search for articles with regular expressions or filter by read status. - Organize your subscriptions with folder-like groupings. -- Single-key [keyboard shortcuts](https://github.com/yang991178/fluent-reader/wiki/Support#keyboard-shortcuts). +- Single-key [keyboard shortcuts](https://github.com/HaleShaw/fluent-reader/wiki/Support#keyboard-shortcuts). - Hide, mark as read, or star articles automatically as they arrive with regular expression rules. - Fetch articles in the background and send push notifications. -Support for other RSS services are [under fundraising](https://github.com/yang991178/fluent-reader/issues/23). +Support for other RSS services are [under fundraising](https://github.com/HaleShaw/fluent-reader/issues/23). ## Development ### Contribute -Help make Fluent Reader better by reporting bugs or opening feature requests through [GitHub issues](https://github.com/yang991178/fluent-reader/issues). +Help make Fluent Reader better by reporting bugs or opening feature requests through [GitHub issues](https://github.com/HaleShaw/fluent-reader/issues). -You can also help internationalize the app by providing [translations into additional languages](https://github.com/yang991178/fluent-reader/tree/master/src/scripts/i18n). -Refer to the repo of [react-intl-universal](https://github.com/alibaba/react-intl-universal) to get started on internationalization. +You can also help internationalize the app by providing [translations into additional languages](https://github.com/HaleShaw/fluent-reader/tree/master/src/scripts/i18n). +Refer to the repo of [react-intl-universal](https://github.com/alibaba/react-intl-universal) to get started on internationalization. -If you enjoy using this app, consider supporting its development by donating through [GitHub Sponsors](https://github.com/sponsors/yang991178), [Paypal](https://www.paypal.me/yang991178), or [Alipay](https://hyliu.me/fluent-reader/imgs/alipay.jpg). +If you enjoy using this app, consider supporting its development by donating through [GitHub Sponsors](https://github.com/sponsors/HaleShaw), [Paypal](https://www.paypal.me/yang991178), or [Alipay](https://hyliu.me/fluent-reader/imgs/alipay.jpg). ### Build from source + ```bash # Install dependencies npm install @@ -65,9 +66,13 @@ npm run electron # Generate certificate for signature electron-builder create-self-signed-cert -# Package the app for Windows + +# Package the app for Windows: x64, x32, arm64 npm run package-win +# Package the app for Windows x64 +npm run package-win-x64 + ``` ### Developed with From 49b866cf3beb9d41cdb89b40605c8f0e002b8a1b Mon Sep 17 00:00:00 2001 From: HaleShaw Date: Thu, 1 Jan 2026 22:41:04 +0800 Subject: [PATCH 9/9] update the readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c44743b4..dc472922 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@

Fluent Reader

A modern desktop RSS reader

- - - + + +


@@ -39,7 +39,7 @@ The repo of the mobile version of this app [can be found here](https://github.co - Hide, mark as read, or star articles automatically as they arrive with regular expression rules. - Fetch articles in the background and send push notifications. -Support for other RSS services are [under fundraising](https://github.com/HaleShaw/fluent-reader/issues/23). +Support for other RSS services are [under fundraising](https://github.com/yang991178/fluent-reader/issues/23). ## Development