mirror of
https://github.com/ragestudio/relic.git
synced 2025-06-09 10:34:18 +00:00
use new router model
This commit is contained in:
parent
fbb87e46d1
commit
4ab0bc5975
@ -45,7 +45,7 @@ const PackageItem = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onClickOptions = () => {
|
const onClickOptions = () => {
|
||||||
app.location.push(`/package/${manifest.id}`)
|
app.location.push(`/pkg/${manifest.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickCancelInstall = () => {
|
const onClickCancelInstall = () => {
|
||||||
|
@ -2,69 +2,122 @@ import React from "react"
|
|||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
|
import PathsDecorators from "config/paths_decorators"
|
||||||
|
|
||||||
import GlobalStateContext from "contexts/global"
|
import GlobalStateContext from "contexts/global"
|
||||||
|
|
||||||
import Icon from "../../../../assets/icon"
|
import Icon from "../../../../assets/icon"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
const Header = (props) => {
|
const Header = (props) => {
|
||||||
const ctx = React.useContext(GlobalStateContext)
|
const ctx = React.useContext(GlobalStateContext)
|
||||||
|
|
||||||
return <antd.Layout.Header className="app_header">
|
const [decorator, setDecorator] = React.useState({})
|
||||||
<div className="branding" onClick={() => app.location.push("/")}>
|
const [navMode, setNavMode] = React.useState(false)
|
||||||
<Icon />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
function onChangeAppLocation(_path, state) {
|
||||||
!ctx.loading && <div className="menu">
|
const isNavigable = _path !== "/"
|
||||||
{
|
|
||||||
ctx.authorizedServices?.drive && <Icons.SiGoogledrive
|
|
||||||
style={{
|
|
||||||
color: `var(--primary-color)`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{/* {
|
const decorator = PathsDecorators.find((route) => {
|
||||||
ctx.updateText && <antd.Button
|
const routePath = route.path.replace(/\*/g, ".*").replace(/!/g, "^")
|
||||||
size="small"
|
|
||||||
icon={<Icons.MdRefresh />}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
{ctx.updateText}
|
|
||||||
</antd.Button>
|
|
||||||
} */}
|
|
||||||
|
|
||||||
{
|
return new RegExp(routePath).test(_path)
|
||||||
ctx.updateAvailable && <antd.Button
|
})
|
||||||
size="small"
|
|
||||||
icon={<Icons.MdDownload />}
|
|
||||||
onClick={app.applyUpdate}
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
Update now
|
|
||||||
</antd.Button>
|
|
||||||
}
|
|
||||||
|
|
||||||
<antd.Button
|
document.startViewTransition(() => {
|
||||||
size="small"
|
setDecorator({})
|
||||||
icon={<Icons.MdHome />}
|
setNavMode(isNavigable)
|
||||||
onClick={() => app.location.push("/")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<antd.Button
|
if (decorator) {
|
||||||
size="small"
|
setDecorator(decorator)
|
||||||
icon={<Icons.MdSettings />}
|
}
|
||||||
onClick={() => app.location.push("/settings")}
|
})
|
||||||
/>
|
}
|
||||||
|
|
||||||
{
|
React.useEffect(() => {
|
||||||
ctx.pkg && <antd.Tag>
|
app.location.listen(onChangeAppLocation)
|
||||||
v{ctx.pkg.version}
|
|
||||||
</antd.Tag>
|
return () => {
|
||||||
}
|
app.location.unlisten(onChangeAppLocation)
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</antd.Layout.Header>
|
}, [])
|
||||||
|
|
||||||
|
return <div className="app_header_wrapper">
|
||||||
|
<antd.Layout.Header className="app_header">
|
||||||
|
{
|
||||||
|
!navMode && <>
|
||||||
|
<div className="branding" onClick={() => app.location.push("/")}>
|
||||||
|
<Icon />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
!ctx.loading && <div className="menu">
|
||||||
|
{
|
||||||
|
ctx.authorizedServices?.drive && <Icons.SiGoogledrive
|
||||||
|
style={{
|
||||||
|
color: `var(--primary-color)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ctx.updateAvailable && <antd.Button
|
||||||
|
size="small"
|
||||||
|
icon={<Icons.MdDownload />}
|
||||||
|
onClick={app.applyUpdate}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
Update now
|
||||||
|
</antd.Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<antd.Button
|
||||||
|
size="small"
|
||||||
|
icon={<Icons.MdHome />}
|
||||||
|
onClick={() => app.location.push("/")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<antd.Button
|
||||||
|
size="small"
|
||||||
|
icon={<Icons.MdSettings />}
|
||||||
|
onClick={() => app.location.push("/settings")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
ctx.pkg && <antd.Tag>
|
||||||
|
v{ctx.pkg.version}
|
||||||
|
</antd.Tag>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
navMode && <div className="app_header_nav">
|
||||||
|
<div className="app_header_nav_back">
|
||||||
|
<Icons.MdChevronLeft
|
||||||
|
onClick={() => {
|
||||||
|
app.location.back()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="app_header_nav_title"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
{
|
||||||
|
decorator.label
|
||||||
|
}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</antd.Layout.Header>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Header
|
export default Header
|
90
src/renderer/src/layout/components/Header/index.less
Normal file
90
src/renderer/src/layout/components/Header/index.less
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
@import "style/vars.less";
|
||||||
|
|
||||||
|
.app_header_wrapper {
|
||||||
|
z-index: 1500;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_header {
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
view-transition-name: main-header;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background-color: darken(@var-background-color-primary, 10%);
|
||||||
|
|
||||||
|
gap: 30px;
|
||||||
|
|
||||||
|
height: var(--app_header_height);
|
||||||
|
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
.branding {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: var(--primary-color);
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_header_nav {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 15px;
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.app_header_nav_back {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_header_nav_title {
|
||||||
|
view-transition-name: main-header-text;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -264,7 +264,7 @@ const PackageOptions = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PackageOptionsLoader = (props) => {
|
const PackageOptionsLoader = (props) => {
|
||||||
const { pkg_id } = useParams()
|
const { pkg_id } = props.params
|
||||||
const [manifest, setManifest] = React.useState(null)
|
const [manifest, setManifest] = React.useState(null)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -279,17 +279,6 @@ const PackageOptionsLoader = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div className="package_options-wrapper">
|
return <div className="package_options-wrapper">
|
||||||
<div className="package_options-wrapper-header">
|
|
||||||
<div className="package_options-wrapper-header-back">
|
|
||||||
<Icons.MdChevronLeft
|
|
||||||
onClick={() => {
|
|
||||||
app.location.push("/")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
Back
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<PackageOptions
|
<PackageOptions
|
||||||
manifest={manifest}
|
manifest={manifest}
|
||||||
{...props}
|
{...props}
|
@ -3,33 +3,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.package_options-wrapper-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
.package_options-wrapper-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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.package_options {
|
.package_options {
|
||||||
|
@ -154,28 +154,11 @@ const SettingsList = ({ settings }) => {
|
|||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
return <div className="app_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">
|
<div className="app_settings-list">
|
||||||
<SettingsList
|
<SettingsList
|
||||||
settings={settingsList}
|
settings={settingsList}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,54 +1,112 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import BarLoader from "react-spinners/BarLoader"
|
import BarLoader from "react-spinners/BarLoader"
|
||||||
import { HashRouter, Route, Routes, useNavigate } from "react-router-dom"
|
import { Skeleton } from "antd"
|
||||||
|
|
||||||
|
import { HashRouter, Route, Routes, useNavigate, useParams } from "react-router-dom"
|
||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
import GlobalStateContext from "contexts/global"
|
import GlobalStateContext from "contexts/global"
|
||||||
|
|
||||||
import PackagesMangerPage from "pages/manager"
|
const DefaultNotFoundRender = () => {
|
||||||
import SettingsPage from "pages/settings"
|
return <div>Not found</div>
|
||||||
import PackageOptionsPage from "pages/pkg"
|
}
|
||||||
|
|
||||||
|
const DefaultLoadingRender = () => {
|
||||||
|
return <Skeleton active />
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuildPageController = (route, element, bindProps) => {
|
||||||
|
return React.createElement((props) => {
|
||||||
|
const params = useParams()
|
||||||
|
const url = new URL(window.location)
|
||||||
|
const query = new Proxy(url, {
|
||||||
|
get: (target, prop) => target.searchParams.get(prop),
|
||||||
|
})
|
||||||
|
|
||||||
|
route = route.replace(/\?.+$/, "").replace(/\/{2,}/g, "/")
|
||||||
|
route = route.replace(/\/$/, "")
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
loadable(element, {
|
||||||
|
fallback: React.createElement(DefaultLoadingRender),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
...bindProps,
|
||||||
|
url: url,
|
||||||
|
params: params,
|
||||||
|
query: query,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const NavigationController = (props) => {
|
const NavigationController = (props) => {
|
||||||
if (!app.location) {
|
if (!app.location) {
|
||||||
app.location = Object()
|
app.location = Object()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [initialized, setInitialized] = React.useState(false)
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
async function setLocation(to, state = {}) {
|
async function setLocation(to, state = {}) {
|
||||||
// clean double slashes
|
// clean double slashes
|
||||||
to = to.replace(/\/{2,}/g, "/")
|
to = to.replace(/\/{2,}/g, "/")
|
||||||
|
|
||||||
// if state is a number, it's a delay
|
|
||||||
if (typeof state !== "object") {
|
if (typeof state !== "object") {
|
||||||
state = {}
|
state = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const listener of app.location.listeners) {
|
||||||
|
if (typeof listener === "function") {
|
||||||
|
listener(to, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.location.path = to
|
||||||
|
|
||||||
app.location.last = window.location
|
app.location.last = window.location
|
||||||
|
|
||||||
return navigate(to, {
|
document.startViewTransition(() => {
|
||||||
state
|
navigate(to, {
|
||||||
|
state
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function backLocation() {
|
async function backLocation() {
|
||||||
app.location.last = window.location
|
return setLocation(app.location.last.pathname + app.location.last.search, app.location.last.state)
|
||||||
|
}
|
||||||
|
|
||||||
if (transitionDuration >= 100) {
|
function pushToListeners(listener) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, transitionDuration))
|
app.location.listeners.push(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.history.back()
|
function removeFromListeners(listener) {
|
||||||
|
app.location.listeners = app.location.listeners.filter((item) => {
|
||||||
|
return item !== listener
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
app.location = {
|
app.location = {
|
||||||
last: window.location,
|
last: window.location,
|
||||||
|
path: "/",
|
||||||
|
listeners: [],
|
||||||
|
listen: pushToListeners,
|
||||||
|
unlisten: removeFromListeners,
|
||||||
push: setLocation,
|
push: setLocation,
|
||||||
back: backLocation,
|
back: backLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInitialized(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
return <Skeleton />
|
||||||
|
}
|
||||||
|
|
||||||
return props.children
|
return props.children
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +118,31 @@ export const InternalRouter = (props) => {
|
|||||||
</HashRouter>
|
</HashRouter>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PageRender = () => {
|
export const PageRender = (props) => {
|
||||||
|
const routes = React.useMemo(() => {
|
||||||
|
let paths = {
|
||||||
|
...import.meta.glob("/src/pages/**/[a-z[]*.jsx"),
|
||||||
|
...import.meta.glob("/src/pages/**/[a-z[]*.tsx"),
|
||||||
|
}
|
||||||
|
|
||||||
|
paths = Object.keys(paths).map((route) => {
|
||||||
|
let path = route
|
||||||
|
.replace(/\/src\/pages|index|\.jsx$/g, "")
|
||||||
|
.replace(/\/src\/pages|index|\.tsx$/g, "")
|
||||||
|
.replace(/\/src\/pages|index|\.mobile|\.jsx$/g, "")
|
||||||
|
.replace(/\/src\/pages|index|\.mobile|\.tsx$/g, "")
|
||||||
|
|
||||||
|
path = path.replace(/\[\.{3}.+\]/, "*").replace(/\[(.+)\]/, ":$1")
|
||||||
|
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
element: paths[route],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}, [])
|
||||||
|
|
||||||
const globalState = React.useContext(GlobalStateContext)
|
const globalState = React.useContext(GlobalStateContext)
|
||||||
|
|
||||||
if (globalState.initializing_text && globalState.loading) {
|
if (globalState.initializing_text && globalState.loading) {
|
||||||
@ -79,8 +161,20 @@ export const PageRender = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <Routes>
|
return <Routes>
|
||||||
<Route exact path="/" element={<PackagesMangerPage />} />
|
{
|
||||||
<Route exact path="/settings" element={<SettingsPage />} />
|
routes.map((route, index) => {
|
||||||
<Route exact path="/package/:pkg_id" element={<PackageOptionsPage />} />
|
return <Route
|
||||||
|
key={index}
|
||||||
|
path={route.path}
|
||||||
|
element={BuildPageController(route.path, route.element, props)}
|
||||||
|
exact
|
||||||
|
/>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
element={React.createElement(DefaultNotFoundRender)}
|
||||||
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
}
|
}
|
@ -10,6 +10,9 @@
|
|||||||
--primary-color: @var-primary-color;
|
--primary-color: @var-primary-color;
|
||||||
--text-color: @var-text-color;
|
--text-color: @var-text-color;
|
||||||
--border-color: @var-border-color;
|
--border-color: @var-border-color;
|
||||||
|
|
||||||
|
--app_header_height: @var-app_header_height;
|
||||||
|
--app_global_padding: @var-app_global_padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
@ -47,47 +50,6 @@ body {
|
|||||||
background-color: var(--background-color-primary);
|
background-color: var(--background-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_header {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
background-color: darken(@var-background-color-primary, 10%);
|
|
||||||
|
|
||||||
gap: 30px;
|
|
||||||
|
|
||||||
padding: 0 20px;
|
|
||||||
|
|
||||||
.branding {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
color: var(--primary-color);
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.app_content {
|
.app_content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -2,4 +2,7 @@
|
|||||||
@var-background-color-primary: #424549;
|
@var-background-color-primary: #424549;
|
||||||
@var-background-color-secondary: #1e2124;
|
@var-background-color-secondary: #1e2124;
|
||||||
@var-primary-color: #36d7b7; //#F3B61F;
|
@var-primary-color: #36d7b7; //#F3B61F;
|
||||||
@var-border-color: #a1a2a2;
|
@var-border-color: #a1a2a2;
|
||||||
|
|
||||||
|
@var-app_header_height: 64px;
|
||||||
|
@var-app_global_padding: 20px;
|
Loading…
x
Reference in New Issue
Block a user