Commit 2bb8cff2 authored by timel's avatar timel

refactor: route

parent 9dc08cb0
......@@ -25,3 +25,7 @@ declare global {
) => (...args: Parameters<console.log>) => void;
}
}
declare module "react-router-dom" {
export declare function useLoaderData<T>(): T;
}
......@@ -40,7 +40,7 @@ body {
font-size: 14px;
display: flex;
margin: 0;
place-items: center;
// place-items: center;
min-width: 320px;
position: fixed;
top: 0;
......
export const Component = () => <>build</>;
Component.displayName = "Build";
......@@ -6,7 +6,7 @@
flex-wrap: wrap;
align-content: center;
padding: 0 2rem;
gap: 1rem;
gap: 1.5rem;
box-sizing: border-box;
.logo {
width: 60px;
......
import "@/styles/core.scss";
import { NavLink, Outlet } from "react-router-dom";
import { useConfig } from "@/config";
......@@ -19,7 +20,7 @@ export const NeosLayout = () => {
<NavLink to="/match">匹配</NavLink>
<NavLink to="/build">组卡</NavLink>
<span style={{ flexGrow: 1 }} />
<NavLink to="/home">个人中心</NavLink>
<NavLink to="/profile">个人中心</NavLink>
</nav>
<Outlet />
</>
......
......@@ -9,14 +9,14 @@ import {
import { NeosLayout } from "./Layout";
import LazyLoad, { Loading } from "./LazyLoad";
const Start = React.lazy(() => import("./Start"));
const Match = React.lazy(() => import("./Match"));
const WaitRoom = React.lazy(() => import("./WaitRoom"));
const Mora = React.lazy(() => import("./Mora"));
const NeosDuel = React.lazy(() => import("./Duel/Main"));
const Replay = React.lazy(() => import("./Replay"));
const SSO = React.lazy(() => import("./SSO"));
const Home = React.lazy(() => import("./Home"));
// const Match = React.lazy(() => import("./Match"));
// const WaitRoom = React.lazy(() => import("./WaitRoom"));
// const Mora = React.lazy(() => import("./Mora"));
// const NeosDuel = React.lazy(() => import("./Duel/Main"));
// const Replay = React.lazy(() => import("./Replay"));
// const SSO = React.lazy(() => import("./SSO"));
// const Profile = React.lazy(() => import("./Profile"));
// const Build = React.lazy(() => import("./Build"));
// TODO: finish this
const _router = createBrowserRouter([
......@@ -26,51 +26,66 @@ const _router = createBrowserRouter([
children: [
{
path: "/",
element: <LazyLoad lazy={<Start />} />,
lazy: () => import("./Start"),
},
{
path: "/match",
element: <LazyLoad lazy={<Match />} />,
path: "/match/*",
lazy: () => import("./NewMatch"),
},
{
path: "/build",
lazy: () => import("./Build"),
},
{
path: "/profile",
lazy: () => import("./Profile"),
},
{
path: "/waitroom/:ip/:player/:passWd",
lazy: () => import("./NewWaitRoom"),
},
{
path: "/duel/:ip/:player/:passWd",
},
],
},
]);
// 暂且先这么写,为后重构为createBrowserRouter打基础
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<NeosLayout />}>
<Route path="/" element={<LazyLoad lazy={<Start />} />} />
<Route path="/home" element={<LazyLoad lazy={<Home />} />} />
<Route path="/match" element={<LazyLoad lazy={<Match />} />} />
<Route
path="/room/:player/:passWd/:ip"
element={
<Suspense fallback={<Loading />}>
<WaitRoom />
</Suspense>
}
/>
<Route
path="/mora/:player/:passWd/:ip"
element={
<Suspense fallback={<Loading />}>
<Mora />
</Suspense>
}
/>
<Route path="/replay" element={<LazyLoad lazy={<Replay />} />} />
<Route
path="/duel/:player/:passWd/:ip"
element={
<Suspense fallback={<Loading />}>
<NeosDuel />
</Suspense>
}
/>
<Route path="/sso/*" element={<LazyLoad lazy={<SSO />} />} />
</Route>
)
);
// const router = createBrowserRouter(
// createRoutesFromElements(
// <Route path="/" element={<NeosLayout />}>
// <Route path="/" element={<LazyLoad lazy={<Start />} />} />
// <Route path="/profile" element={<LazyLoad lazy={<Profile />} />} />
// <Route path="/match" element={<LazyLoad lazy={<Match />} />} />
// <Route
// path="/room/:player/:passWd/:ip"
// element={
// <Suspense fallback={<Loading />}>
// <WaitRoom />
// </Suspense>
// }
// />
// <Route
// path="/mora/:player/:passWd/:ip"
// element={
// <Suspense fallback={<Loading />}>
// <Mora />
// </Suspense>
// }
// />
// <Route path="/replay" element={<LazyLoad lazy={<Replay />} />} />
// <Route
// path="/duel/:player/:passWd/:ip"
// element={
// <Suspense fallback={<Loading />}>
// <NeosDuel />
// </Suspense>
// }
// />
// <Route path="/sso/*" element={<LazyLoad lazy={<SSO />} />} />
// </Route>
// )
// );
export const NeosRouter = () => <RouterProvider router={router} />;
export const NeosRouter = () => <RouterProvider router={_router} />;
import { Link, type LoaderFunction } from "react-router-dom";
import { accountStore, type User } from "@/stores";
export const loader: LoaderFunction = () => {
const sso = new URLSearchParams(location.search).get("sso");
const user = sso ? getSSOUser(new URLSearchParams(atob(sso))) : undefined;
user && accountStore.login(user);
return null;
};
export const Component: React.FC = () => (
<>
new match
<Link to="/waitroom/1/2/3">waitroom</Link>
</>
);
Component.displayName = "Match";
/** 从SSO跳转回的URL之中,解析用户信息 */
function getSSOUser(searchParams: URLSearchParams): User {
return Object.fromEntries(searchParams) as unknown as User;
}
import { useLoaderData, type LoaderFunction } from "react-router-dom";
type Params = {
player?: string;
passWd?: string;
ip?: string;
};
export const loader: LoaderFunction = async ({ params }) => params;
export const Component: React.FC = () => {
const params = useLoaderData<Params>();
return <>{JSON.stringify(params)}</>;
};
......@@ -11,7 +11,7 @@ import styles from "./index.module.scss";
const NeosConfig = useConfig();
const DefaultUserName = "Guest";
const Home: React.FC = () => {
export const Component: React.FC = () => {
const { user } = useSnapshot(accountStore);
const navigate = useNavigate();
......@@ -86,5 +86,4 @@ const Home: React.FC = () => {
</>
);
};
export default Home;
Component.displayName = "Profile";
.hint {
font-size: 20px;
font-family: "Gill Sans", sans-serif;
color: white;
text-align: center;
margin-top: 100px;
}
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useConfig } from "@/config";
import { accountStore, User } from "@/stores";
import styles from "./index.module.scss";
const NeosConfig = useConfig();
const SSO: React.FC = () => {
const sso = new URL(location.href).searchParams.get("sso");
const user: User | undefined = sso
? getSSOUser(new URLSearchParams(atob(sso)))
: undefined;
const [logined, setLogined] = useState(false);
const navigate = useNavigate();
useEffect(() => {
if (!sso) {
const ssoUrl = getSSOUrl(location.href);
window.location.href = ssoUrl;
}
if (user) {
accountStore.login(user);
setLogined(true);
navigate("/home");
}
}, []);
return (
<div className={styles.hint}>
{logined ? "登录成功,正在跳转......" : "登录萌卡账号中......"}
</div>
);
};
function getSSOUrl(callbackUrl: string): string {
let params = new URLSearchParams();
params.set("return_sso_url", callbackUrl);
const payload = btoa(params.toString());
const url = new URL(NeosConfig.accountUrl);
params = url.searchParams;
params.set("sso", payload);
return url.toString();
}
function getSSOUser(searchParams: URLSearchParams): User {
const sso = {};
for (const [key, value] of searchParams) {
// @ts-ignore
sso[key] = value;
}
return sso as any;
}
export default SSO;
import { RightOutlined } from "@ant-design/icons";
import React from "react";
import { Link } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { accountStore } from "@/stores";
import { useConfig } from "@/config";
import styles from "./index.module.scss";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const Start: React.FC = () => {
export const Component: React.FC = () => {
const { user } = useSnapshot(accountStore);
return (
<>
<div className={styles.wrapper}>
......@@ -31,13 +33,41 @@ const Start: React.FC = () => {
alt="neos"
/>
</main>
<Link to="/sso" className={styles["start-btn"]}>
<span>登录游戏</span>
<RightOutlined />
</Link>
<LoginBtn logined={Boolean(user)} />
</div>
</>
);
};
Component.displayName = "Start";
const LoginBtn: React.FC<{ logined: boolean }> = ({ logined }) => {
const navigate = useNavigate();
const loginViaSSO = () =>
// 跳转回match页
location.replace(getSSOUrl(`${location.origin}/match/}`));
const goToMatch = () => navigate("/match");
return (
<span
className={styles["start-btn"]}
onClick={logined ? goToMatch : loginViaSSO}
>
{logined ? <span>开始游戏</span> : <span>登录游戏</span>}
<RightOutlined />
</span>
);
};
/** 构建一个单点登录(Single Sign-On,简称SSO)的URL */
function getSSOUrl(callbackUrl: string): string {
const params = new URLSearchParams({
sso: btoa(new URLSearchParams({ return_sso_url: callbackUrl }).toString()),
});
const url = new URL(NeosConfig.accountUrl);
url.search = params.toString();
export default Start;
return url.toString();
}
......@@ -20,7 +20,7 @@ import {
Upload,
} from "antd";
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useNavigate, useLoaderData } from "react-router-dom";
import rustInit from "rust-src";
import { useSnapshot } from "valtio";
import YGOProDeck from "ygopro-deck-encode";
......@@ -41,20 +41,22 @@ const {
automation: { isAiMode },
} = useConfig();
const WaitRoom = () => {
type Params = {
player?: string;
passWd?: string;
ip?: string;
};
export const Loader = (params: Params) => params;
export const Component = () => {
const snapJoin = useSnapshot(joinStore);
const snapChat = useSnapshot(chatStore);
const snapMora = useSnapshot(moraStore);
const snapPlayer = useSnapshot(playerStore);
const params = useParams<{
player?: string;
passWd?: string;
ip?: string;
}>();
const [choseDeck, setChoseDeck] = useState<boolean>(false);
const { player, passWd, ip } = params;
const { player, passWd, ip } = useLoaderData<Params>();
useEffect(() => {
if (ip && player && player.length != 0 && passWd && passWd.length != 0) {
......@@ -284,5 +286,4 @@ const WaitRoom = () => {
</>
);
};
export default WaitRoom;
Component.displayName = "WaitRoom";
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment