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:
SrGooglo 2025-06-16 20:56:15 +00:00
parent 0499f64e74
commit 14f38b87c5
5 changed files with 30 additions and 270 deletions

View File

@ -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/*",

View File

@ -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",

View File

@ -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,19 +119,21 @@ 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
user={this.auth.user} user={this.auth.user}
staticRenders={ComtyApp.staticRenders} staticRenders={ComtyApp.staticRenders}
> >
{this.state.firstInitialized && ( {this.state.firstInitialized && (
<Router.PageRender /> <Router.Render
)} declarations={routesDeclarations}
</Layout> staticRenders={ComtyApp.staticRenders}
</ThemeProvider> />
</Router.InternalRouter> )}
</Layout>
</ThemeProvider>
</React.Fragment> </React.Fragment>
) )
} }

View File

@ -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>
})

View File

@ -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,
} }