Skip to content

Commit

Permalink
Merge pull request #146 from yang991178/0.9.0
Browse files Browse the repository at this point in the history
Version 0.9.0
  • Loading branch information
yang991178 authored Dec 26, 2020
2 parents 71aabe6 + bbe320e commit b2f1a98
Show file tree
Hide file tree
Showing 12 changed files with 695 additions and 9 deletions.
9 changes: 4 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fluent-reader",
"version": "0.8.1",
"version": "0.9.0",
"description": "Modern desktop RSS reader",
"main": "./dist/electron.js",
"scripts": {
Expand Down
7 changes: 7 additions & 0 deletions src/components/settings/service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ServiceConfigs, SyncService } from "../../schema-types"
import { Stack, Icon, Link, Dropdown, IDropdownOption } from "@fluentui/react"
import FeverConfigsTab from "./services/fever"
import FeedbinConfigsTab from "./services/feedbin"
import GReaderConfigsTab from "./services/greader"
import InoreaderConfigsTab from "./services/inoreader"

type ServiceTabProps = {
configs: ServiceConfigs
Expand All @@ -12,6 +14,7 @@ type ServiceTabProps = {
remove: () => Promise<void>
blockActions: () => void
authenticate: (configs: ServiceConfigs) => Promise<boolean>
reauthenticate: (configs: ServiceConfigs) => Promise<ServiceConfigs>
}

export type ServiceConfigsTabProps = ServiceTabProps & {
Expand All @@ -33,6 +36,8 @@ export class ServiceTab extends React.Component<ServiceTabProps, ServiceTabState
serviceOptions = (): IDropdownOption[] => [
{ key: SyncService.Fever, text: "Fever API" },
{ key: SyncService.Feedbin, text: "Feedbin" },
{ key: SyncService.GReader, text: "Google Reader API (Beta)" },
{ key: SyncService.Inoreader, text: "Inoreader" },
{ key: -1, text: intl.get("service.suggest") },
]

Expand All @@ -52,6 +57,8 @@ export class ServiceTab extends React.Component<ServiceTabProps, ServiceTabState
switch (this.state.type) {
case SyncService.Fever: return <FeverConfigsTab {...this.props} exit={this.exitConfigsTab} />
case SyncService.Feedbin: return <FeedbinConfigsTab {...this.props} exit={this.exitConfigsTab} />
case SyncService.GReader: return <GReaderConfigsTab {...this.props} exit={this.exitConfigsTab} />
case SyncService.Inoreader: return <InoreaderConfigsTab {...this.props} exit={this.exitConfigsTab} />
default: return null
}
}
Expand Down
1 change: 0 additions & 1 deletion src/components/settings/services/fever.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ 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 { exists } from "fs"

type FeverConfigsTabState = {
existing: boolean
Expand Down
180 changes: 180 additions & 0 deletions src/components/settings/services/greader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import * as React from "react"
import intl from "react-intl-universal"
import { ServiceConfigsTabProps } from "../service"
import { GReaderConfigs } from "../../../scripts/models/services/greader"
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"

type GReaderConfigsTabState = {
existing: boolean
endpoint: string
username: string
password: string
fetchLimit: number
importGroups: boolean
}

class GReaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReaderConfigsTabState> {
constructor(props: ServiceConfigsTabProps) {
super(props)
const configs = props.configs as GReaderConfigs
this.state = {
existing: configs.type === SyncService.GReader,
endpoint: configs.endpoint || "",
username: configs.username || "",
password: "",
fetchLimit: configs.fetchLimit || 250,
importGroups: true,
}
}

fetchLimitOptions = (): IDropdownOption[] => [
{ key: 250, text: intl.get("service.fetchLimitNum", { count: 250 }) },
{ key: 500, text: intl.get("service.fetchLimitNum", { count: 500 }) },
{ key: 750, text: intl.get("service.fetchLimitNum", { count: 750 }) },
{ key: 1000, text: intl.get("service.fetchLimitNum", { count: 1000 }) },
{ key: 1500, text: intl.get("service.fetchLimitNum", { count: 1500 }) },
{ key: Number.MAX_SAFE_INTEGER, text: intl.get("service.fetchUnlimited") },
]
onFetchLimitOptionChange = (_, option: IDropdownOption) => {
this.setState({ fetchLimit: option.key as number })
}

handleInputChange = (event) => {
const name: string = event.target.name
// @ts-expect-error
this.setState({[name]: event.target.value})
}

checkNotEmpty = (v: string) => {
return (!this.state.existing && v.length == 0) ? intl.get("emptyField") : ""
}

validateForm = () => {
return urlTest(this.state.endpoint.trim()) && (this.state.existing || (this.state.username && this.state.password))
}

save = async () => {
let configs: GReaderConfigs
if (this.state.existing) {
configs = {
...this.props.configs,
endpoint: this.state.endpoint,
fetchLimit: this.state.fetchLimit
} as GReaderConfigs
} else {
configs = {
type: SyncService.GReader,
endpoint: this.state.endpoint,
username: this.state.username,
password: this.state.password,
fetchLimit: this.state.fetchLimit,
useInt64: !this.state.endpoint.endsWith("theoldreader.com")
}
if (this.state.importGroups) configs.importGroups = true
}
this.props.blockActions()
configs = await this.props.reauthenticate(configs) as GReaderConfigs
const valid = await this.props.authenticate(configs)
if (valid) {
this.props.save(configs)
this.setState({ existing: true })
this.props.sync()
} else {
this.props.blockActions()
window.utils.showErrorBox(intl.get("service.failure"), intl.get("service.failureHint"))
}
}

remove = async () => {
this.props.exit()
await this.props.remove()
}

render() {
return <>
{!this.state.existing && (
<MessageBar messageBarType={MessageBarType.warning}>{intl.get("service.overwriteWarning")}</MessageBar>
)}
<Stack horizontalAlign="center" style={{marginTop: 48}}>
<Icon iconName="Communications" style={{color: "var(--black)", transform: "rotate(220deg)", fontSize: 32, userSelect: "none"}} />
<Label style={{margin: "8px 0 36px"}}>Google Reader API</Label>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>{intl.get("service.endpoint")}</Label>
</Stack.Item>
<Stack.Item grow>
<TextField
onGetErrorMessage={v => urlTest(v.trim()) ? "" : intl.get("sources.badUrl")}
validateOnLoad={false}
name="endpoint"
value={this.state.endpoint}
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>{intl.get("service.username")}</Label>
</Stack.Item>
<Stack.Item grow>
<TextField
disabled={this.state.existing}
onGetErrorMessage={this.checkNotEmpty}
validateOnLoad={false}
name="username"
value={this.state.username}
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>{intl.get("service.password")}</Label>
</Stack.Item>
<Stack.Item grow>
<TextField
type="password"
placeholder={this.state.existing ? intl.get("service.unchanged") : ""}
onGetErrorMessage={this.checkNotEmpty}
validateOnLoad={false}
name="password"
value={this.state.password}
onChange={this.handleInputChange} />
</Stack.Item>
</Stack>
<Stack className="login-form" horizontal>
<Stack.Item>
<Label>{intl.get("service.fetchLimit")}</Label>
</Stack.Item>
<Stack.Item grow>
<Dropdown
options={this.fetchLimitOptions()}
selectedKey={this.state.fetchLimit}
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})} />}
<Stack horizontal style={{marginTop: 32}}>
<Stack.Item>
<PrimaryButton
disabled={!this.validateForm()}
onClick={this.save}
text={this.state.existing ? intl.get("edit") : intl.get("confirm")} />
</Stack.Item>
<Stack.Item>
{this.state.existing
? <DangerButton onClick={this.remove} text={intl.get("delete")} />
: <DefaultButton onClick={this.props.exit} text={intl.get("cancel")} />
}
</Stack.Item>
</Stack>
</Stack>
</>
}
}

export default GReaderConfigsTab
Loading

0 comments on commit b2f1a98

Please sign in to comment.