Merge pull request #152 from yang991178/0.9.1

Version 0.9.1
This commit is contained in:
Haoyuan Liu 2020-12-29 18:10:17 +08:00 committed by GitHub
commit dfed4bf3e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 19 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "fluent-reader",
"version": "0.9.0",
"version": "0.9.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "fluent-reader",
"version": "0.9.0",
"version": "0.9.1",
"description": "Modern desktop RSS reader",
"main": "./dist/electron.js",
"scripts": {
@ -68,6 +68,7 @@
"category": "public.app-category.news",
"electronLanguages": [
"zh_CN",
"zh_TW",
"en",
"fr",
"es",

View File

@ -64,6 +64,7 @@ class GReaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReaderC
endpoint: this.state.endpoint,
fetchLimit: this.state.fetchLimit
} as GReaderConfigs
if (this.state.password) configs.password = this.state.password
} else {
configs = {
type: SyncService.GReader,

View File

@ -4,7 +4,7 @@ import { ServiceConfigsTabProps } from "../service"
import { GReaderConfigs } from "../../../scripts/models/services/greader"
import { SyncService } from "../../../schema-types"
import { Stack, Label, TextField, PrimaryButton, DefaultButton, Checkbox,
MessageBar, MessageBarType, Dropdown, IDropdownOption, MessageBarButton } from "@fluentui/react"
MessageBar, MessageBarType, Dropdown, IDropdownOption, MessageBarButton, Link } from "@fluentui/react"
import DangerButton from "../../utils/danger-button"
type GReaderConfigsTabState = {
@ -12,8 +12,10 @@ type GReaderConfigsTabState = {
endpoint: string
username: string
password: string
apiId: string
apiKey: string
removeAd: boolean
fetchLimit: number
importGroups: boolean
}
const endpointOptions: IDropdownOption[] = [
@ -33,8 +35,10 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
endpoint: configs.endpoint || "https://www.inoreader.com",
username: configs.username || "",
password: "",
apiId: configs.inoreaderId || "",
apiKey: configs.inoreaderKey || "",
removeAd: configs.removeInoreaderAd === undefined ? true : configs.removeInoreaderAd,
fetchLimit: configs.fetchLimit || 250,
importGroups: true,
}
}
@ -62,7 +66,8 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
}
validateForm = () => {
return this.state.existing || (this.state.username && this.state.password)
return (this.state.existing || (this.state.username && this.state.password))
&& this.state.apiId && this.state.apiKey
}
save = async () => {
@ -71,18 +76,25 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
configs = {
...this.props.configs,
endpoint: this.state.endpoint,
fetchLimit: this.state.fetchLimit
fetchLimit: this.state.fetchLimit,
inoreaderId: this.state.apiId,
inoreaderKey: this.state.apiKey,
removeInoreaderAd: this.state.removeAd,
} as GReaderConfigs
if (this.state.password) configs.password = this.state.password
} else {
configs = {
type: SyncService.Inoreader,
endpoint: this.state.endpoint,
username: this.state.username,
password: this.state.password,
inoreaderId: this.state.apiId,
inoreaderKey: this.state.apiKey,
removeInoreaderAd: this.state.removeAd,
fetchLimit: this.state.fetchLimit,
importGroups: true,
useInt64: true
}
if (this.state.importGroups) configs.importGroups = true
}
this.props.blockActions()
configs = await this.props.reauthenticate(configs) as GReaderConfigs
@ -97,6 +109,8 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
}
}
createKey = () => window.utils.openExternal(this.state.endpoint + "/all_articles#preferences-developer")
remove = async () => {
this.props.exit()
await this.props.remove()
@ -106,8 +120,9 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
return <>
<MessageBar messageBarType={MessageBarType.severeWarning}
isMultiline={false}
actions={<MessageBarButton text={intl.get("rules.help")} onClick={openSupport} />}>
actions={<MessageBarButton text={intl.get("create")} onClick={this.createKey} />}>
{intl.get("service.rateLimitWarning")}
<Link onClick={openSupport} style={{marginLeft: 6}}>{intl.get("rules.help")}</Link>
</MessageBar>
{!this.state.existing && (
<MessageBar messageBarType={MessageBarType.warning}>{intl.get("service.overwriteWarning")}</MessageBar>
@ -155,6 +170,32 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>API ID</Label>
</Stack.Item>
<Stack.Item grow>
<TextField
onGetErrorMessage={this.checkNotEmpty}
validateOnLoad={false}
name="apiId"
value={this.state.apiId}
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>API Key</Label>
</Stack.Item>
<Stack.Item grow>
<TextField
onGetErrorMessage={this.checkNotEmpty}
validateOnLoad={false}
name="apiKey"
value={this.state.apiKey}
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>{intl.get("service.fetchLimit")}</Label>
@ -166,10 +207,10 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
onChange={this.onFetchLimitOptionChange} />
</Stack.Item>
</Stack>
{!this.state.existing && <Checkbox
label={intl.get("service.importGroups")}
checked={this.state.importGroups}
onChange={(_, c) => this.setState({importGroups: c})} />}
<Checkbox
label={intl.get("service.removeAd")}
checked={this.state.removeAd}
onChange={(_, c) => this.setState({removeAd: c})} />
<Stack horizontal style={{marginTop: 32}}>
<Stack.Item>
<PrimaryButton

View File

@ -191,7 +191,8 @@
"suggest": "Suggest a new service",
"overwriteWarning": "Local sources will be deleted if they exist in the service.",
"groupsWarning": "Groups aren't automatically synced with the service.",
"rateLimitWarning": "If connection errors persist, the app may have been rate limited by the service.",
"rateLimitWarning": "To avoid rate limiting, you need to create your own API Key.",
"removeAd": "Remove Ad",
"endpoint": "Endpoint",
"username": "Username",
"password": "Password",

View File

@ -189,7 +189,8 @@
"suggest": "建议一项新服务",
"overwriteWarning": "若本地与服务端存在URL相同的订阅源则本地订阅源将被删除",
"groupsWarning": "分组不会自动与服务端保持同步",
"rateLimitWarning": "若反复出现错误,则原因可能是应用被服务限流",
"rateLimitWarning": "为避免限流,您需要新建自己的 API Key",
"removeAd": "移除广告",
"endpoint": "端点",
"username": "用户名",
"password": "密码",

View File

@ -189,7 +189,8 @@
"suggest": "建議一項新服務",
"overwriteWarning": "若本地與服務端存在URL相同的訂閱源則本地訂閱源將被刪除",
"groupsWarning": "分組不會自動與服務端保持同步",
"rateLimitWarning": "若反覆出現錯誤,則原因可能是應用被服務限流",
"rateLimitWarning": "為避免限流,您需要新建自己的 API Key",
"removeAd": "移除廣告",
"endpoint": "端點",
"username": "使用者名稱",
"password": "密碼",

View File

@ -23,14 +23,22 @@ export interface GReaderConfigs extends ServiceConfigs {
lastId?: string
auth?: string
useInt64: boolean // The Old Reader uses ids longer than 64 bits
inoreaderId?: string
inoreaderKey?: string
removeInoreaderAd?: boolean
}
async function fetchAPI(configs: GReaderConfigs, params: string, method="GET", body:BodyInit=null) {
const headers = new Headers()
if (configs.auth !== null) headers.set("Authorization", configs.auth)
if (configs.type == SyncService.Inoreader) {
headers.set("AppId", "999999298")
headers.set("AppKey", "KPbKYXTfgrKbwmroOeYC7mcW21ZRwF5Y")
if (configs.inoreaderId) {
headers.set("AppId", configs.inoreaderId)
headers.set("AppKey", configs.inoreaderKey)
} else {
headers.set("AppId", "999999298")
headers.set("AppKey", "KPbKYXTfgrKbwmroOeYC7mcW21ZRwF5Y")
}
}
return await fetch(configs.endpoint + params, {
method: method,
@ -189,13 +197,18 @@ export const gReaderServiceHooks: ServiceHooks = {
const source = fidMap.get(i.origin.streamId)
if (source === undefined) return
const dom = domParser.parseFromString(i.summary.content, "text/html")
if (configs.type == SyncService.Inoreader && configs.removeInoreaderAd !== false) {
if (dom.documentElement.textContent.trim().startsWith("Ads from Inoreader")) {
dom.body.firstChild.remove()
}
}
const item = {
source: source.sid,
title: i.title,
link: i.canonical[0].href,
date: new Date(i.published * 1000),
fetchedDate: new Date(parseInt(i.crawlTimeMsec)),
content: i.summary.content,
content: dom.body.innerHTML,
snippet: dom.documentElement.textContent.trim(),
creator: i.author,
hasRead: false,