diff --git a/apps/desktop/layer/renderer/src/modules/subscription-column/CategoryUnsubscribeDialogContent.tsx b/apps/desktop/layer/renderer/src/modules/subscription-column/CategoryUnsubscribeDialogContent.tsx
new file mode 100644
index 000000000..1833ef478
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/subscription-column/CategoryUnsubscribeDialogContent.tsx
@@ -0,0 +1,65 @@
+import { Button } from "@follow/components/ui/button/index.js"
+import type { FeedViewType } from "@follow/constants"
+import { getCategoryFeedIds } from "@follow/store/subscription/getter"
+import { subscriptionSyncService } from "@follow/store/subscription/store"
+import { useMutation } from "@tanstack/react-query"
+import { useMemo } from "react"
+import { useTranslation } from "react-i18next"
+import { toast } from "sonner"
+
+import { createErrorToaster } from "~/lib/error-parser"
+
+import { useCurrentModal } from "../../components/ui/modal/stacked/hooks"
+
+export function CategoryUnsubscribeDialogContent({
+ category,
+ view,
+}: {
+ category: string
+ view: FeedViewType
+}) {
+ const { t } = useTranslation()
+ const feedIds = useMemo(() => getCategoryFeedIds(category, view), [category, view])
+ const count = feedIds.length
+
+ const unsubscribeMutation = useMutation({
+ mutationFn: async (ids: string[]) => {
+ if (ids.length === 0) return
+ await subscriptionSyncService.unsubscribe(ids)
+ },
+ onError: createErrorToaster(t("sidebar.category_unsubscribe_dialog.error")),
+ onSuccess: () => {
+ toast.success(t("sidebar.category_unsubscribe_dialog.success", { count, category }))
+ },
+ })
+
+ const { dismiss } = useCurrentModal()
+
+ return (
+
+
+ {t("sidebar.category_unsubscribe_dialog.description", {
+ category,
+ count,
+ })}
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/desktop/layer/renderer/src/modules/subscription-column/FeedCategory.tsx b/apps/desktop/layer/renderer/src/modules/subscription-column/FeedCategory.tsx
index 3722666e7..b82d5c0f1 100644
--- a/apps/desktop/layer/renderer/src/modules/subscription-column/FeedCategory.tsx
+++ b/apps/desktop/layer/renderer/src/modules/subscription-column/FeedCategory.tsx
@@ -3,11 +3,9 @@ import { useMobile } from "@follow/components/hooks/useMobile.js"
import { MotionButtonBase } from "@follow/components/ui/button/index.js"
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
import { useScrollViewElement } from "@follow/components/ui/scroll-area/hooks.js"
-import { ShrinkingFocusBorder } from "@follow/components/ui/shrinking-focus-border/index.js"
import type { FeedViewType } from "@follow/constants"
import { getViewList } from "@follow/constants"
-import { useInputComposition, useRefValue } from "@follow/hooks"
-import { useFeedStore } from "@follow/store/feed/store"
+import { useRefValue } from "@follow/hooks"
import { useOwnedListByView } from "@follow/store/list/hooks"
import {
useSubscriptionByFeedId,
@@ -17,15 +15,13 @@ import { subscriptionActions, subscriptionSyncService } from "@follow/store/subs
import { getDefaultCategory } from "@follow/store/subscription/utils"
import { useSortedIdsByUnread, useUnreadByIds } from "@follow/store/unread/hooks"
import { unreadSyncService } from "@follow/store/unread/store"
-import { nextFrame, stopPropagation } from "@follow/utils/dom"
-import { cn, sortByAlphabet } from "@follow/utils/utils"
+import { stopPropagation } from "@follow/utils/dom"
+import { cn } from "@follow/utils/utils"
import { useMutation } from "@tanstack/react-query"
import { AnimatePresence, m } from "motion/react"
-import type { FC } from "react"
import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
-import { toast } from "sonner"
-import { useEventCallback, useOnClickOutside } from "usehooks-ts"
+import { useEventCallback } from "usehooks-ts"
import type { MenuItemInput } from "~/atoms/context-menu"
import { MenuItemSeparator, MenuItemText, useShowContextMenu } from "~/atoms/context-menu"
@@ -35,14 +31,13 @@ import { useAddFeedToFeedList } from "~/hooks/biz/useFeedActions"
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
import { getRouteParams, useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
import { useContextMenu } from "~/hooks/common/useContextMenu"
-import { createErrorToaster } from "~/lib/error-parser"
-import { getPreferredTitle } from "~/store/feed/hooks"
import { useModalStack } from "../../components/ui/modal/stacked/hooks"
import { ListCreationModalContent } from "../settings/tabs/lists/modals"
-import { useFeedListSortSelector } from "./atom"
import { CategoryRemoveDialogContent } from "./CategoryRemoveDialogContent"
-import { FeedItemAutoHideUnread } from "./FeedItem"
+import { CategoryUnsubscribeDialogContent } from "./CategoryUnsubscribeDialogContent"
+import { RenameCategoryForm } from "./RenameCategoryForm"
+import { SortedFeedItems } from "./SortedFeedItems"
import { feedColumnStyles } from "./styles"
import { UnreadNumber } from "./UnreadNumber"
@@ -256,17 +251,31 @@ function FeedCategoryImpl({
},
}),
new MenuItemText({
- label: t("sidebar.feed_column.context_menu.delete_category"),
+ label: t("sidebar.feed_column.context_menu.ungroup_category"),
hide: !folderName || isAutoGroupedCategory,
click: () => {
present({
- title: t("sidebar.feed_column.context_menu.delete_category_confirmation", {
+ title: t("sidebar.feed_column.context_menu.ungroup_category_confirmation", {
folderName,
}),
content: () => ,
})
},
}),
+ new MenuItemText({
+ label: t("sidebar.feed_column.context_menu.unsubscribe_category"),
+ hide: !folderName || isAutoGroupedCategory,
+ click: () => {
+ present({
+ title: t("sidebar.category_unsubscribe_dialog.title", {
+ folderName,
+ }),
+ content: () => (
+
+ ),
+ })
+ },
+ }),
],
e,
)
@@ -385,165 +394,3 @@ export const FeedCategoryAutoHideUnread = memo(function FeedCategoryAutoHideUnre
}
return
})
-
-const RenameCategoryForm: FC<{
- currentCategory: string
- view: FeedViewType
- onFinished: () => void
-}> = ({ currentCategory, view, onFinished }) => {
- const navigate = useNavigateEntry()
- const { t } = useTranslation()
- const renameMutation = useMutation({
- mutationFn: async ({
- lastCategory,
- newCategory,
- }: {
- lastCategory: string
- newCategory: string
- }) => subscriptionSyncService.renameCategory({ lastCategory, newCategory, view }),
- onMutate({ lastCategory, newCategory }) {
- const routeParams = getRouteParams()
-
- if (routeParams.folderName === lastCategory) {
- navigate({
- folderName: newCategory,
- })
- }
-
- onFinished()
- },
- onError: createErrorToaster(t("sidebar.feed_column.context_menu.rename_category_error")),
- onSuccess: () => {
- toast.success(t("sidebar.feed_column.context_menu.rename_category_success"))
- },
- })
- const formRef = useRef(null)
- const [isFocused, setIsFocused] = useState(false)
-
- useOnClickOutside(
- formRef as React.RefObject,
- () => {
- onFinished()
- },
- "mousedown",
- )
- const inputRef = useRef(null)
- useEffect(() => {
- nextFrame(() => {
- inputRef.current?.focus()
- setIsFocused(true)
- })
- }, [])
- const compositionInputProps = useInputComposition({
- onKeyDown: (e) => {
- if (e.key === "Escape") {
- onFinished()
- }
- },
- })
- return (
-
-
-
-
- )
-}
-
-type SortListProps = {
- ids: string[]
- view: FeedViewType
- showCollapse: boolean
-}
-const SortedFeedItems = memo((props: SortListProps) => {
- const by = useFeedListSortSelector((s) => s.by)
- switch (by) {
- case "count": {
- return
- }
- case "alphabetical": {
- return
- }
-
- default: {
- return
- }
- }
-})
-
-const SortByAlphabeticalList = (props: SortListProps) => {
- const { ids, showCollapse, view } = props
- const isDesc = useFeedListSortSelector((s) => s.order === "desc")
- const sortedFeedList = useFeedStore(
- useCallback(
- (state) => {
- const res = ids.sort((a, b) => {
- const feedTitleA = getPreferredTitle(state.feeds[a]) || ""
- const feedTitleB = getPreferredTitle(state.feeds[b]) || ""
- return sortByAlphabet(feedTitleA, feedTitleB)
- })
-
- if (isDesc) {
- return res
- }
- return res.reverse()
- },
- [ids, isDesc],
- ),
- )
- return (
-
- {sortedFeedList.map((feedId) => (
-
- ))}
-
- )
-}
-const SortByUnreadList = ({ ids, showCollapse, view }: SortListProps) => {
- const isDesc = useFeedListSortSelector((s) => s.order === "desc")
- const sortByUnreadFeedList = useSortedIdsByUnread(ids, isDesc)
-
- return (
-
- {sortByUnreadFeedList.map((feedId) => (
-
- ))}
-
- )
-}
diff --git a/apps/desktop/layer/renderer/src/modules/subscription-column/RenameCategoryForm.tsx b/apps/desktop/layer/renderer/src/modules/subscription-column/RenameCategoryForm.tsx
new file mode 100644
index 000000000..2daeb2dd0
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/subscription-column/RenameCategoryForm.tsx
@@ -0,0 +1,110 @@
+import { MotionButtonBase } from "@follow/components/ui/button/index.js"
+import { ShrinkingFocusBorder } from "@follow/components/ui/shrinking-focus-border/index.js"
+import type { FeedViewType } from "@follow/constants"
+import { useInputComposition } from "@follow/hooks"
+import { subscriptionSyncService } from "@follow/store/subscription/store"
+import { nextFrame } from "@follow/utils/dom"
+import { useMutation } from "@tanstack/react-query"
+import { useEffect, useRef, useState } from "react"
+import { useTranslation } from "react-i18next"
+import { toast } from "sonner"
+import { useOnClickOutside } from "usehooks-ts"
+
+import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
+import { getRouteParams } from "~/hooks/biz/useRouteParams"
+import { createErrorToaster } from "~/lib/error-parser"
+
+export const RenameCategoryForm = ({
+ currentCategory,
+ view,
+ onFinished,
+}: {
+ currentCategory: string
+ view: FeedViewType
+ onFinished: () => void
+}) => {
+ const navigate = useNavigateEntry()
+ const { t } = useTranslation()
+ const renameMutation = useMutation({
+ mutationFn: async ({
+ lastCategory,
+ newCategory,
+ }: {
+ lastCategory: string
+ newCategory: string
+ }) => subscriptionSyncService.renameCategory({ lastCategory, newCategory, view }),
+ onMutate({ lastCategory, newCategory }) {
+ const routeParams = getRouteParams()
+
+ if (routeParams.folderName === lastCategory) {
+ navigate({
+ folderName: newCategory,
+ })
+ }
+
+ onFinished()
+ },
+ onError: createErrorToaster(t("sidebar.feed_column.context_menu.rename_category_error")),
+ onSuccess: () => {
+ toast.success(t("sidebar.feed_column.context_menu.rename_category_success"))
+ },
+ })
+ const formRef = useRef(null)
+ const [isFocused, setIsFocused] = useState(false)
+
+ useOnClickOutside(
+ formRef as React.RefObject,
+ () => {
+ onFinished()
+ },
+ "mousedown",
+ )
+ const inputRef = useRef(null)
+ useEffect(() => {
+ nextFrame(() => {
+ inputRef.current?.focus()
+ setIsFocused(true)
+ })
+ }, [])
+ const compositionInputProps = useInputComposition({
+ onKeyDown: (e) => {
+ if (e.key === "Escape") {
+ onFinished()
+ }
+ },
+ })
+ return (
+
+
+
+
+ )
+}
diff --git a/apps/desktop/layer/renderer/src/modules/subscription-column/SortedFeedItems.tsx b/apps/desktop/layer/renderer/src/modules/subscription-column/SortedFeedItems.tsx
new file mode 100644
index 000000000..56a7426e8
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/subscription-column/SortedFeedItems.tsx
@@ -0,0 +1,84 @@
+import type { FeedViewType } from "@follow/constants"
+import { useFeedStore } from "@follow/store/feed/store"
+import { useSortedIdsByUnread } from "@follow/store/unread/hooks"
+import { sortByAlphabet } from "@follow/utils/utils"
+import { Fragment, memo, useCallback } from "react"
+
+import { getPreferredTitle } from "~/store/feed/hooks"
+
+import { useFeedListSortSelector } from "./atom"
+import { FeedItemAutoHideUnread } from "./FeedItem"
+
+type SortListProps = {
+ ids: string[]
+ view: FeedViewType
+ showCollapse: boolean
+}
+
+export const SortedFeedItems = memo((props: SortListProps) => {
+ const by = useFeedListSortSelector((s) => s.by)
+ switch (by) {
+ case "count": {
+ return
+ }
+ case "alphabetical": {
+ return
+ }
+
+ default: {
+ return
+ }
+ }
+})
+
+const SortByAlphabeticalList = (props: SortListProps) => {
+ const { ids, showCollapse, view } = props
+ const isDesc = useFeedListSortSelector((s) => s.order === "desc")
+ const sortedFeedList = useFeedStore(
+ useCallback(
+ (state) => {
+ const res = ids.sort((a, b) => {
+ const feedTitleA = getPreferredTitle(state.feeds[a]) || ""
+ const feedTitleB = getPreferredTitle(state.feeds[b]) || ""
+ return sortByAlphabet(feedTitleA, feedTitleB)
+ })
+
+ if (isDesc) {
+ return res
+ }
+ return res.reverse()
+ },
+ [ids, isDesc],
+ ),
+ )
+ return (
+
+ {sortedFeedList.map((feedId) => (
+
+ ))}
+
+ )
+}
+
+const SortByUnreadList = ({ ids, showCollapse, view }: SortListProps) => {
+ const isDesc = useFeedListSortSelector((s) => s.order === "desc")
+ const sortByUnreadFeedList = useSortedIdsByUnread(ids, isDesc)
+
+ return (
+
+ {sortByUnreadFeedList.map((feedId) => (
+
+ ))}
+
+ )
+}
diff --git a/locales/app/en.json b/locales/app/en.json
index 9201af85b..a76e33a5b 100644
--- a/locales/app/en.json
+++ b/locales/app/en.json
@@ -353,11 +353,26 @@
"sidebar.add_more_feeds": "Add more feeds",
"sidebar.already_on_discover_page": "You're already on the Discover page! Add the content you want to follow to the panel on the right.",
"sidebar.category_remove_dialog.cancel": "Cancel",
- "sidebar.category_remove_dialog.continue": "Continue",
- "sidebar.category_remove_dialog.description": "This operation will delete your category, but the feeds it contains will be retained and grouped by website.",
- "sidebar.category_remove_dialog.error": "Failed to remove category",
- "sidebar.category_remove_dialog.success": "Category removed successfully",
- "sidebar.category_remove_dialog.title": "Remove Category",
+ "sidebar.category_remove_dialog.continue": "Ungroup",
+ "sidebar.category_remove_dialog.description": "Feeds in this category will be ungrouped and moved back to their default grouping.",
+ "sidebar.category_remove_dialog.error": "Failed to ungroup category",
+ "sidebar.category_remove_dialog.success": "Category ungrouped successfully",
+ "sidebar.category_remove_dialog.title": "Ungroup Category",
+ "sidebar.category_unsubscribe_dialog.cancel": "Cancel",
+ "sidebar.category_unsubscribe_dialog.confirm": "Unsubscribe {{count}} feeds",
+ "sidebar.category_unsubscribe_dialog.confirm_one": "Unsubscribe 1 feed",
+ "sidebar.category_unsubscribe_dialog.confirm_other": "Unsubscribe {{count}} feeds",
+ "sidebar.category_unsubscribe_dialog.confirm_zero": "Unsubscribe 0 feeds",
+ "sidebar.category_unsubscribe_dialog.description": "You are about to unsubscribe from {{count}} feeds in {{category}}. This action cannot be undone.",
+ "sidebar.category_unsubscribe_dialog.description_one": "You are about to unsubscribe from the only feed in {{category}}. This action cannot be undone.",
+ "sidebar.category_unsubscribe_dialog.description_other": "You are about to unsubscribe from {{count}} feeds in {{category}}. This action cannot be undone.",
+ "sidebar.category_unsubscribe_dialog.description_zero": "There are no feeds in {{category}} to unsubscribe.",
+ "sidebar.category_unsubscribe_dialog.error": "Failed to unsubscribe from category",
+ "sidebar.category_unsubscribe_dialog.success": "Unsubscribed from {{count}} feeds in {{category}}.",
+ "sidebar.category_unsubscribe_dialog.success_one": "Unsubscribed from 1 feed in {{category}}.",
+ "sidebar.category_unsubscribe_dialog.success_other": "Unsubscribed from {{count}} feeds in {{category}}.",
+ "sidebar.category_unsubscribe_dialog.success_zero": "No feeds were unsubscribed in {{category}}.",
+ "sidebar.category_unsubscribe_dialog.title": "Unsubscribe {{folderName}}",
"sidebar.feed_actions.claim": "Claim",
"sidebar.feed_actions.claim_feed": "Claim Feed",
"sidebar.feed_actions.copy_email_address": "Copy Email Address",
@@ -393,8 +408,6 @@
"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.create_category": "New Category",
- "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",
"sidebar.feed_column.context_menu.new_category_modal.category_name": "Category Name",
"sidebar.feed_column.context_menu.new_category_modal.create": "Create",
@@ -402,6 +415,9 @@
"sidebar.feed_column.context_menu.rename_category_error": "Failed to rename category",
"sidebar.feed_column.context_menu.rename_category_success": "Category renamed successfully",
"sidebar.feed_column.context_menu.title": "Move to New Category",
+ "sidebar.feed_column.context_menu.ungroup_category": "Ungroup Category",
+ "sidebar.feed_column.context_menu.ungroup_category_confirmation": "Ungroup category {{folderName}}?",
+ "sidebar.feed_column.context_menu.unsubscribe_category": "Unsubscribe All in Category",
"sidebar.select_sort_method": "Select a sort method",
"sidebar.timeline_tabs.customize": "Customize View Tabs...",
"sidebar.timeline_tabs.hide_tab": "Hide this view",
diff --git a/locales/app/ja.json b/locales/app/ja.json
index ec3c670b1..3b306ee0d 100644
--- a/locales/app/ja.json
+++ b/locales/app/ja.json
@@ -349,11 +349,26 @@
"sidebar.add_more_feeds": "さらにフィードを追加",
"sidebar.already_on_discover_page": "すでに 発見 ページにいます!フォローしたいコンテンツを右側のパネルに追加してください。",
"sidebar.category_remove_dialog.cancel": "キャンセル",
- "sidebar.category_remove_dialog.continue": "続行",
- "sidebar.category_remove_dialog.description": "この操作によりカテゴリーが削除されますが、含まれているフィードは保持され、ウェブサイトごとにグループ化されます。",
- "sidebar.category_remove_dialog.error": "カテゴリーの削除に失敗しました",
- "sidebar.category_remove_dialog.success": "カテゴリーの削除に成功しました",
- "sidebar.category_remove_dialog.title": "カテゴリーを削除",
+ "sidebar.category_remove_dialog.continue": "グループ解除",
+ "sidebar.category_remove_dialog.description": "この操作によりカテゴリーのグループが解除され、含まれるフィードは既定のグループに戻ります。",
+ "sidebar.category_remove_dialog.error": "カテゴリーのグループ解除に失敗しました",
+ "sidebar.category_remove_dialog.success": "カテゴリーのグループ解除に成功しました",
+ "sidebar.category_remove_dialog.title": "カテゴリーのグループ解除",
+ "sidebar.category_unsubscribe_dialog.cancel": "キャンセル",
+ "sidebar.category_unsubscribe_dialog.confirm": "{{count}} 件のフィードを解除",
+ "sidebar.category_unsubscribe_dialog.confirm_one": "1 件のフィードを解除",
+ "sidebar.category_unsubscribe_dialog.confirm_other": "{{count}} 件のフィードを解除",
+ "sidebar.category_unsubscribe_dialog.confirm_zero": "0 件のフィードを解除",
+ "sidebar.category_unsubscribe_dialog.description": "この操作によりカテゴリー「{{category}}」内の {{count}} 件のフィードの購読を解除します。この操作は元に戻せません。",
+ "sidebar.category_unsubscribe_dialog.description_one": "この操作によりカテゴリー「{{category}}」内の 1 件のフィードの購読を解除します。この操作は元に戻せません。",
+ "sidebar.category_unsubscribe_dialog.description_other": "この操作によりカテゴリー「{{category}}」内の {{count}} 件のフィードの購読を解除します。この操作は元に戻せません。",
+ "sidebar.category_unsubscribe_dialog.description_zero": "カテゴリー「{{category}}」には解除できるフィードがありません。",
+ "sidebar.category_unsubscribe_dialog.error": "カテゴリーの購読解除に失敗しました",
+ "sidebar.category_unsubscribe_dialog.success": "カテゴリー「{{category}}」内の {{count}} 件のフィードを解除しました。",
+ "sidebar.category_unsubscribe_dialog.success_one": "カテゴリー「{{category}}」内の 1 件のフィードを解除しました。",
+ "sidebar.category_unsubscribe_dialog.success_other": "カテゴリー「{{category}}」内の {{count}} 件のフィードを解除しました。",
+ "sidebar.category_unsubscribe_dialog.success_zero": "カテゴリー「{{category}}」で解除されたフィードはありません。",
+ "sidebar.category_unsubscribe_dialog.title": "「{{folderName}}」の購読を解除",
"sidebar.feed_actions.claim": "クレーム",
"sidebar.feed_actions.claim_feed": "フィードをクレーム",
"sidebar.feed_actions.copy_email_address": "Email アドレスをコピー",
@@ -389,8 +404,6 @@
"sidebar.feed_column.context_menu.add_feeds_to_list": "フィードをリストに追加",
"sidebar.feed_column.context_menu.change_to_other_view": "他のビューに切り替え",
"sidebar.feed_column.context_menu.create_category": "新規カテゴリーを作成",
- "sidebar.feed_column.context_menu.delete_category": "カテゴリーを削除",
- "sidebar.feed_column.context_menu.delete_category_confirmation": "カテゴリー{{folderName}} を削除しますか?",
"sidebar.feed_column.context_menu.mark_as_read": "既読にする",
"sidebar.feed_column.context_menu.new_category_modal.category_name": "カテゴリー名",
"sidebar.feed_column.context_menu.new_category_modal.create": "作成",
@@ -398,6 +411,9 @@
"sidebar.feed_column.context_menu.rename_category_error": "カテゴリーのリネームに失敗しました",
"sidebar.feed_column.context_menu.rename_category_success": "カテゴリーのリネームに成功しました",
"sidebar.feed_column.context_menu.title": "新規カテゴリーに移動する",
+ "sidebar.feed_column.context_menu.ungroup_category": "カテゴリーのグループ解除",
+ "sidebar.feed_column.context_menu.ungroup_category_confirmation": "カテゴリー「{{folderName}}」のグループを解除しますか?",
+ "sidebar.feed_column.context_menu.unsubscribe_category": "カテゴリー内の購読をすべて解除",
"sidebar.select_sort_method": "並べ替え方法を選択",
"sidebar.timeline_tabs.customize": "ビュータブをカスタマイズ...",
"sidebar.timeline_tabs.hide_tab": "このビューを非表示にする",
diff --git a/locales/app/zh-CN.json b/locales/app/zh-CN.json
index 8b7a274cc..2ef89e38b 100644
--- a/locales/app/zh-CN.json
+++ b/locales/app/zh-CN.json
@@ -352,11 +352,26 @@
"sidebar.add_more_feeds": "添加订阅源",
"sidebar.already_on_discover_page": "你已位于发现页面!请将想要订阅的内容添加到右侧面板。",
"sidebar.category_remove_dialog.cancel": "取消",
- "sidebar.category_remove_dialog.continue": "继续",
- "sidebar.category_remove_dialog.description": "正在删除分类,操作后仍保留订阅源并按网站域名分组。",
- "sidebar.category_remove_dialog.error": "删除分类失败",
- "sidebar.category_remove_dialog.success": "分类删除成功",
- "sidebar.category_remove_dialog.title": "删除分类",
+ "sidebar.category_remove_dialog.continue": "取消分组",
+ "sidebar.category_remove_dialog.description": "此操作会取消当前分类,将其中的订阅恢复为默认分组。",
+ "sidebar.category_remove_dialog.error": "取消分类分组失败",
+ "sidebar.category_remove_dialog.success": "分类取消分组成功",
+ "sidebar.category_remove_dialog.title": "取消分类分组",
+ "sidebar.category_unsubscribe_dialog.cancel": "取消",
+ "sidebar.category_unsubscribe_dialog.confirm": "取消订阅 {{count}} 个源",
+ "sidebar.category_unsubscribe_dialog.confirm_one": "取消订阅 1 个源",
+ "sidebar.category_unsubscribe_dialog.confirm_other": "取消订阅 {{count}} 个源",
+ "sidebar.category_unsubscribe_dialog.confirm_zero": "取消订阅 0 个源",
+ "sidebar.category_unsubscribe_dialog.description": "此操作将取消订阅分类「{{category}}」中的 {{count}} 个源,且无法撤销。",
+ "sidebar.category_unsubscribe_dialog.description_one": "此操作将取消订阅分类「{{category}}」中的 1 个源,且无法撤销。",
+ "sidebar.category_unsubscribe_dialog.description_other": "此操作将取消订阅分类「{{category}}」中的 {{count}} 个源,且无法撤销。",
+ "sidebar.category_unsubscribe_dialog.description_zero": "分类「{{category}}」中没有可取消订阅的源。",
+ "sidebar.category_unsubscribe_dialog.error": "取消分类订阅失败",
+ "sidebar.category_unsubscribe_dialog.success": "已取消订阅分类「{{category}}」中的 {{count}} 个源。",
+ "sidebar.category_unsubscribe_dialog.success_one": "已取消订阅分类「{{category}}」中的 1 个源。",
+ "sidebar.category_unsubscribe_dialog.success_other": "已取消订阅分类「{{category}}」中的 {{count}} 个源。",
+ "sidebar.category_unsubscribe_dialog.success_zero": "分类「{{category}}」中没有源被取消订阅。",
+ "sidebar.category_unsubscribe_dialog.title": "取消订阅 {{folderName}}",
"sidebar.feed_actions.claim": "认证",
"sidebar.feed_actions.claim_feed": "认证订阅",
"sidebar.feed_actions.copy_email_address": "复制邮件地址",
@@ -392,8 +407,6 @@
"sidebar.feed_column.context_menu.add_feeds_to_list": "添加订阅源到列表",
"sidebar.feed_column.context_menu.change_to_other_view": "更改为其他视图",
"sidebar.feed_column.context_menu.create_category": "新分类",
- "sidebar.feed_column.context_menu.delete_category": "删除分类",
- "sidebar.feed_column.context_menu.delete_category_confirmation": "删除分类 {{folderName}}?",
"sidebar.feed_column.context_menu.mark_as_read": "标记为已读",
"sidebar.feed_column.context_menu.new_category_modal.category_name": "分类名称",
"sidebar.feed_column.context_menu.new_category_modal.create": "创建",
@@ -401,6 +414,9 @@
"sidebar.feed_column.context_menu.rename_category_error": "重命名分类失败",
"sidebar.feed_column.context_menu.rename_category_success": "分类重命名成功",
"sidebar.feed_column.context_menu.title": "移动至新分类",
+ "sidebar.feed_column.context_menu.ungroup_category": "取消分类分组",
+ "sidebar.feed_column.context_menu.ungroup_category_confirmation": "取消分类「{{folderName}}」的分组?",
+ "sidebar.feed_column.context_menu.unsubscribe_category": "取消分类内所有订阅",
"sidebar.select_sort_method": "选择排序方法",
"sidebar.timeline_tabs.customize": "自定义视图标签...",
"sidebar.timeline_tabs.hide_tab": "隐藏此视图",
diff --git a/locales/app/zh-TW.json b/locales/app/zh-TW.json
index a9f5e301b..49c230959 100644
--- a/locales/app/zh-TW.json
+++ b/locales/app/zh-TW.json
@@ -350,11 +350,26 @@
"sidebar.add_more_feeds": "新增 RSS 摘要",
"sidebar.already_on_discover_page": "你已位於探索頁面!請將想要訂閱的內容加入右側面板。",
"sidebar.category_remove_dialog.cancel": "取消",
- "sidebar.category_remove_dialog.continue": "繼續",
- "sidebar.category_remove_dialog.description": "正在刪除類別,但仍保留 RSS 摘要並按網站分組。",
- "sidebar.category_remove_dialog.error": "刪除類別失敗",
- "sidebar.category_remove_dialog.success": "類別刪除成功",
- "sidebar.category_remove_dialog.title": "移除類別",
+ "sidebar.category_remove_dialog.continue": "取消分組",
+ "sidebar.category_remove_dialog.description": "此操作會取消分類分組,並將其中的訂閱恢復為預設分組。",
+ "sidebar.category_remove_dialog.error": "取消分類分組失敗",
+ "sidebar.category_remove_dialog.success": "分類取消分組成功",
+ "sidebar.category_remove_dialog.title": "取消分類分組",
+ "sidebar.category_unsubscribe_dialog.cancel": "取消",
+ "sidebar.category_unsubscribe_dialog.confirm": "取消訂閱 {{count}} 個來源",
+ "sidebar.category_unsubscribe_dialog.confirm_one": "取消訂閱 1 個來源",
+ "sidebar.category_unsubscribe_dialog.confirm_other": "取消訂閱 {{count}} 個來源",
+ "sidebar.category_unsubscribe_dialog.confirm_zero": "取消訂閱 0 個來源",
+ "sidebar.category_unsubscribe_dialog.description": "此操作將取消訂閱分類「{{category}}」中的 {{count}} 個來源,且無法復原。",
+ "sidebar.category_unsubscribe_dialog.description_one": "此操作將取消訂閱分類「{{category}}」中的 1 個來源,且無法復原。",
+ "sidebar.category_unsubscribe_dialog.description_other": "此操作將取消訂閱分類「{{category}}」中的 {{count}} 個來源,且無法復原。",
+ "sidebar.category_unsubscribe_dialog.description_zero": "分類「{{category}}」中沒有可取消訂閱的來源。",
+ "sidebar.category_unsubscribe_dialog.error": "取消分類訂閱失敗",
+ "sidebar.category_unsubscribe_dialog.success": "已取消訂閱分類「{{category}}」中的 {{count}} 個來源。",
+ "sidebar.category_unsubscribe_dialog.success_one": "已取消訂閱分類「{{category}}」中的 1 個來源。",
+ "sidebar.category_unsubscribe_dialog.success_other": "已取消訂閱分類「{{category}}」中的 {{count}} 個來源。",
+ "sidebar.category_unsubscribe_dialog.success_zero": "分類「{{category}}」中沒有來源被取消訂閱。",
+ "sidebar.category_unsubscribe_dialog.title": "取消訂閱 {{folderName}}",
"sidebar.feed_actions.claim": "認領",
"sidebar.feed_actions.claim_feed": "認領RSS 摘要",
"sidebar.feed_actions.copy_email_address": "複製電子信箱",
@@ -390,8 +405,6 @@
"sidebar.feed_column.context_menu.add_feeds_to_list": "新增 RSS 摘要到列表",
"sidebar.feed_column.context_menu.change_to_other_view": "切換到其他視圖",
"sidebar.feed_column.context_menu.create_category": "新增分類",
- "sidebar.feed_column.context_menu.delete_category": "刪除分類",
- "sidebar.feed_column.context_menu.delete_category_confirmation": "刪除分類 {{folderName}}?",
"sidebar.feed_column.context_menu.mark_as_read": "標記為已讀",
"sidebar.feed_column.context_menu.new_category_modal.category_name": "分類名稱",
"sidebar.feed_column.context_menu.new_category_modal.create": "創建",
@@ -399,6 +412,9 @@
"sidebar.feed_column.context_menu.rename_category_error": "重命名分類失敗",
"sidebar.feed_column.context_menu.rename_category_success": "分類重新命名成功",
"sidebar.feed_column.context_menu.title": "移動到新分類",
+ "sidebar.feed_column.context_menu.ungroup_category": "取消分類分組",
+ "sidebar.feed_column.context_menu.ungroup_category_confirmation": "取消分類「{{folderName}}」的分組?",
+ "sidebar.feed_column.context_menu.unsubscribe_category": "取消分類內所有訂閱",
"sidebar.select_sort_method": "選擇排序方式",
"signin.continue_with": "透過 {{provider}} 登入",
"signin.sign_in_to": "登入",