Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: displaying archived memos #2933

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 77 additions & 64 deletions web/src/pages/Archived.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Tooltip } from "@mui/joy";
import { Button, Tooltip } from "@mui/joy";
import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";
import { showCommonDialog } from "@/components/Dialog/CommonDialog";
import Empty from "@/components/Empty";
Expand All @@ -9,49 +9,56 @@ import MemoContent from "@/components/MemoContent";
import MemoFilter from "@/components/MemoFilter";
import MobileHeader from "@/components/MobileHeader";
import SearchBar from "@/components/SearchBar";
import { memoServiceClient } from "@/grpcweb";
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
import { getTimeStampByDate } from "@/helpers/datetime";
import { getDateTimeString } from "@/helpers/datetime";
import useCurrentUser from "@/hooks/useCurrentUser";
import useFilterWithUrlParams from "@/hooks/useFilterWithUrlParams";
import useLoading from "@/hooks/useLoading";
import { useMemoStore } from "@/store/v1";
import { useMemoList, useMemoStore } from "@/store/v1";
import { RowStatus } from "@/types/proto/api/v2/common";
import { Memo } from "@/types/proto/api/v2/memo_service";
import { useTranslate } from "@/utils/i18n";

const Archived = () => {
const t = useTranslate();
const loadingState = useLoading();
const user = useCurrentUser();
const memoStore = useMemoStore();
const [archivedMemos, setArchivedMemos] = useState<Memo[]>([]);
const memoList = useMemoList();
const [isRequesting, setIsRequesting] = useState(true);
const nextPageTokenRef = useRef<string | undefined>(undefined);
const { tag: tagQuery, text: textQuery } = useFilterWithUrlParams();
const sortedMemos = memoList.value
.filter((memo) => memo.rowStatus === RowStatus.ARCHIVED)
.sort((a, b) => getTimeStampByDate(b.displayTime) - getTimeStampByDate(a.displayTime));

useEffect(() => {
(async () => {
try {
const filters = [`creator == "${user.name}"`, "row_status == 'ARCHIVED'"];
const contentSearch: string[] = [];
if (tagQuery) {
contentSearch.push(JSON.stringify(`#${tagQuery}`));
}
if (textQuery) {
contentSearch.push(JSON.stringify(textQuery));
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
}
const { memos } = await memoServiceClient.listMemos({
filter: filters.join(" && "),
});
setArchivedMemos(memos);
} catch (error: unknown) {
toast.error((error as ClientError).details);
}
loadingState.setFinish();
})();
nextPageTokenRef.current = undefined;
memoList.reset();
fetchMemos();
}, [tagQuery, textQuery]);

const fetchMemos = async () => {
const filters = [`creator == "${user.name}"`, `row_status == "ARCHIVED"`];
const contentSearch: string[] = [];
if (tagQuery) {
contentSearch.push(JSON.stringify(`#${tagQuery}`));
}
if (textQuery) {
contentSearch.push(JSON.stringify(textQuery));
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
}
setIsRequesting(true);
const data = await memoStore.fetchMemos({
pageSize: DEFAULT_MEMO_LIMIT,
filter: filters.join(" && "),
pageToken: nextPageTokenRef.current,
});
setIsRequesting(false);
nextPageTokenRef.current = data.nextPageToken;
};

const handleDeleteMemoClick = async (memo: Memo) => {
showCommonDialog({
title: t("memo.delete-memo"),
Expand All @@ -60,7 +67,6 @@ const Archived = () => {
dialogName: "delete-memo-dialog",
onConfirm: async () => {
await memoStore.deleteMemo(memo.id);
setArchivedMemos((prev) => prev.filter((m) => m.id !== memo.id));
},
});
};
Expand All @@ -74,7 +80,6 @@ const Archived = () => {
},
["row_status"],
);
setArchivedMemos((prev) => prev.filter((m) => m.id !== memo.id));
toast(t("message.restored-successfully"));
} catch (error: unknown) {
console.error(error);
Expand All @@ -83,7 +88,7 @@ const Archived = () => {
};

return (
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-start 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 />
<div className="w-full px-4 sm:px-6">
<div className="w-full flex flex-col justify-start items-start">
Expand All @@ -93,41 +98,49 @@ const Archived = () => {
</div>
</div>
<MemoFilter className="px-2 pb-2" />
{loadingState.isLoading ? (
<div className="w-full h-32 flex flex-col justify-center items-center">
<p className="opacity-70">{t("memo.fetching-data")}</p>
{sortedMemos.map((memo) => (
<div
key={memo.id}
className="relative flex flex-col justify-start items-start w-full p-4 pt-3 mb-2 bg-white dark:bg-zinc-800 rounded-lg"
>
<div className="w-full mb-1 flex flex-row justify-between items-center">
<div className="w-full max-w-[calc(100%-20px)] flex flex-row justify-start items-center mr-1">
<span className="text-sm text-gray-400 select-none">{getDateTimeString(memo.displayTime)}</span>
</div>
<div className="flex flex-row justify-end items-center gap-x-2">
<Tooltip title={t("common.restore")} placement="top">
<button onClick={() => handleRestoreMemoClick(memo)}>
<Icon.ArchiveRestore className="w-4 h-auto cursor-pointer text-gray-500 dark:text-gray-400" />
</button>
</Tooltip>
<Tooltip title={t("common.delete")} placement="top">
<button onClick={() => handleDeleteMemoClick(memo)} className="text-gray-500 dark:text-gray-400">
<Icon.Trash className="w-4 h-auto cursor-pointer" />
</button>
</Tooltip>
</div>
</div>
<MemoContent key={`${memo.id}-${memo.displayTime}`} memoId={memo.id} content={memo.content} readonly={true} />
</div>
) : archivedMemos.length === 0 ? (
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
))}
{isRequesting ? (
<div className="flex flex-row justify-center items-center w-full my-4 text-gray-400">
<Icon.Loader className="w-4 h-auto animate-spin mr-1" />
<p className="text-sm italic">{t("memo.fetching-data")}</p>
</div>
) : (
archivedMemos.map((memo) => (
<div
key={memo.id}
className="relative flex flex-col justify-start items-start w-full p-4 pt-3 mb-2 bg-white dark:bg-zinc-800 rounded-lg"
>
<div className="w-full mb-1 flex flex-row justify-between items-center">
<div className="w-full max-w-[calc(100%-20px)] flex flex-row justify-start items-center mr-1">
<span className="text-sm text-gray-400 select-none">{getDateTimeString(memo.updateTime)}</span>
</div>
<div className="flex flex-row justify-end items-center gap-x-2">
<Tooltip title={t("common.restore")} placement="top">
<button onClick={() => handleRestoreMemoClick(memo)}>
<Icon.ArchiveRestore className="w-4 h-auto cursor-pointer text-gray-500 dark:text-gray-400" />
</button>
</Tooltip>
<Tooltip title={t("common.delete")} placement="top">
<button onClick={() => handleDeleteMemoClick(memo)} className="text-gray-500 dark:text-gray-400">
<Icon.Trash className="w-4 h-auto cursor-pointer" />
</button>
</Tooltip>
</div>
</div>
<MemoContent key={`${memo.id}-${memo.updateTime}`} memoId={memo.id} content={memo.content} readonly={true} />
) : !nextPageTokenRef.current ? (
sortedMemos.length === 0 && (
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
))
)
) : (
<div className="w-full flex flex-row justify-center items-center my-4">
<Button variant="plain" endDecorator={<Icon.ArrowDown className="w-5 h-auto" />} onClick={fetchMemos}>
{t("memo.fetch-more")}
</Button>
</div>
)}
</div>
</div>
Expand Down
Loading