mirror of
https://github.com/usememos/memos.git
synced 2025-02-14 18:30:42 +01:00
chore: implement useMemoList store
This commit is contained in:
parent
a3feeceace
commit
fd9c3ccbae
@ -176,8 +176,10 @@ func (s *APIV2Service) UpdateMemo(ctx context.Context, request *apiv2pb.UpdateMe
|
|||||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentTs := time.Now().Unix()
|
||||||
update := &store.UpdateMemo{
|
update := &store.UpdateMemo{
|
||||||
ID: request.Id,
|
ID: request.Id,
|
||||||
|
UpdatedTs: ¤tTs,
|
||||||
}
|
}
|
||||||
for _, path := range request.UpdateMask.Paths {
|
for _, path := range request.UpdateMask.Paths {
|
||||||
if path == "content" {
|
if path == "content" {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Empty from "@/components/Empty";
|
import Empty from "@/components/Empty";
|
||||||
import MemoFilter from "@/components/MemoFilter";
|
import MemoFilter from "@/components/MemoFilter";
|
||||||
import MemoView from "@/components/MemoView";
|
import MemoView from "@/components/MemoView";
|
||||||
@ -7,8 +7,7 @@ import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
|||||||
import { getTimeStampByDate } from "@/helpers/datetime";
|
import { getTimeStampByDate } from "@/helpers/datetime";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useFilterStore } from "@/store/module";
|
import { useFilterStore } from "@/store/module";
|
||||||
import { useMemoV1Store } from "@/store/v1";
|
import { useMemoList, useMemoV1Store } from "@/store/v1";
|
||||||
import { Memo } from "@/types/proto/api/v2/memo_service";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const Explore = () => {
|
const Explore = () => {
|
||||||
@ -16,14 +15,14 @@ const Explore = () => {
|
|||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const memoStore = useMemoV1Store();
|
const memoStore = useMemoV1Store();
|
||||||
|
const memoList = useMemoList();
|
||||||
const [isComplete, setIsComplete] = useState(false);
|
const [isComplete, setIsComplete] = useState(false);
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
const memosRef = useRef<Memo[]>([]);
|
|
||||||
const { tag: tagQuery, text: textQuery } = filterStore.state;
|
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(() => {
|
useEffect(() => {
|
||||||
memosRef.current = [];
|
memoList.reset();
|
||||||
fetchMemos();
|
fetchMemos();
|
||||||
}, [tagQuery, textQuery]);
|
}, [tagQuery, textQuery]);
|
||||||
|
|
||||||
@ -42,11 +41,10 @@ const Explore = () => {
|
|||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
const data = await memoStore.fetchMemos({
|
const data = await memoStore.fetchMemos({
|
||||||
limit: DEFAULT_MEMO_LIMIT,
|
limit: DEFAULT_MEMO_LIMIT,
|
||||||
offset: memosRef.current.length,
|
offset: memoList.size(),
|
||||||
filter: filters.join(" && "),
|
filter: filters.join(" && "),
|
||||||
});
|
});
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
memosRef.current = [...memosRef.current, ...data];
|
|
||||||
setIsComplete(data.length < DEFAULT_MEMO_LIMIT);
|
setIsComplete(data.length < DEFAULT_MEMO_LIMIT);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,12 +57,11 @@ const Explore = () => {
|
|||||||
<MemoView key={memo.id} memo={memo} lazyRendering showCreator showParent />
|
<MemoView key={memo.id} memo={memo} lazyRendering showCreator showParent />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{isRequesting && (
|
{isRequesting ? (
|
||||||
<div className="flex flex-col justify-start items-center w-full my-8">
|
<div className="flex flex-col justify-start items-center w-full my-8">
|
||||||
<p className="text-sm text-gray-400 italic">{t("memo.fetching-data")}</p>
|
<p className="text-sm text-gray-400 italic">{t("memo.fetching-data")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : isComplete ? (
|
||||||
{isComplete ? (
|
|
||||||
sortedMemos.length === 0 && (
|
sortedMemos.length === 0 && (
|
||||||
<div className="w-full mt-12 mb-8 flex flex-col justify-center items-center italic">
|
<div className="w-full mt-12 mb-8 flex flex-col justify-center items-center italic">
|
||||||
<Empty />
|
<Empty />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Empty from "@/components/Empty";
|
import Empty from "@/components/Empty";
|
||||||
import HomeSidebar from "@/components/HomeSidebar";
|
import HomeSidebar from "@/components/HomeSidebar";
|
||||||
import HomeSidebarDrawer from "@/components/HomeSidebarDrawer";
|
import HomeSidebarDrawer from "@/components/HomeSidebarDrawer";
|
||||||
@ -11,8 +11,7 @@ import { getTimeStampByDate } from "@/helpers/datetime";
|
|||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||||
import { useFilterStore } from "@/store/module";
|
import { useFilterStore } from "@/store/module";
|
||||||
import { useMemoV1Store } from "@/store/v1";
|
import { useMemoList, useMemoV1Store } from "@/store/v1";
|
||||||
import { Memo } from "@/types/proto/api/v2/memo_service";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
@ -23,14 +22,14 @@ const Home = () => {
|
|||||||
const memoStore = useMemoV1Store();
|
const memoStore = useMemoV1Store();
|
||||||
const [isComplete, setIsComplete] = useState(false);
|
const [isComplete, setIsComplete] = useState(false);
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
const memosRef = useRef<Memo[]>([]);
|
const memoList = useMemoList();
|
||||||
const { tag: tagQuery, text: textQuery } = filterStore.state;
|
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) => getTimeStampByDate(b.displayTime) - getTimeStampByDate(a.displayTime))
|
||||||
.sort((a, b) => Number(b.pinned) - Number(a.pinned));
|
.sort((a, b) => Number(b.pinned) - Number(a.pinned));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
memosRef.current = [];
|
memoList.reset();
|
||||||
fetchMemos();
|
fetchMemos();
|
||||||
}, [tagQuery, textQuery]);
|
}, [tagQuery, textQuery]);
|
||||||
|
|
||||||
@ -49,36 +48,29 @@ const Home = () => {
|
|||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
const data = await memoStore.fetchMemos({
|
const data = await memoStore.fetchMemos({
|
||||||
limit: DEFAULT_MEMO_LIMIT,
|
limit: DEFAULT_MEMO_LIMIT,
|
||||||
offset: memosRef.current.length,
|
offset: memoList.size(),
|
||||||
filter: filters.join(" && "),
|
filter: filters.join(" && "),
|
||||||
});
|
});
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
memosRef.current = [...memosRef.current, ...data];
|
|
||||||
setIsComplete(data.length < DEFAULT_MEMO_LIMIT);
|
setIsComplete(data.length < DEFAULT_MEMO_LIMIT);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMemoCreated = async (memoId: number) => {
|
|
||||||
const memo = await memoStore.getOrFetchMemoById(memoId);
|
|
||||||
memosRef.current = [memo, ...memosRef.current];
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-5xl flex flex-row justify-center items-start">
|
<div className="w-full max-w-5xl flex flex-row justify-center items-start">
|
||||||
<div className="w-full sm:pt-3 md:pt-6">
|
<div className="w-full sm:pt-3 md:pt-6">
|
||||||
<MobileHeader>{!md && <HomeSidebarDrawer />}</MobileHeader>
|
<MobileHeader>{!md && <HomeSidebarDrawer />}</MobileHeader>
|
||||||
<div className="w-full px-4 sm:px-6 md:pr-2">
|
<div className="w-full px-4 sm:px-6 md:pr-2">
|
||||||
<MemoEditor className="mb-2" cacheKey="home-memo-editor" onConfirm={handleMemoCreated} />
|
<MemoEditor className="mb-2" cacheKey="home-memo-editor" />
|
||||||
<div className="flex flex-col justify-start items-start w-full max-w-full overflow-y-scroll pb-28 hide-scrollbar">
|
<div className="flex flex-col justify-start items-start w-full max-w-full overflow-y-scroll pb-28 hide-scrollbar">
|
||||||
<MemoFilter />
|
<MemoFilter />
|
||||||
{sortedMemos.map((memo) => (
|
{sortedMemos.map((memo) => (
|
||||||
<MemoView key={memo.id} memo={memo} lazyRendering showVisibility showPinnedStyle showParent />
|
<MemoView key={`${memo.id}-${memo.updateTime}`} memo={memo} lazyRendering showVisibility showPinnedStyle showParent />
|
||||||
))}
|
))}
|
||||||
{isRequesting && (
|
{isRequesting ? (
|
||||||
<div className="flex flex-col justify-start items-center w-full my-8">
|
<div className="flex flex-col justify-start items-center w-full my-8">
|
||||||
<p className="text-sm text-gray-400 italic">{t("memo.fetching-data")}</p>
|
<p className="text-sm text-gray-400 italic">{t("memo.fetching-data")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : isComplete ? (
|
||||||
{isComplete ? (
|
|
||||||
sortedMemos.length === 0 && (
|
sortedMemos.length === 0 && (
|
||||||
<div className="w-full mt-12 mb-8 flex flex-col justify-center items-center italic">
|
<div className="w-full mt-12 mb-8 flex flex-col justify-center items-center italic">
|
||||||
<Empty />
|
<Empty />
|
||||||
|
@ -10,33 +10,29 @@ import DatePicker from "@/components/kit/DatePicker";
|
|||||||
import { DAILY_TIMESTAMP } from "@/helpers/consts";
|
import { DAILY_TIMESTAMP } from "@/helpers/consts";
|
||||||
import { getDateStampByDate, getNormalizedDateString, getTimeStampByDate } from "@/helpers/datetime";
|
import { getDateStampByDate, getNormalizedDateString, getTimeStampByDate } from "@/helpers/datetime";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useMemoV1Store } from "@/store/v1";
|
import { useMemoList, useMemoV1Store } from "@/store/v1";
|
||||||
import { Memo } from "@/types/proto/api/v2/memo_service";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const Timeline = () => {
|
const Timeline = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const memoStore = useMemoV1Store();
|
|
||||||
const currentUser = useCurrentUser();
|
const currentUser = useCurrentUser();
|
||||||
|
const memoStore = useMemoV1Store();
|
||||||
|
const memoList = useMemoList();
|
||||||
const currentDateStamp = getDateStampByDate(getNormalizedDateString()) as number;
|
const currentDateStamp = getDateStampByDate(getNormalizedDateString()) as number;
|
||||||
const [selectedDateStamp, setSelectedDateStamp] = useState<number>(currentDateStamp as number);
|
const [selectedDateStamp, setSelectedDateStamp] = useState<number>(currentDateStamp as number);
|
||||||
const [memos, setMemos] = useState<Memo[]>([]);
|
|
||||||
const [showDatePicker, toggleShowDatePicker] = useToggle(false);
|
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(() => {
|
useEffect(() => {
|
||||||
|
memoList.reset();
|
||||||
const filters = [
|
const filters = [
|
||||||
`creator == "${currentUser.name}"`,
|
`creator == "${currentUser.name}"`,
|
||||||
`created_ts_after == ${selectedDateStamp / 1000}`,
|
`created_ts_after == ${selectedDateStamp / 1000}`,
|
||||||
`created_ts_before == ${(selectedDateStamp + DAILY_TIMESTAMP) / 1000}`,
|
`created_ts_before == ${(selectedDateStamp + DAILY_TIMESTAMP) / 1000}`,
|
||||||
];
|
];
|
||||||
memoStore
|
memoStore.fetchMemos({
|
||||||
.fetchMemos({
|
filter: filters.join(" && "),
|
||||||
filter: filters.join(" && "),
|
});
|
||||||
})
|
|
||||||
.then((memos: Memo[]) => {
|
|
||||||
setMemos(memos);
|
|
||||||
});
|
|
||||||
}, [selectedDateStamp]);
|
}, [selectedDateStamp]);
|
||||||
|
|
||||||
const handleDataPickerChange = (datestamp: number): void => {
|
const handleDataPickerChange = (datestamp: number): void => {
|
||||||
@ -44,12 +40,6 @@ const Timeline = () => {
|
|||||||
toggleShowDatePicker(false);
|
toggleShowDatePicker(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMemoCreate = async (id: number) => {
|
|
||||||
await memoStore.getOrFetchMemoById(id).then((memo: Memo) => {
|
|
||||||
setMemos([memo, ...memos]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8">
|
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8">
|
||||||
<MobileHeader />
|
<MobileHeader />
|
||||||
@ -108,7 +98,7 @@ const Timeline = () => {
|
|||||||
))}
|
))}
|
||||||
{selectedDateStamp === currentDateStamp && (
|
{selectedDateStamp === currentDateStamp && (
|
||||||
<div className="w-full pl-0 sm:pl-12 sm:mt-4">
|
<div className="w-full pl-0 sm:pl-12 sm:mt-4">
|
||||||
<MemoEditor cacheKey="timeline-editor" onConfirm={handleMemoCreate} />
|
<MemoEditor cacheKey="timeline-editor" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { combine } from "zustand/middleware";
|
import { combine } from "zustand/middleware";
|
||||||
import { memoServiceClient } from "@/grpcweb";
|
import { memoServiceClient } from "@/grpcweb";
|
||||||
import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v2/memo_service";
|
import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v2/memo_service";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
memoById: Map<number, Memo>;
|
||||||
|
}
|
||||||
|
|
||||||
export const useMemoV1Store = create(
|
export const useMemoV1Store = create(
|
||||||
combine({ memoById: new Map<number, Memo>() }, (set, get) => ({
|
combine({ memoById: new Map<number, Memo>() }, (set, get) => ({
|
||||||
|
setState: (state: State) => set(state),
|
||||||
getState: () => get(),
|
getState: () => get(),
|
||||||
fetchMemos: async (request: Partial<ListMemosRequest>) => {
|
fetchMemos: async (request: Partial<ListMemosRequest>) => {
|
||||||
const { memos } = await memoServiceClient.listMemos(request);
|
const { memos } = await memoServiceClient.listMemos(request);
|
||||||
|
set((state) => {
|
||||||
|
for (const memo of memos) {
|
||||||
|
state.memoById.set(memo.id, memo);
|
||||||
|
}
|
||||||
|
return cloneDeep(state);
|
||||||
|
});
|
||||||
return memos;
|
return memos;
|
||||||
},
|
},
|
||||||
getOrFetchMemoById: async (id: number) => {
|
getOrFetchMemoById: async (id: number) => {
|
||||||
@ -25,9 +37,8 @@ export const useMemoV1Store = create(
|
|||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.memoById.set(id, res.memo as Memo);
|
state.memoById.set(id, res.memo as Memo);
|
||||||
return state;
|
return cloneDeep(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.memo;
|
return res.memo;
|
||||||
},
|
},
|
||||||
getMemoById: (id: number) => {
|
getMemoById: (id: number) => {
|
||||||
@ -41,7 +52,7 @@ export const useMemoV1Store = create(
|
|||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.memoById.set(memo.id, memo);
|
state.memoById.set(memo.id, memo);
|
||||||
return state;
|
return cloneDeep(state);
|
||||||
});
|
});
|
||||||
return memo;
|
return memo;
|
||||||
},
|
},
|
||||||
@ -57,18 +68,18 @@ export const useMemoV1Store = create(
|
|||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.memoById.set(memo.id, memo);
|
state.memoById.set(memo.id, memo);
|
||||||
return state;
|
return cloneDeep(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
return memo;
|
return memo;
|
||||||
},
|
},
|
||||||
deleteMemo: async (id: number) => {
|
deleteMemo: async (id: number) => {
|
||||||
await memoServiceClient.deleteMemo({
|
await memoServiceClient.deleteMemo({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.memoById.delete(id);
|
state.memoById.delete(id);
|
||||||
return state;
|
return cloneDeep(state);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
fetchMemoResources: async (id: number) => {
|
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<number, Memo>() });
|
||||||
|
};
|
||||||
|
|
||||||
|
const size = () => {
|
||||||
|
return memoStore.getState().memoById.size;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: memos,
|
||||||
|
reset,
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user