chore: update data initial requests (#538)

This commit is contained in:
boojack 2022-11-22 23:45:11 +08:00 committed by GitHub
parent 362306a9cb
commit a0667abec8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 111 additions and 135 deletions

View file

@ -3,9 +3,9 @@ package api
import "github.com/usememos/memos/server/profile"
type SystemStatus struct {
Host *User `json:"host"`
Profile *profile.Profile `json:"profile"`
DBSize int64 `json:"dbSize"`
Host *User `json:"host"`
Profile profile.Profile `json:"profile"`
DBSize int64 `json:"dbSize"`
// System settings
// Allow sign up.

View file

@ -41,7 +41,7 @@ func (s *Server) registerSystemRoutes(g *echo.Group) {
systemStatus := api.SystemStatus{
Host: hostUser,
Profile: s.Profile,
Profile: *s.Profile,
DBSize: 0,
AllowSignUp: false,
AdditionalStyle: "",

View file

@ -2,49 +2,43 @@ import { CssVarsProvider } from "@mui/joy/styles";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { RouterProvider } from "react-router-dom";
import { globalService, locationService } from "./services";
import { locationService } from "./services";
import { useAppSelector } from "./store";
import router from "./router";
import * as api from "./helpers/api";
import * as storage from "./helpers/storage";
function App() {
const { i18n } = useTranslation();
const global = useAppSelector((state) => state.global);
const { locale, systemStatus } = useAppSelector((state) => state.global);
useEffect(() => {
locationService.updateStateWithLocation();
window.onpopstate = () => {
locationService.updateStateWithLocation();
};
globalService.initialState();
}, []);
// Inject additional style and script codes.
useEffect(() => {
api.getSystemStatus().then(({ data }) => {
const { data: status } = data;
if (status.additionalStyle) {
const styleEl = document.createElement("style");
styleEl.innerHTML = status.additionalStyle;
styleEl.setAttribute("type", "text/css");
document.head.appendChild(styleEl);
}
if (status.additionalScript) {
const scriptEl = document.createElement("script");
scriptEl.innerHTML = status.additionalScript;
document.head.appendChild(scriptEl);
}
});
}, []);
if (systemStatus.additionalStyle) {
const styleEl = document.createElement("style");
styleEl.innerHTML = systemStatus.additionalStyle;
styleEl.setAttribute("type", "text/css");
document.head.appendChild(styleEl);
}
if (systemStatus.additionalScript) {
const scriptEl = document.createElement("script");
scriptEl.innerHTML = systemStatus.additionalScript;
document.head.appendChild(scriptEl);
}
}, [systemStatus]);
useEffect(() => {
i18n.changeLanguage(global.locale);
i18n.changeLanguage(locale);
storage.set({
locale: global.locale,
locale: locale,
});
}, [global.locale]);
}, [locale]);
return (
<CssVarsProvider>

View file

@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import * as api from "../helpers/api";
import { useAppSelector } from "../store";
import Icon from "./Icon";
import { generateDialog } from "./Dialog";
import GitHubBadge from "./GitHubBadge";
@ -10,23 +9,7 @@ type Props = DialogProps;
const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
const { t } = useTranslation();
const [profile, setProfile] = useState<Profile>();
useEffect(() => {
try {
api.getSystemStatus().then(({ data }) => {
const {
data: { profile },
} = data;
setProfile(profile);
});
} catch (error) {
setProfile({
mode: "dev",
version: "0.0.0",
});
}
}, []);
const profile = useAppSelector((state) => state.global.systemStatus.profile);
const handleCloseBtnClick = () => {
destroy();
@ -49,15 +32,13 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
<br />
<div className="addtion-info-container">
<GitHubBadge />
{profile !== undefined && (
<>
{t("common.version")}:
<span className="pre-text">
{profile?.version}-{profile?.mode}
</span>
🎉
</>
)}
<>
{t("common.version")}:
<span className="pre-text">
{profile.version}-{profile.mode}
</span>
🎉
</>
</div>
</div>
</>

View file

@ -8,6 +8,7 @@ import "../less/memo-filter.less";
const MemoFilter = () => {
const { t } = useTranslation();
useAppSelector((state) => state.shortcut.shortcuts);
const query = useAppSelector((state) => state.location.query);
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = query;
const shortcut = shortcutId ? shortcutService.getShortcutById(shortcutId) : null;

View file

@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import { useAppSelector } from "../store";
import * as api from "../helpers/api";
import * as storage from "../helpers/storage";
import Icon from "./Icon";
@ -10,35 +11,23 @@ interface State {
}
const UpdateVersionBanner: React.FC = () => {
const profile = useAppSelector((state) => state.global.systemStatus.profile);
const [state, setState] = useState<State>({
latestVersion: "",
show: false,
});
useEffect(() => {
Promise.all([api.getRepoLatestTag(), api.getSystemStatus()])
.then(
([
latestTag,
{
data: {
data: { profile },
},
},
]) => {
const { skippedVersion } = storage.get(["skippedVersion"]);
const latestVersion = latestTag.slice(1) || "0.0.0";
const currentVersion = profile.version;
const skipped = skippedVersion ? skippedVersion === latestVersion : false;
setState({
latestVersion,
show: !skipped && currentVersion < latestVersion,
});
}
)
.catch(() => {
// do nth
api.getRepoLatestTag().then((latestTag) => {
const { skippedVersion } = storage.get(["skippedVersion"]);
const latestVersion = latestTag.slice(1) || "0.0.0";
const currentVersion = profile.version;
const skipped = skippedVersion ? skippedVersion === latestVersion : false;
setState({
latestVersion,
show: !skipped && currentVersion < latestVersion,
});
});
}, []);
const onSkip = () => {

View file

@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useAppSelector } from "../store";
import * as api from "../helpers/api";
import { validate, ValidatorConfig } from "../helpers/validator";
import useLoading from "../hooks/useLoading";
@ -19,23 +20,11 @@ const validateConfig: ValidatorConfig = {
const Auth = () => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const pageLoadingState = useLoading(true);
const [systemStatus, setSystemStatus] = useState<SystemStatus>();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const systemStatus = useAppSelector((state) => state.global.systemStatus);
const actionBtnLoadingState = useLoading(false);
useEffect(() => {
api.getSystemStatus().then(({ data }) => {
const { data: status } = data;
setSystemStatus(status);
if (status.profile.mode === "dev") {
setEmail("demo@usememos.com");
setPassword("secret");
}
pageLoadingState.setFinish();
});
}, []);
const mode = systemStatus.profile.mode;
const [email, setEmail] = useState(mode === "dev" ? "demo@usememos.com" : "");
const [password, setPassword] = useState(mode === "dev" ? "secret" : "");
const handleEmailInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;
@ -138,39 +127,32 @@ const Auth = () => {
</div>
</div>
<div className="action-btns-container">
{!pageLoadingState.isLoading && (
{systemStatus?.host ? (
<>
{systemStatus?.host ? (
<>
{actionBtnLoadingState.isLoading && <Icon.Loader className="w-4 h-auto animate-spin" />}
{systemStatus?.allowSignUp && (
<>
<button
className={`btn signup-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSignUpBtnsClick("USER")}
>
{t("common.sign-up")}
</button>
<span className="mr-2 font-mono text-gray-200">/</span>
</>
)}
<button
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={handleSigninBtnsClick}
>
{t("common.sign-in")}
</button>
</>
) : (
{actionBtnLoadingState.isLoading && <Icon.Loader className="w-4 h-auto animate-spin" />}
{systemStatus?.allowSignUp && (
<>
<button
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSignUpBtnsClick("HOST")}
className={`btn signup-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSignUpBtnsClick("USER")}
>
{t("auth.signup-as-host")}
{t("common.sign-up")}
</button>
<span className="mr-2 font-mono text-gray-200">/</span>
</>
)}
<button className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`} onClick={handleSigninBtnsClick}>
{t("common.sign-in")}
</button>
</>
) : (
<>
<button
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSignUpBtnsClick("HOST")}
>
{t("auth.signup-as-host")}
</button>
</>
)}
</div>

View file

@ -1,6 +1,6 @@
import { createBrowserRouter, redirect } from "react-router-dom";
import { isNullorUndefined } from "../helpers/utils";
import { userService } from "../services";
import { globalService, userService } from "../services";
import Auth from "../pages/Auth";
import Explore from "../pages/Explore";
import Home from "../pages/Home";
@ -10,12 +10,20 @@ const router = createBrowserRouter([
{
path: "/auth",
element: <Auth />,
loader: async () => {
try {
await globalService.initialState();
} catch (error) {
// do nth
}
},
},
{
path: "/",
element: <Home />,
loader: async () => {
try {
await globalService.initialState();
await userService.initialState();
} catch (error) {
// do nth
@ -34,6 +42,7 @@ const router = createBrowserRouter([
element: <Home />,
loader: async () => {
try {
await globalService.initialState();
await userService.initialState();
} catch (error) {
// do nth
@ -50,6 +59,7 @@ const router = createBrowserRouter([
element: <Explore />,
loader: async () => {
try {
await globalService.initialState();
await userService.initialState();
} catch (error) {
// do nth
@ -66,6 +76,7 @@ const router = createBrowserRouter([
element: <MemoDetail />,
loader: async () => {
try {
await globalService.initialState();
await userService.initialState();
} catch (error) {
// do nth

View file

@ -2,7 +2,6 @@ import store from "../store";
import * as api from "../helpers/api";
import * as storage from "../helpers/storage";
import { setGlobalState, setLocale } from "../store/modules/global";
import { convertResponseModelUser } from "./userService";
const globalService = {
getState: () => {
@ -12,19 +11,22 @@ const globalService = {
initialState: async () => {
const defaultGlobalState = {
locale: "en" as Locale,
systemStatus: {
allowSignUp: false,
additionalStyle: "",
additionalScript: "",
} as SystemStatus,
};
const { locale: storageLocale } = storage.get(["locale"]);
if (storageLocale) {
defaultGlobalState.locale = storageLocale;
}
try {
const { data } = (await api.getMyselfUser()).data;
const { data } = (await api.getSystemStatus()).data;
if (data) {
const user = convertResponseModelUser(data);
if (user.setting.locale) {
defaultGlobalState.locale = user.setting.locale;
}
defaultGlobalState.systemStatus = data;
}
} catch (error) {
// do nth

View file

@ -1,7 +1,8 @@
import { locationService } from ".";
import { globalService, locationService } from ".";
import * as api from "../helpers/api";
import { UNKNOWN_ID } from "../helpers/consts";
import store from "../store";
import { setLocale } from "../store/modules/global";
import { setUser, patchUser, setHost, setOwner } from "../store/modules/user";
const defauleSetting: Setting = {
@ -35,11 +36,9 @@ const userService = {
},
initialState: async () => {
const {
data: { host },
} = (await api.getSystemStatus()).data;
if (host) {
store.dispatch(setHost(convertResponseModelUser(host)));
const { systemStatus } = globalService.getState();
if (systemStatus.host) {
store.dispatch(setHost(convertResponseModelUser(systemStatus.host)));
}
const ownerUserId = userService.getUserIdFromPath();
@ -53,6 +52,9 @@ const userService = {
const { data: user } = (await api.getMyselfUser()).data;
if (user) {
store.dispatch(setUser(convertResponseModelUser(user)));
if (user.setting.locale) {
store.dispatch(setLocale(user.setting.locale));
}
}
},

View file

@ -2,11 +2,25 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface State {
locale: Locale;
systemStatus: SystemStatus;
}
const globalSlice = createSlice({
name: "global",
initialState: {} as State,
initialState: {
locale: "en",
systemStatus: {
host: undefined,
profile: {
mode: "dev",
version: "",
},
dbSize: 0,
allowSignUp: false,
additionalStyle: "",
additionalScript: "",
},
} as State,
reducers: {
setGlobalState: (_, action: PayloadAction<State>) => {
return action.payload;

View file

@ -4,7 +4,7 @@ interface Profile {
}
interface SystemStatus {
host: User;
host?: User;
profile: Profile;
dbSize: number;
// System settings