mirror of https://github.com/usememos/memos
refactor: migrate memo store
parent
dcd68bc5f4
commit
785c250f3c
@ -1,2 +1 @@
|
|||||||
export * from "./memo";
|
|
||||||
export * from "./memoFilter";
|
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 resourceStore from "./resource";
|
||||||
import userStore from "./user";
|
import userStore from "./user";
|
||||||
import viewStore from "./view";
|
import viewStore from "./view";
|
||||||
import workspaceStore from "./workspace";
|
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