mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-18 06:54:15 +00:00
Refactor routing to use @ragestudio/vessel/router package
- Remove custom router implementation - Update App.jsx to use new Router.Render with route declarations - Add react-router dependency - Update static renders to match new router structure
This commit is contained in:
parent
0499f64e74
commit
14f38b87c5
@ -14,6 +14,11 @@ export default [
|
|||||||
useLayout: "default",
|
useLayout: "default",
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/tv/*",
|
||||||
|
useLayout: "default",
|
||||||
|
centeredContent: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/featured-event/*",
|
path: "/featured-event/*",
|
||||||
useLayout: "default",
|
useLayout: "default",
|
||||||
@ -31,7 +36,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: "/music/*",
|
path: "/music/*",
|
||||||
useLayout: "default",
|
useLayout: "default",
|
||||||
centeredContent: true,
|
centeredContent: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/nfc/*",
|
path: "/nfc/*",
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
"react-modal-image": "^2.6.0",
|
"react-modal-image": "^2.6.0",
|
||||||
"react-player": "^2.16.0",
|
"react-player": "^2.16.0",
|
||||||
"react-rnd": "^10.4.14",
|
"react-rnd": "^10.4.14",
|
||||||
|
"react-router": "^7.6.2",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"react-useanimations": "^2.10.0",
|
"react-useanimations": "^2.10.0",
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Runtime } from "@ragestudio/vessel"
|
import { Runtime } from "@ragestudio/vessel"
|
||||||
|
import * as Router from "@ragestudio/vessel/router"
|
||||||
|
import routesDeclarations from "@config/routes"
|
||||||
|
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import * as Sentry from "@sentry/browser"
|
import * as Sentry from "@sentry/browser"
|
||||||
import { invoke } from "@tauri-apps/api/tauri"
|
import { invoke } from "@tauri-apps/api/tauri"
|
||||||
@ -14,7 +17,6 @@ import DesktopTopBar from "@components/DesktopTopBar"
|
|||||||
import { ThemeProvider } from "@cores/style/style.core.jsx"
|
import { ThemeProvider } from "@cores/style/style.core.jsx"
|
||||||
|
|
||||||
import Layout from "./layout"
|
import Layout from "./layout"
|
||||||
import * as Router from "./router"
|
|
||||||
|
|
||||||
import StaticMethods from "./statics/methods"
|
import StaticMethods from "./statics/methods"
|
||||||
import StaticEvents from "./statics/events"
|
import StaticEvents from "./statics/events"
|
||||||
@ -117,7 +119,7 @@ class ComtyApp extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<meta property="og:title" content={config.app.siteName} />
|
<meta property="og:title" content={config.app.siteName} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Router.InternalRouter>
|
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
{window.__TAURI__ && <DesktopTopBar />}
|
{window.__TAURI__ && <DesktopTopBar />}
|
||||||
<Layout
|
<Layout
|
||||||
@ -125,11 +127,13 @@ class ComtyApp extends React.Component {
|
|||||||
staticRenders={ComtyApp.staticRenders}
|
staticRenders={ComtyApp.staticRenders}
|
||||||
>
|
>
|
||||||
{this.state.firstInitialized && (
|
{this.state.firstInitialized && (
|
||||||
<Router.PageRender />
|
<Router.Render
|
||||||
|
declarations={routesDeclarations}
|
||||||
|
staticRenders={ComtyApp.staticRenders}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Layout>
|
</Layout>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</Router.InternalRouter>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,244 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { BrowserRouter, Route, Routes, useNavigate, useParams } from "react-router-dom"
|
|
||||||
import { Skeleton } from "antd"
|
|
||||||
import config from "@config"
|
|
||||||
import loadable from "@loadable/component"
|
|
||||||
|
|
||||||
import routesDeclaration from "@config/routes"
|
|
||||||
|
|
||||||
const DefaultNotFoundRender = () => {
|
|
||||||
return <div>Not found</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const DefaultLoadingRender = () => {
|
|
||||||
return <Skeleton active />
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPagePaths = () => {
|
|
||||||
let paths = {
|
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.jsx"),
|
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.tsx"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.isMobile) {
|
|
||||||
paths = {
|
|
||||||
...paths,
|
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.mobile.jsx"),
|
|
||||||
...import.meta.glob("/src/pages/**/[a-z[]*.mobile.tsx"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// find & replace matching non mobile routes with mobile routes
|
|
||||||
Object.keys(paths).forEach((path) => {
|
|
||||||
const mobilePath = path.replace(/\.jsx$/, ".mobile.jsx").replace(/\.tsx$/, ".mobile.tsx")
|
|
||||||
|
|
||||||
if (paths[mobilePath]) {
|
|
||||||
delete paths[path]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateRoutes = () => {
|
|
||||||
let paths = getPagePaths()
|
|
||||||
|
|
||||||
return 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(/\[([a-z]+)\]/g, ":$1")
|
|
||||||
path = path.replace(/\[\.{3}.+\]/, "*").replace(/\[(.+)\]/, ":$1")
|
|
||||||
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
element: paths[route],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function findRouteDeclaration(route) {
|
|
||||||
return routesDeclaration.find((layout) => {
|
|
||||||
const routePath = layout.path.replace(/\*/g, ".*").replace(/!/g, "^")
|
|
||||||
|
|
||||||
return new RegExp(routePath).test(route)
|
|
||||||
}) ?? {
|
|
||||||
path: route,
|
|
||||||
useLayout: "default",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAuthenticated() {
|
|
||||||
return !!app.userData
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRouteDeclaration(declaration) {
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (declaration) {
|
|
||||||
// if not authenticated and is not in public route, redirect
|
|
||||||
if (!isAuthenticated() && !declaration.public && (window.location.pathname !== config.app?.authPath)) {
|
|
||||||
if (typeof window.app.location.push === "function") {
|
|
||||||
window.app.location.push(config.app?.authPath ?? "/login")
|
|
||||||
|
|
||||||
app.cores.notifications.new({
|
|
||||||
title: "Please login to use this feature.",
|
|
||||||
duration: 15,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
window.location.href = config.app?.authPath ?? "/login"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (declaration.useLayout) {
|
|
||||||
app.layout.set(declaration.useLayout)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof declaration.centeredContent !== "undefined") {
|
|
||||||
let finalBool = null
|
|
||||||
|
|
||||||
if (typeof declaration.centeredContent === "boolean") {
|
|
||||||
finalBool = declaration.centeredContent
|
|
||||||
} else {
|
|
||||||
if (app.isMobile) {
|
|
||||||
finalBool = declaration.centeredContent?.mobile ?? null
|
|
||||||
} else {
|
|
||||||
finalBool = declaration.centeredContent?.desktop ?? null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.layout.toggleCenteredContent(finalBool)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof declaration.useTitle !== "undefined") {
|
|
||||||
if (typeof declaration.useTitle === "function") {
|
|
||||||
declaration.useTitle = declaration.useTitle(path, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = `${declaration.useTitle} - ${config.app.siteName}`
|
|
||||||
} else {
|
|
||||||
document.title = config.app.siteName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
function generatePageElementWrapper(path, element, props, declaration) {
|
|
||||||
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),
|
|
||||||
})
|
|
||||||
|
|
||||||
handleRouteDeclaration(declaration)
|
|
||||||
|
|
||||||
return React.createElement(
|
|
||||||
loadable(element, {
|
|
||||||
fallback: React.createElement(props.staticRenders?.PageLoad || DefaultLoadingRender),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
...props,
|
|
||||||
...props,
|
|
||||||
url: url,
|
|
||||||
params: params,
|
|
||||||
query: query,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
await navigate(to, {
|
|
||||||
state
|
|
||||||
})
|
|
||||||
|
|
||||||
app.eventBus.emit("router.navigate", to, {
|
|
||||||
state,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
to,
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function backLocation() {
|
|
||||||
return window.history.back()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onHistoryChange() {
|
|
||||||
setTimeout(() => {
|
|
||||||
app.eventBus.emit("router.navigate", window.location.pathname, {
|
|
||||||
state: window.location.state,
|
|
||||||
})
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
app.location = {
|
|
||||||
last: window.location,
|
|
||||||
push: setLocation,
|
|
||||||
back: backLocation,
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("popstate", onHistoryChange)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("popstate", onHistoryChange)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return props.children
|
|
||||||
}
|
|
||||||
|
|
||||||
export const InternalRouter = (props) => {
|
|
||||||
return <BrowserRouter>
|
|
||||||
<NavigationController>
|
|
||||||
{
|
|
||||||
props.children
|
|
||||||
}
|
|
||||||
</NavigationController>
|
|
||||||
</BrowserRouter>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PageRender = React.memo((props) => {
|
|
||||||
let routes = generateRoutes()
|
|
||||||
|
|
||||||
return <Routes>
|
|
||||||
{
|
|
||||||
routes.map((route, index) => {
|
|
||||||
const declaration = findRouteDeclaration(route.path)
|
|
||||||
|
|
||||||
return <Route
|
|
||||||
key={index}
|
|
||||||
path={route.path}
|
|
||||||
element={generatePageElementWrapper(route.path, route.element, props, declaration)}
|
|
||||||
exact
|
|
||||||
/>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<Route
|
|
||||||
path="*"
|
|
||||||
element={React.createElement(props.staticRenders?.NotFound || DefaultNotFoundRender)}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
})
|
|
@ -1,14 +1,8 @@
|
|||||||
import { NotFound, RenderError, Crash } from "@components"
|
import { NotFound, Crash, Skeleton } from "@components"
|
||||||
|
import ErrorCatcher from "@components/ErrorCatcher"
|
||||||
export default {
|
export default {
|
||||||
PageLoad: () => {
|
Loading: Skeleton,
|
||||||
return <antd.Skeleton active />
|
NotFound: NotFound,
|
||||||
},
|
RenderError: ErrorCatcher,
|
||||||
NotFound: (props) => {
|
|
||||||
return <NotFound />
|
|
||||||
},
|
|
||||||
RenderError: (props) => {
|
|
||||||
return <RenderError {...props} />
|
|
||||||
},
|
|
||||||
Crash: Crash.CrashWrapper,
|
Crash: Crash.CrashWrapper,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user