diff --git a/README.md b/README.md
index b1b7c869..dc472922 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
Fluent Reader
A modern desktop RSS reader
@@ -12,11 +12,11 @@
## 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/yang991178/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
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"
}
}
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 && (
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 => {
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
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")}
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)
}
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 &&