add settings context bridge

This commit is contained in:
刘浩远 2020-06-29 19:17:33 +08:00
parent e1a5de5963
commit b4c1ddd587
27 changed files with 330 additions and 220 deletions

71
src/bridges/settings.ts Normal file
View 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

View File

@ -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 & {

View File

@ -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"

View File

@ -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"

View File

@ -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 = {

View File

@ -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}} />

View File

@ -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"

View File

@ -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)),

View File

@ -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 {

View File

@ -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"

View File

@ -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

View File

@ -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) => {

View File

@ -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

View File

@ -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") {

View File

@ -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
View 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
})

View File

@ -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
View File

@ -0,0 +1,4 @@
import { contextBridge } from "electron"
import SettingsBridge from "./bridges/settings"
window.settings = SettingsBridge

41
src/schema-types.ts Normal file
View 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
}

View File

@ -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]> }>;
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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()
}

View File

@ -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"
}

View File

@ -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',