diff --git a/apps/renderer/src/hooks/biz/useEntryActions.tsx b/apps/renderer/src/hooks/biz/useEntryActions.tsx index f69a747b0..439b6ca4b 100644 --- a/apps/renderer/src/hooks/biz/useEntryActions.tsx +++ b/apps/renderer/src/hooks/biz/useEntryActions.tsx @@ -127,6 +127,21 @@ export const useUnread = () => }), }) +export const useDeleteInboxEntry = () => { + const { t } = useTranslation() + return useMutation({ + mutationFn: async (entryId: string) => { + await entryActions.deleteInboxEntry(entryId) + }, + onSuccess: () => { + toast.success(t("entry_actions.deleted")) + }, + onError: () => { + toast.error(t("entry_actions.failed_to_delete")) + }, + }) +} + export const useEntryActions = ({ view, entry, @@ -216,6 +231,8 @@ export const useEntryActions = ({ refetchOnWindowFocus: false, }) + const deleteInboxEntry = useDeleteInboxEntry() + const items = useMemo(() => { if (!populatedEntry || view === undefined) return [] const items: { @@ -456,6 +473,14 @@ export const useEntryActions = ({ uncollect.mutate() }, }, + { + key: "delete", + name: t("entry_actions.delete"), + hide: !isInbox, + onClick: () => { + deleteInboxEntry.mutate(populatedEntry.entries.id) + }, + }, { key: "copyLink", name: t("entry_actions.copy_link"), diff --git a/apps/renderer/src/hooks/biz/useFeedActions.tsx b/apps/renderer/src/hooks/biz/useFeedActions.tsx index d66e65ef0..aae6ba9f1 100644 --- a/apps/renderer/src/hooks/biz/useFeedActions.tsx +++ b/apps/renderer/src/hooks/biz/useFeedActions.tsx @@ -138,6 +138,7 @@ export const useFeedActions = ({ type: "text" as const, label: isEntryList ? t("sidebar.feed_actions.edit_feed") : t("sidebar.feed_actions.edit"), shortcut: "E", + disabled: isInbox, click: () => { present({ title: t("sidebar.feed_actions.edit_feed"), diff --git a/apps/renderer/src/store/entry/store.ts b/apps/renderer/src/store/entry/store.ts index b290137cd..47daa3799 100644 --- a/apps/renderer/src/store/entry/store.ts +++ b/apps/renderer/src/store/entry/store.ts @@ -442,6 +442,31 @@ class EntryActions { }, })) } + + async deleteInboxEntry(entryId: string) { + const entry = get().flatMapEntries[entryId] + if (!entry) return + + set((state) => + produce(state, (draft) => { + delete draft.flatMapEntries[entryId] + for (const feedId in draft.entries) { + const index = draft.entries[feedId].indexOf(entryId) + if (index !== -1) { + draft.entries[feedId].splice(index, 1) + } + } + }), + ) + await doMutationAndTransaction( + async () => { + await apiClient.entries.inbox.$delete({ json: { entryId } }) + }, + async () => { + await EntryService.deleteEntries([entryId]) + }, + ) + } } export const entryActions = new EntryActions() diff --git a/locales/app/en.json b/locales/app/en.json index 9c1a5fcff..0405f7040 100644 --- a/locales/app/en.json +++ b/locales/app/en.json @@ -57,6 +57,9 @@ "entry_actions.copied_notify": "{{which}} copied to clipboard.", "entry_actions.copy_link": "Copy Link", "entry_actions.copy_title": "Copy Title", + "entry_actions.delete": "Delete", + "entry_actions.deleted": "Deleted.", + "entry_actions.failed_to_delete": "Failed to delete.", "entry_actions.failed_to_save_to_eagle": "Failed to save to Eagle.", "entry_actions.failed_to_save_to_instapaper": "Failed to save to Instapaper.", "entry_actions.failed_to_save_to_obsidian": "Failed to save to Obsidian", diff --git a/locales/errors/en.json b/locales/errors/en.json index 32ee1f9cc..a1e54e44f 100644 --- a/locales/errors/en.json +++ b/locales/errors/en.json @@ -41,5 +41,6 @@ "9001": "Achievement already received", "10000": "Inbox not found", "10001": "Inbox already exists", - "10002": "Inbox limit exceeded" + "10002": "Inbox limit exceeded", + "10003": "Inbox permission denied" } diff --git a/packages/shared/src/hono.ts b/packages/shared/src/hono.ts index 8448dca6f..6d410905f 100644 --- a/packages/shared/src/hono.ts +++ b/packages/shared/src/hono.ts @@ -5927,6 +5927,18 @@ declare const _routes: hono_hono_base.HonoBase