diff --git a/api/system.go b/api/system.go index 525a29e0..ed6086c5 100644 --- a/api/system.go +++ b/api/system.go @@ -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. diff --git a/server/system.go b/server/system.go index 02dae990..6c08a3be 100644 --- a/server/system.go +++ b/server/system.go @@ -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: "", diff --git a/web/src/App.tsx b/web/src/App.tsx index a45b5a0b..4900de74 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -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 ( diff --git a/web/src/components/AboutSiteDialog.tsx b/web/src/components/AboutSiteDialog.tsx index 942fb129..ef82775f 100644 --- a/web/src/components/AboutSiteDialog.tsx +++ b/web/src/components/AboutSiteDialog.tsx @@ -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 = ({ destroy }: Props) => { const { t } = useTranslation(); - const [profile, setProfile] = useState(); - - 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 = ({ destroy }: Props) => {
- {profile !== undefined && ( - <> - {t("common.version")}: - - {profile?.version}-{profile?.mode} - - 🎉 - - )} + <> + {t("common.version")}: + + {profile.version}-{profile.mode} + + 🎉 +
diff --git a/web/src/components/MemoFilter.tsx b/web/src/components/MemoFilter.tsx index 95555e70..ff497aef 100644 --- a/web/src/components/MemoFilter.tsx +++ b/web/src/components/MemoFilter.tsx @@ -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; diff --git a/web/src/components/UpdateVersionBanner.tsx b/web/src/components/UpdateVersionBanner.tsx index b793ff69..31651270 100644 --- a/web/src/components/UpdateVersionBanner.tsx +++ b/web/src/components/UpdateVersionBanner.tsx @@ -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({ 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 = () => { diff --git a/web/src/pages/Auth.tsx b/web/src/pages/Auth.tsx index ccc3b0ec..d49f9c23 100644 --- a/web/src/pages/Auth.tsx +++ b/web/src/pages/Auth.tsx @@ -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(); - 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) => { const text = e.target.value as string; @@ -138,39 +127,32 @@ const Auth = () => {
- {!pageLoadingState.isLoading && ( + {systemStatus?.host ? ( <> - {systemStatus?.host ? ( - <> - {actionBtnLoadingState.isLoading && } - {systemStatus?.allowSignUp && ( - <> - - / - - )} - - - ) : ( + {actionBtnLoadingState.isLoading && } + {systemStatus?.allowSignUp && ( <> + / )} + + + ) : ( + <> + )}
diff --git a/web/src/router/index.tsx b/web/src/router/index.tsx index e1e57092..682a97e9 100644 --- a/web/src/router/index.tsx +++ b/web/src/router/index.tsx @@ -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: , + loader: async () => { + try { + await globalService.initialState(); + } catch (error) { + // do nth + } + }, }, { path: "/", element: , loader: async () => { try { + await globalService.initialState(); await userService.initialState(); } catch (error) { // do nth @@ -34,6 +42,7 @@ const router = createBrowserRouter([ element: , loader: async () => { try { + await globalService.initialState(); await userService.initialState(); } catch (error) { // do nth @@ -50,6 +59,7 @@ const router = createBrowserRouter([ element: , loader: async () => { try { + await globalService.initialState(); await userService.initialState(); } catch (error) { // do nth @@ -66,6 +76,7 @@ const router = createBrowserRouter([ element: , loader: async () => { try { + await globalService.initialState(); await userService.initialState(); } catch (error) { // do nth diff --git a/web/src/services/globalService.ts b/web/src/services/globalService.ts index eb53f246..6a46c0d8 100644 --- a/web/src/services/globalService.ts +++ b/web/src/services/globalService.ts @@ -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 diff --git a/web/src/services/userService.ts b/web/src/services/userService.ts index e135a394..3e1acd6b 100644 --- a/web/src/services/userService.ts +++ b/web/src/services/userService.ts @@ -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)); + } } }, diff --git a/web/src/store/modules/global.ts b/web/src/store/modules/global.ts index d9eceace..7156d37f 100644 --- a/web/src/store/modules/global.ts +++ b/web/src/store/modules/global.ts @@ -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) => { return action.payload; diff --git a/web/src/types/modules/system.d.ts b/web/src/types/modules/system.d.ts index dcd3ad49..3e1a040b 100644 --- a/web/src/types/modules/system.d.ts +++ b/web/src/types/modules/system.d.ts @@ -4,7 +4,7 @@ interface Profile { } interface SystemStatus { - host: User; + host?: User; profile: Profile; dbSize: number; // System settings