split widget and default App components

use router in default App for retrieving quizId from url
add QuizIdContext
This commit is contained in:
nflnkr 2024-02-01 16:18:16 +03:00
parent db541d97ef
commit fb3d46110f
9 changed files with 133 additions and 54 deletions

@ -27,6 +27,7 @@
"notistack": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.3",
"swr": "^2.2.4",
"typescript": "^5.2.2",
"use-debounce": "^9.0.4",

@ -1,56 +1,24 @@
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import { ruRU } from '@mui/x-date-pickers/locales';
import moment from "moment";
import { SnackbarProvider } from 'notistack';
import { SWRConfig } from "swr";
import { Box } from "@mui/material";
import ContextProviders from "ContextProviders";
import { QuizIdContext } from "contexts/QuizIdContext";
import { useParams } from "react-router-dom";
import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
import lightTheme from "./utils/themes/light";
const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2";
moment.locale("ru");
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
interface Props {
widget?: boolean;
quizId?: string;
}
export default function App({ widget = false, quizId }: Props) {
quizId ??= defaultQuizId;
export default function App() {
const quizId = useParams().quizId ?? defaultQuizId;
return (
<SWRConfig value={{
revalidateOnFocus: false,
shouldRetryOnError: false,
}}>
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
<ThemeProvider theme={lightTheme}>
<SnackbarProvider
preventDuplicate={true}
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
>
<CssBaseline />
{widget ? (
<Box sx={{
width: "100%",
height: "100%",
}}>
<ViewPage quizId={quizId} />
</Box>
) : (
<Box sx={{
height: "100dvh",
}}>
<ViewPage quizId={quizId} />
</Box>
)}
</SnackbarProvider>
</ThemeProvider>
</LocalizationProvider>
</SWRConfig>
<QuizIdContext.Provider value={quizId}>
<ContextProviders>
<Box sx={{
height: "100dvh",
}}>
<ViewPage />
</Box>
</ContextProviders>
</QuizIdContext.Provider>
);
}

38
src/ContextProviders.tsx Normal file

@ -0,0 +1,38 @@
import { CssBaseline, ThemeProvider } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import { ruRU } from '@mui/x-date-pickers/locales';
import moment from "moment";
import { SnackbarProvider } from 'notistack';
import { SWRConfig } from "swr";
import lightTheme from "./utils/themes/light";
moment.locale("ru");
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
interface Props {
children: JSX.Element;
}
export default function ContextProviders({ children }: Props) {
return (
<SWRConfig value={{
revalidateOnFocus: false,
shouldRetryOnError: false,
}}>
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
<ThemeProvider theme={lightTheme}>
<SnackbarProvider
preventDuplicate={true}
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
>
<CssBaseline />
{children}
</SnackbarProvider>
</ThemeProvider>
</LocalizationProvider>
</SWRConfig>
);
}

25
src/WidgetApp.tsx Normal file

@ -0,0 +1,25 @@
import { Box } from "@mui/material";
import ContextProviders from "ContextProviders";
import { QuizIdContext } from "contexts/QuizIdContext";
import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage";
interface Props {
quizId: string;
}
export default function WidgetApp({ quizId }: Props) {
return (
<QuizIdContext.Provider value={quizId}>
<ContextProviders>
<Box sx={{
width: "100%",
height: "100%",
}}>
<ViewPage />
</Box>
</ContextProviders>
</QuizIdContext.Provider>
);
}

@ -0,0 +1,11 @@
import { createContext, useContext } from "react";
export const QuizIdContext = createContext<string | null>(null);
export const useQuizId = () => {
const quizId = useContext(QuizIdContext);
if (quizId === null) throw new Error("quizId context is null");
return quizId;
};

@ -1,7 +1,24 @@
import { createRoot } from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import App from "./App";
const router = createBrowserRouter([
{
path: "/",
children: [
{
index: true,
element: <App />,
},
{
path: ":quizId",
element: <App />,
},
]
}
]);
const root = createRoot(document.getElementById("root")!);
root.render(<App />);
root.render(<RouterProvider router={router} />);

@ -13,13 +13,12 @@ import { Question } from "./Question";
import { StartPageViewPublication } from "./StartPageViewPublication";
import { parseQuizData } from "@model/api/getQuizData";
import { useQuizId } from "contexts/QuizIdContext";
import { replaceSpacesToEmptyLines } from "./tools/replaceSpacesToEmptyLines";
type Props = {
quizId: string;
};
export const ViewPage = ({ quizId }: Props) => {
export const ViewPage = () => {
const quizId = useQuizId();
const { isLoading, error } = useSWR(["quizData", quizId], params => getQuizData(params[1]), {
onSuccess: setQuizData,
});

@ -1,5 +1,5 @@
import WidgetApp from "./WidgetApp";
import { Root, createRoot } from "react-dom/client";
import App from "./App";
let root: Root | undefined = undefined;
@ -14,7 +14,7 @@ const widget = {
root = createRoot(element);
root.render(<App widget quizId={quizId} />);
root.render(<WidgetApp quizId={quizId} />);
},
unmount() {
if (root) root.unmount();

@ -737,6 +737,11 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@remix-run/router@1.14.2":
version "1.14.2"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.2.tgz#4d58f59908d9197ba3179310077f25c88e49ed17"
integrity sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==
"@rollup/rollup-android-arm-eabi@4.9.5":
version "4.9.5"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz#b752b6c88a14ccfcbdf3f48c577ccc3a7f0e66b9"
@ -2724,6 +2729,21 @@ react-refresh@^0.14.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
react-router-dom@^6.21.3:
version "6.21.3"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.21.3.tgz#ef3a7956a3699c7b82c21fcb3dbc63c313ed8c5d"
integrity sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==
dependencies:
"@remix-run/router" "1.14.2"
react-router "6.21.3"
react-router@6.21.3:
version "6.21.3"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.21.3.tgz#8086cea922c2bfebbb49c6594967418f1f167d70"
integrity sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==
dependencies:
"@remix-run/router" "1.14.2"
react-transition-group@^4.4.5:
version "4.4.5"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"