diff --git a/web/src/App.tsx b/web/src/App.tsx index 7c89376a7..5a6b53059 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -2,29 +2,30 @@ import { useColorScheme } from "@mui/joy"; import { useEffect, Suspense } from "react"; import { useTranslation } from "react-i18next"; import { RouterProvider } from "react-router-dom"; -import { globalService, locationService } from "./services"; -import { useAppSelector } from "./store"; import router from "./router"; +import { useLocationStore, useGlobalStore } from "./store/module"; import * as storage from "./helpers/storage"; import { getSystemColorScheme } from "./helpers/utils"; import Loading from "./pages/Loading"; -function App() { +const App = () => { const { i18n } = useTranslation(); - const { appearance, locale, systemStatus } = useAppSelector((state) => state.global); + const globalStore = useGlobalStore(); + const locationStore = useLocationStore(); const { mode, setMode } = useColorScheme(); + const { appearance, locale, systemStatus } = globalStore.state; useEffect(() => { - locationService.updateStateWithLocation(); + locationStore.updateStateWithLocation(); window.onpopstate = () => { - locationService.updateStateWithLocation(); + locationStore.updateStateWithLocation(); }; }, []); useEffect(() => { const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handleColorSchemeChange = (e: MediaQueryListEvent) => { - if (globalService.getState().appearance === "system") { + if (globalStore.getState().appearance === "system") { const mode = e.matches ? "dark" : "light"; setMode(mode); } @@ -91,6 +92,6 @@ function App() { ); -} +}; export default App; diff --git a/web/src/components/AboutSiteDialog.tsx b/web/src/components/AboutSiteDialog.tsx index dd8098357..6aead8373 100644 --- a/web/src/components/AboutSiteDialog.tsx +++ b/web/src/components/AboutSiteDialog.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; +import { useGlobalStore } from "../store/module"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import GitHubBadge from "./GitHubBadge"; @@ -9,7 +9,8 @@ type Props = DialogProps; const AboutSiteDialog: React.FC = ({ destroy }: Props) => { const { t } = useTranslation(); - const profile = useAppSelector((state) => state.global.systemStatus.profile); + const globalStore = useGlobalStore(); + const profile = globalStore.state.systemStatus.profile; const handleCloseBtnClick = () => { destroy(); diff --git a/web/src/components/AppearanceSelect.tsx b/web/src/components/AppearanceSelect.tsx index 2ddf3f04d..69a00942d 100644 --- a/web/src/components/AppearanceSelect.tsx +++ b/web/src/components/AppearanceSelect.tsx @@ -1,15 +1,16 @@ import { Option, Select } from "@mui/joy"; import { useTranslation } from "react-i18next"; -import { globalService, userService } from "../services"; -import { useAppSelector } from "../store"; +import { useGlobalStore, useUserStore } from "../store/module"; import Icon from "./Icon"; const appearanceList = ["system", "light", "dark"]; const AppearanceSelect = () => { - const user = useAppSelector((state) => state.user.user); - const appearance = useAppSelector((state) => state.global.appearance); const { t } = useTranslation(); + const globalStore = useGlobalStore(); + const userStore = useUserStore(); + const { appearance } = globalStore.state; + const user = userStore.state.user; const getPrefixIcon = (apperance: Appearance) => { const className = "w-4 h-auto"; @@ -24,9 +25,9 @@ const AppearanceSelect = () => { const handleSelectChange = async (appearance: Appearance) => { if (user) { - await userService.upsertUserSetting("appearance", appearance); + await userStore.upsertUserSetting("appearance", appearance); } - globalService.setAppearance(appearance); + globalStore.setAppearance(appearance); }; return ( diff --git a/web/src/components/ArchivedMemo.tsx b/web/src/components/ArchivedMemo.tsx index a919460e7..95d981f6a 100644 --- a/web/src/components/ArchivedMemo.tsx +++ b/web/src/components/ArchivedMemo.tsx @@ -1,7 +1,7 @@ import { useTranslation } from "react-i18next"; +import { useMemoStore } from "../store/module"; import * as utils from "../helpers/utils"; import useToggle from "../hooks/useToggle"; -import { memoService } from "../services"; import toastHelper from "./Toast"; import MemoContent from "./MemoContent"; import MemoResources from "./MemoResources"; @@ -14,13 +14,13 @@ interface Props { const ArchivedMemo: React.FC = (props: Props) => { const { memo } = props; const { t } = useTranslation(); - + const memoStore = useMemoStore(); const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false); const handleDeleteMemoClick = async () => { if (showConfirmDeleteBtn) { try { - await memoService.deleteMemoById(memo.id); + await memoStore.deleteMemoById(memo.id); } catch (error: any) { console.error(error); toastHelper.error(error.response.data.message); @@ -32,11 +32,11 @@ const ArchivedMemo: React.FC = (props: Props) => { const handleRestoreMemoClick = async () => { try { - await memoService.patchMemo({ + await memoStore.patchMemo({ id: memo.id, rowStatus: "NORMAL", }); - await memoService.fetchMemos(); + await memoStore.fetchMemos(); toastHelper.info(t("message.restored-successfully")); } catch (error: any) { console.error(error); diff --git a/web/src/components/ArchivedMemoDialog.tsx b/web/src/components/ArchivedMemoDialog.tsx index 1b6085fd4..a68ac5743 100644 --- a/web/src/components/ArchivedMemoDialog.tsx +++ b/web/src/components/ArchivedMemoDialog.tsx @@ -1,8 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useMemoStore } from "../store/module"; import useLoading from "../hooks/useLoading"; -import { memoService } from "../services"; -import { useAppSelector } from "../store"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; @@ -14,12 +13,13 @@ type Props = DialogProps; const ArchivedMemoDialog: React.FC = (props: Props) => { const { t } = useTranslation(); const { destroy } = props; - const memos = useAppSelector((state) => state.memo.memos); + const memoStore = useMemoStore(); + const memos = memoStore.state.memos; const loadingState = useLoading(); const [archivedMemos, setArchivedMemos] = useState([]); useEffect(() => { - memoService + memoStore .fetchArchivedMemos() .then((result) => { setArchivedMemos(result); diff --git a/web/src/components/ChangeMemberPasswordDialog.tsx b/web/src/components/ChangeMemberPasswordDialog.tsx index 873961a01..ce49643ce 100644 --- a/web/src/components/ChangeMemberPasswordDialog.tsx +++ b/web/src/components/ChangeMemberPasswordDialog.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useUserStore } from "../store/module"; import { validate, ValidatorConfig } from "../helpers/validator"; -import { userService } from "../services"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; @@ -20,6 +20,7 @@ interface Props extends DialogProps { const ChangeMemberPasswordDialog: React.FC = (props: Props) => { const { user: propsUser, destroy } = props; const { t } = useTranslation(); + const userStore = useUserStore(); const [newPassword, setNewPassword] = useState(""); const [newPasswordAgain, setNewPasswordAgain] = useState(""); @@ -60,7 +61,7 @@ const ChangeMemberPasswordDialog: React.FC = (props: Props) => { } try { - await userService.patchUser({ + await userStore.patchUser({ id: propsUser.id, password: newPassword, }); diff --git a/web/src/components/ChangeMemoCreatedTsDialog.tsx b/web/src/components/ChangeMemoCreatedTsDialog.tsx index b43e5de19..02a1889ab 100644 --- a/web/src/components/ChangeMemoCreatedTsDialog.tsx +++ b/web/src/components/ChangeMemoCreatedTsDialog.tsx @@ -1,7 +1,7 @@ import dayjs from "dayjs"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { memoService } from "../services"; +import { useMemoStore } from "../store/module"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; @@ -14,11 +14,12 @@ interface Props extends DialogProps { const ChangeMemoCreatedTsDialog: React.FC = (props: Props) => { const { t } = useTranslation(); const { destroy, memoId } = props; + const memoStore = useMemoStore(); const [createdAt, setCreatedAt] = useState(""); const maxDatetimeValue = dayjs().format("YYYY-MM-DDTHH:mm"); useEffect(() => { - memoService.getMemoById(memoId).then((memo) => { + memoStore.getMemoById(memoId).then((memo) => { if (memo) { const datetime = dayjs(memo.createdTs).format("YYYY-MM-DDTHH:mm"); setCreatedAt(datetime); @@ -48,7 +49,7 @@ const ChangeMemoCreatedTsDialog: React.FC = (props: Props) => { } try { - await memoService.patchMemo({ + await memoStore.patchMemo({ id: memoId, createdTs, }); diff --git a/web/src/components/ChangePasswordDialog.tsx b/web/src/components/ChangePasswordDialog.tsx index 45c61e924..599eeda60 100644 --- a/web/src/components/ChangePasswordDialog.tsx +++ b/web/src/components/ChangePasswordDialog.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useUserStore } from "../store/module"; import { validate, ValidatorConfig } from "../helpers/validator"; -import { userService } from "../services"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; @@ -17,6 +17,7 @@ type Props = DialogProps; const ChangePasswordDialog: React.FC = ({ destroy }: Props) => { const { t } = useTranslation(); + const userStore = useUserStore(); const [newPassword, setNewPassword] = useState(""); const [newPasswordAgain, setNewPasswordAgain] = useState(""); @@ -57,8 +58,8 @@ const ChangePasswordDialog: React.FC = ({ destroy }: Props) => { } try { - const user = userService.getState().user as User; - await userService.patchUser({ + const user = userStore.getState().user as User; + await userStore.patchUser({ id: user.id, password: newPassword, }); diff --git a/web/src/components/ChangeResourceFilenameDialog.tsx b/web/src/components/ChangeResourceFilenameDialog.tsx index f1c5dad2e..82878f99e 100644 --- a/web/src/components/ChangeResourceFilenameDialog.tsx +++ b/web/src/components/ChangeResourceFilenameDialog.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { resourceService } from "../services"; +import { useResourceStore } from "../store/module"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; @@ -24,8 +24,9 @@ const validateFilename = (filename: string): boolean => { }; const ChangeResourceFilenameDialog: React.FC = (props: Props) => { - const { t } = useTranslation(); const { destroy, resourceId, resourceFilename } = props; + const { t } = useTranslation(); + const resourceStore = useResourceStore(); const [filename, setFilename] = useState(resourceFilename); const handleFilenameChanged = (e: React.ChangeEvent) => { @@ -47,7 +48,7 @@ const ChangeResourceFilenameDialog: React.FC = (props: Props) => { return; } try { - await resourceService.patchResource({ + await resourceStore.patchResource({ id: resourceId, filename: filename, }); diff --git a/web/src/components/CreateShortcutDialog.tsx b/web/src/components/CreateShortcutDialog.tsx index dc851253f..ee71d9f3a 100644 --- a/web/src/components/CreateShortcutDialog.tsx +++ b/web/src/components/CreateShortcutDialog.tsx @@ -1,7 +1,7 @@ import dayjs from "dayjs"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { memoService, shortcutService } from "../services"; +import { useMemoStore, useShortcutStore } from "../store/module"; import { filterConsts, getDefaultFilter, relationConsts } from "../helpers/filter"; import useLoading from "../hooks/useLoading"; import Icon from "./Icon"; @@ -16,6 +16,7 @@ interface Props extends DialogProps { const CreateShortcutDialog: React.FC = (props: Props) => { const { destroy, shortcutId } = props; + const shortcutStore = useShortcutStore(); const [title, setTitle] = useState(""); const [filters, setFilters] = useState([]); const requestState = useLoading(false); @@ -23,7 +24,7 @@ const CreateShortcutDialog: React.FC = (props: Props) => { useEffect(() => { if (shortcutId) { - const shortcutTemp = shortcutService.getShortcutById(shortcutId); + const shortcutTemp = shortcutStore.getShortcutById(shortcutId); if (shortcutTemp) { setTitle(shortcutTemp.title); const temp = JSON.parse(shortcutTemp.payload); @@ -52,13 +53,13 @@ const CreateShortcutDialog: React.FC = (props: Props) => { } try { if (shortcutId) { - await shortcutService.patchShortcut({ + await shortcutStore.patchShortcut({ id: shortcutId, title, payload: JSON.stringify(filters), }); } else { - await shortcutService.createShortcut({ + await shortcutStore.createShortcut({ title, payload: JSON.stringify(filters), }); @@ -161,9 +162,9 @@ interface MemoFilterInputerProps { const MemoFilterInputer: React.FC = (props: MemoFilterInputerProps) => { const { index, filter, handleFilterChange, handleFilterRemove } = props; const { t } = useTranslation(); + const memoStore = useMemoStore(); const [value, setValue] = useState(filter.value.value); - - const tags = Array.from(memoService.getState().tags); + const tags = Array.from(memoStore.getState().tags); const { type } = filter; const operatorDataSource = Object.values(filterConsts[type as FilterType].operators).map(({ text, value }) => ({ text: t(text), value })); diff --git a/web/src/components/DailyReviewDialog.tsx b/web/src/components/DailyReviewDialog.tsx index c4c6f869f..502f94cf0 100644 --- a/web/src/components/DailyReviewDialog.tsx +++ b/web/src/components/DailyReviewDialog.tsx @@ -1,6 +1,6 @@ import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; +import { useMemoStore } from "../store/module"; import toImage from "../labs/html2image"; import useToggle from "../hooks/useToggle"; import { DAILY_TIMESTAMP } from "../helpers/consts"; @@ -21,7 +21,8 @@ const weekdayChineseStrArray = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] const DailyReviewDialog: React.FC = (props: Props) => { const { t } = useTranslation(); - const memos = useAppSelector((state) => state.memo.memos); + const memoStore = useMemoStore(); + const memos = memoStore.state.memos; const [currentDateStamp, setCurrentDateStamp] = useState(utils.getDateStampByDate(utils.getDateString(props.currentDateStamp))); const [showDatePicker, toggleShowDatePicker] = useToggle(false); const memosElRef = useRef(null); diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index 774b8c87f..2817c3964 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -3,7 +3,7 @@ import dayjs from "dayjs"; import { memo, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { editorStateService, locationService, memoService, userService } from "../services"; +import { useEditorStore, useLocationStore, useMemoStore, useUserStore } from "../store/module"; import Icon from "./Icon"; import toastHelper from "./Toast"; import MemoContent from "./MemoContent"; @@ -30,9 +30,13 @@ const Memo: React.FC = (props: Props) => { const { memo, highlightWord } = props; const { t, i18n } = useTranslation(); const navigate = useNavigate(); + const editorStore = useEditorStore(); + const locationStore = useLocationStore(); + const userStore = useUserStore(); + const memoStore = useMemoStore(); const [displayTimeStr, setDisplayTimeStr] = useState(getFormatedMemoTimeStr(memo.displayTs, i18n.language)); const memoContainerRef = useRef(null); - const isVisitorMode = userService.isVisitorMode(); + const isVisitorMode = userStore.isVisitorMode(); useEffect(() => { let intervalFlag: any = -1; @@ -59,9 +63,9 @@ const Memo: React.FC = (props: Props) => { const handleTogglePinMemoBtnClick = async () => { try { if (memo.pinned) { - await memoService.unpinMemo(memo.id); + await memoStore.unpinMemo(memo.id); } else { - await memoService.pinMemo(memo.id); + await memoStore.pinMemo(memo.id); } } catch (error) { // do nth @@ -69,12 +73,12 @@ const Memo: React.FC = (props: Props) => { }; const handleEditMemoClick = () => { - editorStateService.setEditMemoWithId(memo.id); + editorStore.setEditMemoWithId(memo.id); }; const handleArchiveMemoClick = async () => { try { - await memoService.patchMemo({ + await memoStore.patchMemo({ id: memo.id, rowStatus: "ARCHIVED", }); @@ -83,8 +87,8 @@ const Memo: React.FC = (props: Props) => { toastHelper.error(error.response.data.message); } - if (editorStateService.getState().editMemoId === memo.id) { - editorStateService.clearEditMemo(); + if (editorStore.getState().editMemoId === memo.id) { + editorStore.clearEditMemo(); } }; @@ -97,14 +101,14 @@ const Memo: React.FC = (props: Props) => { if (targetEl.className === "tag-span") { const tagName = targetEl.innerText.slice(1); - const currTagQuery = locationService.getState().query?.tag; + const currTagQuery = locationStore.getState().query?.tag; if (currTagQuery === tagName) { - locationService.setTagQuery(undefined); + locationStore.setTagQuery(undefined); } else { - locationService.setTagQuery(tagName); + locationStore.setTagQuery(tagName); } } else if (targetEl.classList.contains("todo-block")) { - if (userService.isVisitorMode()) { + if (userStore.isVisitorMode()) { return; } @@ -128,7 +132,7 @@ const Memo: React.FC = (props: Props) => { finalContent += `${tempList[i]}`; } } - await memoService.patchMemo({ + await memoStore.patchMemo({ id: memo.id, content: finalContent, }); @@ -151,7 +155,7 @@ const Memo: React.FC = (props: Props) => { return; } - editorStateService.setEditMemoWithId(memo.id); + editorStore.setEditMemoWithId(memo.id); }; const handleMemoDisplayTimeClick = () => { @@ -159,11 +163,11 @@ const Memo: React.FC = (props: Props) => { }; const handleMemoVisibilityClick = (visibility: Visibility) => { - const currVisibilityQuery = locationService.getState().query?.visibility; + const currVisibilityQuery = locationStore.getState().query?.visibility; if (currVisibilityQuery === visibility) { - locationService.setMemoVisibilityQuery(undefined); + locationStore.setMemoVisibilityQuery(undefined); } else { - locationService.setMemoVisibilityQuery(visibility); + locationStore.setMemoVisibilityQuery(visibility); } }; diff --git a/web/src/components/MemoContent.tsx b/web/src/components/MemoContent.tsx index 9c7179afe..a486eb038 100644 --- a/web/src/components/MemoContent.tsx +++ b/web/src/components/MemoContent.tsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useUserStore } from "../store/module"; import { marked } from "../labs/marked"; import { highlightWithWord } from "../labs/highlighter"; import Icon from "./Icon"; -import { useAppSelector } from "../store"; import "../less/memo-content.less"; export interface DisplayConfig { @@ -36,7 +36,8 @@ const MemoContent: React.FC = (props: Props) => { return firstHorizontalRuleIndex !== -1 ? content.slice(0, firstHorizontalRuleIndex) : content; }, [content]); const { t } = useTranslation(); - const user = useAppSelector((state) => state.user.user); + const userStore = useUserStore(); + const user = userStore.state.user; const [state, setState] = useState({ expandButtonStatus: -1, @@ -84,6 +85,9 @@ const MemoContent: React.FC = (props: Props) => { setState({ expandButtonStatus: Number(expandButtonStatus) as ExpandButtonStatus, }); + if (!expandButtonStatus) { + memoContentContainerRef.current?.scrollIntoView(); + } }; return ( diff --git a/web/src/components/MemoEditor.tsx b/web/src/components/MemoEditor.tsx index 73cc73ece..15ff907bd 100644 --- a/web/src/components/MemoEditor.tsx +++ b/web/src/components/MemoEditor.tsx @@ -3,8 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { useTranslation } from "react-i18next"; import { deleteMemoResource, upsertMemoResource } from "../helpers/api"; import { TAB_SPACE_WIDTH, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts"; -import { editorStateService, locationService, memoService, resourceService } from "../services"; -import { useAppSelector } from "../store"; +import { useEditorStore, useLocationStore, useMemoStore, useResourceStore, useUserStore } from "../store/module"; import * as storage from "../helpers/storage"; import Icon from "./Icon"; import toastHelper from "./Toast"; @@ -41,19 +40,25 @@ interface State { const MemoEditor = () => { const { t, i18n } = useTranslation(); - const user = useAppSelector((state) => state.user.user as User); - const setting = user.setting; - const editorState = useAppSelector((state) => state.editor); - const tags = useAppSelector((state) => state.memo.tags); + const userStore = useUserStore(); + const editorStore = useEditorStore(); + const locationStore = useLocationStore(); + const memoStore = useMemoStore(); + const resourceStore = useResourceStore(); + const [state, setState] = useState({ isUploadingResource: false, fullscreen: false, shouldShowEmojiPicker: false, }); const [allowSave, setAllowSave] = useState(false); + const editorState = editorStore.state; const prevEditorStateRef = useRef(editorState); const editorRef = useRef(null); const tagSelectorRef = useRef(null); + const user = userStore.state.user as User; + const setting = user.setting; + const tags = memoStore.state.tags; const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => { return { value: item.value, @@ -64,22 +69,22 @@ const MemoEditor = () => { useEffect(() => { const { editingMemoIdCache, editingMemoVisibilityCache } = storage.get(["editingMemoIdCache", "editingMemoVisibilityCache"]); if (editingMemoIdCache) { - editorStateService.setEditMemoWithId(editingMemoIdCache); + editorStore.setEditMemoWithId(editingMemoIdCache); } if (editingMemoVisibilityCache) { - editorStateService.setMemoVisibility(editingMemoVisibilityCache as "PUBLIC" | "PROTECTED" | "PRIVATE"); + editorStore.setMemoVisibility(editingMemoVisibilityCache as "PUBLIC" | "PROTECTED" | "PRIVATE"); } else { - editorStateService.setMemoVisibility(setting.memoVisibility); + editorStore.setMemoVisibility(setting.memoVisibility); } }, []); useEffect(() => { if (editorState.editMemoId) { - memoService.getMemoById(editorState.editMemoId ?? UNKNOWN_ID).then((memo) => { + memoStore.getMemoById(editorState.editMemoId ?? UNKNOWN_ID).then((memo) => { if (memo) { handleEditorFocus(); - editorStateService.setMemoVisibility(memo.visibility); - editorStateService.setResourceList(memo.resourceList); + editorStore.setMemoVisibility(memo.visibility); + editorStore.setResourceList(memo.resourceList); editorRef.current?.setContent(memo.content ?? ""); } }); @@ -180,8 +185,8 @@ const MemoEditor = () => { } } if (uploadedResourceList.length > 0) { - const resourceList = editorStateService.getState().resourceList; - editorStateService.setResourceList([...resourceList, ...uploadedResourceList]); + const resourceList = editorStore.getState().resourceList; + editorStore.setResourceList([...resourceList, ...uploadedResourceList]); } }; @@ -210,7 +215,7 @@ const MemoEditor = () => { let resource = undefined; try { - resource = await resourceService.upload(file); + resource = await resourceStore.upload(file); } catch (error: any) { console.error(error); toastHelper.error(error.response.data.message); @@ -233,26 +238,26 @@ const MemoEditor = () => { } try { - const { editMemoId } = editorStateService.getState(); + const { editMemoId } = editorStore.getState(); if (editMemoId && editMemoId !== UNKNOWN_ID) { - const prevMemo = await memoService.getMemoById(editMemoId ?? UNKNOWN_ID); + const prevMemo = await memoStore.getMemoById(editMemoId ?? UNKNOWN_ID); if (prevMemo) { - await memoService.patchMemo({ + await memoStore.patchMemo({ id: prevMemo.id, content, visibility: editorState.memoVisibility, resourceIdList: editorState.resourceList.map((resource) => resource.id), }); } - editorStateService.clearEditMemo(); + editorStore.clearEditMemo(); } else { - await memoService.createMemo({ + await memoStore.createMemo({ content, visibility: editorState.memoVisibility, resourceIdList: editorState.resourceList.map((resource) => resource.id), }); - locationService.clearQuery(); + locationStore.clearQuery(); } } catch (error: any) { console.error(error); @@ -265,7 +270,7 @@ const MemoEditor = () => { fullscreen: false, }; }); - editorStateService.clearResourceList(); + editorStore.clearResourceList(); setEditorContentCache(""); storage.remove(["editingMemoVisibilityCache"]); editorRef.current?.setContent(""); @@ -273,8 +278,8 @@ const MemoEditor = () => { const handleCancelEdit = () => { if (editorState.editMemoId) { - editorStateService.clearEditMemo(); - editorStateService.clearResourceList(); + editorStore.clearEditMemo(); + editorStore.clearResourceList(); editorRef.current?.setContent(""); setEditorContentCache(""); storage.remove(["editingMemoVisibilityCache"]); @@ -338,7 +343,7 @@ const MemoEditor = () => { } } } - editorStateService.setResourceList([...editorState.resourceList, ...resourceList]); + editorStore.setResourceList([...editorState.resourceList, ...resourceList]); document.body.removeChild(inputEl); }; inputEl.click(); @@ -361,7 +366,7 @@ const MemoEditor = () => { }, []); const handleDeleteResource = async (resourceId: ResourceId) => { - editorStateService.setResourceList(editorState.resourceList.filter((resource) => resource.id !== resourceId)); + editorStore.setResourceList(editorState.resourceList.filter((resource) => resource.id !== resourceId)); if (editorState.editMemoId) { await deleteMemoResource(editorState.editMemoId, resourceId); } @@ -369,7 +374,7 @@ const MemoEditor = () => { const handleMemoVisibilityOptionChanged = async (value: string) => { const visibilityValue = value as Visibility; - editorStateService.setMemoVisibility(visibilityValue); + editorStore.setMemoVisibility(visibilityValue); setEditingMemoVisibilityCache(visibilityValue); }; diff --git a/web/src/components/MemoFilter.tsx b/web/src/components/MemoFilter.tsx index 16a6de28b..1a31ad957 100644 --- a/web/src/components/MemoFilter.tsx +++ b/web/src/components/MemoFilter.tsx @@ -1,6 +1,5 @@ import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; -import { locationService, shortcutService } from "../services"; +import { useLocationStore, useShortcutStore } from "../store/module"; import * as utils from "../helpers/utils"; import { getTextWithMemoType } from "../helpers/filter"; import Icon from "./Icon"; @@ -8,10 +7,11 @@ import "../less/memo-filter.less"; const MemoFilter = () => { const { t } = useTranslation(); - useAppSelector((state) => state.shortcut.shortcuts); - const query = useAppSelector((state) => state.location.query); + const locationStore = useLocationStore(); + const shortcutStore = useShortcutStore(); + const query = locationStore.state.query; const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = query; - const shortcut = shortcutId ? shortcutService.getShortcutById(shortcutId) : null; + const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null; const showFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility); return ( @@ -20,7 +20,7 @@ const MemoFilter = () => {
{ - locationService.setMemoShortcut(undefined); + locationStore.setMemoShortcut(undefined); }} > {shortcut?.title} @@ -28,7 +28,7 @@ const MemoFilter = () => {
{ - locationService.setTagQuery(undefined); + locationStore.setTagQuery(undefined); }} > {tagQuery} @@ -36,7 +36,7 @@ const MemoFilter = () => {
{ - locationService.setMemoTypeQuery(undefined); + locationStore.setMemoTypeQuery(undefined); }} > {t(getTextWithMemoType(memoType as MemoSpecType))} @@ -44,7 +44,7 @@ const MemoFilter = () => {
{ - locationService.setMemoVisibilityQuery(undefined); + locationStore.setMemoVisibilityQuery(undefined); }} > {visibility} @@ -53,7 +53,7 @@ const MemoFilter = () => {
{ - locationService.setFromAndToQuery(); + locationStore.setFromAndToQuery(); }} > {utils.getDateString(duration.from)} to {utils.getDateString(duration.to)} @@ -62,7 +62,7 @@ const MemoFilter = () => {
{ - locationService.setTextQuery(undefined); + locationStore.setTextQuery(undefined); }} > {textQuery} diff --git a/web/src/components/MemoList.tsx b/web/src/components/MemoList.tsx index 223d8f758..55c7a00eb 100644 --- a/web/src/components/MemoList.tsx +++ b/web/src/components/MemoList.tsx @@ -1,10 +1,9 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { memoService, shortcutService } from "../services"; -import { DEFAULT_MEMO_LIMIT } from "../services/memoService"; -import { useAppSelector } from "../store"; +import { useLocationStore, useMemoStore, useShortcutStore, useUserStore } from "../store/module"; import { TAG_REG, LINK_REG } from "../labs/marked/parser"; import * as utils from "../helpers/utils"; +import { DEFAULT_MEMO_LIMIT } from "../helpers/consts"; import { checkShouldShowMemoWithFilters } from "../helpers/filter"; import toastHelper from "./Toast"; import Memo from "./Memo"; @@ -12,14 +11,18 @@ import "../less/memo-list.less"; const MemoList = () => { const { t } = useTranslation(); - const query = useAppSelector((state) => state.location.query); - const memoDisplayTsOption = useAppSelector((state) => state.user.user?.setting.memoDisplayTsOption); - const { memos, isFetching } = useAppSelector((state) => state.memo); + const userStore = useUserStore(); + const memoStore = useMemoStore(); + const shortcutStore = useShortcutStore(); + const locationStore = useLocationStore(); + const query = locationStore.state.query; + const memoDisplayTsOption = userStore.state.user?.setting.memoDisplayTsOption; + const { memos, isFetching } = memoStore.state; const [isComplete, setIsComplete] = useState(false); const [highlightWord, setHighlightWord] = useState(""); const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = query ?? {}; - const shortcut = shortcutId ? shortcutService.getShortcutById(shortcutId) : null; + const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null; const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility); const shownMemos = @@ -84,7 +87,7 @@ const MemoList = () => { const sortedMemos = pinnedMemos.concat(unpinnedMemos).filter((m) => m.rowStatus === "NORMAL"); useEffect(() => { - memoService + memoStore .fetchMemos() .then((fetchedMemos) => { if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) { @@ -118,7 +121,7 @@ const MemoList = () => { const handleFetchMoreClick = async () => { try { - const fetchedMemos = await memoService.fetchMemos(DEFAULT_MEMO_LIMIT, memos.length); + const fetchedMemos = await memoStore.fetchMemos(DEFAULT_MEMO_LIMIT, memos.length); if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) { setIsComplete(true); } else { diff --git a/web/src/components/MemosHeader.tsx b/web/src/components/MemosHeader.tsx index 303022d95..795825daa 100644 --- a/web/src/components/MemosHeader.tsx +++ b/web/src/components/MemosHeader.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { memoService, shortcutService } from "../services"; -import { useAppSelector } from "../store"; +import { useLocationStore, useMemoStore, useShortcutStore } from "../store/module"; import Icon from "./Icon"; import SearchBar from "./SearchBar"; import { toggleSidebar } from "./Sidebar"; @@ -9,8 +8,11 @@ import "../less/memos-header.less"; let prevRequestTimestamp = Date.now(); const MemosHeader = () => { - const query = useAppSelector((state) => state.location.query); - const shortcuts = useAppSelector((state) => state.shortcut.shortcuts); + const locationStore = useLocationStore(); + const memoStore = useMemoStore(); + const shortcutStore = useShortcutStore(); + const query = locationStore.state.query; + const shortcuts = shortcutStore.state.shortcuts; const [titleText, setTitleText] = useState("MEMOS"); useEffect(() => { @@ -19,7 +21,7 @@ const MemosHeader = () => { return; } - const shortcut = shortcutService.getShortcutById(query?.shortcutId); + const shortcut = shortcutStore.getShortcutById(query?.shortcutId); if (shortcut) { setTitleText(shortcut.title); } @@ -29,7 +31,7 @@ const MemosHeader = () => { const now = Date.now(); if (now - prevRequestTimestamp > 1 * 1000) { prevRequestTimestamp = now; - memoService.fetchMemos().catch(() => { + memoStore.fetchMemos().catch(() => { // do nth }); } diff --git a/web/src/components/ResourcesDialog.tsx b/web/src/components/ResourcesDialog.tsx index 259e7aa7d..dbd2b8b7f 100644 --- a/web/src/components/ResourcesDialog.tsx +++ b/web/src/components/ResourcesDialog.tsx @@ -3,8 +3,7 @@ import copy from "copy-to-clipboard"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import useLoading from "../hooks/useLoading"; -import { resourceService } from "../services"; -import { useAppSelector } from "../store"; +import { useResourceStore } from "../store/module"; import Icon from "./Icon"; import toastHelper from "./Toast"; import Dropdown from "./common/Dropdown"; @@ -24,13 +23,14 @@ const ResourcesDialog: React.FC = (props: Props) => { const { destroy } = props; const { t } = useTranslation(); const loadingState = useLoading(); - const { resources } = useAppSelector((state) => state.resource); + const resourceStore = useResourceStore(); + const resources = resourceStore.state.resources; const [state, setState] = useState({ isUploadingResource: false, }); useEffect(() => { - resourceService + resourceStore .fetchResourceList() .catch((error) => { console.error(error); @@ -66,7 +66,7 @@ const ResourcesDialog: React.FC = (props: Props) => { for (const file of inputEl.files) { try { - await resourceService.upload(file); + await resourceStore.upload(file); } catch (error: any) { console.error(error); toastHelper.error(error.response.data.message); @@ -127,7 +127,7 @@ const ResourcesDialog: React.FC = (props: Props) => { style: "warning", onConfirm: async () => { for (const resource of unusedResources) { - await resourceService.deleteResourceById(resource.id); + await resourceStore.deleteResourceById(resource.id); } }, }); @@ -144,7 +144,7 @@ const ResourcesDialog: React.FC = (props: Props) => { content: warningText, style: "warning", onConfirm: async () => { - await resourceService.deleteResourceById(resource.id); + await resourceStore.deleteResourceById(resource.id); }, }); }; diff --git a/web/src/components/ResourcesSelectorDialog.tsx b/web/src/components/ResourcesSelectorDialog.tsx index b31b98cf0..d0ca80f5d 100644 --- a/web/src/components/ResourcesSelectorDialog.tsx +++ b/web/src/components/ResourcesSelectorDialog.tsx @@ -2,8 +2,7 @@ import { Checkbox, Tooltip } from "@mui/joy"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import useLoading from "../hooks/useLoading"; -import { editorStateService, resourceService } from "../services"; -import { useAppSelector } from "../store"; +import { useEditorStore, useResourceStore } from "../store/module"; import Icon from "./Icon"; import toastHelper from "./Toast"; import { generateDialog } from "./Dialog"; @@ -20,14 +19,15 @@ const ResourcesSelectorDialog: React.FC = (props: Props) => { const { destroy } = props; const { t } = useTranslation(); const loadingState = useLoading(); - const { resources } = useAppSelector((state) => state.resource); - const editorState = useAppSelector((state) => state.editor); + const editorStore = useEditorStore(); + const resourceStore = useResourceStore(); + const resources = resourceStore.state.resources; const [state, setState] = useState({ checkedArray: [], }); useEffect(() => { - resourceService + resourceStore .fetchResourceList() .catch((error) => { console.error(error); @@ -39,7 +39,7 @@ const ResourcesSelectorDialog: React.FC = (props: Props) => { }, []); useEffect(() => { - const checkedResourceIdArray = editorState.resourceList.map((resource) => resource.id); + const checkedResourceIdArray = editorStore.state.resourceList.map((resource) => resource.id); setState({ checkedArray: resources.map((resource) => { return checkedResourceIdArray.includes(resource.id); @@ -75,7 +75,7 @@ const ResourcesSelectorDialog: React.FC = (props: Props) => { const resourceList = resources.filter((_, index) => { return state.checkedArray[index]; }); - editorStateService.setResourceList(resourceList); + editorStore.setResourceList(resourceList); destroy(); }; diff --git a/web/src/components/SearchBar.tsx b/web/src/components/SearchBar.tsx index 5a2912776..37a77a6a0 100644 --- a/web/src/components/SearchBar.tsx +++ b/web/src/components/SearchBar.tsx @@ -1,33 +1,33 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { locationService } from "../services"; -import { useAppSelector } from "../store"; +import { useLocationStore } from "../store/module"; import { memoSpecialTypes } from "../helpers/filter"; import Icon from "./Icon"; import "../less/search-bar.less"; const SearchBar = () => { const { t } = useTranslation(); - const memoType = useAppSelector((state) => state.location.query?.type); + const locationStore = useLocationStore(); + const memoType = locationStore.state.query.type; const [queryText, setQueryText] = useState(""); useEffect(() => { - const text = locationService.getState().query.text; + const text = locationStore.getState().query.text; setQueryText(text === undefined ? "" : text); - }, [locationService.getState().query.text]); + }, [locationStore.getState().query.text]); const handleMemoTypeItemClick = (type: MemoSpecType | undefined) => { - const { type: prevType } = locationService.getState().query ?? {}; + const { type: prevType } = locationStore.getState().query ?? {}; if (type === prevType) { type = undefined; } - locationService.setMemoTypeQuery(type); + locationStore.setMemoTypeQuery(type); }; const handleTextQueryInput = (event: React.FormEvent) => { const text = event.currentTarget.value; setQueryText(text); - locationService.setTextQuery(text.length === 0 ? undefined : text); + locationStore.setTextQuery(text.length === 0 ? undefined : text); }; return ( diff --git a/web/src/components/SettingDialog.tsx b/web/src/components/SettingDialog.tsx index e9da0b2de..646c2a0aa 100644 --- a/web/src/components/SettingDialog.tsx +++ b/web/src/components/SettingDialog.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import MyAccountSection from "./Settings/MyAccountSection"; @@ -8,6 +7,7 @@ import PreferencesSection from "./Settings/PreferencesSection"; import MemberSection from "./Settings/MemberSection"; import SystemSection from "./Settings/SystemSection"; import "../less/setting-dialog.less"; +import { useUserStore } from "../store/module"; type Props = DialogProps; @@ -20,7 +20,8 @@ interface State { const SettingDialog: React.FC = (props: Props) => { const { destroy } = props; const { t } = useTranslation(); - const user = useAppSelector((state) => state.user.user); + const userStore = useUserStore(); + const user = userStore.state.user; const [state, setState] = useState({ selectedSection: "my-account", }); diff --git a/web/src/components/Settings/MemberSection.tsx b/web/src/components/Settings/MemberSection.tsx index 1d7504a33..79d83998a 100644 --- a/web/src/components/Settings/MemberSection.tsx +++ b/web/src/components/Settings/MemberSection.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { userService } from "../../services"; -import { useAppSelector } from "../../store"; +import { useUserStore } from "../../store/module"; import * as api from "../../helpers/api"; import toastHelper from "../Toast"; import Dropdown from "../common/Dropdown"; @@ -16,7 +15,8 @@ interface State { const PreferencesSection = () => { const { t } = useTranslation(); - const currentUser = useAppSelector((state) => state.user.user); + const userStore = useUserStore(); + const currentUser = userStore.state.user; const [state, setState] = useState({ createUserUsername: "", createUserPassword: "", @@ -80,7 +80,7 @@ const PreferencesSection = () => { content: `❗️Are you sure to archive ${user.username}?`, style: "warning", onConfirm: async () => { - await userService.patchUser({ + await userStore.patchUser({ id: user.id, rowStatus: "ARCHIVED", }); @@ -90,7 +90,7 @@ const PreferencesSection = () => { }; const handleRestoreUserClick = async (user: User) => { - await userService.patchUser({ + await userStore.patchUser({ id: user.id, rowStatus: "NORMAL", }); @@ -103,7 +103,7 @@ const PreferencesSection = () => { content: `Are you sure to delete ${user.username}? THIS ACTION IS IRREVERSIABLE.❗️`, style: "warning", onConfirm: async () => { - await userService.deleteUser({ + await userStore.deleteUser({ id: user.id, }); fetchUserList(); diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index c912ed142..b732fe8ce 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -1,6 +1,5 @@ import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../../store"; -import { userService } from "../../services"; +import { useUserStore } from "../../store/module"; import { showCommonDialog } from "../Dialog/CommonDialog"; import showChangePasswordDialog from "../ChangePasswordDialog"; import showUpdateAccountDialog from "../UpdateAccountDialog"; @@ -8,7 +7,8 @@ import "../../less/settings/my-account-section.less"; const MyAccountSection = () => { const { t } = useTranslation(); - const user = useAppSelector((state) => state.user.user as User); + const userStore = useUserStore(); + const user = userStore.state.user as User; const openAPIRoute = `${window.location.origin}/api/memo?openId=${user.openId}`; const handleResetOpenIdBtnClick = async () => { @@ -17,7 +17,7 @@ const MyAccountSection = () => { content: "❗️The existing API will be invalidated and a new one will be generated, are you sure you want to reset?", style: "warning", onConfirm: async () => { - await userService.patchUser({ + await userStore.patchUser({ id: user.id, resetOpenId: true, }); diff --git a/web/src/components/Settings/PreferencesSection.tsx b/web/src/components/Settings/PreferencesSection.tsx index 65575c91a..e26100b9b 100644 --- a/web/src/components/Settings/PreferencesSection.tsx +++ b/web/src/components/Settings/PreferencesSection.tsx @@ -1,7 +1,6 @@ import { Select, Switch, Option } from "@mui/joy"; import { useTranslation } from "react-i18next"; -import { globalService, userService } from "../../services"; -import { useAppSelector } from "../../store"; +import { useGlobalStore, useUserStore } from "../../store/module"; import { VISIBILITY_SELECTOR_ITEMS, MEMO_DISPLAY_TS_OPTION_SELECTOR_ITEMS } from "../../helpers/consts"; import Icon from "../Icon"; import AppearanceSelect from "../AppearanceSelect"; @@ -44,7 +43,9 @@ const localeSelectorItems = [ const PreferencesSection = () => { const { t } = useTranslation(); - const { setting, localSetting } = useAppSelector((state) => state.user.user as User); + const globalStore = useGlobalStore(); + const userStore = useUserStore(); + const { setting, localSetting } = userStore.state.user as User; const visibilitySelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => { return { value: item.value, @@ -60,20 +61,20 @@ const PreferencesSection = () => { }); const handleLocaleChanged = async (value: string) => { - await userService.upsertUserSetting("locale", value); - globalService.setLocale(value as Locale); + await userStore.upsertUserSetting("locale", value); + globalStore.setLocale(value as Locale); }; const handleDefaultMemoVisibilityChanged = async (value: string) => { - await userService.upsertUserSetting("memoVisibility", value); + await userStore.upsertUserSetting("memoVisibility", value); }; const handleMemoDisplayTsOptionChanged = async (value: string) => { - await userService.upsertUserSetting("memoDisplayTsOption", value); + await userStore.upsertUserSetting("memoDisplayTsOption", value); }; const handleIsFoldingEnabledChanged = (event: React.ChangeEvent) => { - userService.upsertLocalSetting("enableFoldMemo", event.target.checked); + userStore.upsertLocalSetting("enableFoldMemo", event.target.checked); }; return ( diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index 32294b2b3..b5610500b 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -4,10 +4,10 @@ import { useTranslation } from "react-i18next"; import copy from "copy-to-clipboard"; import { toLower } from "lodash"; import toImage from "../labs/html2image"; +import { useMemoStore, useUserStore } from "../store/module"; import { VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts"; import * as utils from "../helpers/utils"; import { getMemoStats } from "../helpers/api"; -import { memoService, userService } from "../services"; import useLoading from "../hooks/useLoading"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; @@ -29,7 +29,9 @@ interface State { const ShareMemoDialog: React.FC = (props: Props) => { const { memo: propsMemo, destroy } = props; const { t } = useTranslation(); - const user = userService.getState().user as User; + const userStore = useUserStore(); + const memoStore = useMemoStore(); + const user = userStore.state.user as User; const [state, setState] = useState({ memoAmount: 0, memoVisibility: propsMemo.visibility, @@ -113,7 +115,7 @@ const ShareMemoDialog: React.FC = (props: Props) => { ...state, memoVisibility: visibilityValue, }); - await memoService.patchMemo({ + await memoStore.patchMemo({ id: memo.id, visibility: visibilityValue, }); diff --git a/web/src/components/ShortcutList.tsx b/web/src/components/ShortcutList.tsx index 89c5d60a8..a3bc97c72 100644 --- a/web/src/components/ShortcutList.tsx +++ b/web/src/components/ShortcutList.tsx @@ -1,7 +1,6 @@ import { useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { locationService, shortcutService } from "../services"; -import { useAppSelector } from "../store"; +import { useLocationStore, useShortcutStore } from "../store/module"; import * as utils from "../helpers/utils"; import useToggle from "../hooks/useToggle"; import useLoading from "../hooks/useLoading"; @@ -11,10 +10,12 @@ import showCreateShortcutDialog from "./CreateShortcutDialog"; import "../less/shortcut-list.less"; const ShortcutList = () => { - const query = useAppSelector((state) => state.location.query); - const shortcuts = useAppSelector((state) => state.shortcut.shortcuts); - const loadingState = useLoading(); const { t } = useTranslation(); + const locationStore = useLocationStore(); + const shortcutStore = useShortcutStore(); + const query = locationStore.state.query; + const shortcuts = shortcutStore.state.shortcuts; + const loadingState = useLoading(); const pinnedShortcuts = shortcuts .filter((s) => s.rowStatus === "ARCHIVED") @@ -25,7 +26,7 @@ const ShortcutList = () => { const sortedShortcuts = pinnedShortcuts.concat(unpinnedShortcuts); useEffect(() => { - shortcutService + shortcutStore .getMyAllShortcuts() .catch(() => { // do nth @@ -60,13 +61,15 @@ interface ShortcutContainerProps { const ShortcutContainer: React.FC = (props: ShortcutContainerProps) => { const { shortcut, isActive } = props; const { t } = useTranslation(); + const locationStore = useLocationStore(); + const shortcutStore = useShortcutStore(); const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false); const handleShortcutClick = () => { if (isActive) { - locationService.setMemoShortcut(undefined); + locationStore.setMemoShortcut(undefined); } else { - locationService.setMemoShortcut(shortcut.id); + locationStore.setMemoShortcut(shortcut.id); } }; @@ -75,10 +78,10 @@ const ShortcutContainer: React.FC = (props: ShortcutCont if (showConfirmDeleteBtn) { try { - await shortcutService.deleteShortcutById(shortcut.id); - if (locationService.getState().query?.shortcutId === shortcut.id) { + await shortcutStore.deleteShortcutById(shortcut.id); + if (locationStore.getState().query?.shortcutId === shortcut.id) { // need clear shortcut filter - locationService.setMemoShortcut(undefined); + locationStore.setMemoShortcut(undefined); } } catch (error: any) { console.error(error); @@ -102,7 +105,7 @@ const ShortcutContainer: React.FC = (props: ShortcutCont id: shortcut.id, rowStatus: shortcut.rowStatus === "ARCHIVED" ? "NORMAL" : "ARCHIVED", }; - await shortcutService.patchShortcut(shortcutPatch); + await shortcutStore.patchShortcut(shortcutPatch); } catch (error) { // do nth } diff --git a/web/src/components/Sidebar.tsx b/web/src/components/Sidebar.tsx index 7ecfeef4c..7317ad3e2 100644 --- a/web/src/components/Sidebar.tsx +++ b/web/src/components/Sidebar.tsx @@ -2,8 +2,7 @@ import { isUndefined } from "lodash-es"; import { useEffect } from "react"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { userService } from "../services"; -import { useAppSelector } from "../store"; +import { useLocationStore, useUserStore } from "../store/module"; import showDailyReviewDialog from "./DailyReviewDialog"; import showSettingDialog from "./SettingDialog"; import UserBanner from "./UserBanner"; @@ -14,11 +13,13 @@ import "../less/siderbar.less"; const Sidebar = () => { const { t } = useTranslation(); - const location = useAppSelector((state) => state.location); + const userStore = useUserStore(); + const locationStore = useLocationStore(); + const query = locationStore.state.query; useEffect(() => { toggleSidebar(false); - }, [location.query]); + }, [query]); const handleSettingBtnClick = () => { showSettingDialog(); @@ -34,7 +35,7 @@ const Sidebar = () => { - {!userService.isVisitorMode() && ( + {!userStore.isVisitorMode() && ( <> 🏂 {t("common.explore")} @@ -45,7 +46,7 @@ const Sidebar = () => { )}
- {!userService.isVisitorMode() && } + {!userStore.isVisitorMode() && } diff --git a/web/src/components/TagList.tsx b/web/src/components/TagList.tsx index 3a614291d..0613ed930 100644 --- a/web/src/components/TagList.tsx +++ b/web/src/components/TagList.tsx @@ -1,7 +1,6 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; -import { locationService, memoService, userService } from "../services"; +import { useLocationStore, useMemoStore, useUserStore } from "../store/module"; import useToggle from "../hooks/useToggle"; import Icon from "./Icon"; import "../less/tag-list.less"; @@ -14,13 +13,16 @@ interface Tag { const TagList = () => { const { t } = useTranslation(); - const { memos, tags: tagsText } = useAppSelector((state) => state.memo); - const query = useAppSelector((state) => state.location.query); + const locationStore = useLocationStore(); + const userStore = useUserStore(); + const memoStore = useMemoStore(); + const { memos, tags: tagsText } = memoStore.state; + const query = locationStore.state.query; const [tags, setTags] = useState([]); useEffect(() => { if (memos.length > 0) { - memoService.updateTagsState(); + memoStore.updateTagsState(); } }, [memos]); @@ -75,7 +77,7 @@ const TagList = () => { {tags.map((t, idx) => ( ))} - {!userService.isVisitorMode() && tags.length === 0 &&

{t("tag-list.tip-text")}

} + {!userStore.isVisitorMode() && tags.length === 0 &&

{t("tag-list.tip-text")}

}
); @@ -87,6 +89,7 @@ interface TagItemContainerProps { } const TagItemContainer: React.FC = (props: TagItemContainerProps) => { + const locationStore = useLocationStore(); const { tag, tagQuery } = props; const isActive = tagQuery === tag.text; const hasSubTags = tag.subTags.length > 0; @@ -94,9 +97,9 @@ const TagItemContainer: React.FC = (props: TagItemContain const handleTagClick = () => { if (isActive) { - locationService.setTagQuery(undefined); + locationStore.setTagQuery(undefined); } else { - locationService.setTagQuery(tag.text); + locationStore.setTagQuery(tag.text); } }; diff --git a/web/src/components/UpdateAccountDialog.tsx b/web/src/components/UpdateAccountDialog.tsx index 5591b9ec8..01493e9c9 100644 --- a/web/src/components/UpdateAccountDialog.tsx +++ b/web/src/components/UpdateAccountDialog.tsx @@ -1,12 +1,11 @@ import { isEqual } from "lodash"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useAppSelector } from "../store"; -import { userService } from "../services"; +import { useUserStore } from "../store/module"; +import { validate, ValidatorConfig } from "../helpers/validator"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; -import { validate, ValidatorConfig } from "../helpers/validator"; const validateConfig: ValidatorConfig = { minLength: 4, @@ -25,7 +24,8 @@ interface State { const UpdateAccountDialog: React.FC = ({ destroy }: Props) => { const { t } = useTranslation(); - const user = useAppSelector((state) => state.user.user as User); + const userStore = useUserStore(); + const user = userStore.state.user as User; const [state, setState] = useState({ username: user.username, nickname: user.nickname, @@ -78,7 +78,7 @@ const UpdateAccountDialog: React.FC = ({ destroy }: Props) => { } try { - const user = userService.getState().user as User; + const user = userStore.getState().user as User; const userPatch: UserPatch = { id: user.id, }; @@ -91,7 +91,7 @@ const UpdateAccountDialog: React.FC = ({ destroy }: Props) => { if (!isEqual(user.email, state.email)) { userPatch.email = state.email; } - await userService.patchUser(userPatch); + await userStore.patchUser(userPatch); toastHelper.info("Update succeed"); handleCloseBtnClick(); } catch (error: any) { diff --git a/web/src/components/UpdateVersionBanner.tsx b/web/src/components/UpdateVersionBanner.tsx index 31651270d..89792a383 100644 --- a/web/src/components/UpdateVersionBanner.tsx +++ b/web/src/components/UpdateVersionBanner.tsx @@ -1,9 +1,9 @@ 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"; import "../less/about-site-dialog.less"; +import { useGlobalStore } from "../store/module"; interface State { latestVersion: string; @@ -11,7 +11,8 @@ interface State { } const UpdateVersionBanner: React.FC = () => { - const profile = useAppSelector((state) => state.global.systemStatus.profile); + const globalStore = useGlobalStore(); + const profile = globalStore.state.systemStatus.profile; const [state, setState] = useState({ latestVersion: "", show: false, diff --git a/web/src/components/UsageHeatMap.tsx b/web/src/components/UsageHeatMap.tsx index 234395f39..ccf612259 100644 --- a/web/src/components/UsageHeatMap.tsx +++ b/web/src/components/UsageHeatMap.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useAppSelector } from "../store"; -import { locationService, userService } from "../services"; +import { useLocationStore, useMemoStore, useUserStore } from "../store/module"; import { getMemoStats } from "../helpers/api"; import { DAILY_TIMESTAMP } from "../helpers/consts"; import * as utils from "../helpers/utils"; @@ -28,19 +27,21 @@ interface DailyUsageStat { } const UsageHeatMap = () => { + const locationStore = useLocationStore(); + const userStore = useUserStore(); + const memoStore = useMemoStore(); const todayTimeStamp = utils.getDateStampByDate(Date.now()); const todayDay = new Date(todayTimeStamp).getDay() + 1; const nullCell = new Array(7 - todayDay).fill(0); const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay; const beginDayTimestamp = todayTimeStamp - usedDaysAmount * DAILY_TIMESTAMP; - - const { memos } = useAppSelector((state) => state.memo); + const memos = memoStore.state.memos; const [allStat, setAllStat] = useState(getInitialUsageStat(usedDaysAmount, beginDayTimestamp)); const [currentStat, setCurrentStat] = useState(null); const containerElRef = useRef(null); useEffect(() => { - getMemoStats(userService.getCurrentUserId()) + getMemoStats(userStore.getCurrentUserId()) .then(({ data: { data } }) => { const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp); for (const record of data) { @@ -84,11 +85,11 @@ const UsageHeatMap = () => { }, []); const handleUsageStatItemClick = useCallback((item: DailyUsageStat) => { - if (locationService.getState().query?.duration?.from === item.timestamp) { - locationService.setFromAndToQuery(); + if (locationStore.getState().query?.duration?.from === item.timestamp) { + locationStore.setFromAndToQuery(); setCurrentStat(null); } else if (item.count > 0) { - locationService.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP); + locationStore.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP); setCurrentStat(item); } }, []); diff --git a/web/src/components/UserBanner.tsx b/web/src/components/UserBanner.tsx index 182adcd62..f7f67da57 100644 --- a/web/src/components/UserBanner.tsx +++ b/web/src/components/UserBanner.tsx @@ -1,11 +1,9 @@ import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { useLocationStore, useMemoStore, useUserStore } from "../store/module"; import { getMemoStats } from "../helpers/api"; import * as utils from "../helpers/utils"; -import userService from "../services/userService"; -import { locationService } from "../services"; -import { useAppSelector } from "../store"; import Icon from "./Icon"; import Dropdown from "./common/Dropdown"; import showResourcesDialog from "./ResourcesDialog"; @@ -16,12 +14,15 @@ import "../less/user-banner.less"; const UserBanner = () => { const { t } = useTranslation(); const navigate = useNavigate(); - const { user, owner } = useAppSelector((state) => state.user); - const { memos, tags } = useAppSelector((state) => state.memo); + const locationStore = useLocationStore(); + const userStore = useUserStore(); + const memoStore = useMemoStore(); + const { user, owner } = userStore.state; + const { memos, tags } = memoStore.state; const [username, setUsername] = useState("Memos"); const [memoAmount, setMemoAmount] = useState(0); const [createdDays, setCreatedDays] = useState(0); - const isVisitorMode = userService.isVisitorMode(); + const isVisitorMode = userStore.isVisitorMode(); useEffect(() => { if (isVisitorMode) { @@ -37,7 +38,7 @@ const UserBanner = () => { }, [isVisitorMode, user, owner]); useEffect(() => { - getMemoStats(userService.getCurrentUserId()) + getMemoStats(userStore.getCurrentUserId()) .then(({ data: { data } }) => { setMemoAmount(data.length); }) @@ -47,7 +48,7 @@ const UserBanner = () => { }, [memos]); const handleUsernameClick = useCallback(() => { - locationService.clearQuery(); + locationStore.clearQuery(); }, []); const handleResourcesBtnClick = () => { @@ -78,7 +79,7 @@ const UserBanner = () => { actionsClassName="min-w-36" actions={ <> - {!userService.isVisitorMode() && ( + {!userStore.isVisitorMode() && ( <> - {!userService.isVisitorMode() && ( + {!userStore.isVisitorMode() && (