mirror of
https://github.com/ragestudio/relic.git
synced 2025-06-09 10:34:18 +00:00
added settings
This commit is contained in:
parent
979a4fa515
commit
2c0533a16f
@ -1,32 +1,16 @@
|
|||||||
import lodash from "lodash"
|
import sendToRender from "./utils/sendToRender"
|
||||||
|
|
||||||
global.sendToRenderer = (event, data) => {
|
global.SettingsStore = new Store({
|
||||||
function serializeIpc(data) {
|
name: "settings",
|
||||||
const copy = lodash.cloneDeep(data)
|
watch: true,
|
||||||
|
})
|
||||||
// remove fns
|
|
||||||
if (!Array.isArray(copy)) {
|
|
||||||
Object.keys(copy).forEach((key) => {
|
|
||||||
if (typeof copy[key] === "function") {
|
|
||||||
delete copy[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
|
|
||||||
global.win.webContents.send(event, serializeIpc(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
const { autoUpdater } = require("electron-differential-updater")
|
|
||||||
const ProtocolRegistry = require("protocol-registry")
|
|
||||||
|
|
||||||
import path from "node:path"
|
import path from "node:path"
|
||||||
|
|
||||||
import { app, shell, BrowserWindow, ipcMain } from "electron"
|
import { app, shell, BrowserWindow, ipcMain } from "electron"
|
||||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils"
|
import { electronApp, optimizer, is } from "@electron-toolkit/utils"
|
||||||
import isDev from "electron-is-dev"
|
import isDev from "electron-is-dev"
|
||||||
|
import Store from "electron-store"
|
||||||
|
|
||||||
import open from "open"
|
import open from "open"
|
||||||
|
|
||||||
@ -37,6 +21,11 @@ import setup from "./setup"
|
|||||||
import PkgManager from "./pkg_mng"
|
import PkgManager from "./pkg_mng"
|
||||||
import { readManifest } from "./utils/readManifest"
|
import { readManifest } from "./utils/readManifest"
|
||||||
|
|
||||||
|
import GoogleDriveAPI from "./lib/google_drive"
|
||||||
|
|
||||||
|
const { autoUpdater } = require("electron-differential-updater")
|
||||||
|
const ProtocolRegistry = require("protocol-registry")
|
||||||
|
|
||||||
const protocolRegistryNamespace = "rsbundle"
|
const protocolRegistryNamespace = "rsbundle"
|
||||||
|
|
||||||
class ElectronApp {
|
class ElectronApp {
|
||||||
@ -46,9 +35,6 @@ class ElectronApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlers = {
|
handlers = {
|
||||||
pkg: () => {
|
|
||||||
return pkg
|
|
||||||
},
|
|
||||||
"get:installations": async () => {
|
"get:installations": async () => {
|
||||||
return await this.pkgManager.getInstallations()
|
return await this.pkgManager.getInstallations()
|
||||||
},
|
},
|
||||||
@ -73,9 +59,6 @@ class ElectronApp {
|
|||||||
"pkg:apply_changes": (event, manifest_id, changes) => {
|
"pkg:apply_changes": (event, manifest_id, changes) => {
|
||||||
this.pkgManager.applyChanges(manifest_id, changes)
|
this.pkgManager.applyChanges(manifest_id, changes)
|
||||||
},
|
},
|
||||||
"check:setup": async () => {
|
|
||||||
return await setup()
|
|
||||||
},
|
|
||||||
"updater:check": () => {
|
"updater:check": () => {
|
||||||
autoUpdater.checkForUpdates()
|
autoUpdater.checkForUpdates()
|
||||||
},
|
},
|
||||||
@ -83,7 +66,32 @@ class ElectronApp {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
autoUpdater.quitAndInstall()
|
autoUpdater.quitAndInstall()
|
||||||
}, 3000)
|
}, 3000)
|
||||||
}
|
},
|
||||||
|
"settings:get": (e, key) => {
|
||||||
|
return global.SettingsStore.get(key)
|
||||||
|
},
|
||||||
|
"settings:set": (e, key, value) => {
|
||||||
|
return global.SettingsStore.set(key, value)
|
||||||
|
},
|
||||||
|
"settings:delete": (e, key) => {
|
||||||
|
return global.SettingsStore.delete(key)
|
||||||
|
},
|
||||||
|
"settings:has": (e, key) => {
|
||||||
|
return global.SettingsStore.has(key)
|
||||||
|
},
|
||||||
|
"app:init": async (event, data) => {
|
||||||
|
await setup()
|
||||||
|
|
||||||
|
// check if can decode google drive token
|
||||||
|
const googleDrive_enabled = !!await GoogleDriveAPI.readCredentials()
|
||||||
|
|
||||||
|
return {
|
||||||
|
pkg: pkg,
|
||||||
|
authorizedServices: {
|
||||||
|
drive: googleDrive_enabled,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
events = {
|
events = {
|
||||||
@ -92,11 +100,6 @@ class ElectronApp {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToRender(event, ...args) {
|
|
||||||
console.log(`[sendToRender][${event}]`, ...args)
|
|
||||||
this.win.webContents.send(event, ...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
createWindow() {
|
createWindow() {
|
||||||
this.win = global.win = new BrowserWindow({
|
this.win = global.win = new BrowserWindow({
|
||||||
width: 450,
|
width: 450,
|
||||||
@ -129,8 +132,6 @@ class ElectronApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleURLProtocol(url) {
|
handleURLProtocol(url) {
|
||||||
console.log(url)
|
|
||||||
|
|
||||||
const urlStarter = `${protocolRegistryNamespace}://`
|
const urlStarter = `${protocolRegistryNamespace}://`
|
||||||
|
|
||||||
if (url.startsWith(urlStarter)) {
|
if (url.startsWith(urlStarter)) {
|
||||||
@ -143,18 +144,17 @@ class ElectronApp {
|
|||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "install": {
|
case "install": {
|
||||||
return this.sendToRender("installation:invoked", value)
|
return sendToRender("installation:invoked", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return this.sendToRender("new:message", {
|
return sendToRender("new:message", {
|
||||||
message: "Unrecognized URL action",
|
message: "Unrecognized URL action",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// by default if no action is specified, assume is a install action
|
// by default if no action is specified, assume is a install action
|
||||||
return this.sendToRender("installation:invoked", urlValue)
|
return sendToRender("installation:invoked", urlValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ class ElectronApp {
|
|||||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||||
console.log(info)
|
console.log(info)
|
||||||
|
|
||||||
this.sendToRender("update-available", info)
|
sendToRender("update-available", info)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
@ -240,7 +240,9 @@ class ElectronApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createWindow()
|
await GoogleDriveAPI.init()
|
||||||
|
|
||||||
|
await this.createWindow()
|
||||||
|
|
||||||
app.on("activate", () => {
|
app.on("activate", () => {
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
@ -1,7 +1,22 @@
|
|||||||
import { contextBridge, ipcRenderer } from "electron"
|
import { contextBridge, ipcRenderer } from "electron"
|
||||||
import { electronAPI } from "@electron-toolkit/preload"
|
import { electronAPI } from "@electron-toolkit/preload"
|
||||||
|
|
||||||
const api = {}
|
const api = {
|
||||||
|
settings: {
|
||||||
|
get: (key) => {
|
||||||
|
return ipcRenderer.invoke("settings:get", key)
|
||||||
|
},
|
||||||
|
set: (key, value) => {
|
||||||
|
return ipcRenderer.invoke("settings:set", key, value)
|
||||||
|
},
|
||||||
|
delete: (key) => {
|
||||||
|
return ipcRenderer.invoke("settings:delete", key)
|
||||||
|
},
|
||||||
|
has: (key) => {
|
||||||
|
return ipcRenderer.invoke("settings:has", key)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if (process.contextIsolated) {
|
if (process.contextIsolated) {
|
||||||
try {
|
try {
|
||||||
@ -21,8 +36,8 @@ if (process.contextIsolated) {
|
|||||||
ipcRenderer.removeListener(channel, listener)
|
ipcRenderer.removeListener(channel, listener)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("electron", electronAPI)
|
contextBridge.exposeInMainWorld("electron", electronAPI)
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("api", api)
|
contextBridge.exposeInMainWorld("api", api)
|
||||||
|
@ -10,7 +10,7 @@ import ManifestInfo from "components/ManifestInfo"
|
|||||||
import AppHeader from "layout/components/Header"
|
import AppHeader from "layout/components/Header"
|
||||||
import AppModalDialog from "layout/components/ModalDialog"
|
import AppModalDialog from "layout/components/ModalDialog"
|
||||||
|
|
||||||
import { PageRender } from "./router.jsx"
|
import { InternalRouter, PageRender } from "./router.jsx"
|
||||||
|
|
||||||
globalThis.getRootCssVar = getRootCssVar
|
globalThis.getRootCssVar = getRootCssVar
|
||||||
globalThis.notification = antd.notification
|
globalThis.notification = antd.notification
|
||||||
@ -32,7 +32,10 @@ window.app = {
|
|||||||
app.modal.close()
|
app.modal.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
checkUpdates: () => {
|
||||||
|
ipc.exec("updater:check")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
@ -41,6 +44,10 @@ class App extends React.Component {
|
|||||||
pkg: null,
|
pkg: null,
|
||||||
initializing: false,
|
initializing: false,
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
|
|
||||||
|
authorizedServices: {
|
||||||
|
drive: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcEvents = {
|
ipcEvents = {
|
||||||
@ -50,14 +57,6 @@ class App extends React.Component {
|
|||||||
"runtime:info": (event, data) => {
|
"runtime:info": (event, data) => {
|
||||||
antd.message.info(data)
|
antd.message.info(data)
|
||||||
},
|
},
|
||||||
"initializing_text": (event, data) => {
|
|
||||||
this.setState({
|
|
||||||
initializing_text: data,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"installation:invoked": (event, manifest) => {
|
|
||||||
app.invokeInstall(manifest)
|
|
||||||
},
|
|
||||||
"new:notification": (event, data) => {
|
"new:notification": (event, data) => {
|
||||||
antd.notification[data.type || "info"]({
|
antd.notification[data.type || "info"]({
|
||||||
message: data.message,
|
message: data.message,
|
||||||
@ -71,26 +70,47 @@ class App extends React.Component {
|
|||||||
antd.message[data.type || "info"](data.message)
|
antd.message[data.type || "info"](data.message)
|
||||||
},
|
},
|
||||||
"update-available": (event, data) => {
|
"update-available": (event, data) => {
|
||||||
|
this.onUpdateAvailable(data)
|
||||||
|
},
|
||||||
|
"initializing_text": (event, data) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
updateAvailable: true,
|
initializing_text: data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"installation:invoked": (event, manifest) => {
|
||||||
|
app.invokeInstall(manifest)
|
||||||
|
},
|
||||||
|
"drive:authorized": (event, data) => {
|
||||||
|
this.setState({
|
||||||
|
authorizedServices: {
|
||||||
|
drive: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(data)
|
message.success("Google Drive API authorized")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
antd.Modal.confirm({
|
onUpdateAvailable = () => {
|
||||||
title: "Update Available",
|
this.setState({
|
||||||
content: <>
|
updateAvailable: true,
|
||||||
<p>
|
})
|
||||||
A new version of the application is available.
|
|
||||||
</p>
|
console.log(data)
|
||||||
</>,
|
|
||||||
okText: "Update",
|
antd.Modal.confirm({
|
||||||
cancelText: "Later",
|
title: "Update Available",
|
||||||
onOk: () => {
|
content: <>
|
||||||
app.applyUpdate()
|
<p>
|
||||||
}
|
A new version of the application is available.
|
||||||
})
|
</p>
|
||||||
}
|
</>,
|
||||||
|
okText: "Update",
|
||||||
|
cancelText: "Later",
|
||||||
|
onOk: () => {
|
||||||
|
app.applyUpdate()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
@ -98,13 +118,16 @@ class App extends React.Component {
|
|||||||
ipc.on(event, this.ipcEvents[event])
|
ipc.on(event, this.ipcEvents[event])
|
||||||
}
|
}
|
||||||
|
|
||||||
const pkg = await ipc.exec("pkg")
|
const initResult = await ipc.exec("app:init")
|
||||||
|
|
||||||
await ipc.exec("check:setup")
|
console.log(`[INIT] >`, initResult)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
pkg: pkg,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
|
pkg: initResult.pkg,
|
||||||
|
authorizedServices: {
|
||||||
|
drive: initResult.authorizedServices?.drive ?? false
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,17 +148,19 @@ class App extends React.Component {
|
|||||||
algorithm: antd.theme.darkAlgorithm
|
algorithm: antd.theme.darkAlgorithm
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GlobalStateContext.Provider value={this.state}>
|
<InternalRouter>
|
||||||
<AppModalDialog />
|
<GlobalStateContext.Provider value={this.state}>
|
||||||
|
<AppModalDialog />
|
||||||
|
|
||||||
<antd.Layout className="app_layout">
|
<antd.Layout className="app_layout">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
|
|
||||||
<antd.Layout.Content className="app_content">
|
<antd.Layout.Content className="app_content">
|
||||||
<PageRender />
|
<PageRender />
|
||||||
</antd.Layout.Content>
|
</antd.Layout.Content>
|
||||||
</antd.Layout>
|
</antd.Layout>
|
||||||
</GlobalStateContext.Provider>
|
</GlobalStateContext.Provider>
|
||||||
|
</InternalRouter>
|
||||||
</antd.ConfigProvider>
|
</antd.ConfigProvider>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/renderer/src/components/Icons/index.jsx
Normal file
19
src/renderer/src/components/Icons/index.jsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
import * as MDIcons from "react-icons/md"
|
||||||
|
import * as SIIcons from "react-icons/si"
|
||||||
|
|
||||||
|
export const Icons = {
|
||||||
|
...MDIcons,
|
||||||
|
...SIIcons,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Icon = ({ icon }) => {
|
||||||
|
if (icon && Icons[icon]) {
|
||||||
|
return React.createElement(Icons[icon])
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Icons
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { MdFolder, MdSettings, MdDownload } from "react-icons/md"
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
import GlobalStateContext from "contexts/global"
|
import GlobalStateContext from "contexts/global"
|
||||||
|
|
||||||
@ -10,16 +10,24 @@ const Header = (props) => {
|
|||||||
const ctx = React.useContext(GlobalStateContext)
|
const ctx = React.useContext(GlobalStateContext)
|
||||||
|
|
||||||
return <antd.Layout.Header className="app_header">
|
return <antd.Layout.Header className="app_header">
|
||||||
<div className="branding">
|
<div className="branding" onClick={() => app.location.push("/")}>
|
||||||
<Icon />
|
<Icon />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
!ctx.loading && <div className="menu">
|
!ctx.loading && <div className="menu">
|
||||||
|
{
|
||||||
|
ctx.authorizedServices?.drive && <Icons.SiGoogledrive
|
||||||
|
style={{
|
||||||
|
color: `var(--primary-color)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ctx.updateAvailable && <antd.Button
|
ctx.updateAvailable && <antd.Button
|
||||||
size="small"
|
size="small"
|
||||||
icon={<MdDownload />}
|
icon={<Icons.MdDownload />}
|
||||||
onClick={app.applyUpdate}
|
onClick={app.applyUpdate}
|
||||||
>
|
>
|
||||||
Update now
|
Update now
|
||||||
@ -28,12 +36,13 @@ const Header = (props) => {
|
|||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
size="small"
|
size="small"
|
||||||
icon={<MdSettings />}
|
icon={<Icons.MdSettings />}
|
||||||
|
onClick={() => app.location.push("/settings")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
size="small"
|
size="small"
|
||||||
icon={<MdFolder />}
|
icon={<Icons.MdFolder />}
|
||||||
onClick={() => ipc.send("open-runtime-path")}
|
onClick={() => ipc.send("open-runtime-path")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -1,15 +1,178 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
import * as antd from "antd"
|
||||||
|
import { Icons, Icon } from "components/Icons"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
const settingsList = [
|
const settingsList = [
|
||||||
{
|
{
|
||||||
id: "drive_auth",
|
id: "drive_auth",
|
||||||
name: "Google Drive Authorize",
|
name: "Google Drive Authorize",
|
||||||
description: "Authorize your Google Drive account to be used for bundles installation.",
|
description: "Authorize your Google Drive account to be used for bundles installation.",
|
||||||
|
icon: "SiGoogledrive",
|
||||||
|
type: "button",
|
||||||
|
value: async () => {
|
||||||
|
return api.settings.get("drive_auth")
|
||||||
|
},
|
||||||
|
render: (props) => {
|
||||||
|
return <antd.Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
if (!props.value) {
|
||||||
|
message.info("Authorizing...")
|
||||||
|
return ipc.exec("drive:authorize")
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.settings.delete("drive_auth")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
props.value ? "Deauthorize" : "Authorize"
|
||||||
|
}
|
||||||
|
</antd.Button>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "check_update",
|
||||||
|
name: "Check for updates",
|
||||||
|
description: "Check for updates to the app.",
|
||||||
|
icon: "MdUpdate",
|
||||||
|
type: "button",
|
||||||
|
props: {
|
||||||
|
children: "Check",
|
||||||
|
onClick: () => {
|
||||||
|
message.info("Checking for updates...")
|
||||||
|
app.checkUpdates()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const Settings = () => {
|
const SettingTypeToComponent = {
|
||||||
|
switch: antd.Switch,
|
||||||
|
button: antd.Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SettingItem = (props) => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
icon,
|
||||||
|
props: _props,
|
||||||
|
render,
|
||||||
|
} = props.setting
|
||||||
|
|
||||||
|
const [loading, setLoading] = React.useState(false)
|
||||||
|
const [value, setValue] = React.useState(null)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (typeof props.setting.value === "function") {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
props.setting.value().then((value) => {
|
||||||
|
setValue(value)
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}, [props.setting.value])
|
||||||
|
|
||||||
|
let componentProps = {
|
||||||
|
value: value,
|
||||||
|
..._props,
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleChange(value) {
|
||||||
|
console.log(`Setting [${id}] set to >`, value)
|
||||||
|
setValue(value)
|
||||||
|
api.settings.set(id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "switch": {
|
||||||
|
componentProps.defaultChecked = defaultProps.defaultChecked ?? false
|
||||||
|
componentProps.onChange = (e) => {
|
||||||
|
handleChange(e)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Component = SettingTypeToComponent[type.toLowerCase()]
|
||||||
|
const Render = () => {
|
||||||
|
if (typeof render === "function") {
|
||||||
|
return render(componentProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
return React.createElement(Component, componentProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div
|
||||||
|
className="app_settings-list-item"
|
||||||
|
>
|
||||||
|
<div className="app_settings-list-item-info">
|
||||||
|
<div className="app_settings-list-item-label">
|
||||||
|
<Icon icon={icon} />
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
{name}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="app_settings-list-item-description">
|
||||||
|
<p>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="app_settings-list-item-component">
|
||||||
|
{
|
||||||
|
loading && <antd.Spin />
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!loading && <Render />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Settings = () => {
|
||||||
|
return <div className="app_settings">
|
||||||
|
<div className="app_settings-header">
|
||||||
|
<div className="app_settings-header-back">
|
||||||
|
<Icons.MdChevronLeft
|
||||||
|
onClick={() => {
|
||||||
|
app.location.push("/")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Back
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="app_settings-header-title">
|
||||||
|
<Icons.MdSettings />
|
||||||
|
<h1>Settings</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="app_settings-list">
|
||||||
|
{
|
||||||
|
settingsList.map((setting, index) => {
|
||||||
|
return <SettingItem
|
||||||
|
key={index}
|
||||||
|
setting={setting}
|
||||||
|
/>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="software_info">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Settings
|
export default Settings
|
100
src/renderer/src/pages/settings/index.less
Normal file
100
src/renderer/src/pages/settings/index.less
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
@import "style/vars.less";
|
||||||
|
|
||||||
|
.app_settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.app_settings-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.app_settings-header-back {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: var(--primary-color);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_settings-header-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_settings-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
background-color: var(--background-color-secondary);
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
.app_settings-list-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background-color: mix(#fff, @var-background-color-secondary, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.app_settings-list-item-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.app_settings-list-item-label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_settings-list-item-description {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_settings-list-item-component {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,63 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import BarLoader from "react-spinners/BarLoader"
|
import BarLoader from "react-spinners/BarLoader"
|
||||||
|
import { BrowserRouter, Route, Routes, useNavigate } from "react-router-dom"
|
||||||
|
|
||||||
import GlobalStateContext from "contexts/global"
|
import GlobalStateContext from "contexts/global"
|
||||||
|
|
||||||
import InstallationsManager from "pages/manager"
|
import PackagesMangerPage from "pages/manager"
|
||||||
|
import SettingsPage from "pages/settings"
|
||||||
|
|
||||||
|
const NavigationController = (props) => {
|
||||||
|
if (!app.location) {
|
||||||
|
app.location = Object()
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
async function setLocation(to, state = {}) {
|
||||||
|
// clean double slashes
|
||||||
|
to = to.replace(/\/{2,}/g, "/")
|
||||||
|
|
||||||
|
// if state is a number, it's a delay
|
||||||
|
if (typeof state !== "object") {
|
||||||
|
state = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.location.last = window.location
|
||||||
|
|
||||||
|
return navigate(to, {
|
||||||
|
state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function backLocation() {
|
||||||
|
app.location.last = window.location
|
||||||
|
|
||||||
|
if (transitionDuration >= 100) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, transitionDuration))
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.history.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
app.location = {
|
||||||
|
last: window.location,
|
||||||
|
push: setLocation,
|
||||||
|
back: backLocation,
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return props.children
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InternalRouter = (props) => {
|
||||||
|
return <BrowserRouter>
|
||||||
|
<NavigationController>
|
||||||
|
{props.children}
|
||||||
|
</NavigationController>
|
||||||
|
</BrowserRouter>
|
||||||
|
}
|
||||||
|
|
||||||
export const PageRender = () => {
|
export const PageRender = () => {
|
||||||
const globalState = React.useContext(GlobalStateContext)
|
const globalState = React.useContext(GlobalStateContext)
|
||||||
@ -23,5 +77,8 @@ export const PageRender = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
return <InstallationsManager />
|
return <Routes>
|
||||||
|
<Route path="/" element={<PackagesMangerPage />} />
|
||||||
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
|
</Routes>
|
||||||
}
|
}
|
@ -1,11 +1,7 @@
|
|||||||
@import "style/reset.css";
|
@import "style/reset.css";
|
||||||
@import "style/fix.less";
|
@import "style/fix.less";
|
||||||
|
|
||||||
@var-text-color: #fff;
|
@import "style/vars.less";
|
||||||
@var-background-color-primary: #424549;
|
|
||||||
@var-background-color-secondary: #1e2124;
|
|
||||||
@var-primary-color: #36d7b7; //#F3B61F;
|
|
||||||
@var-border-color: #a1a2a2;
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background-color-primary: @var-background-color-primary;
|
--background-color-primary: @var-background-color-primary;
|
||||||
|
5
src/renderer/src/style/vars.less
Normal file
5
src/renderer/src/style/vars.less
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@var-text-color: #fff;
|
||||||
|
@var-background-color-primary: #424549;
|
||||||
|
@var-background-color-secondary: #1e2124;
|
||||||
|
@var-primary-color: #36d7b7; //#F3B61F;
|
||||||
|
@var-border-color: #a1a2a2;
|
Loading…
x
Reference in New Issue
Block a user