diff --git a/web/src/components/MemoContent/EmbeddedContent/EmbeddedResource.tsx b/web/src/components/MemoContent/EmbeddedContent/EmbeddedResource.tsx
index 7711158ce..3690a1136 100644
--- a/web/src/components/MemoContent/EmbeddedContent/EmbeddedResource.tsx
+++ b/web/src/components/MemoContent/EmbeddedContent/EmbeddedResource.tsx
@@ -1,7 +1,8 @@
+import { observer } from "mobx-react-lite";
import { useEffect } from "react";
import MemoResourceListView from "@/components/MemoResourceListView";
import useLoading from "@/hooks/useLoading";
-import { useResourceStore } from "@/store/v1";
+import { resourceStore } from "@/store/v2";
import { cn } from "@/utils";
import Error from "./Error";
@@ -35,9 +36,8 @@ const getAdditionalClassNameWithParams = (params: URLSearchParams) => {
return additionalClassNames.join(" ");
};
-const EmbeddedResource = ({ resourceId: uid, params: paramsStr }: Props) => {
+const EmbeddedResource = observer(({ resourceId: uid, params: paramsStr }: Props) => {
const loadingState = useLoading();
- const resourceStore = useResourceStore();
const resource = resourceStore.getResourceByName(uid);
const params = new URLSearchParams(paramsStr);
@@ -57,6 +57,6 @@ const EmbeddedResource = ({ resourceId: uid, params: paramsStr }: Props) => {
);
-};
+});
export default EmbeddedResource;
diff --git a/web/src/components/MemoEditor/ActionButton/UploadResourceButton.tsx b/web/src/components/MemoEditor/ActionButton/UploadResourceButton.tsx
index 074fa24a7..e63a5c924 100644
--- a/web/src/components/MemoEditor/ActionButton/UploadResourceButton.tsx
+++ b/web/src/components/MemoEditor/ActionButton/UploadResourceButton.tsx
@@ -1,8 +1,9 @@
import { Button } from "@usememos/mui";
import { LoaderIcon, PaperclipIcon } from "lucide-react";
+import { observer } from "mobx-react-lite";
import { useContext, useRef, useState } from "react";
import toast from "react-hot-toast";
-import { useResourceStore } from "@/store/v1";
+import { resourceStore } from "@/store/v2";
import { Resource } from "@/types/proto/api/v1/resource_service";
import { MemoEditorContext } from "../types";
@@ -14,9 +15,8 @@ interface State {
uploadingFlag: boolean;
}
-const UploadResourceButton = (props: Props) => {
+const UploadResourceButton = observer((props: Props) => {
const context = useContext(MemoEditorContext);
- const resourceStore = useResourceStore();
const [state, setState] = useState({
uploadingFlag: false,
});
@@ -86,6 +86,6 @@ const UploadResourceButton = (props: Props) => {
/>
);
-};
+});
export default UploadResourceButton;
diff --git a/web/src/components/MemoEditor/index.tsx b/web/src/components/MemoEditor/index.tsx
index 0b71b0640..724ffeaa8 100644
--- a/web/src/components/MemoEditor/index.tsx
+++ b/web/src/components/MemoEditor/index.tsx
@@ -13,8 +13,8 @@ import { TAB_SPACE_WIDTH } from "@/helpers/consts";
import { isValidUrl } from "@/helpers/utils";
import useAsyncEffect from "@/hooks/useAsyncEffect";
import useCurrentUser from "@/hooks/useCurrentUser";
-import { useMemoStore, useResourceStore } from "@/store/v1";
-import { userStore, workspaceStore } from "@/store/v2";
+import { useMemoStore } from "@/store/v1";
+import { 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";
@@ -62,7 +62,6 @@ const MemoEditor = observer((props: Props) => {
const t = useTranslate();
const { i18n } = useTranslation();
const memoStore = useMemoStore();
- const resourceStore = useResourceStore();
const currentUser = useCurrentUser();
const [state, setState] = useState({
memoVisibility: Visibility.PRIVATE,
diff --git a/web/src/store/v1/index.ts b/web/src/store/v1/index.ts
index 65cd971de..b090b900a 100644
--- a/web/src/store/v1/index.ts
+++ b/web/src/store/v1/index.ts
@@ -1,3 +1,2 @@
export * from "./memo";
-export * from "./resource";
export * from "./memoFilter";
diff --git a/web/src/store/v1/resource.ts b/web/src/store/v1/resource.ts
deleted file mode 100644
index c95752227..000000000
--- a/web/src/store/v1/resource.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { create } from "zustand";
-import { combine } from "zustand/middleware";
-import { resourceServiceClient } from "@/grpcweb";
-import { CreateResourceRequest, Resource, UpdateResourceRequest } from "@/types/proto/api/v1/resource_service";
-
-interface State {
- resourceMapByName: Record;
-}
-
-const getDefaultState = (): State => ({
- resourceMapByName: {},
-});
-
-export const useResourceStore = create(
- combine(getDefaultState(), (set, get) => ({
- setState: (state: State) => set(state),
- getState: () => get(),
- fetchResourceByName: async (name: string) => {
- const resource = await resourceServiceClient.getResource({
- name,
- });
- const resourceMap = get().resourceMapByName;
- resourceMap[resource.name] = resource;
- set({ resourceMapByName: resourceMap });
- return resource;
- },
- getResourceByName: (name: string) => {
- const resourceMap = get().resourceMapByName;
- return Object.values(resourceMap).find((r) => r.name === name);
- },
- async createResource(create: CreateResourceRequest): Promise {
- const resource = await resourceServiceClient.createResource(create);
- const resourceMap = get().resourceMapByName;
- resourceMap[resource.name] = resource;
- return resource;
- },
- async updateResource(update: UpdateResourceRequest): Promise {
- const resource = await resourceServiceClient.updateResource(update);
- const resourceMap = get().resourceMapByName;
- resourceMap[resource.name] = resource;
- return resource;
- },
- })),
-);
diff --git a/web/src/store/v2/index.ts b/web/src/store/v2/index.ts
index 94307e441..4747e2bbf 100644
--- a/web/src/store/v2/index.ts
+++ b/web/src/store/v2/index.ts
@@ -1,5 +1,6 @@
+import resourceStore from "./resource";
import userStore from "./user";
import viewStore from "./view";
import workspaceStore from "./workspace";
-export { workspaceStore, userStore, viewStore };
+export { resourceStore, workspaceStore, userStore, viewStore };
diff --git a/web/src/store/v2/resource.ts b/web/src/store/v2/resource.ts
new file mode 100644
index 000000000..09164c687
--- /dev/null
+++ b/web/src/store/v2/resource.ts
@@ -0,0 +1,59 @@
+import { makeAutoObservable } from "mobx";
+import { resourceServiceClient } from "@/grpcweb";
+import { CreateResourceRequest, Resource, UpdateResourceRequest } from "@/types/proto/api/v1/resource_service";
+
+class LocalState {
+ resourceMapByName: Record = {};
+
+ constructor() {
+ makeAutoObservable(this);
+ }
+
+ setPartial(partial: Partial) {
+ Object.assign(this, partial);
+ }
+}
+
+const resourceStore = (() => {
+ const state = new LocalState();
+
+ const fetchResourceByName = async (name: string) => {
+ const resource = await resourceServiceClient.getResource({
+ name,
+ });
+ const resourceMap = { ...state.resourceMapByName };
+ resourceMap[resource.name] = resource;
+ state.setPartial({ resourceMapByName: resourceMap });
+ return resource;
+ };
+
+ const getResourceByName = (name: string) => {
+ return Object.values(state.resourceMapByName).find((r) => r.name === name);
+ };
+
+ const createResource = async (create: CreateResourceRequest): Promise => {
+ const resource = await resourceServiceClient.createResource(create);
+ const resourceMap = { ...state.resourceMapByName };
+ resourceMap[resource.name] = resource;
+ state.setPartial({ resourceMapByName: resourceMap });
+ return resource;
+ };
+
+ const updateResource = async (update: UpdateResourceRequest): Promise => {
+ const resource = await resourceServiceClient.updateResource(update);
+ const resourceMap = { ...state.resourceMapByName };
+ resourceMap[resource.name] = resource;
+ state.setPartial({ resourceMapByName: resourceMap });
+ return resource;
+ };
+
+ return {
+ state,
+ fetchResourceByName,
+ getResourceByName,
+ createResource,
+ updateResource,
+ };
+})();
+
+export default resourceStore;