mirror of https://github.com/usememos/memos
refactor: migrate memo store
parent
dcd68bc5f4
commit
785c250f3c
@ -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