refactor: migrate memo store

pull/4716/head
Steven 6 months ago
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…
Cancel
Save