feat: use react-router

This commit is contained in:
Steven 2022-09-19 21:53:27 +08:00
parent 4608894e56
commit 307483e499
12 changed files with 155 additions and 149 deletions

View file

@ -1,32 +1,23 @@
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { RouterProvider } from "react-router-dom";
import useI18n from "./hooks/useI18n";
import { appRouterSwitch } from "./routers";
import { globalService, locationService } from "./services";
import { useAppSelector } from "./store";
import router from "./router";
import * as storage from "./helpers/storage";
function App() {
const { setLocale } = useI18n();
const user = useAppSelector((state) => state.user.user);
const global = useAppSelector((state) => state.global);
const pathname = useAppSelector((state) => state.location.pathname);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
locationService.updateStateWithLocation();
window.onpopstate = () => {
locationService.updateStateWithLocation();
};
globalService.initialState().then(() => {
setLoading(false);
});
}, []);
useEffect(() => {
if (user?.setting.locale) {
globalService.setLocale(user.setting.locale);
}
}, [user?.setting.locale]);
globalService.initialState();
}, []);
useEffect(() => {
setLocale(global.locale);
@ -35,7 +26,7 @@ function App() {
});
}, [global.locale]);
return <>{isLoading ? null : appRouterSwitch(pathname)}</>;
return <RouterProvider router={router} />;
}
export default App;

View file

@ -1,5 +1,6 @@
import { useEffect, useRef } from "react";
import { locationService, userService } from "../services";
import { useNavigate } from "react-router-dom";
import { userService } from "../services";
import useI18n from "../hooks/useI18n";
import Only from "./common/OnlyWhen";
import showAboutSiteDialog from "./AboutSiteDialog";
@ -15,6 +16,7 @@ interface Props {
const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
const { shownStatus, setShownStatus } = props;
const { t } = useI18n();
const navigate = useNavigate();
const popupElRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@ -48,8 +50,7 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
userService
.doSignOut()
.then(() => {
locationService.replaceHistory("/auth");
window.location.reload();
navigate("/auth");
})
.catch(() => {
// do nth

View file

@ -1,4 +1,5 @@
import { locationService, userService } from "../services";
import { Link } from "react-router-dom";
import { userService } from "../services";
import useI18n from "../hooks/useI18n";
import Icon from "./Icon";
import Only from "./common/OnlyWhen";
@ -17,10 +18,6 @@ const Sidebar = () => {
showSettingDialog();
};
const handleExploreBtnClick = () => {
locationService.pushHistory("/explore");
};
return (
<aside className="sidebar-wrapper">
<div className="close-container">
@ -35,9 +32,9 @@ const Sidebar = () => {
<span className="icon">📅</span> {t("sidebar.daily-review")}
</button>
<Only when={!userService.isVisitorMode()}>
<button className="btn action-btn" onClick={() => handleExploreBtnClick()}>
<Link to="/explore" className="btn action-btn">
<span className="icon">🏂</span> {t("common.explore")}
</button>
</Link>
<button className="btn action-btn" onClick={handleSettingBtnClick}>
<span className="icon"></span> {t("sidebar.setting")}
</button>

View file

@ -1,4 +1,8 @@
import { assign } from "lodash-es";
import { assign, isNull, isUndefined } from "lodash-es";
export const isNullorUndefined = (value: any) => {
return isNull(value) || isUndefined(value);
};
export function getNowTimeStamp(): number {
return Date.now();

View file

@ -25,7 +25,7 @@
> .action-button-container {
> .btn {
@apply text-gray-600 font-mono text-base py-1 border px-3 rounded-xl hover:opacity-80 hover:underline;
@apply block text-gray-600 font-mono text-base py-1 border px-3 leading-8 rounded-xl hover:opacity-80 hover:underline;
> .icon {
@apply text-lg;

View file

@ -1,6 +1,5 @@
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import I18nProvider from "./labs/i18n/I18nProvider";
import store from "./store";
import App from "./App";
@ -11,11 +10,9 @@ import "./css/index.css";
const container = document.getElementById("root");
const root = createRoot(container as HTMLElement);
root.render(
<BrowserRouter>
<I18nProvider>
<Provider store={store}>
<App />
</Provider>
</I18nProvider>
</BrowserRouter>
<I18nProvider>
<Provider store={store}>
<App />
</Provider>
</I18nProvider>
);

View file

@ -1,9 +1,10 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import * as api from "../helpers/api";
import { validate, ValidatorConfig } from "../helpers/validator";
import useI18n from "../hooks/useI18n";
import useLoading from "../hooks/useLoading";
import { globalService, locationService, userService } from "../services";
import { globalService, userService } from "../services";
import Icon from "../components/Icon";
import Only from "../components/common/OnlyWhen";
import toastHelper from "../components/Toast";
@ -18,6 +19,7 @@ const validateConfig: ValidatorConfig = {
const Auth = () => {
const { t, locale } = useI18n();
const navigate = useNavigate();
const pageLoadingState = useLoading(true);
const [siteHost, setSiteHost] = useState<User>();
const [email, setEmail] = useState("");
@ -68,7 +70,7 @@ const Auth = () => {
await api.signin(email, password);
const user = await userService.doSignIn();
if (user) {
locationService.replaceHistory("/");
navigate("/");
} else {
toastHelper.error(t("message.login-failed"));
}
@ -101,7 +103,7 @@ const Auth = () => {
await api.signup(email, password, "HOST");
const user = await userService.doSignIn();
if (user) {
locationService.replaceHistory("/");
navigate("/");
} else {
toastHelper.error(t("common.singup-failed"));
}

View file

@ -1,6 +1,8 @@
import dayjs from "dayjs";
import { useEffect, useState } from "react";
import { locationService, memoService, userService } from "../services";
import { Link, useNavigate } from "react-router-dom";
import { memoService, userService } from "../services";
import { isNullorUndefined } from "../helpers/utils";
import { useAppSelector } from "../store";
import useI18n from "../hooks/useI18n";
import useQuery from "../hooks/useQuery";
@ -16,6 +18,7 @@ interface State {
const Explore = () => {
const { t, locale } = useI18n();
const navigate = useNavigate();
const query = useQuery();
const user = useAppSelector((state) => state.user.user);
const location = useAppSelector((state) => state.location);
@ -25,33 +28,28 @@ const Explore = () => {
const loadingState = useLoading();
useEffect(() => {
userService
.initialState()
.catch()
.finally(async () => {
const { host } = userService.getState();
if (!host) {
locationService.replaceHistory("/auth");
return;
}
const { host } = userService.getState();
if (isNullorUndefined(host)) {
navigate("/auth");
return;
}
memoService.fetchAllMemos().then((memos) => {
let filteredMemos = memos;
memoService.fetchAllMemos().then((memos) => {
let filteredMemos = memos;
const memoId = Number(query.get("memoId"));
if (memoId && !isNaN(memoId)) {
filteredMemos = filteredMemos.filter((memo) => {
return memo.id === memoId;
});
}
setState({
...state,
memos: filteredMemos,
});
const memoId = Number(query.get("memoId"));
if (memoId && !isNaN(memoId)) {
filteredMemos = filteredMemos.filter((memo) => {
return memo.id === memoId;
});
loadingState.setFinish();
}
setState({
...state,
memos: filteredMemos,
});
loadingState.setFinish();
});
}, [location]);
return (
@ -65,13 +63,13 @@ const Explore = () => {
<div className="action-button-container">
<Only when={!loadingState.isLoading}>
{user ? (
<button className="btn" onClick={() => (window.location.href = "/")}>
<Link to="/" className="btn">
<span className="icon">🏠</span> {t("common.back-to-home")}
</button>
</Link>
) : (
<button className="btn" onClick={() => (window.location.href = "/auth")}>
<Link to="/auth" className="btn">
<span className="icon">👉</span> {t("common.sign-in")}
</button>
</Link>
)}
</Only>
</div>

View file

@ -1,77 +1,77 @@
import { useEffect } from "react";
import { locationService, userService } from "../services";
import { useLocation, useNavigate } from "react-router-dom";
import { globalService, userService } from "../services";
import { useAppSelector } from "../store";
import useI18n from "../hooks/useI18n";
import useLoading from "../hooks/useLoading";
import { isNullorUndefined } from "../helpers/utils";
import Only from "../components/common/OnlyWhen";
import toastHelper from "../components/Toast";
import Sidebar from "../components/Sidebar";
import MemosHeader from "../components/MemosHeader";
import MemoEditor from "../components/MemoEditor";
import MemoFilter from "../components/MemoFilter";
import MemoList from "../components/MemoList";
import toastHelper from "../components/Toast";
import "../less/home.less";
function Home() {
const { t } = useI18n();
const location = useLocation();
const navigate = useNavigate();
const user = useAppSelector((state) => state.user.user);
const location = useAppSelector((state) => state.location);
const loadingState = useLoading();
useEffect(() => {
userService
.initialState()
.catch()
.finally(async () => {
const { host, owner, user } = userService.getState();
if (!host) {
locationService.replaceHistory("/auth");
return;
}
const { host, owner, user } = userService.getState();
if (userService.isVisitorMode()) {
if (!owner) {
toastHelper.error(t("message.user-not-found"));
}
} else {
if (!user) {
locationService.replaceHistory(`/explore`);
}
}
loadingState.setFinish();
});
if (isNullorUndefined(host)) {
navigate("/auth");
return;
}
if (userService.isVisitorMode()) {
if (!owner) {
toastHelper.error(t("message.user-not-found"));
}
} else {
if (isNullorUndefined(user)) {
navigate("/explore");
}
}
}, [location]);
useEffect(() => {
if (user?.setting.locale) {
globalService.setLocale(user.setting.locale);
}
}, [user?.setting.locale]);
return (
<section className="page-wrapper home">
{loadingState.isLoading ? null : (
<div className="page-container">
<Sidebar />
<main className="memos-wrapper">
<div className="memos-editor-wrapper">
<MemosHeader />
<Only when={!userService.isVisitorMode()}>
<MemoEditor />
</Only>
<MemoFilter />
</div>
<MemoList />
<Only when={userService.isVisitorMode()}>
<div className="addtion-btn-container">
{user ? (
<button className="btn" onClick={() => (window.location.href = "/")}>
<span className="icon">🏠</span> {t("common.back-to-home")}
</button>
) : (
<button className="btn" onClick={() => (window.location.href = "/auth")}>
<span className="icon">👉</span> {t("common.sign-in")}
</button>
)}
</div>
<div className="page-container">
<Sidebar />
<main className="memos-wrapper">
<div className="memos-editor-wrapper">
<MemosHeader />
<Only when={!userService.isVisitorMode()}>
<MemoEditor />
</Only>
</main>
</div>
)}
<MemoFilter />
</div>
<MemoList />
<Only when={userService.isVisitorMode()}>
<div className="addtion-btn-container">
{user ? (
<button className="btn" onClick={() => (window.location.href = "/")}>
<span className="icon">🏠</span> {t("common.back-to-home")}
</button>
) : (
<button className="btn" onClick={() => (window.location.href = "/auth")}>
<span className="icon">👉</span> {t("common.sign-in")}
</button>
)}
</div>
</Only>
</main>
</div>
</section>
);
}

47
web/src/router/index.tsx Normal file
View file

@ -0,0 +1,47 @@
import { createBrowserRouter } from "react-router-dom";
import { userService } from "../services";
import Auth from "../pages/Auth";
import Explore from "../pages/Explore";
import Home from "../pages/Home";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
loader: async () => {
try {
await userService.initialState();
} catch (error) {
// do nth
}
},
},
{
path: "/auth",
element: <Auth />,
},
{
path: "/u/:userId",
element: <Home />,
loader: async () => {
try {
await userService.initialState();
} catch (error) {
// do nth
}
},
},
{
path: "/explore",
element: <Explore />,
loader: async () => {
try {
await userService.initialState();
} catch (error) {
// do nth
}
},
},
]);
export default router;

View file

@ -1,11 +0,0 @@
import Home from "../pages/Home";
import Auth from "../pages/Auth";
import Explore from "../pages/Explore";
const appRouter = {
"/auth": <Auth />,
"/explore": <Explore />,
"*": <Home />,
};
export default appRouter;

View file

@ -1,20 +0,0 @@
import appRouter from "./appRouter";
// just like React-Router
interface Router {
[key: string]: JSX.Element | null;
"*": JSX.Element | null;
}
const routerSwitch = (router: Router) => {
return (pathname: string) => {
for (const key of Object.keys(router)) {
if (key === pathname) {
return router[key];
}
}
return router["*"];
};
};
export const appRouterSwitch = routerSwitch(appRouter);