mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 19:14:16 +00:00
improve router
This commit is contained in:
parent
7369126c65
commit
f09f695b46
@ -45,13 +45,12 @@
|
|||||||
"electron-is": "^3.0.0",
|
"electron-is": "^3.0.0",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"evite": "0.13.7",
|
"evite": "0.13.9",
|
||||||
"fast-average-color": "^9.2.0",
|
"fast-average-color": "^9.2.0",
|
||||||
"faye": "1.4.0",
|
"faye": "1.4.0",
|
||||||
"feather-reactjs": "2.0.13",
|
"feather-reactjs": "2.0.13",
|
||||||
"fuse.js": "6.5.3",
|
"fuse.js": "6.5.3",
|
||||||
"global": "4.4.0",
|
"global": "4.4.0",
|
||||||
"history": "5.2.0",
|
|
||||||
"hls.js": "^1.1.5",
|
"hls.js": "^1.1.5",
|
||||||
"howler": "2.2.3",
|
"howler": "2.2.3",
|
||||||
"i18next": "21.6.6",
|
"i18next": "21.6.6",
|
||||||
@ -59,7 +58,9 @@
|
|||||||
"jwt-decode": "3.1.2",
|
"jwt-decode": "3.1.2",
|
||||||
"less": "4.1.2",
|
"less": "4.1.2",
|
||||||
"linebridge": "0.13.0",
|
"linebridge": "0.13.0",
|
||||||
|
"localforage": "^1.10.0",
|
||||||
"luxon": "^3.0.4",
|
"luxon": "^3.0.4",
|
||||||
|
"match-sorter": "^6.3.1",
|
||||||
"moment": "2.29.4",
|
"moment": "2.29.4",
|
||||||
"moment-timezone": "^0.5.37",
|
"moment-timezone": "^0.5.37",
|
||||||
"mpegts.js": "^1.6.10",
|
"mpegts.js": "^1.6.10",
|
||||||
@ -89,10 +90,12 @@
|
|||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
"react-reveal": "1.2.2",
|
"react-reveal": "1.2.2",
|
||||||
"react-rnd": "10.3.5",
|
"react-rnd": "10.3.5",
|
||||||
|
"react-router-dom": "^6.6.2",
|
||||||
"react-ticker": "^1.3.2",
|
"react-ticker": "^1.3.2",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"rxjs": "^7.5.5",
|
"rxjs": "^7.5.5",
|
||||||
|
"sort-by": "^1.2.0",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"wait-on": "^6.0.1"
|
"wait-on": "^6.0.1"
|
||||||
|
@ -53,6 +53,7 @@ import { EviteRuntime } from "evite"
|
|||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Toast } from "antd-mobile"
|
import { Toast } from "antd-mobile"
|
||||||
|
import { BrowserRouter } from "react-router-dom"
|
||||||
import { StatusBar, Style } from "@capacitor/status-bar"
|
import { StatusBar, Style } from "@capacitor/status-bar"
|
||||||
import { App as CapacitorApp } from "@capacitor/app"
|
import { App as CapacitorApp } from "@capacitor/app"
|
||||||
import { Translation } from "react-i18next"
|
import { Translation } from "react-i18next"
|
||||||
@ -455,15 +456,14 @@ class App extends React.Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
StatusBar.setOverlaysWebView({ overlay: true })
|
StatusBar.setOverlaysWebView({ overlay: true })
|
||||||
//window.app.hideStatusBar()
|
|
||||||
|
|
||||||
CapacitorApp.addListener('backButton', ({ canGoBack }) => {
|
CapacitorApp.addListener("backButton", ({ canGoBack }) => {
|
||||||
if (!canGoBack) {
|
if (!canGoBack) {
|
||||||
CapacitorApp.exitApp();
|
CapacitorApp.exitApp()
|
||||||
} else {
|
} else {
|
||||||
window.history.back();
|
window.history.back()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const userAgentPlatform = window.navigator.userAgent.toLowerCase()
|
const userAgentPlatform = window.navigator.userAgent.toLowerCase()
|
||||||
@ -532,6 +532,7 @@ class App extends React.Component {
|
|||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
await this.__SessionInit()
|
await this.__SessionInit()
|
||||||
|
await this.__UserInit()
|
||||||
|
|
||||||
app.eventBus.emit("app.initialization.session_success")
|
app.eventBus.emit("app.initialization.session_success")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -543,20 +544,6 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async () => {
|
|
||||||
try {
|
|
||||||
await this.__UserInit()
|
|
||||||
|
|
||||||
app.eventBus.emit("app.initialization.user_success")
|
|
||||||
} catch (error) {
|
|
||||||
app.eventBus.emit("app.initialization.user_error", error)
|
|
||||||
|
|
||||||
throw {
|
|
||||||
cause: "Cannot initialize user data",
|
|
||||||
details: error.message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
await Promise.tasked(initializationTasks).catch((reason) => {
|
await Promise.tasked(initializationTasks).catch((reason) => {
|
||||||
@ -607,8 +594,8 @@ class App extends React.Component {
|
|||||||
<meta name="og:description" content={config.app.siteDescription} />
|
<meta name="og:description" content={config.app.siteDescription} />
|
||||||
<meta property="og:title" content={config.app.siteName} />
|
<meta property="og:title" content={config.app.siteName} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<antd.ConfigProvider>
|
<BrowserRouter>
|
||||||
<Router.InternalRouter>
|
<antd.ConfigProvider>
|
||||||
<Layout
|
<Layout
|
||||||
user={this.state.user}
|
user={this.state.user}
|
||||||
staticRenders={App.staticRenders}
|
staticRenders={App.staticRenders}
|
||||||
@ -622,8 +609,8 @@ class App extends React.Component {
|
|||||||
>
|
>
|
||||||
<Router.PageRender />
|
<Router.PageRender />
|
||||||
</Layout>
|
</Layout>
|
||||||
</Router.InternalRouter>
|
</antd.ConfigProvider>
|
||||||
</antd.ConfigProvider>
|
</BrowserRouter>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,35 +54,8 @@ export default class Layout extends React.PureComponent {
|
|||||||
|
|
||||||
document.querySelector("#transitionLayer").classList.add("fade-opacity-leave")
|
document.querySelector("#transitionLayer").classList.add("fade-opacity-leave")
|
||||||
},
|
},
|
||||||
"router.transitionStart": () => {
|
"router.navigate": (path, options) => {
|
||||||
this.progressBar.start()
|
this.makePageTransition(path, options)
|
||||||
|
|
||||||
if (!app.settings.get("reduceAnimations")) {
|
|
||||||
// add "fade-transverse-leave" class to `transitionLayer`
|
|
||||||
const transitionLayer = document.getElementById("transitionLayer")
|
|
||||||
|
|
||||||
if (!transitionLayer) {
|
|
||||||
console.warn("transitionLayer not found, no animation will be played")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionLayer.classList.add("fade-transverse-leave")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"router.transitionFinish": () => {
|
|
||||||
this.progressBar.done()
|
|
||||||
|
|
||||||
if (!app.settings.get("reduceAnimations")) {
|
|
||||||
// remove "fade-transverse-leave" class to `transitionLayer`
|
|
||||||
const transitionLayer = document.getElementById("transitionLayer")
|
|
||||||
|
|
||||||
if (!transitionLayer) {
|
|
||||||
console.warn("transitionLayer not found, no animation will be played")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionLayer.classList.remove("fade-transverse-leave")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +85,31 @@ export default class Layout extends React.PureComponent {
|
|||||||
this.setState({ renderError: { info, stack } })
|
this.setState({ renderError: { info, stack } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makePageTransition(path, options = {}) {
|
||||||
|
this.progressBar.start()
|
||||||
|
|
||||||
|
if (app.settings.get("reduceAnimations") || options.state.noTransition) {
|
||||||
|
this.progressBar.done()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const transitionLayer = document.getElementById("transitionLayer")
|
||||||
|
|
||||||
|
if (!transitionLayer) {
|
||||||
|
console.warn("transitionLayer not found, no animation will be played")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionLayer.classList.add("fade-transverse-leave")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.progressBar.done()
|
||||||
|
|
||||||
|
transitionLayer.classList.remove("fade-transverse-leave")
|
||||||
|
}, options.state.transitionDelay ?? 250)
|
||||||
|
}
|
||||||
|
|
||||||
setLayout = (layout) => {
|
setLayout = (layout) => {
|
||||||
if (typeof Layouts[layout] === "function") {
|
if (typeof Layouts[layout] === "function") {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Switch, Route, BrowserRouter, withRouter } from "react-router-dom"
|
import { Route, Routes, Navigate, useLocation, useNavigate, useParams } from "react-router-dom"
|
||||||
|
import { Skeleton } from "antd"
|
||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
const NotFoundRender = () => {
|
const NotFoundRender = () => {
|
||||||
return <div>Not found</div>
|
return <div>Not found</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LoadingRender = () => {
|
||||||
|
return <Skeleton active />
|
||||||
|
}
|
||||||
|
|
||||||
const paths = {
|
const paths = {
|
||||||
...import.meta.glob("/src/debug/components/**/[a-z[]*.jsx"),
|
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.jsx"),
|
...import.meta.glob("/src/pages/**/[a-z[]*.jsx"),
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.tsx"),
|
...import.meta.glob("/src/pages/**/[a-z[]*.tsx"),
|
||||||
}
|
}
|
||||||
@ -16,166 +21,103 @@ const pathsMobile = {
|
|||||||
...import.meta.glob("/src/pages/**/[a-z[]*.mobile.tsx"),
|
...import.meta.glob("/src/pages/**/[a-z[]*.mobile.tsx"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateElementWrapper(route, element, bindProps) {
|
||||||
|
return React.createElement((props) => {
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
loadable(element, {
|
||||||
|
fallback: React.createElement(LoadingRender),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
...bindProps,
|
||||||
|
params: params,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const routes = Object.keys(paths).map((route) => {
|
const routes = Object.keys(paths).map((route) => {
|
||||||
const path = route
|
const path = route
|
||||||
.replace(/\/src\/pages|index|\.jsx$/g, "")
|
.replace(/\/src\/pages|index|\.jsx$/g, "")
|
||||||
.replace(/\/src\/pages|index|\.tsx$/g, "")
|
.replace(/\/src\/pages|index|\.tsx$/g, "")
|
||||||
.replace(/\/src\/debug\/components/g, "/debug")
|
|
||||||
.replace(/\[\.{3}.+\]/, "*")
|
.replace(/\[\.{3}.+\]/, "*")
|
||||||
.replace(/\[(.+)\]/, ":$1")
|
.replace(/\[(.+)\]/, ":$1")
|
||||||
|
|
||||||
return { path, component: React.lazy(paths[route]) }
|
return {
|
||||||
|
path,
|
||||||
|
element: paths[route]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const mobileComponents = Object.fromEntries(Object.keys(pathsMobile).map((route) => {
|
const mobileRoutes = Object.keys(pathsMobile).map((route) => {
|
||||||
const path = route
|
const path = route
|
||||||
.replace(/\/src\/pages|index|\.mobile|\.jsx$/g, "")
|
.replace(/\/src\/pages|index|\.mobile|\.jsx$/g, "")
|
||||||
.replace(/\/src\/pages|index|\.mobile|\.tsx$/g, "")
|
.replace(/\/src\/pages|index|\.mobile|\.tsx$/g, "")
|
||||||
.replace(/\/src\/debug\/components/g, "/debug")
|
|
||||||
.replace(/\[\.{3}.+\]/, "*")
|
.replace(/\[\.{3}.+\]/, "*")
|
||||||
.replace(/\[(.+)\]/, ":$1")
|
.replace(/\[(.+)\]/, ":$1")
|
||||||
|
|
||||||
return [path, React.lazy(pathsMobile[route])]
|
return {
|
||||||
}))
|
path,
|
||||||
|
element: pathsMobile[path]
|
||||||
export function BindContexts(component) {
|
|
||||||
let contexts = {
|
|
||||||
main: {},
|
|
||||||
app: {},
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (typeof component.bindApp === "string") {
|
export const PageRender = React.memo((props) => {
|
||||||
if (component.bindApp === "all") {
|
const navigate = useNavigate()
|
||||||
Object.keys(app).forEach((key) => {
|
app.location = useLocation()
|
||||||
contexts.app[key] = app[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Array.isArray(component.bindApp)) {
|
|
||||||
component.bindApp.forEach((key) => {
|
|
||||||
contexts.app[key] = app[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof component.bindMain === "string") {
|
|
||||||
if (component.bindMain === "all") {
|
|
||||||
Object.keys(main).forEach((key) => {
|
|
||||||
contexts.main[key] = main[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Array.isArray(component.bindMain)) {
|
|
||||||
component.bindMain.forEach((key) => {
|
|
||||||
contexts.main[key] = main[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (props) => React.createElement(component, { ...props, contexts })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Router = withRouter((props) => {
|
|
||||||
const defaultTransitionDelay = 150
|
|
||||||
const forceUpdate = React.useReducer(() => ({}))[1]
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
props.history.listen((event) => {
|
app.setLocation = async (to, state = {}) => {
|
||||||
if (typeof props.onTransitionFinish === "function") {
|
|
||||||
props.onTransitionFinish(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.app.eventBus.emit("router.transitionFinish", event)
|
|
||||||
})
|
|
||||||
|
|
||||||
props.history.setLocation = (to, state = {}, delay = 150) => {
|
|
||||||
// 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 state is a number, it's a delay
|
||||||
if (typeof state !== "object") {
|
if (typeof state !== "object") {
|
||||||
delay = state
|
|
||||||
state = {}
|
state = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastLocation = props.history.lastLocation
|
state.transitionDelay = Number((app.style.getValue("page-transition-duration") ?? "250ms").replace("ms", ""))
|
||||||
|
|
||||||
if (typeof lastLocation !== "undefined" && lastLocation?.pathname === to && lastLocation?.state === state) {
|
app.eventBus.emit("router.navigate", to, {
|
||||||
return false
|
state,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (state.transitionDelay >= 100) {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, state.transitionDelay)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.onTransitionStart === "function") {
|
return navigate(to, {
|
||||||
props.onTransitionStart(delay)
|
state
|
||||||
}
|
})
|
||||||
|
|
||||||
window.app.eventBus.emit("router.transitionStart", delay)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
props.history.push({
|
|
||||||
pathname: to,
|
|
||||||
}, state)
|
|
||||||
|
|
||||||
props.history.lastLocation = window.location
|
|
||||||
}, delay ?? defaultTransitionDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.app.eventBus.on(`router.forceUpdate`, forceUpdate)
|
|
||||||
|
|
||||||
props.history.lastLocation = window.location
|
|
||||||
|
|
||||||
window.app.setLocation = props.history.setLocation
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const router = {
|
return <Routes>
|
||||||
history: props.history,
|
{
|
||||||
lastLocation: props.history.lastLocation,
|
routes.map((route, index) => {
|
||||||
forceUpdate,
|
let Element = route.element
|
||||||
}
|
|
||||||
|
|
||||||
// return children with router in props
|
|
||||||
return React.cloneElement(props.children, { router })
|
|
||||||
})
|
|
||||||
|
|
||||||
export const InternalRouter = (props) => {
|
|
||||||
return <BrowserRouter>
|
|
||||||
<Router {...props} />
|
|
||||||
</BrowserRouter>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PageRender = (props) => {
|
|
||||||
return <React.Suspense fallback={props.staticRenders?.PageLoad ? React.createElement(props.staticRenders?.PageLoad) : "Loading..."}>
|
|
||||||
<Switch>
|
|
||||||
{routes.map(({ path, component: Component = React.Fragment }) => {
|
|
||||||
if (window.isMobile) {
|
if (window.isMobile) {
|
||||||
if (mobileComponents[path]) {
|
const mobileElement = mobileRoutes.find((r) => r.path === route.path)
|
||||||
Component = mobileComponents[path]
|
|
||||||
} else {
|
if (mobileElement) {
|
||||||
console.warn(`No mobile component for ${path}, using default`)
|
Element = mobileElement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateRenderComponent = (props) => {
|
|
||||||
return React.createElement(BindContexts(Component), {
|
|
||||||
...props,
|
|
||||||
history: props.history,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Route
|
return <Route
|
||||||
key={path}
|
key={index}
|
||||||
path={path}
|
path={route.path}
|
||||||
component={generateRenderComponent}
|
element={generateElementWrapper(route.path, route.element, props)}
|
||||||
exact={true}
|
exact
|
||||||
/>
|
/>
|
||||||
})}
|
})
|
||||||
<Route path="*" component={props.staticRenders?.NotFound ?? NotFoundRender} />
|
}
|
||||||
</Switch>
|
<Route path="*" element={React.createElement(props.staticRenders?.NotFound) ?? <NotFoundRender />} />
|
||||||
</React.Suspense>
|
</Routes>
|
||||||
}
|
})
|
||||||
|
|
||||||
export default {
|
|
||||||
routes,
|
|
||||||
BindContexts,
|
|
||||||
InternalRouter,
|
|
||||||
PageRender,
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user