diff --git a/apps/renderer/src/hooks/biz/useFeedActions.tsx b/apps/renderer/src/hooks/biz/useFeedActions.tsx index 3b6c6b8a4..5153f6636 100644 --- a/apps/renderer/src/hooks/biz/useFeedActions.tsx +++ b/apps/renderer/src/hooks/biz/useFeedActions.tsx @@ -7,9 +7,11 @@ import { useModalStack } from "~/components/ui/modal" import type { NativeMenuItem } from "~/lib/native-menu" import { useFeedClaimModal } from "~/modules/claim" import { FeedForm } from "~/modules/discover/feed-form" -import { getFeedById, useFeedById } from "~/store/feed" +import { Queries } from "~/queries" +import { getFeedById, useAddFeedToFeedList, useFeedById } from "~/store/feed" import { subscriptionActions, useSubscriptionByFeedId } from "~/store/subscription" +import { useAuthQuery } from "../common" import { useNavigateEntry } from "./useNavigateEntry" import { getRouteParams } from "./useRouteParams" import { useDeleteSubscription } from "./useSubscriptionActions" @@ -36,50 +38,13 @@ export const useFeedActions = ({ const navigateEntry = useNavigateEntry() const isEntryList = type === "entryList" + const listList = useAuthQuery(Queries.lists.list()) + const addMutation = useAddFeedToFeedList() + const items = useMemo(() => { if (!feed) return [] const isList = feed?.type === "list" const items: NativeMenuItem[] = [ - { - type: "text" as const, - label: isEntryList ? t("sidebar.feed_actions.edit_feed") : t("sidebar.feed_actions.edit"), - shortcut: "E", - click: () => { - present({ - title: isList - ? t("sidebar.feed_actions.edit_list") - : t("sidebar.feed_actions.edit_feed"), - content: ({ dismiss }) => ( - - ), - }) - }, - }, - { - type: "text" as const, - label: isEntryList - ? t("sidebar.feed_actions.unfollow_feed") - : t("sidebar.feed_actions.unfollow"), - shortcut: "Meta+Backspace", - click: () => deleteSubscription.mutate(subscription), - }, - { - type: "text" as const, - label: t( - isList - ? "sidebar.feed_actions.navigate_to_list" - : "sidebar.feed_actions.navigate_to_feed", - ), - shortcut: "Meta+G", - disabled: !isEntryList || getRouteParams().feedId === feedId, - click: () => { - navigateEntry({ feedId }) - }, - }, - { - type: "separator" as const, - disabled: isEntryList, - }, ...(!isList ? [ { @@ -121,6 +86,66 @@ export const useFeedActions = ({ type: "separator" as const, disabled: isEntryList, }, + { + type: "text" as const, + label: t("sidebar.feed_column.context_menu.add_feeds_to_list"), + enabled: !!listList.data?.length, + submenu: listList.data?.map((list) => ({ + label: list.title || "", + type: "text", + icon: list.image, + click() { + return addMutation.mutate({ + feedId, + listId: list.id, + }) + }, + })), + }, + { + type: "separator" as const, + disabled: isEntryList, + }, + { + type: "text" as const, + label: isEntryList ? t("sidebar.feed_actions.edit_feed") : t("sidebar.feed_actions.edit"), + shortcut: "E", + click: () => { + present({ + title: isList + ? t("sidebar.feed_actions.edit_list") + : t("sidebar.feed_actions.edit_feed"), + content: ({ dismiss }) => ( + + ), + }) + }, + }, + { + type: "text" as const, + label: isEntryList + ? t("sidebar.feed_actions.unfollow_feed") + : t("sidebar.feed_actions.unfollow"), + shortcut: "Meta+Backspace", + click: () => deleteSubscription.mutate(subscription), + }, + { + type: "text" as const, + label: t( + isList + ? "sidebar.feed_actions.navigate_to_list" + : "sidebar.feed_actions.navigate_to_feed", + ), + shortcut: "Meta+G", + disabled: !isEntryList || getRouteParams().feedId === feedId, + click: () => { + navigateEntry({ feedId }) + }, + }, + { + type: "separator" as const, + disabled: isEntryList, + }, { type: "text" as const, label: t( diff --git a/apps/renderer/src/modules/feed-column/category.tsx b/apps/renderer/src/modules/feed-column/category.tsx index 596736dc2..506e2f135 100644 --- a/apps/renderer/src/modules/feed-column/category.tsx +++ b/apps/renderer/src/modules/feed-column/category.tsx @@ -10,12 +10,13 @@ import { LoadingCircle } from "~/components/ui/loading" import { ROUTE_FEED_IN_FOLDER, views } from "~/constants" import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry" import { getRouteParams, useRouteParamsSelector } from "~/hooks/biz/useRouteParams" -import { useAnyPointDown, useInputComposition } from "~/hooks/common" +import { useAnyPointDown, useAuthQuery, useInputComposition } from "~/hooks/common" import { stopPropagation } from "~/lib/dom" import type { FeedViewType } from "~/lib/enum" import { showNativeMenu } from "~/lib/native-menu" import { cn, sortByAlphabet } from "~/lib/utils" -import { getPreferredTitle, useFeedStore } from "~/store/feed" +import { Queries } from "~/queries" +import { getPreferredTitle, useAddFeedToFeedList, useFeedStore } from "~/store/feed" import { subscriptionActions, useSubscriptionByFeedId } from "~/store/subscription" import { useFeedUnreadStore } from "~/store/unread" @@ -115,6 +116,9 @@ function FeedCategoryImpl({ }) const isCategoryIsWaiting = isChangePending + const listList = useAuthQuery(Queries.lists.list()) + const addMutation = useAddFeedToFeedList() + return (
{!!showCollapse && ( @@ -133,24 +137,6 @@ function FeedCategoryImpl({ setIsContextMenuOpen(true) showNativeMenu( [ - { - type: "text", - enabled: !!(folderName && typeof view === "number"), - label: t("sidebar.feed_column.context_menu.change_to_other_view"), - submenu: views - .filter((v) => v.view !== view) - .map((v) => ({ - // TODO: fix this type error - label: t(v.name), - enabled: true, - type: "text", - shortcut: (v.view + 1).toString(), - icon: v.icon, - click() { - return changeCategoryView(v.view) - }, - })), - }, { type: "text", label: t("sidebar.feed_column.context_menu.mark_as_read"), @@ -161,6 +147,39 @@ function FeedCategoryImpl({ }, }, { type: "separator" }, + { + type: "text", + label: t("sidebar.feed_column.context_menu.add_feeds_to_list"), + enabled: !!listList.data?.length, + submenu: listList.data?.map((list) => ({ + label: list.title || "", + type: "text", + icon: list.image, + click() { + return addMutation.mutate({ + feedIds: ids, + listId: list.id, + }) + }, + })), + }, + { type: "separator" }, + { + type: "text", + enabled: !!(folderName && typeof view === "number"), + label: t("sidebar.feed_column.context_menu.change_to_other_view"), + submenu: views + .filter((v) => v.view !== view) + .map((v) => ({ + label: t(v.name), + type: "text", + shortcut: (v.view + 1).toString(), + icon: v.icon, + click() { + return changeCategoryView(v.view) + }, + })), + }, { type: "text", label: t("sidebar.feed_column.context_menu.rename_category"), diff --git a/apps/renderer/src/store/feed/hooks.ts b/apps/renderer/src/store/feed/hooks.ts index e0352b6a0..726c58975 100644 --- a/apps/renderer/src/store/feed/hooks.ts +++ b/apps/renderer/src/store/feed/hooks.ts @@ -65,15 +65,14 @@ export const useFeedHeaderTitle = () => { export const useAddFeedToFeedList = (options?: { onSuccess: () => void; onError: () => void }) => { const { t } = useTranslation("settings") return useMutation({ - mutationFn: async (payload: { feedId: string; listId: string }) => { - const feed = await apiClient.lists.feeds.$post({ - json: { - listId: payload.listId, - feedId: payload.feedId, - }, + mutationFn: async ( + payload: { feedId: string; listId: string } | { feedIds: string[]; listId: string }, + ) => { + const feeds = await apiClient.lists.feeds.$post({ + json: payload, }) - feedActions.addFeedToFeedList(payload.listId, feed.data) + feeds.data.forEach((feed) => feedActions.addFeedToFeedList(payload.listId, feed)) }, onSuccess: () => { toast.success(t("lists.feeds.add.success")) diff --git a/locales/app/en.json b/locales/app/en.json index 56ede2fbe..d6e1b0a67 100644 --- a/locales/app/en.json +++ b/locales/app/en.json @@ -182,7 +182,8 @@ "sidebar.feed_actions.open_site_in_browser": "Open site in browser", "sidebar.feed_actions.unfollow": "Unfollow", "sidebar.feed_actions.unfollow_feed": "Unfollow feed", - "sidebar.feed_column.context_menu.change_to_other_view": "Change to other view", + "sidebar.feed_column.context_menu.add_feeds_to_list": "Add feeds to list", + "sidebar.feed_column.context_menu.change_to_other_view": "Switch to another view", "sidebar.feed_column.context_menu.delete_category": "Delete category", "sidebar.feed_column.context_menu.delete_category_confirmation": "Delete category {{folderName}}?", "sidebar.feed_column.context_menu.mark_as_read": "Mark as read", diff --git a/packages/shared/src/hono.ts b/packages/shared/src/hono.ts index 2c5fa3dcc..e244ccef7 100644 --- a/packages/shared/src/hono.ts +++ b/packages/shared/src/hono.ts @@ -3440,6 +3440,9 @@ declare const _routes: hono_hono_base.HonoBase