Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/bridges/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ const settingsBridge = {
ipcRenderer.invoke("set-fetch-interval", interval)
},

getRefreshOnStart: (): boolean => {
return ipcRenderer.sendSync("get-refresh-on-start")
},
setRefreshOnStart: (flag: boolean) => {
ipcRenderer.invoke("set-refresh-on-start", flag)
},

getSearchEngine: (): SearchEngines => {
return ipcRenderer.sendSync("get-search-engine")
},
Expand Down
17 changes: 13 additions & 4 deletions src/components/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type NavProps = {
search: () => void
markAllRead: () => void
fetch: () => void
stopFetch: () => void
logs: () => void
views: () => void
settings: () => void
Expand Down Expand Up @@ -155,10 +156,18 @@ class Nav extends React.Component<NavProps, NavState> {
<span className="title">{this.props.state.title}</span>
<div className="btn-group" style={{ float: "right" }}>
<a
className={"btn" + this.fetching()}
onClick={this.fetch}
title={intl.get("nav.refresh")}>
<Icon iconName="Refresh" />
className="btn"
onClick={
this.canFetch() ? this.fetch : this.props.stopFetch
}
title={
this.canFetch()
? intl.get("nav.refresh")
: intl.get("nav.stop")
}>
<Icon
iconName={this.canFetch() ? "Refresh" : "Cancel"}
/>
</a>
<a
className="btn"
Expand Down
9 changes: 8 additions & 1 deletion src/components/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import intl from "react-intl-universal"
import { Icon } from "@fluentui/react/lib/Icon"
import { AnimationClassNames } from "@fluentui/react/lib/Styling"
import AboutTab from "./settings/about"
import { Pivot, PivotItem, Spinner, FocusTrapZone } from "@fluentui/react"
import { Pivot, PivotItem, Spinner, FocusTrapZone, DefaultButton } from "@fluentui/react"
import SourcesTabContainer from "../containers/settings/sources-container"
import GroupsTabContainer from "../containers/settings/groups-container"
import AppTabContainer from "../containers/settings/app-container"
Expand All @@ -16,6 +16,7 @@ type SettingsProps = {
blocked: boolean
exitting: boolean
close: () => void
stopFetch: () => void
}

class Settings extends React.Component<SettingsProps> {
Expand Down Expand Up @@ -68,6 +69,12 @@ class Settings extends React.Component<SettingsProps> {
label={intl.get("settings.fetching")}
tabIndex={0}
/>
<div style={{ marginTop: 12, textAlign: "center" }}>
<DefaultButton
text="Stop"
onClick={this.props.stopFetch}
/>
</div>
</FocusTrapZone>
)}
<Pivot>
Expand Down
19 changes: 19 additions & 0 deletions src/components/settings/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type AppTabState = {
itemSize: string
cacheSize: string
deleteIndex: string
refreshOnStart: boolean
}

class AppTab extends React.Component<AppTabProps, AppTabState> {
Expand All @@ -52,6 +53,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
itemSize: null,
cacheSize: null,
deleteIndex: null,
refreshOnStart: window.settings.getRefreshOnStart(),
}
this.getItemSize()
this.getCacheSize()
Expand Down Expand Up @@ -156,6 +158,11 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
})
}

toggleRefreshOnStart = (_: any, checked: boolean) => {
window.settings.setRefreshOnStart(checked)
this.setState({ refreshOnStart: checked })
}

handleInputChange = event => {
const name: string = event.target.name
// @ts-ignore
Expand Down Expand Up @@ -196,6 +203,18 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
selectedKey={this.state.themeSettings}
/>

<Stack horizontal verticalAlign="baseline">
<Stack.Item grow>
<Label>{intl.get("app.refreshOnStart")}</Label>
</Stack.Item>
<Stack.Item>
<Toggle
checked={this.state.refreshOnStart}
onChange={this.toggleRefreshOnStart}
/>
</Stack.Item>
</Stack>

<Label>{intl.get("app.fetchInterval")}</Label>
<Stack horizontal>
<Stack.Item>
Expand Down
4 changes: 2 additions & 2 deletions src/containers/nav-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import intl from "react-intl-universal"
import { connect } from "react-redux"
import { createSelector } from "reselect"
import { RootState } from "../scripts/reducer"
import { fetchItems, markAllRead } from "../scripts/models/item"
import { fetchItems, markAllRead, stopFetchItems } from "../scripts/models/item"
import {
toggleMenu,
toggleLogMenu,
Expand All @@ -25,9 +25,9 @@ const mapStateToProps = createSelector(
itemShown: itemShown,
})
)

const mapDispatchToProps = dispatch => ({
fetch: () => dispatch(fetchItems()),
stopFetch: () => dispatch(stopFetchItems()),
menu: () => dispatch(toggleMenu()),
logs: () => dispatch(toggleLogMenu()),
views: () => dispatch(openViewMenu()),
Expand Down
2 changes: 2 additions & 0 deletions src/containers/settings-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { connect } from "react-redux"
import { createSelector } from "reselect"
import { RootState } from "../scripts/reducer"
import { exitSettings } from "../scripts/models/app"
import { stopFetchItems } from "../scripts/models/item"
import Settings from "../components/settings"

const getApp = (state: RootState) => state.app
Expand All @@ -19,6 +20,7 @@ const mapStateToProps = createSelector([getApp], app => ({
const mapDispatchToProps = dispatch => {
return {
close: () => dispatch(exitSettings()),
stopFetch: () => dispatch(stopFetchItems()),
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ ipcMain.handle("set-fetch-interval", (_, interval: number) => {
store.set(FETCH_INTEVAL_STORE_KEY, interval)
})

const REFRESH_ON_START_STORE_KEY = "refreshOnStart"
ipcMain.on("get-refresh-on-start", event => {
event.returnValue = store.get(REFRESH_ON_START_STORE_KEY, true)
})
ipcMain.handle("set-refresh-on-start", (_, flag: boolean) => {
store.set(REFRESH_ON_START_STORE_KEY, flag)
})

const SEARCH_ENGINE_STORE_KEY = "searchEngine"
ipcMain.on("get-search-engine", event => {
event.returnValue = store.get(SEARCH_ENGINE_STORE_KEY, SearchEngines.Google)
Expand Down
1 change: 1 addition & 0 deletions src/schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ export type SchemaTypes = {
filterType: number
listViewConfigs: ViewConfigs
useNeDB: boolean
refreshOnStart: boolean
}
4 changes: 3 additions & 1 deletion src/scripts/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"nav": {
"menu": "Menu",
"refresh": "Refresh",
"stop": "Stop refresh",
"markAllRead": "Mark all as read",
"notifications": "Notifications",
"view": "View",
Expand Down Expand Up @@ -237,6 +238,7 @@
"setPac": "Set PAC",
"pacHint": "For Socks proxies, it is recommended for PAC to return \"SOCKS5\" for proxy-side DNS. Turning off proxy requires restart.",
"fetchInterval": "Automatic fetch interval",
"refreshOnStart": "Refresh feeds on app start",
"never": "Never"
}
}
}
28 changes: 16 additions & 12 deletions src/scripts/models/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,11 @@ export function initApp(): AppThunk {
.then(() => dispatch(initFeeds()))
.then(async () => {
dispatch(selectAllArticles())
await dispatch(fetchItems())
if (window.settings.getRefreshOnStart()) {
await dispatch(fetchItems())
} else {
dispatch(setupAutoFetch())
}
})
.then(() => {
dispatch(updateFavicon())
Expand Down Expand Up @@ -555,17 +559,17 @@ export function appReducer(
action.items.length == 0
? state.logMenu
: {
...state.logMenu,
logs: [
...state.logMenu.logs,
new AppLog(
AppLogType.Info,
intl.get("log.fetchSuccess", {
count: action.items.length,
})
),
],
},
...state.logMenu,
logs: [
...state.logMenu.logs,
new AppLog(
AppLogType.Info,
intl.get("log.fetchSuccess", {
count: action.items.length,
})
),
],
},
}
case ActionStatus.Intermediate:
return {
Expand Down
56 changes: 41 additions & 15 deletions src/scripts/models/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,14 @@ export async function insertItems(items: RSSItem[]): Promise<RSSItem[]> {
.exec()) as RSSItem[]
}

let cancelFetch: () => void = null

export function stopFetchItems(): AppThunk {
return () => {
if (cancelFetch) cancelFetch()
}
}

export function fetchItems(
background = false,
sids: number[] = null
Expand All @@ -225,32 +233,50 @@ export function fetchItems(
let sources =
sids === null
? Object.values(sourcesState).filter(s => {
let last = s.lastFetched ? s.lastFetched.getTime() : 0
return (
!s.serviceRef &&
(last > timenow ||
last + (s.fetchFrequency || 0) * 60000 <=
timenow)
)
})
let last = s.lastFetched ? s.lastFetched.getTime() : 0
return (
!s.serviceRef &&
(last > timenow ||
last + (s.fetchFrequency || 0) * 60000 <=
timenow)
)
})
: sids
.map(sid => sourcesState[sid])
.filter(s => !s.serviceRef)
for (let source of sources) {
.map(sid => sourcesState[sid])
.filter(s => !s.serviceRef)

let results: { status: 'fulfilled' | 'rejected', value?: RSSItem[], reason?: any }[] = new Array(sources.length)

for (let i = 0; i < sources.length; i++) {
let source = sources[i]
let promise = RSSSource.fetchItems(source)
promise.then(() =>
promise.then((items) => {
results[i] = { status: 'fulfilled', value: items }
dispatch(
updateSource({ ...source, lastFetched: new Date() })
)
)
promise.finally(() => dispatch(fetchItemsIntermediate()))
}).catch(err => {
results[i] = { status: 'rejected', reason: err }
}).finally(() => dispatch(fetchItemsIntermediate()))
promises.push(promise)
}
dispatch(fetchItemsRequest(promises.length))
const results = await Promise.allSettled(promises)

const cancelPromise = new Promise<void>(resolve => {
cancelFetch = resolve
})

await Promise.race([
Promise.allSettled(promises),
cancelPromise
])

cancelFetch = null

return await new Promise<void>((resolve, reject) => {
let items = new Array<RSSItem>()
results.map((r, i) => {
if (!r) return // Cancelled/Pending
if (r.status === "fulfilled") items.push(...r.value)
else {
console.log(r.reason)
Expand Down