mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-02-22 06:27:45 +01:00
add settings context bridge
This commit is contained in:
parent
e1a5de5963
commit
b4c1ddd587
71
src/bridges/settings.ts
Normal file
71
src/bridges/settings.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { SourceGroup, ViewType, ThemeSettings } from "../schema-types"
|
||||
import { ipcRenderer } from "electron"
|
||||
|
||||
const SettingsBridge = {
|
||||
saveGroups: (groups: SourceGroup[]) => {
|
||||
ipcRenderer.invoke("set-groups", groups)
|
||||
},
|
||||
loadGroups: (): SourceGroup[] => {
|
||||
return ipcRenderer.sendSync("get-groups")
|
||||
},
|
||||
|
||||
getDefaultMenu: (): boolean => {
|
||||
return ipcRenderer.sendSync("get-menu")
|
||||
},
|
||||
setDefaultMenu: (state: boolean) => {
|
||||
ipcRenderer.invoke("set-menu", state)
|
||||
},
|
||||
|
||||
getProxyStatus: (): boolean => {
|
||||
return ipcRenderer.sendSync("get-proxy-status")
|
||||
},
|
||||
toggleProxyStatus: () => {
|
||||
ipcRenderer.send("toggle-proxy-status")
|
||||
},
|
||||
getProxy: (): string => {
|
||||
return ipcRenderer.sendSync("get-proxy")
|
||||
},
|
||||
setProxy: (address: string = null) => {
|
||||
ipcRenderer.invoke("set-proxy", address)
|
||||
},
|
||||
|
||||
getDefaultView: (): ViewType => {
|
||||
return ipcRenderer.sendSync("get-view")
|
||||
},
|
||||
setDefaultView: (viewType: ViewType) => {
|
||||
ipcRenderer.invoke("set-view", viewType)
|
||||
},
|
||||
|
||||
getThemeSettings: (): ThemeSettings => {
|
||||
return ipcRenderer.sendSync("get-theme")
|
||||
},
|
||||
setThemeSettings: (theme: ThemeSettings) => {
|
||||
ipcRenderer.invoke("set-theme", theme)
|
||||
},
|
||||
shouldUseDarkColors: (): boolean => {
|
||||
return ipcRenderer.sendSync("get-theme-dark-color")
|
||||
},
|
||||
addThemeUpdateListener: (callback: (shouldDark: boolean) => any) => {
|
||||
ipcRenderer.on("theme-updated", (_, shouldDark) => {
|
||||
callback(shouldDark)
|
||||
})
|
||||
},
|
||||
|
||||
setLocaleSettings: (option: string) => {
|
||||
ipcRenderer.invoke("set-locale", option)
|
||||
},
|
||||
getLocaleSettings: (): string => {
|
||||
return ipcRenderer.sendSync("get-locale-settings")
|
||||
},
|
||||
getCurrentLocale: (): string => {
|
||||
return ipcRenderer.sendSync("get-locale")
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
settings: typeof SettingsBridge
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsBridge
|
@ -6,7 +6,7 @@ import { ContextualMenu, IContextualMenuItem, ContextualMenuItemType, Directiona
|
||||
import { ContextMenuType } from "../scripts/models/app"
|
||||
import { RSSItem } from "../scripts/models/item"
|
||||
import { ContextReduxProps } from "../containers/context-menu-container"
|
||||
import { ViewType } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import { FilterType } from "../scripts/models/feed"
|
||||
|
||||
export type ContextMenuProps = ContextReduxProps & {
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import { RSSItem } from "../../scripts/models/item"
|
||||
import { FeedReduxProps } from "../../containers/feed-container"
|
||||
import { RSSFeed } from "../../scripts/models/feed"
|
||||
import { ViewType } from "../../scripts/models/page"
|
||||
import { ViewType } from "../../schema-types"
|
||||
import CardsFeed from "./cards-feed"
|
||||
import ListFeed from "./list-feed"
|
||||
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import intl from "react-intl-universal"
|
||||
import { Icon } from "@fluentui/react/lib/Icon"
|
||||
import { Nav, INavLink, INavLinkGroup } from "office-ui-fabric-react/lib/Nav"
|
||||
import { SourceGroup } from "../scripts/models/group"
|
||||
import { SourceGroup } from "../schema-types"
|
||||
import { SourceState, RSSSource } from "../scripts/models/source"
|
||||
import { ALL } from "../scripts/models/feed"
|
||||
import { AnimationClassNames, Stack } from "@fluentui/react"
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import { FeedContainer } from "../containers/feed-container"
|
||||
import { AnimationClassNames, Icon, FocusTrapZone } from "@fluentui/react"
|
||||
import ArticleContainer from "../containers/article-container"
|
||||
import { ViewType } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import ArticleSearch from "./utils/article-search"
|
||||
|
||||
type PageProps = {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import * as React from "react"
|
||||
import intl from "react-intl-universal"
|
||||
import { urlTest, byteToMB, calculateItemSize } from "../../scripts/utils"
|
||||
import { getProxy, getProxyStatus, toggleProxyStatus, setProxy, getThemeSettings, setThemeSettings, ThemeSettings, getLocaleSettings, exportAll } from "../../scripts/settings"
|
||||
import { ThemeSettings } from "../../schema-types"
|
||||
import { getThemeSettings, setThemeSettings, exportAll } from "../../scripts/settings"
|
||||
import { Stack, Label, Toggle, TextField, DefaultButton, ChoiceGroup, IChoiceGroupOption, loadTheme, Dropdown, IDropdownOption, PrimaryButton } from "@fluentui/react"
|
||||
import { remote } from "electron"
|
||||
import DangerButton from "../utils/danger-button"
|
||||
@ -25,8 +26,8 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
pacStatus: getProxyStatus(),
|
||||
pacUrl: getProxy(),
|
||||
pacStatus: window.settings.getProxyStatus(),
|
||||
pacUrl: window.settings.getProxy(),
|
||||
themeSettings: getThemeSettings(),
|
||||
itemSize: null,
|
||||
cacheSize: null,
|
||||
@ -86,10 +87,10 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
||||
]
|
||||
|
||||
toggleStatus = () => {
|
||||
toggleProxyStatus()
|
||||
window.settings.toggleProxyStatus()
|
||||
this.setState({
|
||||
pacStatus: getProxyStatus(),
|
||||
pacUrl: getProxy()
|
||||
pacStatus: window.settings.getProxyStatus(),
|
||||
pacUrl: window.settings.getProxy()
|
||||
})
|
||||
}
|
||||
|
||||
@ -101,7 +102,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
||||
|
||||
setUrl = (event: React.FormEvent) => {
|
||||
event.preventDefault()
|
||||
if (urlTest(this.state.pacUrl)) setProxy(this.state.pacUrl)
|
||||
if (urlTest(this.state.pacUrl)) window.settings.setProxy(this.state.pacUrl)
|
||||
}
|
||||
|
||||
onThemeChange = (_, option: IChoiceGroupOption) => {
|
||||
@ -127,7 +128,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
||||
<Stack horizontal>
|
||||
<Stack.Item>
|
||||
<Dropdown
|
||||
defaultSelectedKey={getLocaleSettings()}
|
||||
defaultSelectedKey={window.settings.getLocaleSettings()}
|
||||
options={this.languageOptions()}
|
||||
onChanged={option => this.props.setLanguage(String(option.key))}
|
||||
style={{width: 200}} />
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import intl from "react-intl-universal"
|
||||
import { SourceGroup } from "../../scripts/models/group"
|
||||
import { SourceGroup } from "../../schema-types"
|
||||
import { SourceState, RSSSource } from "../../scripts/models/source"
|
||||
import { IColumn, Selection, SelectionMode, DetailsList, Label, Stack,
|
||||
TextField, PrimaryButton, DefaultButton, Dropdown, IDropdownOption, CommandBarButton, MarqueeSelection, IDragDropEvents, IDragDropContext } from "@fluentui/react"
|
||||
|
@ -4,8 +4,8 @@ import { RootState } from "../scripts/reducer"
|
||||
import { ContextMenuType, closeContextMenu, toggleSettings } from "../scripts/models/app"
|
||||
import { ContextMenu } from "../components/context-menu"
|
||||
import { RSSItem, markRead, markUnread, toggleStarred, toggleHidden, markAllRead } from "../scripts/models/item"
|
||||
import { showItem, switchView, ViewType, switchFilter, toggleFilter } from "../scripts/models/page"
|
||||
import { setDefaultView } from "../scripts/settings"
|
||||
import { showItem, switchView, switchFilter, toggleFilter } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import { FilterType } from "../scripts/models/feed"
|
||||
|
||||
const getContext = (state: RootState) => state.app.contextMenu
|
||||
@ -57,7 +57,7 @@ const mapDispatchToProps = dispatch => {
|
||||
dispatch(toggleHidden(item))
|
||||
},
|
||||
switchView: (viewType: ViewType) => {
|
||||
setDefaultView(viewType)
|
||||
window.settings.setDefaultView(viewType)
|
||||
dispatch(switchView(viewType))
|
||||
},
|
||||
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
|
||||
|
@ -4,7 +4,8 @@ import { RootState } from "../scripts/reducer"
|
||||
import { markRead, RSSItem, itemShortcuts } from "../scripts/models/item"
|
||||
import { openItemMenu } from "../scripts/models/app"
|
||||
import { loadMore, RSSFeed } from "../scripts/models/feed"
|
||||
import { showItem, ViewType } from "../scripts/models/page"
|
||||
import { showItem } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import { Feed } from "../components/feeds/feed"
|
||||
|
||||
interface FeedContainerProps {
|
||||
|
@ -3,8 +3,10 @@ import { createSelector } from "reselect"
|
||||
import { RootState } from "../scripts/reducer"
|
||||
import { Menu } from "../components/menu"
|
||||
import { toggleMenu, openGroupMenu } from "../scripts/models/app"
|
||||
import { SourceGroup, toggleGroupExpansion } from "../scripts/models/group"
|
||||
import { selectAllArticles, selectSources, toggleSearch, ViewType } from "../scripts/models/page"
|
||||
import { toggleGroupExpansion } from "../scripts/models/group"
|
||||
import { SourceGroup } from "../schema-types"
|
||||
import { selectAllArticles, selectSources, toggleSearch } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import { initFeeds } from "../scripts/models/feed"
|
||||
import { RSSSource } from "../scripts/models/source"
|
||||
|
||||
|
@ -5,7 +5,8 @@ import { createSelector } from "reselect"
|
||||
import { RootState } from "../scripts/reducer"
|
||||
import { fetchItems, markAllRead } from "../scripts/models/item"
|
||||
import { toggleMenu, toggleLogMenu, toggleSettings, openViewMenu } from "../scripts/models/app"
|
||||
import { ViewType, toggleSearch } from "../scripts/models/page"
|
||||
import { toggleSearch } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import Nav from "../components/nav"
|
||||
|
||||
const getState = (state: RootState) => state.app
|
||||
|
@ -1,6 +1,6 @@
|
||||
import intl from "react-intl-universal"
|
||||
import { connect } from "react-redux"
|
||||
import { setLocaleSettings, importAll } from "../../scripts/settings"
|
||||
import { importAll } from "../../scripts/settings"
|
||||
import { initIntl, saveSettings } from "../../scripts/models/app"
|
||||
import * as db from "../../scripts/db"
|
||||
import AppTab from "../../components/settings/app"
|
||||
@ -9,7 +9,7 @@ import { remote } from "electron"
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setLanguage: (option: string) => {
|
||||
setLocaleSettings(option)
|
||||
window.settings.setLocaleSettings(option)
|
||||
dispatch(initIntl())
|
||||
},
|
||||
deleteArticles: (days: number) => new Promise((resolve) => {
|
||||
|
@ -2,8 +2,9 @@ import { connect } from "react-redux"
|
||||
import { createSelector } from "reselect"
|
||||
import { RootState } from "../../scripts/reducer"
|
||||
import GroupsTab from "../../components/settings/groups"
|
||||
import { createSourceGroup, SourceGroup, updateSourceGroup, addSourceToGroup,
|
||||
import { createSourceGroup, updateSourceGroup, addSourceToGroup,
|
||||
deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/group"
|
||||
import { SourceGroup } from "../../schema-types"
|
||||
|
||||
const getSources = (state: RootState) => state.sources
|
||||
const getGroups = (state: RootState) => state.groups
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { app, ipcMain, BrowserWindow, Menu, nativeTheme } from "electron"
|
||||
import windowStateKeeper = require("electron-window-state")
|
||||
import Store = require("electron-store")
|
||||
import performUpdate from "./scripts/update-scripts"
|
||||
import { ThemeSettings } from "./schema-types"
|
||||
import { store, setThemeListener } from "./main/settings"
|
||||
import performUpdate from "./main/update-scripts"
|
||||
import path = require("path")
|
||||
|
||||
if (!process.mas) {
|
||||
const locked = app.requestSingleInstanceLock()
|
||||
@ -11,14 +13,12 @@ if (!process.mas) {
|
||||
}
|
||||
|
||||
let mainWindow: BrowserWindow
|
||||
let store: Store
|
||||
let restarting: boolean
|
||||
|
||||
function init(setTheme = true) {
|
||||
restarting = false
|
||||
store = new Store()
|
||||
performUpdate(store)
|
||||
if (setTheme) nativeTheme.themeSource = store.get("theme", "system")
|
||||
if (setTheme) nativeTheme.themeSource = store.get("theme", ThemeSettings.Default)
|
||||
}
|
||||
|
||||
init()
|
||||
@ -46,7 +46,8 @@ function createWindow() {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
webviewTag: true,
|
||||
enableRemoteModule: true
|
||||
enableRemoteModule: true,
|
||||
preload: path.join(app.getAppPath(), (app.isPackaged ? "dist/" : "") + "preload.js")
|
||||
}
|
||||
})
|
||||
mainWindowState.manage(mainWindow)
|
||||
@ -54,9 +55,10 @@ function createWindow() {
|
||||
mainWindow.show()
|
||||
mainWindow.focus()
|
||||
if (!app.isPackaged) mainWindow.webContents.openDevTools()
|
||||
});
|
||||
})
|
||||
setThemeListener(mainWindow)
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile((app.isPackaged ? "dist/" : "") + "index.html")
|
||||
mainWindow.loadFile((app.isPackaged ? "dist/" : "") + "index.html", )
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
|
@ -7,10 +7,10 @@ import { initializeIcons } from "@fluentui/react/lib/Icons"
|
||||
import { rootReducer, RootState } from "./scripts/reducer"
|
||||
import Root from "./components/root"
|
||||
import { AppDispatch } from "./scripts/utils"
|
||||
import { setProxy, applyThemeSettings } from "./scripts/settings"
|
||||
import { applyThemeSettings } from "./scripts/settings"
|
||||
import { initApp } from "./scripts/models/app"
|
||||
|
||||
setProxy()
|
||||
window.settings.setProxy()
|
||||
|
||||
applyThemeSettings()
|
||||
initializeIcons("icons/")
|
||||
|
103
src/main/settings.ts
Normal file
103
src/main/settings.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import Store = require("electron-store")
|
||||
import { SchemaTypes, SourceGroup, ViewType, ThemeSettings } from "../schema-types"
|
||||
import { ipcMain, session, nativeTheme, BrowserWindow, app } from "electron"
|
||||
|
||||
export const store = new Store<SchemaTypes>()
|
||||
|
||||
const GROUPS_STORE_KEY = "sourceGroups"
|
||||
ipcMain.handle("set-groups", (_, groups: SourceGroup[]) => {
|
||||
store.set(GROUPS_STORE_KEY, groups)
|
||||
})
|
||||
ipcMain.on("get-groups", (event) => {
|
||||
event.returnValue = store.get(GROUPS_STORE_KEY, [])
|
||||
})
|
||||
|
||||
const MENU_STORE_KEY = "menuOn"
|
||||
ipcMain.on("get-menu", (event) => {
|
||||
event.returnValue = store.get(MENU_STORE_KEY, false)
|
||||
})
|
||||
ipcMain.handle("set-menu", (_, state: boolean) => {
|
||||
store.set(MENU_STORE_KEY, state)
|
||||
})
|
||||
|
||||
const PAC_STORE_KEY = "pac"
|
||||
const PAC_STATUS_KEY = "pacOn"
|
||||
function getProxyStatus() {
|
||||
return store.get(PAC_STATUS_KEY, false)
|
||||
}
|
||||
function toggleProxyStatus() {
|
||||
store.set(PAC_STATUS_KEY, !getProxyStatus())
|
||||
setProxy()
|
||||
}
|
||||
function getProxy() {
|
||||
return store.get(PAC_STORE_KEY, "")
|
||||
}
|
||||
function setProxy(address = null) {
|
||||
if (!address) {
|
||||
address = getProxy()
|
||||
} else {
|
||||
store.set(PAC_STORE_KEY, address)
|
||||
}
|
||||
if (getProxyStatus()) {
|
||||
let rules = { pacScript: address }
|
||||
session.defaultSession.setProxy(rules)
|
||||
session.fromPartition("sandbox").setProxy(rules)
|
||||
}
|
||||
}
|
||||
ipcMain.on("get-proxy-status", (event) => {
|
||||
event.returnValue = getProxyStatus()
|
||||
})
|
||||
ipcMain.on("toggle-proxy-status", () => {
|
||||
toggleProxyStatus()
|
||||
})
|
||||
ipcMain.on("get-proxy", (event) => {
|
||||
event.returnValue = getProxy()
|
||||
})
|
||||
ipcMain.handle("set-proxy", (_, address = null) => {
|
||||
setProxy(address)
|
||||
})
|
||||
|
||||
const VIEW_STORE_KEY = "view"
|
||||
ipcMain.on("get-view", (event) => {
|
||||
event.returnValue = store.get(VIEW_STORE_KEY, ViewType.Cards)
|
||||
})
|
||||
ipcMain.handle("set-view", (_, viewType: ViewType) => {
|
||||
store.set(VIEW_STORE_KEY, viewType)
|
||||
})
|
||||
|
||||
const THEME_STORE_KEY = "theme"
|
||||
ipcMain.on("get-theme", (event) => {
|
||||
event.returnValue = store.get(THEME_STORE_KEY, ThemeSettings.Default)
|
||||
})
|
||||
ipcMain.handle("set-theme", (_, theme: ThemeSettings) => {
|
||||
store.set(THEME_STORE_KEY, theme)
|
||||
nativeTheme.themeSource = theme
|
||||
})
|
||||
ipcMain.on("get-theme-dark-color", (event) => {
|
||||
event.returnValue = nativeTheme.shouldUseDarkColors
|
||||
})
|
||||
export function setThemeListener(window: BrowserWindow) {
|
||||
nativeTheme.removeAllListeners()
|
||||
nativeTheme.on("updated", () => {
|
||||
let contents = window.webContents
|
||||
if (!contents.isDestroyed()) {
|
||||
contents.send("theme-updated", nativeTheme.shouldUseDarkColors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const LOCALE_STORE_KEY = "locale"
|
||||
ipcMain.handle("set-locale", (_, option: string) => {
|
||||
store.set(LOCALE_STORE_KEY, option)
|
||||
})
|
||||
function getLocaleSettings() {
|
||||
return store.get(LOCALE_STORE_KEY, "default")
|
||||
}
|
||||
ipcMain.on("get-locale-settings", (event) => {
|
||||
event.returnValue = getLocaleSettings()
|
||||
})
|
||||
ipcMain.on("get-locale", (event) => {
|
||||
let setting = getLocaleSettings()
|
||||
let locale = setting === "default" ? app.getLocale() : setting
|
||||
event.returnValue = locale
|
||||
})
|
@ -1,7 +1,8 @@
|
||||
import { app } from "electron"
|
||||
import Store = require("electron-store")
|
||||
import { SchemaTypes } from "../schema-types"
|
||||
|
||||
export default function performUpdate(store: Store) {
|
||||
export default function performUpdate(store: Store<SchemaTypes>) {
|
||||
let version = store.get("version", null)
|
||||
let currentVersion = app.getVersion()
|
||||
|
4
src/preload.ts
Normal file
4
src/preload.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { contextBridge } from "electron"
|
||||
import SettingsBridge from "./bridges/settings"
|
||||
|
||||
window.settings = SettingsBridge
|
41
src/schema-types.ts
Normal file
41
src/schema-types.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export class SourceGroup {
|
||||
isMultiple: boolean
|
||||
sids: number[]
|
||||
name?: string
|
||||
expanded?: boolean
|
||||
index?: number // available only from groups tab container
|
||||
|
||||
constructor(sids: number[], name: string = null) {
|
||||
name = (name && name.trim()) || "订阅源组"
|
||||
if (sids.length == 1) {
|
||||
this.isMultiple = false
|
||||
} else {
|
||||
this.isMultiple = true
|
||||
this.name = name
|
||||
this.expanded = true
|
||||
}
|
||||
this.sids = sids
|
||||
}
|
||||
}
|
||||
|
||||
export enum ViewType {
|
||||
Cards, List, Customized
|
||||
}
|
||||
|
||||
export enum ThemeSettings {
|
||||
Default = "system",
|
||||
Light = "light",
|
||||
Dark = "dark"
|
||||
}
|
||||
|
||||
export type SchemaTypes = {
|
||||
version: string
|
||||
theme: ThemeSettings
|
||||
pac: string
|
||||
pacOn: boolean
|
||||
view: ViewType
|
||||
locale: string
|
||||
sourceGroups: SourceGroup[]
|
||||
fontSize: number
|
||||
menuOn: boolean
|
||||
}
|
22
src/scripts/PromiseConstructor.d.ts
vendored
22
src/scripts/PromiseConstructor.d.ts
vendored
@ -1,22 +0,0 @@
|
||||
interface PromiseFulfilledResult<T> {
|
||||
status: "fulfilled";
|
||||
value: T;
|
||||
}
|
||||
|
||||
interface PromiseRejectedResult {
|
||||
status: "rejected";
|
||||
reason: any;
|
||||
}
|
||||
|
||||
type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;
|
||||
|
||||
declare interface PromiseConstructor {
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all
|
||||
* of the provided Promises resolve or reject.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
allSettled<T extends readonly unknown[] | readonly [unknown]>(values: T):
|
||||
Promise<{ -readonly [P in keyof T]: PromiseSettledResult<T[P] extends PromiseLike<infer U> ? U : T[P]> }>;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { ThemeSettings } from "./settings"
|
||||
import { SourceGroup } from "./models/group"
|
||||
|
||||
// Using schema breaks unsafe-eval. Unused.
|
||||
/* export const schema: {[key in keyof schemaTypes]: Schema} = {
|
||||
theme: { type: "string", default: "system" },
|
||||
pac: { type: "string", default: "" },
|
||||
pacOn: { type: "boolean", default: false },
|
||||
view: { type: "number", default: 0 },
|
||||
locale: { type: "string", default: "default" },
|
||||
sourceGroups: { type: "array", default: [] }
|
||||
} */
|
||||
|
||||
export type schemaTypes = {
|
||||
version: string
|
||||
theme: ThemeSettings
|
||||
pac: string
|
||||
pacOn: boolean
|
||||
view: number
|
||||
locale: string
|
||||
sourceGroups: SourceGroup[]
|
||||
fontSize: number
|
||||
menuOn: boolean
|
||||
}
|
@ -5,7 +5,7 @@ import { ActionStatus, AppThunk, getWindowBreakpoint } from "../utils"
|
||||
import { INIT_FEEDS, FeedActionTypes, ALL, initFeeds } from "./feed"
|
||||
import { SourceGroupActionTypes, UPDATE_SOURCE_GROUP, ADD_SOURCE_TO_GROUP, DELETE_SOURCE_GROUP, REMOVE_SOURCE_FROM_GROUP, REORDER_SOURCE_GROUPS } from "./group"
|
||||
import { PageActionTypes, SELECT_PAGE, PageType, selectAllArticles } from "./page"
|
||||
import { getCurrentLocale, setDefaultMenu, getDefaultMenu } from "../settings"
|
||||
import { getCurrentLocale } from "../settings"
|
||||
import locales from "../i18n/_locales"
|
||||
import * as db from "../db"
|
||||
|
||||
@ -38,7 +38,7 @@ export class AppState {
|
||||
fetchingItems = false
|
||||
fetchingProgress = 0
|
||||
fetchingTotal = 0
|
||||
menu = getWindowBreakpoint() && getDefaultMenu()
|
||||
menu = getWindowBreakpoint() && window.settings.getDefaultMenu()
|
||||
menuKey = ALL
|
||||
title = ""
|
||||
settings = {
|
||||
@ -152,7 +152,7 @@ export function openGroupMenu(sids: number[], event: React.MouseEvent): ContextM
|
||||
export function toggleMenu(): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: TOGGLE_MENU })
|
||||
setDefaultMenu(getState().app.menu)
|
||||
window.settings.setDefaultMenu(getState().app.menu)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,43 +1,12 @@
|
||||
import fs = require("fs")
|
||||
import intl from "react-intl-universal"
|
||||
import { SourceActionTypes, ADD_SOURCE, DELETE_SOURCE, addSource, RSSSource } from "./source"
|
||||
|
||||
import { SourceGroup } from "../../schema-types"
|
||||
import { ActionStatus, AppThunk, domParser, AppDispatch } from "../utils"
|
||||
import { saveSettings } from "./app"
|
||||
import { store } from "../settings"
|
||||
import { fetchItemsIntermediate, fetchItemsRequest, fetchItemsSuccess } from "./item"
|
||||
import { remote } from "electron"
|
||||
|
||||
const GROUPS_STORE_KEY = "sourceGroups"
|
||||
|
||||
export class SourceGroup {
|
||||
isMultiple: boolean
|
||||
sids: number[]
|
||||
name?: string
|
||||
expanded?: boolean
|
||||
index?: number // available only from groups tab container
|
||||
|
||||
constructor(sids: number[], name: string = null) {
|
||||
name = (name && name.trim()) || "订阅源组"
|
||||
if (sids.length == 1) {
|
||||
this.isMultiple = false
|
||||
} else {
|
||||
this.isMultiple = true
|
||||
this.name = name
|
||||
this.expanded = true
|
||||
}
|
||||
this.sids = sids
|
||||
}
|
||||
|
||||
static save(groups: SourceGroup[]) {
|
||||
store.set(GROUPS_STORE_KEY, groups)
|
||||
}
|
||||
|
||||
static load(): SourceGroup[] {
|
||||
return store.get(GROUPS_STORE_KEY, [])
|
||||
}
|
||||
}
|
||||
|
||||
export const CREATE_SOURCE_GROUP = "CREATE_SOURCE_GROUP"
|
||||
export const ADD_SOURCE_TO_GROUP = "ADD_SOURCE_TO_GROUP"
|
||||
export const REMOVE_SOURCE_FROM_GROUP = "REMOVE_SOURCE_FROM_GROUP"
|
||||
@ -100,7 +69,7 @@ export function createSourceGroup(name: string): AppThunk<number> {
|
||||
let group = new SourceGroup([], name)
|
||||
dispatch(createSourceGroupDone(group))
|
||||
let groups = getState().groups
|
||||
SourceGroup.save(groups)
|
||||
window.settings.saveGroups(groups)
|
||||
return groups.length - 1
|
||||
}
|
||||
}
|
||||
@ -116,7 +85,7 @@ function addSourceToGroupDone(groupIndex: number, sid: number): SourceGroupActio
|
||||
export function addSourceToGroup(groupIndex: number, sid: number): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(addSourceToGroupDone(groupIndex, sid))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +100,7 @@ function removeSourceFromGroupDone(groupIndex: number, sids: number[]): SourceGr
|
||||
export function removeSourceFromGroup(groupIndex: number, sids: number[]): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(removeSourceFromGroupDone(groupIndex, sids))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,7 +114,7 @@ function deleteSourceGroupDone(groupIndex: number): SourceGroupActionTypes {
|
||||
export function deleteSourceGroup(groupIndex: number): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(deleteSourceGroupDone(groupIndex))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +129,7 @@ function updateSourceGroupDone(group: SourceGroup): SourceGroupActionTypes {
|
||||
export function updateSourceGroup(group: SourceGroup): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(updateSourceGroupDone(group))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +143,7 @@ function reorderSourceGroupsDone(groups: SourceGroup[]): SourceGroupActionTypes
|
||||
export function reorderSourceGroups(groups: SourceGroup[]): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(reorderSourceGroupsDone(groups))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +153,7 @@ export function toggleGroupExpansion(groupIndex: number): AppThunk {
|
||||
type: TOGGLE_GROUP_EXPANSION,
|
||||
groupIndex: groupIndex
|
||||
})
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +268,7 @@ export function exportOPML(path: string): AppThunk {
|
||||
export type GroupState = SourceGroup[]
|
||||
|
||||
export function groupReducer(
|
||||
state = SourceGroup.load(),
|
||||
state = window.settings.loadGroups(),
|
||||
action: SourceActionTypes | SourceGroupActionTypes
|
||||
): GroupState {
|
||||
switch(action.type) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ALL, SOURCE, loadMore, FeedFilter, FilterType, initFeeds, FeedActionTypes, INIT_FEED } from "./feed"
|
||||
import { getWindowBreakpoint, AppThunk, ActionStatus } from "../utils"
|
||||
import { getDefaultView } from "../settings"
|
||||
import { RSSItem, markRead } from "./item"
|
||||
import { SourceActionTypes, DELETE_SOURCE } from "./source"
|
||||
import { toggleMenu } from "./app"
|
||||
import { ViewType } from "../../schema-types"
|
||||
|
||||
export const SELECT_PAGE = "SELECT_PAGE"
|
||||
export const SWITCH_VIEW = "SWITCH_VIEW"
|
||||
@ -17,10 +17,6 @@ export enum PageType {
|
||||
AllArticles, Sources, Page
|
||||
}
|
||||
|
||||
export enum ViewType {
|
||||
Cards, List, Customized
|
||||
}
|
||||
|
||||
interface SelectPageAction {
|
||||
type: typeof SELECT_PAGE
|
||||
pageType: PageType
|
||||
@ -205,7 +201,7 @@ export function performSearch(query: string): AppThunk {
|
||||
}
|
||||
|
||||
export class PageState {
|
||||
viewType = getDefaultView()
|
||||
viewType = window.settings.getDefaultView()
|
||||
filter = new FeedFilter()
|
||||
feedId = ALL
|
||||
itemId = null as string
|
||||
|
@ -3,7 +3,6 @@ import intl from "react-intl-universal"
|
||||
import * as db from "../db"
|
||||
import { fetchFavicon, ActionStatus, AppThunk, parseRSS } from "../utils"
|
||||
import { RSSItem, insertItems, ItemActionTypes, FETCH_ITEMS, MARK_READ, MARK_UNREAD, MARK_ALL_READ } from "./item"
|
||||
import { SourceGroup } from "./group"
|
||||
import { saveSettings } from "./app"
|
||||
import { remote } from "electron"
|
||||
import { SourceRule } from "./rule"
|
||||
@ -249,7 +248,7 @@ export function addSource(url: string, name: string = null, batch = false): AppT
|
||||
return RSSSource.checkItems(inserted, feed.items)
|
||||
.then(items => insertItems(items))
|
||||
.then(() => {
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
return inserted.sid
|
||||
})
|
||||
})
|
||||
@ -310,7 +309,7 @@ export function deleteSource(source: RSSSource, batch = false): AppThunk<Promise
|
||||
resolve()
|
||||
} else {
|
||||
dispatch(deleteSourceDone(source))
|
||||
SourceGroup.save(getState().groups)
|
||||
window.settings.saveGroups(getState().groups)
|
||||
if (!batch) dispatch(saveSettings())
|
||||
resolve()
|
||||
}
|
||||
|
@ -1,54 +1,12 @@
|
||||
import { remote, ipcRenderer } from "electron"
|
||||
import { ViewType } from "./models/page"
|
||||
import { IPartialTheme, loadTheme } from "@fluentui/react"
|
||||
import locales from "./i18n/_locales"
|
||||
import Store = require("electron-store")
|
||||
import { schemaTypes } from "./config-schema"
|
||||
import { ThemeSettings, SchemaTypes } from "../schema-types"
|
||||
import fs = require("fs")
|
||||
import intl from "react-intl-universal"
|
||||
|
||||
export const store = new Store<schemaTypes>()
|
||||
|
||||
const MENU_STORE_KEY = "menuOn"
|
||||
export function getDefaultMenu() {
|
||||
return store.get(MENU_STORE_KEY, false)
|
||||
}
|
||||
export function setDefaultMenu(state: boolean) {
|
||||
store.set(MENU_STORE_KEY, state)
|
||||
}
|
||||
|
||||
const PAC_STORE_KEY = "pac"
|
||||
const PAC_STATUS_KEY = "pacOn"
|
||||
export function getProxyStatus() {
|
||||
return store.get(PAC_STATUS_KEY, false)
|
||||
}
|
||||
export function toggleProxyStatus() {
|
||||
store.set(PAC_STATUS_KEY, !getProxyStatus())
|
||||
setProxy()
|
||||
}
|
||||
export function getProxy() {
|
||||
return store.get(PAC_STORE_KEY, "")
|
||||
}
|
||||
export function setProxy(address = null) {
|
||||
if (!address) {
|
||||
address = getProxy()
|
||||
} else {
|
||||
store.set(PAC_STORE_KEY, address)
|
||||
}
|
||||
if (getProxyStatus()) {
|
||||
let rules = { pacScript: address }
|
||||
remote.getCurrentWebContents().session.setProxy(rules)
|
||||
remote.session.fromPartition("sandbox").setProxy(rules)
|
||||
}
|
||||
}
|
||||
|
||||
const VIEW_STORE_KEY = "view"
|
||||
export const getDefaultView = (): ViewType => {
|
||||
return store.get(VIEW_STORE_KEY, ViewType.Cards)
|
||||
}
|
||||
export const setDefaultView = (viewType: ViewType) => {
|
||||
store.set(VIEW_STORE_KEY, viewType)
|
||||
}
|
||||
export const store = new Store<SchemaTypes>()
|
||||
|
||||
const lightTheme: IPartialTheme = {
|
||||
defaultFontStyle: { fontFamily: '"Segoe UI", "Source Han Sans SC Regular", "Microsoft YaHei", sans-serif' }
|
||||
@ -56,63 +14,50 @@ const lightTheme: IPartialTheme = {
|
||||
const darkTheme: IPartialTheme = {
|
||||
...lightTheme,
|
||||
palette: {
|
||||
neutralLighterAlt: '#282828',
|
||||
neutralLighter: '#313131',
|
||||
neutralLight: '#3f3f3f',
|
||||
neutralQuaternaryAlt: '#484848',
|
||||
neutralQuaternary: '#4f4f4f',
|
||||
neutralTertiaryAlt: '#6d6d6d',
|
||||
neutralTertiary: '#c8c8c8',
|
||||
neutralSecondary: '#d0d0d0',
|
||||
neutralSecondaryAlt: '#d2d0ce',
|
||||
neutralPrimaryAlt: '#dadada',
|
||||
neutralPrimary: '#ffffff',
|
||||
neutralDark: '#f4f4f4',
|
||||
black: '#f8f8f8',
|
||||
white: '#1f1f1f',
|
||||
themePrimary: '#3a96dd',
|
||||
themeLighterAlt: '#020609',
|
||||
themeLighter: '#091823',
|
||||
themeLight: '#112d43',
|
||||
themeTertiary: '#235a85',
|
||||
themeSecondary: '#3385c3',
|
||||
themeDarkAlt: '#4ba0e1',
|
||||
themeDark: '#65aee6',
|
||||
themeDarker: '#8ac2ec',
|
||||
accent: '#3a96dd'
|
||||
neutralLighterAlt: "#282828",
|
||||
neutralLighter: "#313131",
|
||||
neutralLight: "#3f3f3f",
|
||||
neutralQuaternaryAlt: "#484848",
|
||||
neutralQuaternary: "#4f4f4f",
|
||||
neutralTertiaryAlt: "#6d6d6d",
|
||||
neutralTertiary: "#c8c8c8",
|
||||
neutralSecondary: "#d0d0d0",
|
||||
neutralSecondaryAlt: "#d2d0ce",
|
||||
neutralPrimaryAlt: "#dadada",
|
||||
neutralPrimary: "#ffffff",
|
||||
neutralDark: "#f4f4f4",
|
||||
black: "#f8f8f8",
|
||||
white: "#1f1f1f",
|
||||
themePrimary: "#3a96dd",
|
||||
themeLighterAlt: "#020609",
|
||||
themeLighter: "#091823",
|
||||
themeLight: "#112d43",
|
||||
themeTertiary: "#235a85",
|
||||
themeSecondary: "#3385c3",
|
||||
themeDarkAlt: "#4ba0e1",
|
||||
themeDark: "#65aee6",
|
||||
themeDarker: "#8ac2ec",
|
||||
accent: "#3a96dd"
|
||||
}
|
||||
}
|
||||
export enum ThemeSettings {
|
||||
Default = "system",
|
||||
Light = "light",
|
||||
Dark = "dark"
|
||||
}
|
||||
|
||||
const THEME_STORE_KEY = "theme"
|
||||
export function setThemeSettings(theme: ThemeSettings) {
|
||||
store.set(THEME_STORE_KEY, theme)
|
||||
remote.nativeTheme.themeSource = theme
|
||||
window.settings.setThemeSettings(theme)
|
||||
applyThemeSettings()
|
||||
}
|
||||
export function getThemeSettings(): ThemeSettings {
|
||||
return store.get(THEME_STORE_KEY, ThemeSettings.Default)
|
||||
return window.settings.getThemeSettings()
|
||||
}
|
||||
export function applyThemeSettings() {
|
||||
loadTheme(remote.nativeTheme.shouldUseDarkColors ? darkTheme : lightTheme)
|
||||
loadTheme(window.settings.shouldUseDarkColors() ? darkTheme : lightTheme)
|
||||
}
|
||||
remote.nativeTheme.on("updated", () => {
|
||||
applyThemeSettings()
|
||||
window.settings.addThemeUpdateListener((shouldDark) => {
|
||||
loadTheme(shouldDark ? darkTheme : lightTheme)
|
||||
})
|
||||
|
||||
const LOCALE_STORE_KEY = "locale"
|
||||
export function setLocaleSettings(option: string) {
|
||||
store.set(LOCALE_STORE_KEY, option)
|
||||
}
|
||||
export function getLocaleSettings() {
|
||||
return store.get(LOCALE_STORE_KEY, "default")
|
||||
}
|
||||
export function getCurrentLocale() {
|
||||
let set = getLocaleSettings()
|
||||
let locale = set === "default" ? remote.app.getLocale() : set
|
||||
let locale = window.settings.getCurrentLocale()
|
||||
return (locale in locales) ? locale : "en-US"
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,25 @@ module.exports = [
|
||||
filename: 'electron.js'
|
||||
}
|
||||
},
|
||||
{
|
||||
mode: 'production',
|
||||
entry: './src/preload.ts',
|
||||
target: 'electron-preload',
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.ts$/,
|
||||
include: /src/,
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
use: [{ loader: 'ts-loader' }]
|
||||
}]
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/dist',
|
||||
filename: 'preload.js'
|
||||
}
|
||||
},
|
||||
{
|
||||
mode: 'production',
|
||||
entry: './src/index.tsx',
|
||||
|
Loading…
x
Reference in New Issue
Block a user