poc multi page

This commit is contained in:
FoxxMD 2026-06-12 17:57:54 +00:00
parent 892d035d7a
commit 4bfcc40571
5 changed files with 177 additions and 9 deletions

21
next/index.html Normal file
View file

@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="description"
content="Dashboard for Multi Scrobbler"
/>
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="manifest" href="/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Multi Scrobbler</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app. aaaa</noscript>
<div id="root"></div>
<script type="module" src="/src/client/index-next.tsx"></script>
</body>
</html>

View file

@ -82,7 +82,9 @@ export const initServer = async (parentLogger: Logger, appLoggerStream: PassThro
process.env.USE_HASH_ROUTER = root.get('isSubPath').toString();
}
app.get(/^\/next$/, (_, res) => {
res.redirect("/next/")
});
const viteExpressOptions: Parameters<typeof ViteExpress.config>[0] = {
mode: isProd ? 'production' : 'development',

120
src/client/AppNext.tsx Normal file
View file

@ -0,0 +1,120 @@
import React from 'react';
import {
createBrowserRouter,
createHashRouter, RouteObject,
RouterProvider, useLocation,
} from "react-router-dom";
import {connect, ConnectedProps, Provider as ReduxProvider} from 'react-redux'
import './App.css';
import CopyToClipboard from "./components/CopyToClipboard";
import ExternalLink from "./components/ExternalLink";
import {store} from './store';
import Dashboard from "./dashboard/dashboard";
import RecentPage from "./recent/RecentPage";
import ScrobbledPage from "./scrobbled/ScrobbledPage";
import DeadPage from "./deadLetter/DeadPage";
import {clientUpdate, sourceUpdate} from "./status/ducks";
import {useEventSource, useEventSourceListener} from "@react-nano/use-event-source";
import Version from "./Version";
import { MSComponentList } from './components/msComponent/MSComponentList';
import { Provider } from './components/Provider';
function NoMatch() {
const location = useLocation();
return (
<div>
No page for <code>{location.pathname}</code> exists!
</div>
);
}
// https://tailwindflex.com/@sienna/copy-code-block
function MissingDocs() {
return (
<div>
<div>Oops! You need to build docs first. Run the following commands to build:</div>
<code
className="mt-5 text-sm sm:text-base inline-flex text-left items-center space-x-4 bg-gray-900 text-white rounded-lg p-4 pl-6">
<span className="flex gap-4">
<span className="shrink-0 text-gray-500">
$
</span>
<span className="flex-1">
<span>
npm run <span className="text-yellow-500">docs:install</span> && npm run <span
className="text-yellow-500">docs:build</span>
</span>
</span>
</span>
<CopyToClipboard text="npm run docs:install && npm run docs:build"/>
</code>
</div>
);
}
const routes: RouteObject[] = [
{
path: "/next",
element: <MSComponentList components={[]}/>,
},
{
path: "/docs",
element: <MissingDocs/>
},
{
path: "*",
element: <NoMatch/>
}
];
const genRouter = () => {
const useHashRouter = __USE_HASH_ROUTER__ === 'true';
return useHashRouter ? createHashRouter(routes) : createBrowserRouter(routes);
}
const router = genRouter();
// const mapDispatchToProps = (dispatch) => {
// return {
// updateSource: (payload) => dispatch(sourceUpdate(payload)),
// updateClient: (payload) => dispatch(clientUpdate(payload))
// }
// }
// const connector = connect(null, mapDispatchToProps);
// type PropsFromRedux = ConnectedProps<typeof connector>;
// const Global = (props: PropsFromRedux) => {
// const {
// updateSource,
// updateClient
// } = props;
// const [sourceEventSource, eventSourceStatus] = useEventSource("api/events", false);
// useEventSourceListener(sourceEventSource, ['source', 'client'], evt => {
// const data = JSON.parse(evt.data);
// if(data.from === 'source') {
// updateSource(data);
// } else if(data.from === 'client') {
// updateClient(data);
// }
// }, [updateSource, updateClient]);
// return <span/>;
// }
// const ConnectedGlobal = connector(Global);
function App() {
return (
<Provider>
<RouterProvider router={router}/>
</Provider>
);
}
export default App;

18
src/client/index-next.tsx Normal file
View file

@ -0,0 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './AppNext.js';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import relativeTime from 'dayjs/plugin/relativeTime.js';
dayjs.extend(utc)
dayjs.extend(relativeTime);
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View file

@ -2,20 +2,21 @@ import react from '@vitejs/plugin-react';
import normalizeUrl from "normalize-url";
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite'
import { dirname, resolve } from 'node:path'
const QUOTES_UNWRAP_REGEX: RegExp = new RegExp(/^"(.*)"$/);
export const generateBaseURL = (userUrl: string | undefined): URL => {
let cleanUserUrl = userUrl.trim();
if(QUOTES_UNWRAP_REGEX.test(cleanUserUrl)) {
if (QUOTES_UNWRAP_REGEX.test(cleanUserUrl)) {
const results = cleanUserUrl.match(QUOTES_UNWRAP_REGEX);
cleanUserUrl = results[1];
}
const base = normalizeUrl(cleanUserUrl, {removeSingleSlash: true});
const base = normalizeUrl(cleanUserUrl, { removeSingleSlash: true });
const u = new URL(base);
if(u.port === '') {
if(u.protocol === 'https:') {
if (u.port === '') {
if (u.protocol === 'https:') {
u.port = '443';
} else if(userUrl.includes(`${u.hostname}:80`)) {
} else if (userUrl.includes(`${u.hostname}:80`)) {
u.port = '80';
}
}
@ -24,9 +25,9 @@ export const generateBaseURL = (userUrl: string | undefined): URL => {
export default defineConfig(() => {
let baseUrlStr = '/';
if(process.env.BASE_URL !== undefined && process.env.BASE_URL !== '') {
if (process.env.BASE_URL !== undefined && process.env.BASE_URL !== '') {
const baseUrl = generateBaseURL(process.env.BASE_URL);
if(baseUrl.pathname !== '/') {
if (baseUrl.pathname !== '/') {
baseUrlStr = baseUrl.toString();
}
}
@ -48,7 +49,13 @@ export default defineConfig(() => {
],
build: {
sourcemap: true,
cssCodeSplit: true
cssCodeSplit: true,
rolldownOptions: {
input: {
main: resolve(import.meta.dirname, 'index.html'),
next: resolve(import.meta.dirname, 'next/index.html'),
},
},
},
define: {
"__USE_HASH_ROUTER__": JSON.stringify((process.env.USE_HASH_ROUTER ?? false))