diff --git a/.github/workflows/release-main.yml b/.github/workflows/release-main.yml index c7663b0..90e8a1a 100644 --- a/.github/workflows/release-main.yml +++ b/.github/workflows/release-main.yml @@ -32,7 +32,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} - release_name: Fluent Reader v${{ steps.package-version.outputs.current-version }} Beta + release_name: Fluent Reader v${{ steps.package-version.outputs.current-version }} draft: true prerelease: false diff --git a/package-lock.json b/package-lock.json index 0851e42..bfbb1dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fluent-reader", - "version": "0.9.1", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c68f581..a8c15f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fluent-reader", - "version": "0.9.1", + "version": "1.0.0", "description": "Modern desktop RSS reader", "main": "./dist/electron.js", "scripts": { diff --git a/src/components/context-menu.tsx b/src/components/context-menu.tsx index 4fae212..b92c574 100644 --- a/src/components/context-menu.tsx +++ b/src/components/context-menu.tsx @@ -40,7 +40,7 @@ export const shareSubmenu = (item: RSSItem): IContextualMenuItem[] => [ { key: "qr", url: item.link, onRender: renderShareQR } ] -const renderShareQR = (item: IContextualMenuItem) => ( +export const renderShareQR = (item: IContextualMenuItem) => (
+ { this.state.existing && } } diff --git a/src/components/settings/services/fever.tsx b/src/components/settings/services/fever.tsx index 9cc9144..da42b36 100644 --- a/src/components/settings/services/fever.tsx +++ b/src/components/settings/services/fever.tsx @@ -7,6 +7,7 @@ import { SyncService } from "../../../schema-types" import { Stack, Icon, Label, TextField, PrimaryButton, DefaultButton, Checkbox, MessageBar, MessageBarType, Dropdown, IDropdownOption } from "@fluentui/react" import DangerButton from "../../utils/danger-button" import { urlTest } from "../../../scripts/utils" +import LiteExporter from "./lite-exporter" type FeverConfigsTabState = { existing: boolean @@ -173,6 +174,7 @@ class FeverConfigsTab extends React.Component + { this.state.existing && } } diff --git a/src/components/settings/services/greader.tsx b/src/components/settings/services/greader.tsx index 8300e6a..8df9754 100644 --- a/src/components/settings/services/greader.tsx +++ b/src/components/settings/services/greader.tsx @@ -6,6 +6,7 @@ import { SyncService } from "../../../schema-types" import { Stack, Icon, Label, TextField, PrimaryButton, DefaultButton, Checkbox, MessageBar, MessageBarType, Dropdown, IDropdownOption } from "@fluentui/react" import DangerButton from "../../utils/danger-button" import { urlTest } from "../../../scripts/utils" +import LiteExporter from "./lite-exporter" type GReaderConfigsTabState = { existing: boolean @@ -173,6 +174,7 @@ class GReaderConfigsTab extends React.Component + { this.state.existing && } } diff --git a/src/components/settings/services/inoreader.tsx b/src/components/settings/services/inoreader.tsx index a58136f..89ff723 100644 --- a/src/components/settings/services/inoreader.tsx +++ b/src/components/settings/services/inoreader.tsx @@ -6,6 +6,7 @@ import { SyncService } from "../../../schema-types" import { Stack, Label, TextField, PrimaryButton, DefaultButton, Checkbox, MessageBar, MessageBarType, Dropdown, IDropdownOption, MessageBarButton, Link } from "@fluentui/react" import DangerButton from "../../utils/danger-button" +import LiteExporter from "./lite-exporter" type GReaderConfigsTabState = { existing: boolean @@ -225,6 +226,7 @@ class InoreaderConfigsTab extends React.Component + { this.state.existing && } } diff --git a/src/components/settings/services/lite-exporter.tsx b/src/components/settings/services/lite-exporter.tsx new file mode 100644 index 0000000..9aebaf3 --- /dev/null +++ b/src/components/settings/services/lite-exporter.tsx @@ -0,0 +1,73 @@ +import * as React from "react" +import intl from "react-intl-universal" +import { Stack, ContextualMenuItemType, DefaultButton, IContextualMenuProps, DirectionalHint } from "@fluentui/react" +import { ServiceConfigs, SyncService } from "../../../schema-types" +import { renderShareQR } from "../../context-menu" +import { platformCtrl } from "../../../scripts/utils" +import { FeverConfigs } from "../../../scripts/models/services/fever" +import { GReaderConfigs } from "../../../scripts/models/services/greader" +import { FeedbinConfigs } from "../../../scripts/models/services/feedbin" + +type LiteExporterProps = { + serviceConfigs: ServiceConfigs +} + +const LEARN_MORE_URL = "https://github.com/yang991178/fluent-reader/wiki/Support#mobile-app" + +const LiteExporter: React.FunctionComponent = (props) => { + let url = "https://hyliu.me/fr2l/?" + const params = new URLSearchParams() + switch (props.serviceConfigs.type) { + case SyncService.Fever: { + const configs = props.serviceConfigs as FeverConfigs + params.set("t", "f") + params.set("e", configs.endpoint) + params.set("u", configs.username) + params.set("k", configs.apiKey) + break + } + case SyncService.GReader: + case SyncService.Inoreader: { + const configs = props.serviceConfigs as GReaderConfigs + params.set("t", configs.type == SyncService.GReader ? "g" : "i") + params.set("e", configs.endpoint) + params.set("u", configs.username) + params.set("p", btoa(configs.password)) + if (configs.inoreaderId) { + params.set("i", configs.inoreaderId) + params.set("k", configs.inoreaderKey) + } + break + } + case SyncService.Feedbin: { + const configs = props.serviceConfigs as FeedbinConfigs + params.set("t", "fb") + params.set("e", configs.endpoint) + params.set("u", configs.username) + params.set("p", btoa(configs.password)) + break + } + } + url += params.toString() + const menuProps: IContextualMenuProps = { + directionalHint: DirectionalHint.bottomCenter, + items: [ + { key: "qr", url: url, onRender: renderShareQR }, + { key: "divider_1", itemType: ContextualMenuItemType.Divider }, + { + key: "openInBrowser", + text: intl.get("rules.help"), + iconProps: { iconName: "NavigateExternalInline" }, + onClick: e => { window.utils.openExternal(LEARN_MORE_URL, platformCtrl(e)) } + }, + ] + } + return + <>} + menuProps={menuProps} /> + +} + +export default LiteExporter \ No newline at end of file diff --git a/src/scripts/i18n/en-US.json b/src/scripts/i18n/en-US.json index 54a47b9..67b7841 100644 --- a/src/scripts/i18n/en-US.json +++ b/src/scripts/i18n/en-US.json @@ -202,7 +202,8 @@ "importGroups": "Import groups", "failure": "Cannot connect to service", "failureHint": "Please check the service configuration or network status.", - "fetchUnlimited": "Unlimited (not recommended)" + "fetchUnlimited": "Unlimited (not recommended)", + "exportToLite": "Export to Fluent Reader Lite" }, "app": { "cleanup": "Clean up", diff --git a/src/scripts/i18n/zh-CN.json b/src/scripts/i18n/zh-CN.json index c61e1f4..b1ba45d 100644 --- a/src/scripts/i18n/zh-CN.json +++ b/src/scripts/i18n/zh-CN.json @@ -200,7 +200,8 @@ "importGroups": "导入分组", "failure": "连接到服务时出错", "failureHint": "请检查服务配置或网络连接", - "fetchUnlimited": "无限制(不建议)" + "fetchUnlimited": "无限制(不建议)", + "exportToLite": "导出至 Fluent Reader Lite" }, "app": { "cleanup": "清理", diff --git a/src/scripts/i18n/zh-TW.json b/src/scripts/i18n/zh-TW.json index 2c3b949..74d49d1 100644 --- a/src/scripts/i18n/zh-TW.json +++ b/src/scripts/i18n/zh-TW.json @@ -200,7 +200,8 @@ "importGroups": "匯入分組", "failure": "連線到服務時出錯", "failureHint": "請檢查服務配置或網路連線", - "fetchUnlimited": "無限制(不建議)" + "fetchUnlimited": "無限制(不建議)", + "exportToLite": "匯出至 Fluent Reader Lite" }, "app": { "cleanup": "清理", diff --git a/src/scripts/settings.ts b/src/scripts/settings.ts index 9e74b36..d2970d4 100644 --- a/src/scripts/settings.ts +++ b/src/scripts/settings.ts @@ -94,7 +94,7 @@ export async function importAll() { let requests = Object.entries(configs.nedb).map(([key, value]) => { return objectStore.put(value, key) }) - let promises = requests.map(req => new Promise((resolve, reject) => { + let promises = requests.map(req => new Promise((resolve, reject) => { req.onsuccess = () => resolve() req.onerror = () => reject() })) diff --git a/src/scripts/utils.ts b/src/scripts/utils.ts index 91400e8..e594aa7 100644 --- a/src/scripts/utils.ts +++ b/src/scripts/utils.ts @@ -29,7 +29,7 @@ const rssParser = new Parser({ }) const CHARSET_RE = /charset=([^()<>@,;:\"/[\]?.=\s]*)/i -const XML_ENCODING_RE = /^<\?xml.+encoding="(.+)".*?\?>/i +const XML_ENCODING_RE = /^<\?xml.+encoding="(.+?)".*?\?>/i export async function decodeFetchResponse(response: Response, isHTML = false) { const buffer = await response.arrayBuffer() let ctype = response.headers.has("content-type") && response.headers.get("content-type") @@ -122,7 +122,7 @@ export function htmlDecode(input: string) { } export const urlTest = (s: string) => - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi.test(s) + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi.test(s) export const getWindowBreakpoint = () => window.outerWidth >= 1440