mirror of https://github.com/usememos/memos.git
refactor: migrate memo store
This commit is contained in:
parent
dcd68bc5f4
commit
785c250f3c
|
|
@ -6,8 +6,7 @@ import useDebounce from "react-use/lib/useDebounce";
|
|||
import SearchBar from "@/components/SearchBar";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { Routes } from "@/router";
|
||||
import { useMemoList } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { memoStore, userStore } from "@/store/v2";
|
||||
import { cn } from "@/utils";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import MemoFilters from "../MemoFilters";
|
||||
|
|
@ -30,7 +29,6 @@ const HomeSidebar = observer((props: Props) => {
|
|||
const t = useTranslate();
|
||||
const location = useLocation();
|
||||
const currentUser = useCurrentUser();
|
||||
const memoList = useMemoList();
|
||||
|
||||
const homeNavLink: NavLinkItem = {
|
||||
id: "header-home",
|
||||
|
|
@ -61,7 +59,7 @@ const HomeSidebar = observer((props: Props) => {
|
|||
await userStore.fetchUserStats(parent);
|
||||
},
|
||||
300,
|
||||
[memoList.size(), userStore.state.statsStateId, location.pathname],
|
||||
[memoStore.state.memos.length, userStore.state.statsStateId, location.pathname],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { Tooltip } from "@mui/joy";
|
||||
import { InboxIcon, LoaderIcon, MessageCircleIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { activityServiceClient } from "@/grpcweb";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { activityNamePrefix } from "@/store/common";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { memoStore, userStore } from "@/store/v2";
|
||||
import { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service";
|
||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { User } from "@/types/proto/api/v1/user_service";
|
||||
|
|
@ -18,10 +18,9 @@ interface Props {
|
|||
inbox: Inbox;
|
||||
}
|
||||
|
||||
const MemoCommentMessage = ({ inbox }: Props) => {
|
||||
const MemoCommentMessage = observer(({ inbox }: Props) => {
|
||||
const t = useTranslate();
|
||||
const navigateTo = useNavigateTo();
|
||||
const memoStore = useMemoStore();
|
||||
const [relatedMemo, setRelatedMemo] = useState<Memo | undefined>(undefined);
|
||||
const [sender, setSender] = useState<User | undefined>(undefined);
|
||||
const [initialized, setInitialized] = useState<boolean>(false);
|
||||
|
|
@ -124,6 +123,6 @@ const MemoCommentMessage = ({ inbox }: Props) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default MemoCommentMessage;
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ import {
|
|||
TrashIcon,
|
||||
SquareCheckIcon,
|
||||
} from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import toast from "react-hot-toast";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { markdownServiceClient } from "@/grpcweb";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { State } from "@/types/proto/api/v1/common";
|
||||
import { NodeType } from "@/types/proto/api/v1/markdown_service";
|
||||
|
|
@ -43,12 +44,11 @@ const checkHasCompletedTaskList = (memo: Memo) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
const MemoActionMenu = (props: Props) => {
|
||||
const MemoActionMenu = observer((props: Props) => {
|
||||
const { memo, readonly } = props;
|
||||
const t = useTranslate();
|
||||
const location = useLocation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const memoStore = useMemoStore();
|
||||
const hasCompletedTaskList = checkHasCompletedTaskList(memo);
|
||||
const isInMemoDetailPage = location.pathname.startsWith(`/${memo.name}`);
|
||||
const isComment = Boolean(memo.parent);
|
||||
|
|
@ -212,6 +212,6 @@ const MemoActionMenu = (props: Props) => {
|
|||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default MemoActionMenu;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import copy from "copy-to-clipboard";
|
||||
import { ArrowUpRightIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useContext, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { Link } from "react-router-dom";
|
||||
import MemoResourceListView from "@/components/MemoResourceListView";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { extractMemoIdFromName } from "@/store/common";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { cn } from "@/utils";
|
||||
import MemoContent from "..";
|
||||
import { RendererContext } from "../types";
|
||||
|
|
@ -17,10 +18,9 @@ interface Props {
|
|||
params: string;
|
||||
}
|
||||
|
||||
const EmbeddedMemo = ({ resourceId: uid, params: paramsStr }: Props) => {
|
||||
const EmbeddedMemo = observer(({ resourceId: uid, params: paramsStr }: Props) => {
|
||||
const context = useContext(RendererContext);
|
||||
const loadingState = useLoading();
|
||||
const memoStore = useMemoStore();
|
||||
const memoName = `memos/${uid}`;
|
||||
const memo = memoStore.getMemoByName(memoName);
|
||||
|
||||
|
|
@ -87,6 +87,6 @@ const EmbeddedMemo = ({ resourceId: uid, params: paramsStr }: Props) => {
|
|||
{contentNode}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default EmbeddedMemo;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { useContext, useEffect } from "react";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { memoNamePrefix } from "@/store/common";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { RendererContext } from "../types";
|
||||
import Error from "./Error";
|
||||
|
||||
|
|
@ -11,10 +12,9 @@ interface Props {
|
|||
params: string;
|
||||
}
|
||||
|
||||
const ReferencedMemo = ({ resourceId: uid, params: paramsStr }: Props) => {
|
||||
const ReferencedMemo = observer(({ resourceId: uid, params: paramsStr }: Props) => {
|
||||
const navigateTo = useNavigateTo();
|
||||
const loadingState = useLoading();
|
||||
const memoStore = useMemoStore();
|
||||
const memoName = `${memoNamePrefix}${uid}`;
|
||||
const memo = memoStore.getMemoByName(memoName);
|
||||
const params = new URLSearchParams(paramsStr);
|
||||
|
|
@ -50,6 +50,6 @@ const ReferencedMemo = ({ resourceId: uid, params: paramsStr }: Props) => {
|
|||
{displayContent}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default ReferencedMemo;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Checkbox } from "@usememos/mui";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useContext } from "react";
|
||||
import { markdownServiceClient } from "@/grpcweb";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { Node, TaskListItemNode } from "@/types/proto/api/v1/markdown_service";
|
||||
import { cn } from "@/utils";
|
||||
import Renderer from "./Renderer";
|
||||
|
|
@ -16,9 +17,8 @@ interface Props {
|
|||
children: Node[];
|
||||
}
|
||||
|
||||
const TaskListItem: React.FC<Props> = ({ node, complete, children }: Props) => {
|
||||
const TaskListItem = observer(({ node, complete, children }: Props) => {
|
||||
const context = useContext(RendererContext);
|
||||
const memoStore = useMemoStore();
|
||||
|
||||
const handleCheckboxChange = async (on: boolean) => {
|
||||
if (context.readonly || !context.memoName) {
|
||||
|
|
@ -48,6 +48,6 @@ const TaskListItem: React.FC<Props> = ({ node, complete, children }: Props) => {
|
|||
</p>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default TaskListItem;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { memo, useEffect, useRef, useState } from "react";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { Node, NodeType } from "@/types/proto/api/v1/markdown_service";
|
||||
import { cn } from "@/utils";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
|
@ -29,11 +30,10 @@ interface Props {
|
|||
|
||||
type ContentCompactView = "ALL" | "SNIPPET";
|
||||
|
||||
const MemoContent: React.FC<Props> = (props: Props) => {
|
||||
const MemoContent = observer((props: Props) => {
|
||||
const { className, contentClassName, nodes, memoName, embeddedMemos, onClick, onDoubleClick } = props;
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const memoContentContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [showCompactMode, setShowCompactMode] = useState<ContentCompactView | undefined>(undefined);
|
||||
const memo = memoName ? memoStore.getMemoByName(memoName) : null;
|
||||
|
|
@ -122,6 +122,6 @@ const MemoContent: React.FC<Props> = (props: Props) => {
|
|||
</div>
|
||||
</RendererContext.Provider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default memo(MemoContent);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { LinkIcon, XIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { Memo, MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -8,9 +9,8 @@ interface Props {
|
|||
setRelationList: (relationList: MemoRelation[]) => void;
|
||||
}
|
||||
|
||||
const RelationListView = (props: Props) => {
|
||||
const RelationListView = observer((props: Props) => {
|
||||
const { relationList, setRelationList } = props;
|
||||
const memoStore = useMemoStore();
|
||||
const [referencingMemoList, setReferencingMemoList] = useState<Memo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -50,6 +50,6 @@ const RelationListView = (props: Props) => {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default RelationListView;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ import { TAB_SPACE_WIDTH } from "@/helpers/consts";
|
|||
import { isValidUrl } from "@/helpers/utils";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { resourceStore, userStore, workspaceStore } from "@/store/v2";
|
||||
import { memoStore, resourceStore, userStore, workspaceStore } from "@/store/v2";
|
||||
import { Location, Memo, MemoRelation, MemoRelation_Type, Visibility } from "@/types/proto/api/v1/memo_service";
|
||||
import { Resource } from "@/types/proto/api/v1/resource_service";
|
||||
import { UserSetting } from "@/types/proto/api/v1/user_service";
|
||||
|
|
@ -61,7 +60,6 @@ const MemoEditor = observer((props: Props) => {
|
|||
const { className, cacheKey, memoName, parentMemoName, autoFocus, onConfirm, onCancel } = props;
|
||||
const t = useTranslate();
|
||||
const { i18n } = useTranslation();
|
||||
const memoStore = useMemoStore();
|
||||
const currentUser = useCurrentUser();
|
||||
const [state, setState] = useState<State>({
|
||||
memoVisibility: Visibility.PRIVATE,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { Tooltip } from "@mui/joy";
|
||||
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { memo, useCallback, useRef, useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { userStore, workspaceStore } from "@/store/v2";
|
||||
import { State } from "@/types/proto/api/v1/common";
|
||||
import { Memo, MemoRelation_Type, Visibility } from "@/types/proto/api/v1/memo_service";
|
||||
|
|
@ -36,14 +37,13 @@ interface Props {
|
|||
parentPage?: string;
|
||||
}
|
||||
|
||||
const MemoView: React.FC<Props> = (props: Props) => {
|
||||
const MemoView: React.FC<Props> = observer((props: Props) => {
|
||||
const { memo, className } = props;
|
||||
const t = useTranslate();
|
||||
const location = useLocation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const currentUser = useCurrentUser();
|
||||
const user = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const [showEditor, setShowEditor] = useState<boolean>(false);
|
||||
const [creator, setCreator] = useState(userStore.getUserByName(memo.creator));
|
||||
const [showNSFWContent, setShowNSFWContent] = useState(props.showNsfwContent);
|
||||
|
|
@ -250,6 +250,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default memo(MemoView);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import PullToRefresh from "react-simple-pull-to-refresh";
|
|||
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
|
||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||
import { Routes } from "@/router";
|
||||
import { useMemoList, useMemoStore } from "@/store/v1";
|
||||
import { viewStore } from "@/store/v2";
|
||||
import { memoStore, viewStore } from "@/store/v2";
|
||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
|
@ -35,13 +34,11 @@ interface LocalState {
|
|||
const PagedMemoList = observer((props: Props) => {
|
||||
const t = useTranslate();
|
||||
const { md } = useResponsiveWidth();
|
||||
const memoStore = useMemoStore();
|
||||
const memoList = useMemoList();
|
||||
const [state, setState] = useState<LocalState>({
|
||||
isRequesting: true, // Initial request
|
||||
nextPageToken: "",
|
||||
});
|
||||
const sortedMemoList = props.listSort ? props.listSort(memoList.value) : memoList.value;
|
||||
const sortedMemoList = props.listSort ? props.listSort(memoStore.state.memos) : memoStore.state.memos;
|
||||
const showMemoEditor = Boolean(matchPath(Routes.ROOT, window.location.pathname));
|
||||
|
||||
const fetchMoreMemos = async (nextPageToken: string) => {
|
||||
|
|
@ -62,7 +59,7 @@ const PagedMemoList = observer((props: Props) => {
|
|||
};
|
||||
|
||||
const refreshList = async () => {
|
||||
memoList.reset();
|
||||
memoStore.state.updateStateId();
|
||||
setState((state) => ({ ...state, nextPageToken: "" }));
|
||||
await fetchMoreMemos("");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { Dropdown, Menu, MenuButton } from "@mui/joy";
|
||||
import { SmilePlusIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRef, useState } from "react";
|
||||
import useClickAway from "react-use/lib/useClickAway";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { cn } from "@/utils";
|
||||
|
|
@ -14,10 +15,9 @@ interface Props {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
const ReactionSelector = (props: Props) => {
|
||||
const ReactionSelector = observer((props: Props) => {
|
||||
const { memo, className } = props;
|
||||
const currentUser = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const [open, setOpen] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const workspaceMemoRelatedSetting = workspaceStore.state.memoRelatedSetting;
|
||||
|
|
@ -86,6 +86,6 @@ const ReactionSelector = (props: Props) => {
|
|||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default ReactionSelector;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Tooltip } from "@mui/joy";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { State } from "@/types/proto/api/v1/common";
|
||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { User } from "@/types/proto/api/v1/user_service";
|
||||
|
|
@ -28,10 +29,9 @@ const stringifyUsers = (users: User[], reactionType: string): string => {
|
|||
);
|
||||
};
|
||||
|
||||
const ReactionView = (props: Props) => {
|
||||
const ReactionView = observer((props: Props) => {
|
||||
const { memo, reactionType, users } = props;
|
||||
const currentUser = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const hasReaction = users.some((user) => currentUser && user.username === currentUser.username);
|
||||
const readonly = memo.state === State.ARCHIVED;
|
||||
|
||||
|
|
@ -80,6 +80,6 @@ const ReactionView = (props: Props) => {
|
|||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default ReactionView;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Button } from "@usememos/mui";
|
||||
import { ArrowUpLeftFromCircleIcon, MessageCircleIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ClientError } from "nice-grpc-web";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
|
@ -12,20 +13,19 @@ import useCurrentUser from "@/hooks/useCurrentUser";
|
|||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||
import { memoNamePrefix } from "@/store/common";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { Memo, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
|
||||
import { cn } from "@/utils";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const MemoDetail = () => {
|
||||
const MemoDetail = observer(() => {
|
||||
const t = useTranslate();
|
||||
const { md } = useResponsiveWidth();
|
||||
const params = useParams();
|
||||
const navigateTo = useNavigateTo();
|
||||
const { state: locationState } = useLocation();
|
||||
const currentUser = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const uid = params.uid;
|
||||
const memoName = `${memoNamePrefix}${uid}`;
|
||||
const memo = memoStore.getMemoByName(memoName);
|
||||
|
|
@ -176,6 +176,6 @@ const MemoDetail = () => {
|
|||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default MemoDetail;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Button, Input } from "@usememos/mui";
|
|||
import dayjs from "dayjs";
|
||||
import { includes } from "lodash-es";
|
||||
import { PaperclipIcon, SearchIcon, TrashIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import Empty from "@/components/Empty";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
|
|
@ -11,7 +12,7 @@ import { resourceServiceClient } from "@/grpcweb";
|
|||
import useLoading from "@/hooks/useLoading";
|
||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||
import i18n from "@/i18n";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { memoStore } from "@/store/v2";
|
||||
import { Resource } from "@/types/proto/api/v1/resource_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
|
|
@ -33,14 +34,13 @@ interface State {
|
|||
searchQuery: string;
|
||||
}
|
||||
|
||||
const Resources = () => {
|
||||
const Resources = observer(() => {
|
||||
const t = useTranslate();
|
||||
const { md } = useResponsiveWidth();
|
||||
const loadingState = useLoading();
|
||||
const [state, setState] = useState<State>({
|
||||
searchQuery: "",
|
||||
});
|
||||
const memoStore = useMemoStore();
|
||||
const [resources, setResources] = useState<Resource[]>([]);
|
||||
const filteredResources = resources.filter((resource) => includes(resource.filename, state.searchQuery));
|
||||
const groupedResources = groupResourcesByDate(filteredResources.filter((resource) => resource.memo));
|
||||
|
|
@ -165,6 +165,6 @@ const Resources = () => {
|
|||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Resources;
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
export * from "./memo";
|
||||
export * from "./memoFilter";
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
import { uniqueId } from "lodash-es";
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v1/memo_service";
|
||||
|
||||
interface State {
|
||||
// stateId is used to identify the store instance state.
|
||||
// It should be update when any state change.
|
||||
stateId: string;
|
||||
memoMapByName: Record<string, Memo>;
|
||||
currentRequest: AbortController | null;
|
||||
}
|
||||
|
||||
const getDefaultState = (): State => ({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: {},
|
||||
currentRequest: null,
|
||||
});
|
||||
|
||||
export const useMemoStore = create(
|
||||
combine(getDefaultState(), (set, get) => ({
|
||||
setState: (state: State) => set(state),
|
||||
getState: () => get(),
|
||||
updateStateId: () => set({ stateId: uniqueId() }),
|
||||
fetchMemos: async (request: Partial<ListMemosRequest>) => {
|
||||
const currentRequest = get().currentRequest;
|
||||
if (currentRequest) {
|
||||
currentRequest.abort();
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
set({ currentRequest: controller });
|
||||
|
||||
try {
|
||||
const { memos, nextPageToken } = await memoServiceClient.listMemos(
|
||||
{
|
||||
...request,
|
||||
},
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
|
||||
if (!controller.signal.aborted) {
|
||||
const memoMap = request.pageToken ? { ...get().memoMapByName } : {};
|
||||
for (const memo of memos) {
|
||||
memoMap[memo.name] = memo;
|
||||
}
|
||||
set({ stateId: uniqueId(), memoMapByName: memoMap });
|
||||
return { memos, nextPageToken };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.name === "AbortError") {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (get().currentRequest === controller) {
|
||||
set({ currentRequest: null });
|
||||
}
|
||||
}
|
||||
},
|
||||
getOrFetchMemoByName: async (name: string, options?: { skipCache?: boolean; skipStore?: boolean }) => {
|
||||
const memoMap = get().memoMapByName;
|
||||
const memoCache = memoMap[name];
|
||||
if (memoCache && !options?.skipCache) {
|
||||
return memoCache;
|
||||
}
|
||||
|
||||
const memo = await memoServiceClient.getMemo({
|
||||
name,
|
||||
});
|
||||
if (!options?.skipStore) {
|
||||
memoMap[name] = memo;
|
||||
set({ stateId: uniqueId(), memoMapByName: memoMap });
|
||||
}
|
||||
return memo;
|
||||
},
|
||||
getMemoByName: (name: string) => {
|
||||
return get().memoMapByName[name];
|
||||
},
|
||||
createMemo: async (request: CreateMemoRequest) => {
|
||||
const memo = await memoServiceClient.createMemo(request);
|
||||
const memoMap = get().memoMapByName;
|
||||
memoMap[memo.name] = memo;
|
||||
set({ stateId: uniqueId(), memoMapByName: memoMap });
|
||||
return memo;
|
||||
},
|
||||
updateMemo: async (update: Partial<Memo>, updateMask: string[]) => {
|
||||
const memo = await memoServiceClient.updateMemo({
|
||||
memo: update,
|
||||
updateMask,
|
||||
});
|
||||
|
||||
const memoMap = get().memoMapByName;
|
||||
memoMap[memo.name] = memo;
|
||||
set({ stateId: uniqueId(), memoMapByName: memoMap });
|
||||
return memo;
|
||||
},
|
||||
deleteMemo: async (name: string) => {
|
||||
await memoServiceClient.deleteMemo({
|
||||
name,
|
||||
});
|
||||
|
||||
const memoMap = get().memoMapByName;
|
||||
delete memoMap[name];
|
||||
set({ stateId: uniqueId(), memoMapByName: memoMap });
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
export const useMemoList = () => {
|
||||
const memoStore = useMemoStore();
|
||||
const memos = Object.values(memoStore.getState().memoMapByName);
|
||||
|
||||
const reset = () => {
|
||||
memoStore.updateStateId();
|
||||
};
|
||||
|
||||
const size = () => {
|
||||
return Object.keys(memoStore.getState().memoMapByName).length;
|
||||
};
|
||||
|
||||
return {
|
||||
value: memos,
|
||||
reset,
|
||||
size,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import memoStore from "./memo";
|
||||
import resourceStore from "./resource";
|
||||
import userStore from "./user";
|
||||
import viewStore from "./view";
|
||||
import workspaceStore from "./workspace";
|
||||
|
||||
export { resourceStore, workspaceStore, userStore, viewStore };
|
||||
export { memoStore, resourceStore, workspaceStore, userStore, viewStore };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
import { uniqueId } from "lodash-es";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v1/memo_service";
|
||||
|
||||
class LocalState {
|
||||
stateId: string = uniqueId();
|
||||
memoMapByName: Record<string, Memo> = {};
|
||||
currentRequest: AbortController | null = null;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setPartial(partial: Partial<LocalState>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
|
||||
updateStateId() {
|
||||
this.stateId = uniqueId();
|
||||
}
|
||||
|
||||
get memos() {
|
||||
return Object.values(this.memoMapByName);
|
||||
}
|
||||
|
||||
get size() {
|
||||
return Object.keys(this.memoMapByName).length;
|
||||
}
|
||||
}
|
||||
|
||||
const memoStore = (() => {
|
||||
const state = new LocalState();
|
||||
|
||||
const fetchMemos = async (request: Partial<ListMemosRequest>) => {
|
||||
if (state.currentRequest) {
|
||||
state.currentRequest.abort();
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
state.setPartial({ currentRequest: controller });
|
||||
|
||||
try {
|
||||
const { memos, nextPageToken } = await memoServiceClient.listMemos(
|
||||
{
|
||||
...request,
|
||||
},
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
|
||||
if (!controller.signal.aborted) {
|
||||
const memoMap = request.pageToken ? { ...state.memoMapByName } : {};
|
||||
for (const memo of memos) {
|
||||
memoMap[memo.name] = memo;
|
||||
}
|
||||
state.setPartial({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: memoMap,
|
||||
});
|
||||
return { memos, nextPageToken };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.name === "AbortError") {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (state.currentRequest === controller) {
|
||||
state.setPartial({ currentRequest: null });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getOrFetchMemoByName = async (name: string, options?: { skipCache?: boolean; skipStore?: boolean }) => {
|
||||
const memoCache = state.memoMapByName[name];
|
||||
if (memoCache && !options?.skipCache) {
|
||||
return memoCache;
|
||||
}
|
||||
|
||||
const memo = await memoServiceClient.getMemo({
|
||||
name,
|
||||
});
|
||||
|
||||
if (!options?.skipStore) {
|
||||
const memoMap = { ...state.memoMapByName };
|
||||
memoMap[name] = memo;
|
||||
state.setPartial({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: memoMap,
|
||||
});
|
||||
}
|
||||
|
||||
return memo;
|
||||
};
|
||||
|
||||
const getMemoByName = (name: string) => {
|
||||
return state.memoMapByName[name];
|
||||
};
|
||||
|
||||
const createMemo = async (request: CreateMemoRequest) => {
|
||||
const memo = await memoServiceClient.createMemo(request);
|
||||
const memoMap = { ...state.memoMapByName };
|
||||
memoMap[memo.name] = memo;
|
||||
state.setPartial({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: memoMap,
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
const updateMemo = async (update: Partial<Memo>, updateMask: string[]) => {
|
||||
const memo = await memoServiceClient.updateMemo({
|
||||
memo: update,
|
||||
updateMask,
|
||||
});
|
||||
|
||||
const memoMap = { ...state.memoMapByName };
|
||||
memoMap[memo.name] = memo;
|
||||
state.setPartial({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: memoMap,
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
const deleteMemo = async (name: string) => {
|
||||
await memoServiceClient.deleteMemo({
|
||||
name,
|
||||
});
|
||||
|
||||
const memoMap = { ...state.memoMapByName };
|
||||
delete memoMap[name];
|
||||
state.setPartial({
|
||||
stateId: uniqueId(),
|
||||
memoMapByName: memoMap,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
fetchMemos,
|
||||
getOrFetchMemoByName,
|
||||
getMemoByName,
|
||||
createMemo,
|
||||
updateMemo,
|
||||
deleteMemo,
|
||||
};
|
||||
})();
|
||||
|
||||
export default memoStore;
|
||||
Loading…
Reference in New Issue