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