From fd9c3ccbaef29b76e5062f3f9be51aac5092271b Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 22 Dec 2023 09:01:30 +0800 Subject: [PATCH] chore: implement useMemoList store --- api/v2/memo_service.go | 4 +++- web/src/pages/Explore.tsx | 19 ++++++++--------- web/src/pages/Home.tsx | 28 +++++++++---------------- web/src/pages/Timeline.tsx | 28 ++++++++----------------- web/src/store/v1/memo.ts | 42 ++++++++++++++++++++++++++++++++------ 5 files changed, 66 insertions(+), 55 deletions(-) diff --git a/api/v2/memo_service.go b/api/v2/memo_service.go index 3ee7ad24..5f4b9568 100644 --- a/api/v2/memo_service.go +++ b/api/v2/memo_service.go @@ -176,8 +176,10 @@ func (s *APIV2Service) UpdateMemo(ctx context.Context, request *apiv2pb.UpdateMe return nil, status.Errorf(codes.PermissionDenied, "permission denied") } + currentTs := time.Now().Unix() update := &store.UpdateMemo{ - ID: request.Id, + ID: request.Id, + UpdatedTs: ¤tTs, } for _, path := range request.UpdateMask.Paths { if path == "content" { diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 49f70a3d..880197f4 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import Empty from "@/components/Empty"; import MemoFilter from "@/components/MemoFilter"; import MemoView from "@/components/MemoView"; @@ -7,8 +7,7 @@ import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; import { getTimeStampByDate } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useFilterStore } from "@/store/module"; -import { useMemoV1Store } from "@/store/v1"; -import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useMemoList, useMemoV1Store } from "@/store/v1"; import { useTranslate } from "@/utils/i18n"; const Explore = () => { @@ -16,14 +15,14 @@ const Explore = () => { const user = useCurrentUser(); const filterStore = useFilterStore(); const memoStore = useMemoV1Store(); + const memoList = useMemoList(); const [isComplete, setIsComplete] = useState(false); const [isRequesting, setIsRequesting] = useState(false); - const memosRef = useRef([]); const { tag: tagQuery, text: textQuery } = filterStore.state; - const sortedMemos = memosRef.current.sort((a, b) => getTimeStampByDate(b.displayTime) - getTimeStampByDate(a.displayTime)); + const sortedMemos = memoList.value.sort((a, b) => getTimeStampByDate(b.displayTime) - getTimeStampByDate(a.displayTime)); useEffect(() => { - memosRef.current = []; + memoList.reset(); fetchMemos(); }, [tagQuery, textQuery]); @@ -42,11 +41,10 @@ const Explore = () => { setIsRequesting(true); const data = await memoStore.fetchMemos({ limit: DEFAULT_MEMO_LIMIT, - offset: memosRef.current.length, + offset: memoList.size(), filter: filters.join(" && "), }); setIsRequesting(false); - memosRef.current = [...memosRef.current, ...data]; setIsComplete(data.length < DEFAULT_MEMO_LIMIT); }; @@ -59,12 +57,11 @@ const Explore = () => { ))} - {isRequesting && ( + {isRequesting ? (

{t("memo.fetching-data")}

- )} - {isComplete ? ( + ) : isComplete ? ( sortedMemos.length === 0 && (
diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index f4045979..9967fb31 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import Empty from "@/components/Empty"; import HomeSidebar from "@/components/HomeSidebar"; import HomeSidebarDrawer from "@/components/HomeSidebarDrawer"; @@ -11,8 +11,7 @@ import { getTimeStampByDate } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; import useResponsiveWidth from "@/hooks/useResponsiveWidth"; import { useFilterStore } from "@/store/module"; -import { useMemoV1Store } from "@/store/v1"; -import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useMemoList, useMemoV1Store } from "@/store/v1"; import { useTranslate } from "@/utils/i18n"; const Home = () => { @@ -23,14 +22,14 @@ const Home = () => { const memoStore = useMemoV1Store(); const [isComplete, setIsComplete] = useState(false); const [isRequesting, setIsRequesting] = useState(false); - const memosRef = useRef([]); + const memoList = useMemoList(); const { tag: tagQuery, text: textQuery } = filterStore.state; - const sortedMemos = memosRef.current + const sortedMemos = memoList.value .sort((a, b) => getTimeStampByDate(b.displayTime) - getTimeStampByDate(a.displayTime)) .sort((a, b) => Number(b.pinned) - Number(a.pinned)); useEffect(() => { - memosRef.current = []; + memoList.reset(); fetchMemos(); }, [tagQuery, textQuery]); @@ -49,36 +48,29 @@ const Home = () => { setIsRequesting(true); const data = await memoStore.fetchMemos({ limit: DEFAULT_MEMO_LIMIT, - offset: memosRef.current.length, + offset: memoList.size(), filter: filters.join(" && "), }); setIsRequesting(false); - memosRef.current = [...memosRef.current, ...data]; setIsComplete(data.length < DEFAULT_MEMO_LIMIT); }; - const handleMemoCreated = async (memoId: number) => { - const memo = await memoStore.getOrFetchMemoById(memoId); - memosRef.current = [memo, ...memosRef.current]; - }; - return (
{!md && }
- +
{sortedMemos.map((memo) => ( - + ))} - {isRequesting && ( + {isRequesting ? (

{t("memo.fetching-data")}

- )} - {isComplete ? ( + ) : isComplete ? ( sortedMemos.length === 0 && (
diff --git a/web/src/pages/Timeline.tsx b/web/src/pages/Timeline.tsx index bb393601..f1836fb9 100644 --- a/web/src/pages/Timeline.tsx +++ b/web/src/pages/Timeline.tsx @@ -10,33 +10,29 @@ import DatePicker from "@/components/kit/DatePicker"; import { DAILY_TIMESTAMP } from "@/helpers/consts"; import { getDateStampByDate, getNormalizedDateString, getTimeStampByDate } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; -import { useMemoV1Store } from "@/store/v1"; -import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useMemoList, useMemoV1Store } from "@/store/v1"; import { useTranslate } from "@/utils/i18n"; const Timeline = () => { const t = useTranslate(); - const memoStore = useMemoV1Store(); const currentUser = useCurrentUser(); + const memoStore = useMemoV1Store(); + const memoList = useMemoList(); const currentDateStamp = getDateStampByDate(getNormalizedDateString()) as number; const [selectedDateStamp, setSelectedDateStamp] = useState(currentDateStamp as number); - const [memos, setMemos] = useState([]); const [showDatePicker, toggleShowDatePicker] = useToggle(false); - const sortedMemos = memos.sort((a, b) => getTimeStampByDate(a.createTime) - getTimeStampByDate(b.createTime)); + const sortedMemos = memoList.value.sort((a, b) => getTimeStampByDate(a.createTime) - getTimeStampByDate(b.createTime)); useEffect(() => { + memoList.reset(); const filters = [ `creator == "${currentUser.name}"`, `created_ts_after == ${selectedDateStamp / 1000}`, `created_ts_before == ${(selectedDateStamp + DAILY_TIMESTAMP) / 1000}`, ]; - memoStore - .fetchMemos({ - filter: filters.join(" && "), - }) - .then((memos: Memo[]) => { - setMemos(memos); - }); + memoStore.fetchMemos({ + filter: filters.join(" && "), + }); }, [selectedDateStamp]); const handleDataPickerChange = (datestamp: number): void => { @@ -44,12 +40,6 @@ const Timeline = () => { toggleShowDatePicker(false); }; - const handleMemoCreate = async (id: number) => { - await memoStore.getOrFetchMemoById(id).then((memo: Memo) => { - setMemos([memo, ...memos]); - }); - }; - return (
@@ -108,7 +98,7 @@ const Timeline = () => { ))} {selectedDateStamp === currentDateStamp && (
- +
)}
diff --git a/web/src/store/v1/memo.ts b/web/src/store/v1/memo.ts index 42e4f724..265e036a 100644 --- a/web/src/store/v1/memo.ts +++ b/web/src/store/v1/memo.ts @@ -1,13 +1,25 @@ +import { cloneDeep } from "lodash-es"; import { create } from "zustand"; import { combine } from "zustand/middleware"; import { memoServiceClient } from "@/grpcweb"; import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v2/memo_service"; +interface State { + memoById: Map; +} + export const useMemoV1Store = create( combine({ memoById: new Map() }, (set, get) => ({ + setState: (state: State) => set(state), getState: () => get(), fetchMemos: async (request: Partial) => { const { memos } = await memoServiceClient.listMemos(request); + set((state) => { + for (const memo of memos) { + state.memoById.set(memo.id, memo); + } + return cloneDeep(state); + }); return memos; }, getOrFetchMemoById: async (id: number) => { @@ -25,9 +37,8 @@ export const useMemoV1Store = create( set((state) => { state.memoById.set(id, res.memo as Memo); - return state; + return cloneDeep(state); }); - return res.memo; }, getMemoById: (id: number) => { @@ -41,7 +52,7 @@ export const useMemoV1Store = create( set((state) => { state.memoById.set(memo.id, memo); - return state; + return cloneDeep(state); }); return memo; }, @@ -57,18 +68,18 @@ export const useMemoV1Store = create( set((state) => { state.memoById.set(memo.id, memo); - return state; + return cloneDeep(state); }); - return memo; }, deleteMemo: async (id: number) => { await memoServiceClient.deleteMemo({ id: id, }); + set((state) => { state.memoById.delete(id); - return state; + return cloneDeep(state); }); }, fetchMemoResources: async (id: number) => { @@ -85,3 +96,22 @@ export const useMemoV1Store = create( }, })) ); + +export const useMemoList = () => { + const memoStore = useMemoV1Store(); + const memos = Array.from(memoStore.getState().memoById.values()); + + const reset = () => { + memoStore.setState({ memoById: new Map() }); + }; + + const size = () => { + return memoStore.getState().memoById.size; + }; + + return { + value: memos, + reset, + size, + }; +};