diff --git a/src/base/common/promise.ts b/src/base/common/promise.ts new file mode 100644 index 0000000..42e51be --- /dev/null +++ b/src/base/common/promise.ts @@ -0,0 +1,15 @@ +export const createPromise = () => { + let resolve + let reject + + const promise = new Promise((resolveFn, rejectFn) => { + resolve = resolveFn + reject = rejectFn + }) + + return { + promise, + resolve, + reject, + } +} diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index 57eed51..4a53244 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -73,6 +73,13 @@ "browserBtn": "Browser", "setToDefaultBtn": "Set To Default Directory", "applyBtn": "Restart & Apply" + }, + + "proxy": { + "title": "Proxy", + "server": "Server", + "port": "Port", + "none": "None" } } } diff --git a/src/i18n/zh-CN.json b/src/i18n/zh-CN.json index 2c43ae5..186719c 100644 --- a/src/i18n/zh-CN.json +++ b/src/i18n/zh-CN.json @@ -73,6 +73,13 @@ "browserBtn": "选择", "setToDefaultBtn": "设置为默认目录", "applyBtn": "重启生效" + }, + + "proxy": { + "title": "代理", + "server": "服务器", + "port": "端口", + "none": "无" } } } diff --git a/src/services/http/electron-main/http.ts b/src/services/http/electron-main/http.ts new file mode 100644 index 0000000..9084f9d --- /dev/null +++ b/src/services/http/electron-main/http.ts @@ -0,0 +1,9 @@ +import * as http from 'node:http' +import * as https from 'node:https' +import * as settingService from '@/services/settings/electron-main/settings' +import Response from './response' + +export const get = (url: string): Promise => { + +} + diff --git a/src/services/http/electron-main/response.ts b/src/services/http/electron-main/response.ts new file mode 100644 index 0000000..2d9d0d0 --- /dev/null +++ b/src/services/http/electron-main/response.ts @@ -0,0 +1,5 @@ +export default class Response { + constructor() { + + } +} diff --git a/src/ui/apps/settings/appearance-item.tsx b/src/ui/apps/settings/appearance-item.tsx new file mode 100644 index 0000000..959c56f --- /dev/null +++ b/src/ui/apps/settings/appearance-item.tsx @@ -0,0 +1,25 @@ +import { observer } from 'mobx-react-lite' +import intl from 'react-intl-universal' +import { Radio, RadioGroup } from '@fluentui/react-components' +import { store } from '@/ui/store' +import Item from './item' + +const { settingStore } = store + +function AppearanceItem() { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { appearance, appearances, locale } = settingStore + + return ( + + settingStore.setAppearance(data.value as any)} + > + {appearances.map((v) => )} + + + ) +} + +export default observer(AppearanceItem) diff --git a/src/ui/apps/settings/data-path-item.tsx b/src/ui/apps/settings/data-path-item.tsx new file mode 100644 index 0000000..6affac0 --- /dev/null +++ b/src/ui/apps/settings/data-path-item.tsx @@ -0,0 +1,73 @@ +import { useState } from 'react' +import { observer } from 'mobx-react-lite' +import intl from 'react-intl-universal' +import { Input, Button } from '@fluentui/react-components' +import { store } from '@/ui/store' +import { openDirectory } from '@/base/browser/shell' +import Item from './item' + +const { settingStore } = store + +function DataPathItem() { + const { + dataPath: activeDataPath, + defaultDataPath, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + locale, + } = settingStore + + const [dataPath, setDataPath] = useState(activeDataPath) + + const directorBrowserHandler = async () => { + const { canceled, filePaths } = await openDirectory({ + properties: ['createDirectory'], + defaultPath: dataPath, + }) + + if (canceled) { + return + } + + const path = filePaths[0] + setDataPath(path) + } + + return ( + +
+ + +
+
+ + +
+
+ ) +} + +export default observer(DataPathItem) diff --git a/src/ui/apps/settings/index.tsx b/src/ui/apps/settings/index.tsx index 9f18c7c..e3fc405 100644 --- a/src/ui/apps/settings/index.tsx +++ b/src/ui/apps/settings/index.tsx @@ -1,114 +1,17 @@ -import { PropsWithChildren, useState } from 'react' -import { observer } from 'mobx-react-lite' -import intl from 'react-intl-universal' -import { - Radio, - RadioGroup, - Input, - Button, -} from '@fluentui/react-components' -import { store } from '@/ui/store' -import { labels as locales } from '@/i18n' -import { openDirectory } from '@/base/browser/shell' - -const { settingStore } = store - -type ItemProps = PropsWithChildren<{ - title?: string -}> - -function Item({ title, children }: ItemProps) { - return ( -
-
- {title} -
- {children} -
- ) -} - -function Settings() { - const { - appearance, - appearances, - locale, - dataPath: activeDataPath, - defaultDataPath, - } = settingStore - - const [dataPath, setDataPath] = useState(activeDataPath) - - const directorBrowserHandler = async () => { - const { canceled, filePaths } = await openDirectory({ - properties: ['createDirectory'], - defaultPath: dataPath, - }) - - if (canceled) { - return - } - - const path = filePaths[0] - setDataPath(path) - } +import AppearanceItem from './appearance-item' +import LocaleItem from './locale-item' +import DataPathItem from './data-path-item' +import ProxyItem from './proxy-item' +export default function Settings() { return (
- - settingStore.setAppearance(data.value as any)} - > - {appearances.map((v) => )} - - - - settingStore.setLocale(data.value)} - > - {locales.map((v) => )} - - - -
- - -
-
- - -
-
+ + + +
) } - -export default observer(Settings) diff --git a/src/ui/apps/settings/item.tsx b/src/ui/apps/settings/item.tsx new file mode 100644 index 0000000..8a72cfc --- /dev/null +++ b/src/ui/apps/settings/item.tsx @@ -0,0 +1,16 @@ +import type { PropsWithChildren } from 'react' + +type ItemProps = PropsWithChildren<{ + title?: string +}> + +export default function Item({ title, children }: ItemProps) { + return ( +
+
+ {title} +
+ {children} +
+ ) +} diff --git a/src/ui/apps/settings/locale-item.tsx b/src/ui/apps/settings/locale-item.tsx new file mode 100644 index 0000000..119e078 --- /dev/null +++ b/src/ui/apps/settings/locale-item.tsx @@ -0,0 +1,25 @@ +import { observer } from 'mobx-react-lite' +import intl from 'react-intl-universal' +import { Radio, RadioGroup } from '@fluentui/react-components' +import { store } from '@/ui/store' +import { labels as locales } from '@/i18n' +import Item from './item' + +const { settingStore } = store + +function LocaleItem() { + const { locale } = settingStore + + return ( + + settingStore.setLocale(data.value)} + > + {locales.map((v) => )} + + + ) +} + +export default observer(LocaleItem) diff --git a/src/ui/apps/settings/proxy-item.tsx b/src/ui/apps/settings/proxy-item.tsx new file mode 100644 index 0000000..82835d1 --- /dev/null +++ b/src/ui/apps/settings/proxy-item.tsx @@ -0,0 +1,106 @@ +import { observer } from 'mobx-react-lite' +import intl from 'react-intl-universal' +import { + Radio, + RadioGroup, + Input, + makeStyles, + tokens, +} from '@fluentui/react-components' +import type { InputProps } from '@fluentui/react-components' +import { store } from '@/ui/store' +import Item from './item' + +const { settingStore } = store + +const useStyles = makeStyles({ + enabledStyle: { + color: tokens.colorNeutralForeground1, + }, + + disabledStyle: { + color: tokens.colorNeutralForeground3, + }, +}) + +function ProxyItem() { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { locale, proxies } = settingStore + const styles = useStyles() + + const proxyItems = proxies.map((v) => { + const onServerChange: InputProps['onChange'] = (_, data) => { + const { value } = data + settingStore.updateProxy({ + ...v, + server: value, + }) + } + + const onPortChange: InputProps['onChange'] = (_, data) => { + const { value } = data + settingStore.updateProxy({ + ...v, + port: Number(value.replace(/[^\d]/g, '')), + }) + } + + return ( +
+
+ +
+ + +
+
+
+ ) + }) + + const handleTypeChange = (type: (typeof proxies)[number]['type'] | 'none') => { + if (type === 'none') { + settingStore.disableAllProxies() + } else { + settingStore.enableProxy(type) + } + } + + const enabledProxy = proxies.find((v) => v.enabled) + + return ( + + handleTypeChange(data.value as any)} + > + + {proxyItems} + + + ) +} + +export default observer(ProxyItem) diff --git a/src/ui/store/index.ts b/src/ui/store/index.ts index fd40e54..454118e 100644 --- a/src/ui/store/index.ts +++ b/src/ui/store/index.ts @@ -9,7 +9,7 @@ import { SettingStore, Appearance } from './settings' type IAppearance = Exclude class Store { - app: Apps = 'reader' + app: Apps = 'settings' locale = 'en-US' diff --git a/src/ui/store/settings.ts b/src/ui/store/settings.ts index 6a4c05b..cf8a416 100644 --- a/src/ui/store/settings.ts +++ b/src/ui/store/settings.ts @@ -1,3 +1,5 @@ +/* eslint-disable no-param-reassign */ + import { makeAutoObservable, runInAction } from 'mobx' import * as settingService from '@/services/settings/browser/settings' import { ElementType } from '@/types/common' @@ -8,6 +10,13 @@ const appearances = ['light', 'dark', 'auto'] as const export type Appearance = ElementType +type Proxy = { + type: 'http' | 'https' | 'socks5', + server?: string, + port?: number, + enabled?: boolean, +} + export class SettingStore { rootStore: RootStore @@ -22,6 +31,15 @@ export class SettingStore { defaultDataPath = '' + proxies: Proxy[] = [ + { + type: 'http', + }, + { + type: 'https', + }, + ] + constructor(rootStore: RootStore) { makeAutoObservable(this, { rootStore: false, @@ -38,13 +56,21 @@ export class SettingStore { appearance, dataPath, defaultDataPath, + proxies = [], } = await settingService.get() runInAction(() => { this.locale = locale this.appearance = appearance this.dataPath = dataPath - this.defaultDataPath = defaultDataPath + this.defaultDataPath = defaultDataPath; + (proxies as Proxy[]).forEach((item) => { + const index = this.proxies.findIndex((v) => v.type === item.type) + + if (index !== -1) { + this.proxies.splice(index, 1, item) + } + }) }) this.rootStore.changeLocale(locale) @@ -69,4 +95,43 @@ export class SettingStore { this.dataPath = dataPath settingService.set('dataPath', dataPath) } + + private saveProxies() { + settingService.set('proxies', this.proxies.map((v) => ({ ...v }))) + } + + disableAllProxies() { + this.proxies.forEach((v) => { v.enabled = false }) + this.saveProxies() + } + + enableProxy(type: Proxy['type']) { + const proxy = this.proxies.find((v) => v.type === type) + + if (!proxy) { + return + } + + this.proxies.forEach((v) => { v.enabled = false }) + proxy.enabled = true + this.saveProxies() + } + + updateProxy(proxy: Partial) { + const index = this.proxies.findIndex((v) => v.type === proxy.type) + + if (index === -1) { + return + } + + let item = this.proxies.find((v) => v.type === proxy.type) + + item = { + ...item!, + ...proxy, + } + + this.proxies.splice(index, 1, item) + this.saveProxies() + } }