mirror of https://github.com/usememos/memos.git
feat(inbox): handles errors while fetching and adds possibility to delete items (#4908)
This commit is contained in:
parent
db63b1949a
commit
2c7eb23343
|
|
@ -108,6 +108,9 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed to get memo: %v", err)
|
return nil, status.Errorf(codes.Internal, "failed to get memo: %v", err)
|
||||||
}
|
}
|
||||||
|
if memo == nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "memo does not exist")
|
||||||
|
}
|
||||||
relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{
|
relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{
|
||||||
ID: &payload.MemoComment.RelatedMemoId,
|
ID: &payload.MemoComment.RelatedMemoId,
|
||||||
ExcludeContent: true,
|
ExcludeContent: true,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { InboxIcon, LoaderIcon, MessageCircleIcon } from "lucide-react";
|
import { InboxIcon, LoaderIcon, MessageCircleIcon, TrashIcon } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
|
@ -24,24 +24,32 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
|
||||||
const [relatedMemo, setRelatedMemo] = useState<Memo | undefined>(undefined);
|
const [relatedMemo, setRelatedMemo] = useState<Memo | undefined>(undefined);
|
||||||
const [sender, setSender] = useState<User | undefined>(undefined);
|
const [sender, setSender] = useState<User | undefined>(undefined);
|
||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
|
const [hasError, setHasError] = useState<boolean>(false);
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
useAsyncEffect(async () => {
|
||||||
if (!inbox.activityId) {
|
if (!inbox.activityId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activity = await activityServiceClient.getActivity({
|
try {
|
||||||
name: `${activityNamePrefix}${inbox.activityId}`,
|
const activity = await activityServiceClient.getActivity({
|
||||||
});
|
name: `${activityNamePrefix}${inbox.activityId}`,
|
||||||
if (activity.payload?.memoComment) {
|
|
||||||
const memoCommentPayload = activity.payload.memoComment;
|
|
||||||
const memo = await memoStore.getOrFetchMemoByName(memoCommentPayload.relatedMemo, {
|
|
||||||
skipStore: true,
|
|
||||||
});
|
});
|
||||||
setRelatedMemo(memo);
|
|
||||||
const sender = await userStore.getOrFetchUserByName(inbox.sender);
|
if (activity.payload?.memoComment) {
|
||||||
setSender(sender);
|
const memoCommentPayload = activity.payload.memoComment;
|
||||||
setInitialized(true);
|
const memo = await memoStore.getOrFetchMemoByName(memoCommentPayload.relatedMemo, {
|
||||||
|
skipStore: true,
|
||||||
|
});
|
||||||
|
setRelatedMemo(memo);
|
||||||
|
const sender = await userStore.getOrFetchUserByName(inbox.sender);
|
||||||
|
setSender(sender);
|
||||||
|
setInitialized(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch activity:", error);
|
||||||
|
setHasError(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [inbox.activityId]);
|
}, [inbox.activityId]);
|
||||||
|
|
||||||
|
|
@ -69,6 +77,51 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteMessage = async () => {
|
||||||
|
await userStore.deleteInbox(inbox.name);
|
||||||
|
toast.success(t("message.deleted-successfully"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteButton = () => (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<TrashIcon
|
||||||
|
className="w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
|
||||||
|
onClick={() => handleDeleteMessage()}
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common.delete")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const archiveButton = () => (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InboxIcon
|
||||||
|
className="w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
|
||||||
|
onClick={() => handleArchiveMessage()}
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common.archive")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-row justify-start items-start gap-3">
|
<div className="w-full flex flex-row justify-start items-start gap-3">
|
||||||
<div
|
<div
|
||||||
|
|
@ -100,23 +153,7 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
|
||||||
<>
|
<>
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
<span className="text-sm text-muted-foreground">{inbox.createTime?.toLocaleString()}</span>
|
<span className="text-sm text-muted-foreground">{inbox.createTime?.toLocaleString()}</span>
|
||||||
<div>
|
{inbox.status === Inbox_Status.UNREAD ? archiveButton() : deleteButton()}
|
||||||
{inbox.status === Inbox_Status.UNREAD && (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<InboxIcon
|
|
||||||
className="w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
|
|
||||||
onClick={() => handleArchiveMessage()}
|
|
||||||
/>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>{t("common.archive")}</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className="text-base leading-tight cursor-pointer text-muted-foreground hover:underline hover:text-primary"
|
className="text-base leading-tight cursor-pointer text-muted-foreground hover:underline hover:text-primary"
|
||||||
|
|
@ -129,6 +166,11 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
|
) : hasError ? (
|
||||||
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
|
<span className="text-sm text-muted-foreground">{t("inbox.failed-to-load")}</span>
|
||||||
|
{deleteButton()}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full flex flex-row justify-center items-center my-2">
|
<div className="w-full flex flex-row justify-center items-center my-2">
|
||||||
<LoaderIcon className="animate-spin text-muted-foreground" />
|
<LoaderIcon className="animate-spin text-muted-foreground" />
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,8 @@
|
||||||
},
|
},
|
||||||
"inbox": {
|
"inbox": {
|
||||||
"memo-comment": "{{user}} hat einen Kommentar zu {{memo}} hinterlassen.",
|
"memo-comment": "{{user}} hat einen Kommentar zu {{memo}} hinterlassen.",
|
||||||
"version-update": "Die neue Version {{version}} ist jetzt verfügbar!"
|
"version-update": "Die neue Version {{version}} ist jetzt verfügbar!",
|
||||||
|
"failed-to-load": "Fehler beim Laden des Eintrags"
|
||||||
},
|
},
|
||||||
"markdown": {
|
"markdown": {
|
||||||
"checkbox": "Checkbox",
|
"checkbox": "Checkbox",
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,8 @@
|
||||||
},
|
},
|
||||||
"inbox": {
|
"inbox": {
|
||||||
"memo-comment": "{{user}} has a comment on your {{memo}}.",
|
"memo-comment": "{{user}} has a comment on your {{memo}}.",
|
||||||
"version-update": "New version {{version}} is available now!"
|
"version-update": "New version {{version}} is available now!",
|
||||||
|
"failed-to-load": "Failed to load inbox item"
|
||||||
},
|
},
|
||||||
"markdown": {
|
"markdown": {
|
||||||
"checkbox": "Checkbox",
|
"checkbox": "Checkbox",
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,13 @@ const userStore = (() => {
|
||||||
return updatedInbox;
|
return updatedInbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteInbox = async (name: string) => {
|
||||||
|
await inboxServiceClient.deleteInbox({ name });
|
||||||
|
state.setPartial({
|
||||||
|
inboxes: state.inboxes.filter((i) => i.name !== name),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const fetchUserStats = async (user?: string) => {
|
const fetchUserStats = async (user?: string) => {
|
||||||
const userStatsByName: Record<string, UserStats> = {};
|
const userStatsByName: Record<string, UserStats> = {};
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
@ -224,6 +231,7 @@ const userStore = (() => {
|
||||||
fetchShortcuts,
|
fetchShortcuts,
|
||||||
fetchInboxes,
|
fetchInboxes,
|
||||||
updateInbox,
|
updateInbox,
|
||||||
|
deleteInbox,
|
||||||
fetchUserStats,
|
fetchUserStats,
|
||||||
setStatsStateId,
|
setStatsStateId,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue