From a314b5350e269676e8c39eda4cc7842751b1a7fc Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Tue, 1 Sep 2020 02:32:06 +0800
Subject: ...

---
 Timeline/ClientApp/src/app/App.tsx | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

(limited to 'Timeline/ClientApp/src/app/App.tsx')

diff --git a/Timeline/ClientApp/src/app/App.tsx b/Timeline/ClientApp/src/app/App.tsx
index 74deddda..b64414b7 100644
--- a/Timeline/ClientApp/src/app/App.tsx
+++ b/Timeline/ClientApp/src/app/App.tsx
@@ -2,17 +2,17 @@ import React from "react";
 import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
 import { hot } from "react-hot-loader/root";
 
-import AppBar from "./common/AppBar";
-import LoadingPage from "./common/LoadingPage";
-import Home from "./home/Home";
-import Login from "./user/Login";
-import Settings from "./settings/Settings";
-import About from "./about/About";
-import User from "./user/User";
-import TimelinePage from "./timeline/TimelinePage";
-import AlertHost from "./common/AlertHost";
-import { dataStorage } from "./data/common";
-import { userService, useRawUser } from "./data/user";
+import AppBar from "./views/common/AppBar";
+import LoadingPage from "./views/common/LoadingPage";
+import Home from "./views/home";
+import Login from "./views/login";
+import Settings from "./views/settings";
+import About from "./views/about";
+import User from "./views/user";
+import TimelinePage from "./views/timeline";
+import AlertHost from "./views/common/alert/AlertHost";
+import { dataStorage } from "./services/common";
+import { userService, useRawUser } from "./services/user";
 
 const NoMatch: React.FC = () => {
   return (
@@ -25,7 +25,7 @@ const NoMatch: React.FC = () => {
 };
 
 const LazyAdmin = React.lazy(
-  () => import(/* webpackChunkName: "admin" */ "./admin/Admin")
+  () => import(/* webpackChunkName: "admin" */ "./views/admin/Admin")
 );
 
 const App: React.FC = () => {
-- 
cgit v1.2.3


From d9c2d4e6293599c5499d68679e4fe33b241379f1 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Thu, 3 Sep 2020 18:56:40 +0800
Subject: Move AppBar to App .

---
 Timeline/ClientApp/src/app/App.tsx                 |  75 +++++----
 Timeline/ClientApp/src/app/views/about/index.tsx   | 156 +++++++++----------
 Timeline/ClientApp/src/app/views/admin/Admin.tsx   |   2 -
 Timeline/ClientApp/src/app/views/home/index.tsx    |   2 -
 Timeline/ClientApp/src/app/views/login/index.tsx   | 151 +++++++++---------
 .../ClientApp/src/app/views/settings/index.tsx     | 170 ++++++++++-----------
 .../timeline-common/TimelinePageTemplateUI.tsx     |   7 +-
 7 files changed, 268 insertions(+), 295 deletions(-)

(limited to 'Timeline/ClientApp/src/app/App.tsx')

diff --git a/Timeline/ClientApp/src/app/App.tsx b/Timeline/ClientApp/src/app/App.tsx
index b64414b7..b68eddb6 100644
--- a/Timeline/ClientApp/src/app/App.tsx
+++ b/Timeline/ClientApp/src/app/App.tsx
@@ -11,6 +11,7 @@ import About from "./views/about";
 import User from "./views/user";
 import TimelinePage from "./views/timeline";
 import AlertHost from "./views/common/alert/AlertHost";
+
 import { dataStorage } from "./services/common";
 import { userService, useRawUser } from "./services/user";
 
@@ -38,50 +39,46 @@ const App: React.FC = () => {
     void dataStorage.ready().then(() => setLoading(false));
   }, []);
 
-  let body;
   if (user === undefined || loading) {
-    body = <LoadingPage />;
+    return <LoadingPage />;
   } else {
-    body = (
-      <Router>
-        <Switch>
-          <Route exact path="/">
-            <Home />
-          </Route>
-          <Route exact path="/login">
-            <Login />
-          </Route>
-          <Route path="/settings">
-            <Settings />
-          </Route>
-          <Route path="/about">
-            <About />
-          </Route>
-          <Route path="/timelines/:name">
-            <TimelinePage />
-          </Route>
-          <Route path="/users/:username">
-            <User />
-          </Route>
-          {user && user.administrator && (
-            <Route path="/admin">
-              <LazyAdmin user={user} />
+    return (
+      <React.Suspense fallback={<LoadingPage />}>
+        <Router>
+          <AppBar />
+          <Switch>
+            <Route exact path="/">
+              <Home />
+            </Route>
+            <Route exact path="/login">
+              <Login />
+            </Route>
+            <Route path="/settings">
+              <Settings />
+            </Route>
+            <Route path="/about">
+              <About />
+            </Route>
+            <Route path="/timelines/:name">
+              <TimelinePage />
             </Route>
-          )}
-          <Route>
-            <NoMatch />
-          </Route>
-        </Switch>
-      </Router>
+            <Route path="/users/:username">
+              <User />
+            </Route>
+            {user && user.administrator && (
+              <Route path="/admin">
+                <LazyAdmin user={user} />
+              </Route>
+            )}
+            <Route>
+              <NoMatch />
+            </Route>
+          </Switch>
+          <AlertHost />
+        </Router>
+      </React.Suspense>
     );
   }
-
-  return (
-    <React.Suspense fallback={<LoadingPage />}>
-      {body}
-      <AlertHost />
-    </React.Suspense>
-  );
 };
 
 export default hot(App);
diff --git a/Timeline/ClientApp/src/app/views/about/index.tsx b/Timeline/ClientApp/src/app/views/about/index.tsx
index 78cffb5f..e7771cec 100644
--- a/Timeline/ClientApp/src/app/views/about/index.tsx
+++ b/Timeline/ClientApp/src/app/views/about/index.tsx
@@ -1,8 +1,6 @@
 import React from "react";
 import { useTranslation, Trans } from "react-i18next";
 
-import AppBar from "../common/AppBar";
-
 import authorAvatarUrl from "./author-avatar.png";
 import githubLogoUrl from "./github.png";
 
@@ -75,97 +73,91 @@ const AboutPage: React.FC = () => {
   const { t } = useTranslation();
 
   return (
-    <>
-      <AppBar />
-      <div className="mt-appbar px-2 mb-4">
-        <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
-          <h4 id="author-info">{t("about.author.title")}</h4>
-          <div>
-            <div className="d-flex">
-              <img
-                src={authorAvatarUrl}
-                className="align-self-start avatar large rounded-circle"
-              />
-              <div>
-                <p>
-                  <small>{t("about.author.fullname")}</small>
-                  <span className="text-primary">杨宇千</span>
-                </p>
-                <p>
-                  <small>{t("about.author.nickname")}</small>
-                  <span className="text-primary">crupest</span>
-                </p>
-                <p>
-                  <small>{t("about.author.introduction")}</small>
-                  {t("about.author.introductionContent")}
-                </p>
-              </div>
+    <div className="mt-appbar px-2 mb-4">
+      <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
+        <h4 id="author-info">{t("about.author.title")}</h4>
+        <div>
+          <div className="d-flex">
+            <img
+              src={authorAvatarUrl}
+              className="align-self-start avatar large rounded-circle"
+            />
+            <div>
+              <p>
+                <small>{t("about.author.fullname")}</small>
+                <span className="text-primary">杨宇千</span>
+              </p>
+              <p>
+                <small>{t("about.author.nickname")}</small>
+                <span className="text-primary">crupest</span>
+              </p>
+              <p>
+                <small>{t("about.author.introduction")}</small>
+                {t("about.author.introductionContent")}
+              </p>
             </div>
-            <p>
-              <small>{t("about.author.links")}</small>
-              <a
-                href="https://github.com/crupest"
-                target="_blank"
-                rel="noopener noreferrer"
-              >
-                <img
-                  src={githubLogoUrl}
-                  className="about-link-icon text-body"
-                />
-              </a>
-            </p>
           </div>
-        </div>
-        <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
-          <h4>{t("about.site.title")}</h4>
-          <p>
-            <Trans i18nKey="about.site.content">
-              0<span className="text-primary">1</span>2<b>3</b>4
-              <a href="#author-info">5</a>6
-            </Trans>
-          </p>
           <p>
+            <small>{t("about.author.links")}</small>
             <a
-              href="https://github.com/crupest/Timeline"
+              href="https://github.com/crupest"
               target="_blank"
               rel="noopener noreferrer"
             >
-              {t("about.site.repo")}
+              <img src={githubLogoUrl} className="about-link-icon text-body" />
             </a>
           </p>
         </div>
-        <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
-          <h4>{t("about.credits.title")}</h4>
-          <p>{t("about.credits.content")}</p>
-          <p>{t("about.credits.frontend")}</p>
-          <ul>
-            {frontendCredits.map((item, index) => {
-              return (
-                <li key={index}>
-                  <a href={item.url} target="_blank" rel="noopener noreferrer">
-                    {item.name}
-                  </a>
-                </li>
-              );
-            })}
-            <li>...</li>
-          </ul>
-          <p>{t("about.credits.backend")}</p>
-          <ul>
-            {backendCredits.map((item, index) => {
-              return (
-                <li key={index}>
-                  <a href={item.url} target="_blank" rel="noopener noreferrer">
-                    {item.name}
-                  </a>
-                </li>
-              );
-            })}
-            <li>...</li>
-          </ul>
-        </div>
       </div>
-    </>
+      <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
+        <h4>{t("about.site.title")}</h4>
+        <p>
+          <Trans i18nKey="about.site.content">
+            0<span className="text-primary">1</span>2<b>3</b>4
+            <a href="#author-info">5</a>6
+          </Trans>
+        </p>
+        <p>
+          <a
+            href="https://github.com/crupest/Timeline"
+            target="_blank"
+            rel="noopener noreferrer"
+          >
+            {t("about.site.repo")}
+          </a>
+        </p>
+      </div>
+      <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
+        <h4>{t("about.credits.title")}</h4>
+        <p>{t("about.credits.content")}</p>
+        <p>{t("about.credits.frontend")}</p>
+        <ul>
+          {frontendCredits.map((item, index) => {
+            return (
+              <li key={index}>
+                <a href={item.url} target="_blank" rel="noopener noreferrer">
+                  {item.name}
+                </a>
+              </li>
+            );
+          })}
+          <li>...</li>
+        </ul>
+        <p>{t("about.credits.backend")}</p>
+        <ul>
+          {backendCredits.map((item, index) => {
+            return (
+              <li key={index}>
+                <a href={item.url} target="_blank" rel="noopener noreferrer">
+                  {item.name}
+                </a>
+              </li>
+            );
+          })}
+          <li>...</li>
+        </ul>
+      </div>
+    </div>
   );
 };
 
diff --git a/Timeline/ClientApp/src/app/views/admin/Admin.tsx b/Timeline/ClientApp/src/app/views/admin/Admin.tsx
index e0f59b0f..9c0250e7 100644
--- a/Timeline/ClientApp/src/app/views/admin/Admin.tsx
+++ b/Timeline/ClientApp/src/app/views/admin/Admin.tsx
@@ -8,7 +8,6 @@ import {
 } from "react-router";
 import { Nav } from "react-bootstrap";
 
-import AppBar from "../common/AppBar";
 import { UserWithToken } from "@/services/user";
 
 import UserAdmin from "./UserAdmin";
@@ -34,7 +33,6 @@ const Admin: React.FC<AdminProps> = (props) => {
   ): React.ReactNode => {
     return (
       <Route path={`${match.path}/${name}`}>
-        <AppBar />
         <div style={{ height: 56 }} className="flex-fix-length" />
         <Nav variant="tabs">
           <Nav.Item>
diff --git a/Timeline/ClientApp/src/app/views/home/index.tsx b/Timeline/ClientApp/src/app/views/home/index.tsx
index e2e3e6f9..11672c10 100644
--- a/Timeline/ClientApp/src/app/views/home/index.tsx
+++ b/Timeline/ClientApp/src/app/views/home/index.tsx
@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
 import { Row, Container, Button, Col } from "react-bootstrap";
 
 import { useUser } from "@/services/user";
-import AppBar from "../common/AppBar";
 import SearchInput from "../common/SearchInput";
 
 import BoardWithoutUser from "./BoardWithoutUser";
@@ -34,7 +33,6 @@ const HomePage: React.FC = () => {
 
   return (
     <>
-      <AppBar />
       <Container fluid>
         <Row>
           <Col>
diff --git a/Timeline/ClientApp/src/app/views/login/index.tsx b/Timeline/ClientApp/src/app/views/login/index.tsx
index 545fc05a..a3071264 100644
--- a/Timeline/ClientApp/src/app/views/login/index.tsx
+++ b/Timeline/ClientApp/src/app/views/login/index.tsx
@@ -1,7 +1,7 @@
-import React, { Fragment, useState, useEffect } from "react";
+import React from "react";
 import { useHistory } from "react-router";
 import { useTranslation } from "react-i18next";
-import { Form, Spinner, Button } from "react-bootstrap";
+import { Form } from "react-bootstrap";
 
 import { useUser, userService } from "@/services/user";
 
@@ -11,17 +11,17 @@ import LoadingButton from "../common/LoadingButton";
 const LoginPage: React.FC = (_) => {
   const { t } = useTranslation();
   const history = useHistory();
-  const [username, setUsername] = useState<string>("");
-  const [usernameDirty, setUsernameDirty] = useState<boolean>(false);
-  const [password, setPassword] = useState<string>("");
-  const [passwordDirty, setPasswordDirty] = useState<boolean>(false);
-  const [rememberMe, setRememberMe] = useState<boolean>(true);
-  const [process, setProcess] = useState<boolean>(false);
-  const [error, setError] = useState<string | null>(null);
+  const [username, setUsername] = React.useState<string>("");
+  const [usernameDirty, setUsernameDirty] = React.useState<boolean>(false);
+  const [password, setPassword] = React.useState<string>("");
+  const [passwordDirty, setPasswordDirty] = React.useState<boolean>(false);
+  const [rememberMe, setRememberMe] = React.useState<boolean>(true);
+  const [process, setProcess] = React.useState<boolean>(false);
+  const [error, setError] = React.useState<string | null>(null);
 
   const user = useUser();
 
-  useEffect(() => {
+  React.useEffect(() => {
     if (user != null) {
       const id = setTimeout(() => history.push("/"), 3000);
       return () => {
@@ -72,73 +72,70 @@ const LoginPage: React.FC = (_) => {
   }
 
   return (
-    <Fragment>
-      <AppBar />
-      <div className="container login-container mt-appbar">
-        <h1>{t("welcome")}</h1>
-        <Form>
-          <Form.Group>
-            <Form.Label htmlFor="username">{t("user.username")}</Form.Label>
-            <Form.Control
-              id="username"
-              disabled={process}
-              onChange={(e) => {
-                setUsername(e.target.value);
-                setUsernameDirty(true);
-              }}
-              value={username}
-              isInvalid={usernameDirty && username === ""}
-            />
-            {usernameDirty && username === "" && (
-              <Form.Control.Feedback type="invalid">
-                {t("login.emptyUsername")}
-              </Form.Control.Feedback>
-            )}
-          </Form.Group>
-          <Form.Group>
-            <Form.Label htmlFor="password">{t("user.password")}</Form.Label>
-            <Form.Control
-              id="password"
-              type="password"
-              disabled={process}
-              onChange={(e) => {
-                setPassword(e.target.value);
-                setPasswordDirty(true);
-              }}
-              value={password}
-              isInvalid={passwordDirty && password === ""}
-            />
-            {passwordDirty && password === "" && (
-              <Form.Control.Feedback type="invalid">
-                {t("login.emptyPassword")}
-              </Form.Control.Feedback>
-            )}
-          </Form.Group>
-          <Form.Group>
-            <Form.Check<"input">
-              id="remember-me"
-              type="checkbox"
-              checked={rememberMe}
-              onChange={(e) => {
-                setRememberMe(e.target.checked);
-              }}
-              label={t("user.rememberMe")}
-            />
-          </Form.Group>
-          {error ? <p className="text-danger">{t(error)}</p> : null}
-          <div className="text-right">
-            <LoadingButton
-              loading={process}
-              variant="primary"
-              onClick={onSubmit}
-              disabled={username === "" || password === "" ? true : undefined}
-            >
-              {t("user.login")}
-            </LoadingButton>
-          </div>
-        </Form>
-      </div>
-    </Fragment>
+    <div className="container login-container mt-appbar">
+      <h1>{t("welcome")}</h1>
+      <Form>
+        <Form.Group>
+          <Form.Label htmlFor="username">{t("user.username")}</Form.Label>
+          <Form.Control
+            id="username"
+            disabled={process}
+            onChange={(e) => {
+              setUsername(e.target.value);
+              setUsernameDirty(true);
+            }}
+            value={username}
+            isInvalid={usernameDirty && username === ""}
+          />
+          {usernameDirty && username === "" && (
+            <Form.Control.Feedback type="invalid">
+              {t("login.emptyUsername")}
+            </Form.Control.Feedback>
+          )}
+        </Form.Group>
+        <Form.Group>
+          <Form.Label htmlFor="password">{t("user.password")}</Form.Label>
+          <Form.Control
+            id="password"
+            type="password"
+            disabled={process}
+            onChange={(e) => {
+              setPassword(e.target.value);
+              setPasswordDirty(true);
+            }}
+            value={password}
+            isInvalid={passwordDirty && password === ""}
+          />
+          {passwordDirty && password === "" && (
+            <Form.Control.Feedback type="invalid">
+              {t("login.emptyPassword")}
+            </Form.Control.Feedback>
+          )}
+        </Form.Group>
+        <Form.Group>
+          <Form.Check<"input">
+            id="remember-me"
+            type="checkbox"
+            checked={rememberMe}
+            onChange={(e) => {
+              setRememberMe(e.target.checked);
+            }}
+            label={t("user.rememberMe")}
+          />
+        </Form.Group>
+        {error ? <p className="text-danger">{t(error)}</p> : null}
+        <div className="text-right">
+          <LoadingButton
+            loading={process}
+            variant="primary"
+            onClick={onSubmit}
+            disabled={username === "" || password === "" ? true : undefined}
+          >
+            {t("user.login")}
+          </LoadingButton>
+        </div>
+      </Form>
+    </div>
   );
 };
 
diff --git a/Timeline/ClientApp/src/app/views/settings/index.tsx b/Timeline/ClientApp/src/app/views/settings/index.tsx
index 123e1353..964e7442 100644
--- a/Timeline/ClientApp/src/app/views/settings/index.tsx
+++ b/Timeline/ClientApp/src/app/views/settings/index.tsx
@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
 import { Form, Container, Row, Col, Button, Modal } from "react-bootstrap";
 
 import { useUser, userService } from "@/services/user";
-import AppBar from "../common/AppBar";
 import OperationDialog, {
   OperationInputErrorInfo,
 } from "../common/OperationDialog";
@@ -121,92 +120,89 @@ const SettingsPage: React.FC = (_) => {
   const language = i18n.language.slice(0, 2);
 
   return (
-    <>
-      <AppBar />
-      <Container fluid>
-        {user ? (
-          <>
-            <Row className="border-bottom p-3 cursor-pointer">
-              <Col xs="12">
-                <h5
-                  onClick={() => {
-                    history.push(`/users/${user.username}`);
-                  }}
-                >
-                  {t("settings.gotoSelf")}
-                </h5>
-              </Col>
-            </Row>
-            <Row className="border-bottom p-3 cursor-pointer">
-              <Col xs="12">
-                <h5
-                  className="text-danger"
-                  onClick={() => setDialog("changepassword")}
-                >
-                  {t("settings.changePassword")}
-                </h5>
-              </Col>
-            </Row>
-            <Row className="border-bottom p-3 cursor-pointer">
-              <Col xs="12">
-                <h5
-                  className="text-danger"
-                  onClick={() => {
-                    setDialog("logout");
-                  }}
-                >
-                  {t("settings.logout")}
-                </h5>
-              </Col>
-            </Row>
-          </>
-        ) : null}
-        <Row className="align-items-center border-bottom p-3">
-          <Col xs="12" sm="auto">
-            <h5>{t("settings.languagePrimary")}</h5>
-            <p>{t("settings.languageSecondary")}</p>
-          </Col>
-          <Col xs="auto" className="ml-auto">
-            <Form.Control
-              as="select"
-              value={language}
-              onChange={(e) => {
-                void i18n.changeLanguage(e.target.value);
-              }}
-            >
-              <option value="zh">中文</option>
-              <option value="en">English</option>
-            </Form.Control>
-          </Col>
-        </Row>
-        {(() => {
-          switch (dialog) {
-            case "changepassword":
-              return (
-                <ChangePasswordDialog
-                  open
-                  close={() => {
-                    setDialog(null);
-                  }}
-                />
-              );
-            case "logout":
-              return (
-                <ConfirmLogoutDialog
-                  toggle={() => setDialog(null)}
-                  onConfirm={() => {
-                    void userService.logout().then(() => {
-                      history.push("/");
-                    });
-                  }}
-                />
-              );
-            default:
-              return null;
-          }
-        })()}
-      </Container>
-    </>
+    <Container fluid>
+      {user ? (
+        <>
+          <Row className="border-bottom p-3 cursor-pointer">
+            <Col xs="12">
+              <h5
+                onClick={() => {
+                  history.push(`/users/${user.username}`);
+                }}
+              >
+                {t("settings.gotoSelf")}
+              </h5>
+            </Col>
+          </Row>
+          <Row className="border-bottom p-3 cursor-pointer">
+            <Col xs="12">
+              <h5
+                className="text-danger"
+                onClick={() => setDialog("changepassword")}
+              >
+                {t("settings.changePassword")}
+              </h5>
+            </Col>
+          </Row>
+          <Row className="border-bottom p-3 cursor-pointer">
+            <Col xs="12">
+              <h5
+                className="text-danger"
+                onClick={() => {
+                  setDialog("logout");
+                }}
+              >
+                {t("settings.logout")}
+              </h5>
+            </Col>
+          </Row>
+        </>
+      ) : null}
+      <Row className="align-items-center border-bottom p-3">
+        <Col xs="12" sm="auto">
+          <h5>{t("settings.languagePrimary")}</h5>
+          <p>{t("settings.languageSecondary")}</p>
+        </Col>
+        <Col xs="auto" className="ml-auto">
+          <Form.Control
+            as="select"
+            value={language}
+            onChange={(e) => {
+              void i18n.changeLanguage(e.target.value);
+            }}
+          >
+            <option value="zh">中文</option>
+            <option value="en">English</option>
+          </Form.Control>
+        </Col>
+      </Row>
+      {(() => {
+        switch (dialog) {
+          case "changepassword":
+            return (
+              <ChangePasswordDialog
+                open
+                close={() => {
+                  setDialog(null);
+                }}
+              />
+            );
+          case "logout":
+            return (
+              <ConfirmLogoutDialog
+                toggle={() => setDialog(null)}
+                onConfirm={() => {
+                  void userService.logout().then(() => {
+                    history.push("/");
+                  });
+                }}
+              />
+            );
+          default:
+            return null;
+        }
+      })()}
+    </Container>
   );
 };
 
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
index e25ed962..c33bc0e5 100644
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
+++ b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
@@ -310,10 +310,5 @@ export default function TimelinePageTemplateUI<TManageItems>(
     }
   }
 
-  return (
-    <>
-      <AppBar />
-      {body}
-    </>
-  );
+  return body;
 }
-- 
cgit v1.2.3

