fetch frequency limit
This commit is contained in:
parent
66d4d8ac45
commit
5cc25dea02
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react"
|
||||
import intl = require("react-intl-universal")
|
||||
import { Label, DefaultButton, TextField, Stack, PrimaryButton, DetailsList,
|
||||
IColumn, SelectionMode, Selection, IChoiceGroupOption, ChoiceGroup } from "@fluentui/react"
|
||||
IColumn, SelectionMode, Selection, IChoiceGroupOption, ChoiceGroup, IDropdownOption, Dropdown } from "@fluentui/react"
|
||||
import { SourceState, RSSSource, SourceOpenTarget } from "../../scripts/models/source"
|
||||
import { urlTest } from "../../scripts/utils"
|
||||
import DangerButton from "../utils/danger-button"
|
||||
|
@ -11,6 +11,7 @@ type SourcesTabProps = {
|
|||
addSource: (url: string) => void
|
||||
updateSourceName: (source: RSSSource, name: string) => void
|
||||
updateSourceOpenTarget: (source: RSSSource, target: SourceOpenTarget) => void
|
||||
updateFetchFrequency: (source: RSSSource, frequency: number) => void
|
||||
deleteSource: (source: RSSSource) => void
|
||||
importOPML: () => void
|
||||
exportOPML: () => void
|
||||
|
@ -74,6 +75,24 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||
}
|
||||
]
|
||||
|
||||
fetchFrequencyOptions = (): IDropdownOption[] => [
|
||||
{ key: "0", text: intl.get("sources.unlimited") },
|
||||
{ key: "15", text: intl.get("time.m", { m: 15 }) },
|
||||
{ key: "30", text: intl.get("time.m", { m: 30 }) },
|
||||
{ key: "60", text: intl.get("time.h", { h: 1 }) },
|
||||
{ key: "120", text: intl.get("time.h", { h: 2 }) },
|
||||
{ key: "180", text: intl.get("time.h", { h: 3 }) },
|
||||
{ key: "360", text: intl.get("time.h", { h: 6 }) },
|
||||
{ key: "720", text: intl.get("time.h", { h: 12 }) },
|
||||
{ key: "1440", text: intl.get("time.d", { d: 1 }) }
|
||||
]
|
||||
|
||||
onFetchFrequencyChange = (_, option: IDropdownOption) => {
|
||||
let frequency = parseInt(option.key as string)
|
||||
this.props.updateFetchFrequency(this.state.selectedSource, frequency)
|
||||
this.setState({selectedSource: {...this.state.selectedSource, fetchFrequency: frequency} as RSSSource})
|
||||
}
|
||||
|
||||
sourceOpenTargetChoices = (): IChoiceGroupOption[] => [
|
||||
{ key: String(SourceOpenTarget.Local), text: intl.get("sources.rssText") },
|
||||
{ key: String(SourceOpenTarget.Webpage), text: intl.get("sources.loadWebpage") },
|
||||
|
@ -157,6 +176,16 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||
text={intl.get("sources.editName")} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<Label>{intl.get("sources.fetchFrequency")}</Label>
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<Dropdown
|
||||
options={this.fetchFrequencyOptions()}
|
||||
selectedKey={this.state.selectedSource.fetchFrequency ? String(this.state.selectedSource.fetchFrequency) : "0"}
|
||||
onChange={this.onFetchFrequencyChange}
|
||||
style={{width: 200}} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<ChoiceGroup
|
||||
label={intl.get("sources.openTarget")}
|
||||
options={this.sourceOpenTargetChoices()}
|
||||
|
|
|
@ -26,6 +26,9 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
|||
updateSourceOpenTarget: (source: RSSSource, target: SourceOpenTarget) => {
|
||||
dispatch(updateSource({ ...source, openTarget: target } as RSSSource))
|
||||
},
|
||||
updateFetchFrequency: (source: RSSSource, frequency: number) => {
|
||||
dispatch(updateSource({ ...source, fetchFrequency: frequency } as RSSSource))
|
||||
},
|
||||
deleteSource: (source: RSSSource) => dispatch(deleteSource(source)),
|
||||
importOPML: () => {
|
||||
remote.dialog.showOpenDialog(
|
||||
|
|
|
@ -9,10 +9,17 @@ if (!locked) {
|
|||
}
|
||||
|
||||
let mainWindow: BrowserWindow
|
||||
let store = new Store()
|
||||
let restarting = false
|
||||
performUpdate(store)
|
||||
nativeTheme.themeSource = store.get("theme", "system")
|
||||
let store: Store
|
||||
let restarting: boolean
|
||||
|
||||
function init() {
|
||||
restarting = false
|
||||
store = new Store()
|
||||
performUpdate(store)
|
||||
nativeTheme.themeSource = store.get("theme", "system")
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
function createWindow() {
|
||||
let mainWindowState = windowStateKeeper({
|
||||
|
@ -61,9 +68,7 @@ app.on("second-instance", () => {
|
|||
app.on("window-all-closed", function () {
|
||||
mainWindow = null
|
||||
if (restarting) {
|
||||
restarting = false
|
||||
store = new Store()
|
||||
nativeTheme.themeSource = store.get("theme", "system")
|
||||
init()
|
||||
createWindow()
|
||||
} else if (process.platform !== "darwin") {
|
||||
app.quit()
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
"confirmMarkAll": "Do you really want to mark all articles on this page as read?",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"time": {
|
||||
"m": "{m, plural, =1 {# minute} other {# minutes}}",
|
||||
"h": "{h, plural, =1 {# hour} other {# hours}}",
|
||||
"d": "{d, plural, =1 {# day} other {# days}}"
|
||||
},
|
||||
"log": {
|
||||
"empty": "No notifications",
|
||||
"fetchFailure": "Failed to load source \"{name}\".",
|
||||
|
@ -82,6 +87,8 @@
|
|||
"opmlFile": "OPML File",
|
||||
"name": "Source name",
|
||||
"editName": "Edit name",
|
||||
"fetchFrequency": "Fetch frequency limit",
|
||||
"unlimited": "Unlimited",
|
||||
"openTarget": "Default open target for articles",
|
||||
"delete": "Delete source",
|
||||
"add": "Add source",
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
"confirmMarkAll": "确认将本页所有文章标为已读?",
|
||||
"confirm": "确认",
|
||||
"cancel": "取消",
|
||||
"time": {
|
||||
"m": "{m}分钟",
|
||||
"h": "{h}小时",
|
||||
"d": "{d}天"
|
||||
},
|
||||
"log": {
|
||||
"empty": "无消息",
|
||||
"fetchFailure": "无法加载订阅源“{name}”",
|
||||
|
@ -82,6 +87,8 @@
|
|||
"opmlFile": "OPML文件",
|
||||
"name": "订阅源名称",
|
||||
"editName": "修改名称",
|
||||
"fetchFrequency": "抓取频率限制",
|
||||
"unlimited": "无限制",
|
||||
"openTarget": "订阅源文章打开方式",
|
||||
"delete": "删除订阅源",
|
||||
"add": "添加订阅源",
|
||||
|
|
|
@ -147,8 +147,12 @@ export function fetchItems(): AppThunk<Promise<void>> {
|
|||
return (dispatch, getState) => {
|
||||
let promises = new Array<Promise<RSSItem[]>>()
|
||||
if (!getState().app.fetchingItems) {
|
||||
for (let source of <RSSSource[]>Object.values(getState().sources)) {
|
||||
let promise = RSSSource.fetchItems(source, rssParser, db.idb)
|
||||
let timenow = new Date().getTime()
|
||||
let sources = <RSSSource[]>Object.values(getState().sources).filter(s =>
|
||||
(s.lastFetched.getTime() + (s.fetchFrequency || 0) * 60000) <= timenow
|
||||
)
|
||||
for (let source of sources) {
|
||||
let promise = RSSSource.fetchItems(source, rssParser)
|
||||
promise.finally(() => dispatch(fetchItemsIntermediate()))
|
||||
promises.push(promise)
|
||||
}
|
||||
|
|
|
@ -18,11 +18,14 @@ export class RSSSource {
|
|||
name: string
|
||||
openTarget: SourceOpenTarget
|
||||
unreadCount: number
|
||||
lastFetched: Date
|
||||
fetchFrequency?: number // in minutes
|
||||
|
||||
constructor(url: string, name: string = null) {
|
||||
this.url = url
|
||||
this.name = name
|
||||
this.openTarget = SourceOpenTarget.Local
|
||||
this.lastFetched = new Date()
|
||||
}
|
||||
|
||||
async fetchMetaData(parser: Parser) {
|
||||
|
@ -46,10 +49,10 @@ export class RSSSource {
|
|||
}
|
||||
}
|
||||
|
||||
private static checkItem(source: RSSSource, item: Parser.Item, db: Nedb<RSSItem>): Promise<RSSItem> {
|
||||
private static checkItem(source: RSSSource, item: Parser.Item): Promise<RSSItem> {
|
||||
return new Promise<RSSItem>((resolve, reject) => {
|
||||
let i = new RSSItem(item, source)
|
||||
db.findOne({
|
||||
db.idb.findOne({
|
||||
source: i.source,
|
||||
title: i.title,
|
||||
date: i.date
|
||||
|
@ -66,11 +69,11 @@ export class RSSSource {
|
|||
})
|
||||
}
|
||||
|
||||
static checkItems(source: RSSSource, items: Parser.Item[], db: Nedb<RSSItem>): Promise<RSSItem[]> {
|
||||
static checkItems(source: RSSSource, items: Parser.Item[]): Promise<RSSItem[]> {
|
||||
return new Promise<RSSItem[]>((resolve, reject) => {
|
||||
let p = new Array<Promise<RSSItem>>()
|
||||
for (let item of items) {
|
||||
p.push(this.checkItem(source, item, db))
|
||||
p.push(this.checkItem(source, item))
|
||||
}
|
||||
Promise.all(p).then(values => {
|
||||
resolve(values.filter(v => v != null))
|
||||
|
@ -78,9 +81,10 @@ export class RSSSource {
|
|||
})
|
||||
}
|
||||
|
||||
static async fetchItems(source: RSSSource, parser: Parser, db: Nedb<RSSItem>) {
|
||||
static async fetchItems(source: RSSSource, parser: Parser) {
|
||||
let feed = await parser.parseURL(source.url)
|
||||
return await this.checkItems(source, feed.items, db)
|
||||
db.sdb.update({ sid: source.sid }, { $set: { lastFetched: new Date() } })
|
||||
return await this.checkItems(source, feed.items)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +248,7 @@ export function addSource(url: string, name: string = null, batch = false): AppT
|
|||
.then(inserted => {
|
||||
inserted.unreadCount = feed.items.length
|
||||
dispatch(addSourceSuccess(inserted, batch))
|
||||
return RSSSource.checkItems(inserted, feed.items, db.idb)
|
||||
return RSSSource.checkItems(inserted, feed.items)
|
||||
.then(items => insertItems(items))
|
||||
.then(() => {
|
||||
SourceGroup.save(getState().groups)
|
||||
|
|
Loading…
Reference in New Issue