diff --git a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/modal.tsx b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/modal.tsx
index d6fff5132..853802c3d 100644
--- a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/modal.tsx
+++ b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/modal.tsx
@@ -366,7 +366,7 @@ export const ModalInternal = memo(function Modal({
onPointerDownCapture={handleDrag}
onPointerDown={relocateModal}
>
- {!!icon && {icon}}
+ {!!icon && {icon}}
{title}
diff --git a/apps/desktop/layer/renderer/src/hooks/biz/useFeedActions.tsx b/apps/desktop/layer/renderer/src/hooks/biz/useFeedActions.tsx
index b8df350d3..86cc6e83d 100644
--- a/apps/desktop/layer/renderer/src/hooks/biz/useFeedActions.tsx
+++ b/apps/desktop/layer/renderer/src/hooks/biz/useFeedActions.tsx
@@ -1,4 +1,3 @@
-import { Button } from "@follow/components/ui/button/index.js"
import type { FeedViewType } from "@follow/constants"
import { IN_ELECTRON } from "@follow/shared/constants"
import { env } from "@follow/shared/env.desktop"
@@ -22,10 +21,9 @@ import { useCommandShortcuts } from "~/modules/command/hooks/use-command-binding
import { FeedForm } from "~/modules/discover/FeedForm"
import { InboxForm } from "~/modules/discover/InboxForm"
import { ListForm } from "~/modules/discover/ListForm"
-import {
- CategoryCreationModalContent,
- ListCreationModalContent,
-} from "~/modules/settings/tabs/lists/modals"
+import { useConfirmUnsubscribeSubscriptionModal } from "~/modules/modal/hooks/useConfirmUnsubscribeSubscriptionModal"
+import { useCategoryCreationModal } from "~/modules/settings/tabs/lists/hooks"
+import { ListCreationModalContent } from "~/modules/settings/tabs/lists/modals"
import { useResetFeed } from "~/queries/feed"
import { getFeedById, useFeedById } from "~/store/feed"
import { useInboxById } from "~/store/inbox"
@@ -41,24 +39,6 @@ import { useNavigateEntry } from "./useNavigateEntry"
import { getRouteParams } from "./useRouteParams"
import { useBatchUpdateSubscription, useDeleteSubscription } from "./useSubscriptionActions"
-const ConfirmDestroyModalContent = ({ onConfirm }: { onConfirm: () => void }) => {
- const { t } = useTranslation()
-
- return (
-
-
-
- {t("sidebar.feed_actions.unfollow_feed_many_warning")}
-
-
-
-
-
- )
-}
-
export const useFeedActions = ({
feedId,
feedIds,
@@ -89,6 +69,7 @@ export const useFeedActions = ({
useMemo(() => feedIds || [feedId], [feedId, feedIds]),
)
const { present } = useModalStack()
+ const presentDeleteSubscription = useConfirmUnsubscribeSubscriptionModal()
const deleteSubscription = useDeleteSubscription({})
const claimFeed = useFeedClaimModal()
@@ -99,6 +80,7 @@ export const useFeedActions = ({
const { mutateAsync: removeFeedFromListMutation } = useRemoveFeedFromFeedList()
const { mutateAsync: resetFeed } = useResetFeed()
const { mutate: addFeedsToCategoryMutation } = useBatchUpdateSubscription()
+ const presentCategoryCreationModal = useCategoryCreationModal()
const openBoostModal = useBoostModal()
const listByView = useOwnedListByView(view!)
@@ -239,20 +221,7 @@ export const useFeedActions = ({
label: t("sidebar.feed_column.context_menu.create_category"),
icon: ,
click() {
- present({
- title: t("sidebar.feed_column.context_menu.title"),
- content: () => (
- {
- addFeedsToCategoryMutation({
- feedIdList: isMultipleSelection ? feedIds : [feedId],
- category,
- view: view!,
- })
- }}
- />
- ),
- })
+ presentCategoryCreationModal(view!, isMultipleSelection ? feedIds : [feedId])
},
}),
],
@@ -281,17 +250,7 @@ export const useFeedActions = ({
supportMultipleSelection: true,
click: () => {
if (isMultipleSelection) {
- present({
- title: t("sidebar.feed_actions.unfollow_feed_many_confirm"),
- content: ({ dismiss }) => (
- {
- deleteSubscription.mutate({ feedIdList: feedIds })
- dismiss()
- }}
- />
- ),
- })
+ presentDeleteSubscription(feedIds)
return
}
deleteSubscription.mutate({ subscription })
@@ -367,30 +326,32 @@ export const useFeedActions = ({
item.supportMultipleSelection),
)
}, [
+ addFeedToListMutation,
+ addFeedsToCategoryMutation,
+ categories,
+ claimFeed,
+ deleteSubscription,
feed,
+ feedId,
+ feedIds,
inbox,
- t,
- shortcuts,
isEntryList,
isInMASReview,
isInbox,
- listByView,
- categories,
isMultipleSelection,
- feedId,
- feedIds,
- claimFeed,
- resetFeed,
- openBoostModal,
- addFeedToListMutation,
- removeFeedFromListMutation,
- present,
- subscriptions,
- subscription,
- addFeedsToCategoryMutation,
- view,
- deleteSubscription,
+ listByView,
navigateEntry,
+ openBoostModal,
+ present,
+ presentCategoryCreationModal,
+ presentDeleteSubscription,
+ removeFeedFromListMutation,
+ resetFeed,
+ shortcuts,
+ subscription,
+ subscriptions,
+ t,
+ view,
])
return items
diff --git a/apps/desktop/layer/renderer/src/modules/command/commands/global.tsx b/apps/desktop/layer/renderer/src/modules/command/commands/global.tsx
index 1e3c290f8..1d2092e6a 100644
--- a/apps/desktop/layer/renderer/src/modules/command/commands/global.tsx
+++ b/apps/desktop/layer/renderer/src/modules/command/commands/global.tsx
@@ -1,7 +1,7 @@
import { EventBus } from "@follow/utils/event-bus"
import { useTranslation } from "react-i18next"
-import { useShortcutsModal } from "~/modules/modal/shortcuts"
+import { useShortcutsModal } from "~/modules/modal/hooks/useShortcutsModal"
import { useRegisterCommandEffect } from "../hooks/use-register-command"
import type { Command, CommandCategory } from "../types"
diff --git a/apps/desktop/layer/renderer/src/modules/modal/ConfirmDestroyModalContent.tsx b/apps/desktop/layer/renderer/src/modules/modal/ConfirmDestroyModalContent.tsx
new file mode 100644
index 000000000..56ccb2da5
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/modal/ConfirmDestroyModalContent.tsx
@@ -0,0 +1,17 @@
+import { Button } from "@follow/components/ui/button/index.js"
+import { useTranslation } from "react-i18next"
+
+export const ConfirmDestroyModalContent = ({ onConfirm }: { onConfirm: () => void }) => {
+ const { t } = useTranslation()
+
+ return (
+
+
{t("sidebar.feed_actions.unfollow_feed_many_warning")}
+
+
+
+
+ )
+}
diff --git a/apps/desktop/layer/renderer/src/modules/modal/shortcuts.tsx b/apps/desktop/layer/renderer/src/modules/modal/ShortcutModalContent.tsx
similarity index 65%
rename from apps/desktop/layer/renderer/src/modules/modal/shortcuts.tsx
rename to apps/desktop/layer/renderer/src/modules/modal/ShortcutModalContent.tsx
index 5774aa6d0..dc23ba96b 100644
--- a/apps/desktop/layer/renderer/src/modules/modal/shortcuts.tsx
+++ b/apps/desktop/layer/renderer/src/modules/modal/ShortcutModalContent.tsx
@@ -2,16 +2,14 @@ import { MotionButtonBase } from "@follow/components/ui/button/index.js"
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
import { clsx } from "@follow/utils/utils"
import { m, useDragControls } from "motion/react"
-import { useCallback } from "react"
import { useTranslation } from "react-i18next"
import { useUISettingKey } from "~/atoms/settings/ui"
-import { PlainModal } from "~/components/ui/modal/stacked/custom-modal"
-import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
+import { useCurrentModal } from "~/components/ui/modal/stacked/hooks"
import { ShortcutsGuideline } from "../command/shortcuts/SettingShortcuts"
-const ShortcutModalContent = () => {
+export const ShortcutModalContent = () => {
const { dismiss } = useCurrentModal()
const modalOverlay = useUISettingKey("modalOverlay")
const dragControls = useDragControls()
@@ -54,28 +52,3 @@ const ShortcutModalContent = () => {
)
}
-
-export const useShortcutsModal = () => {
- const { present, dismiss, getModalStackById } = useModalStack()
- const id = "shortcuts"
-
- const showShortcutsModal = useCallback(() => {
- present({
- title: "Shortcuts",
- id,
- overlay: false,
- content: () => ,
- CustomModalComponent: PlainModal,
- clickOutsideToDismiss: true,
- })
- }, [present])
-
- return useCallback(() => {
- const shortcutsModal = getModalStackById(id)
- if (shortcutsModal && shortcutsModal.modal) {
- dismiss(id)
- return
- }
- showShortcutsModal()
- }, [dismiss, getModalStackById, showShortcutsModal])
-}
diff --git a/apps/desktop/layer/renderer/src/modules/modal/hooks/useConfirmUnsubscribeSubscriptionModal.tsx b/apps/desktop/layer/renderer/src/modules/modal/hooks/useConfirmUnsubscribeSubscriptionModal.tsx
new file mode 100644
index 000000000..845862d77
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/modal/hooks/useConfirmUnsubscribeSubscriptionModal.tsx
@@ -0,0 +1,30 @@
+import { useCallback } from "react"
+import { useTranslation } from "react-i18next"
+
+import { useModalStack } from "~/components/ui/modal/stacked/hooks"
+import { useDeleteSubscription } from "~/hooks/biz/useSubscriptionActions"
+
+import { ConfirmDestroyModalContent } from "../ConfirmDestroyModalContent"
+
+export const useConfirmUnsubscribeSubscriptionModal = () => {
+ const { present } = useModalStack()
+ const deleteSubscription = useDeleteSubscription({})
+ const { t } = useTranslation()
+ return useCallback(
+ (feedIds: string[]) => {
+ present({
+ title: t("sidebar.feed_actions.unfollow_feed_many_confirm"),
+ icon: ,
+ content: ({ dismiss }) => (
+ {
+ deleteSubscription.mutate({ feedIdList: feedIds })
+ dismiss()
+ }}
+ />
+ ),
+ })
+ },
+ [deleteSubscription, present, t],
+ )
+}
diff --git a/apps/desktop/layer/renderer/src/modules/modal/hooks/useShortcutsModal.tsx b/apps/desktop/layer/renderer/src/modules/modal/hooks/useShortcutsModal.tsx
new file mode 100644
index 000000000..c49e3c530
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/modal/hooks/useShortcutsModal.tsx
@@ -0,0 +1,31 @@
+import { useCallback } from "react"
+
+import { PlainModal } from "~/components/ui/modal/stacked/custom-modal"
+import { useModalStack } from "~/components/ui/modal/stacked/hooks"
+
+import { ShortcutModalContent } from "../ShortcutModalContent"
+
+export const useShortcutsModal = () => {
+ const { present, dismiss, getModalStackById } = useModalStack()
+ const id = "shortcuts"
+
+ const showShortcutsModal = useCallback(() => {
+ present({
+ title: "Shortcuts",
+ id,
+ overlay: false,
+ content: () => ,
+ CustomModalComponent: PlainModal,
+ clickOutsideToDismiss: true,
+ })
+ }, [present])
+
+ return useCallback(() => {
+ const shortcutsModal = getModalStackById(id)
+ if (shortcutsModal && shortcutsModal.modal) {
+ dismiss(id)
+ return
+ }
+ showShortcutsModal()
+ }, [dismiss, getModalStackById, showShortcutsModal])
+}
diff --git a/apps/desktop/layer/renderer/src/modules/settings/tabs/feeds.tsx b/apps/desktop/layer/renderer/src/modules/settings/tabs/feeds.tsx
index 392303a53..6b877bab4 100644
--- a/apps/desktop/layer/renderer/src/modules/settings/tabs/feeds.tsx
+++ b/apps/desktop/layer/renderer/src/modules/settings/tabs/feeds.tsx
@@ -1,3 +1,6 @@
+import { Spring } from "@follow/components/constants/spring.js"
+import { MotionButtonBase } from "@follow/components/ui/button/index.js"
+import { Checkbox } from "@follow/components/ui/checkbox/index.js"
import { Divider } from "@follow/components/ui/divider/index.js"
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
@@ -10,27 +13,296 @@ import {
TableRow,
} from "@follow/components/ui/table/index.jsx"
import { EllipsisHorizontalTextWithTooltip } from "@follow/components/ui/typography/index.js"
+import { views } from "@follow/constants"
+import clsx from "clsx"
+import { AnimatePresence, m } from "motion/react"
+import type { FC } from "react"
+import { memo, useCallback, useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
+import { useIsInMASReview } from "~/atoms/server-configs"
+import { RelativeDay } from "~/components/ui/datetime"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "~/components/ui/dropdown-menu/dropdown-menu"
+import { useDialog } from "~/components/ui/modal/stacked/hooks"
+import { useBatchUpdateSubscription } from "~/hooks/biz/useSubscriptionActions"
import { useAuthQuery } from "~/hooks/common"
import { UrlBuilder } from "~/lib/url-builder"
import { FeedIcon } from "~/modules/feed/feed-icon"
+import { useConfirmUnsubscribeSubscriptionModal } from "~/modules/modal/hooks/useConfirmUnsubscribeSubscriptionModal"
import { Balance } from "~/modules/wallet/balance"
import { Queries } from "~/queries"
+import { useFeedById } from "~/store/feed"
+import { useAllFeeds, useSubscriptionByFeedId } from "~/store/subscription"
export const SettingFeeds = () => {
+ const inMas = useIsInMASReview()
+ return (
+
+
+ {!inMas && }
+
+ )
+}
+
+const GRID_COLS_CLASSNAME = tw`grid-cols-[30px_auto_150px_100px]`
+
+const SubscriptionFeedsSection = () => {
+ const { t } = useTranslation("settings")
+ const feedList = useAllFeeds()
+ const [selectedFeeds, setSelectedFeeds] = useState>(() => new Set())
+
+ const handleSelectAll = useCallback(
+ (checked: boolean) => {
+ if (checked) {
+ setSelectedFeeds(new Set(feedList.map((feed) => feed.id)))
+ } else {
+ setSelectedFeeds(new Set())
+ }
+ },
+ [feedList],
+ )
+
+ const handleSelectFeed = useCallback((feedId: string, checked: boolean) => {
+ setSelectedFeeds((prev) => {
+ const newSet = new Set(prev)
+ if (checked) {
+ newSet.add(feedId)
+ } else {
+ newSet.delete(feedId)
+ }
+ return newSet
+ })
+ }, [])
+
+ const isAllSelected = feedList.length > 0 && selectedFeeds.size === feedList.length
+
+ const presentDeleteSubscription = useConfirmUnsubscribeSubscriptionModal()
+ const handleBatchUnsubscribe = useCallback(() => {
+ const feedIds = Array.from(selectedFeeds)
+ presentDeleteSubscription(feedIds)
+ }, [presentDeleteSubscription, selectedFeeds])
+
+ return (
+
+ {t("feeds.subscription")}
+
+ {feedList.length > 0 && (
+
+ {/* Header - Sticky */}
+
+
+
+
+
{t("feeds.tableHeaders.name")}
+
View
+
Date
+
+
+ {/* Feed List */}
+ {feedList.map((item) => {
+ return (
+
+ )
+ })}
+
+ {/* Sticky Action Bar at bottom when scrolled */}
+
+ {selectedFeeds.size > 0 && (
+ <>
+
+
+ {selectedFeeds.size} item(s) selected
+
+
+
+
+
+ {/*
+
+
+
+
+
+
+
+ */}
+
+
+
+ Move to View
+
+
+
+
+
+
+
+
+ Unsubscribe
+
+
+
+ >
+ )}
+
+
+ )}
+
+ )
+}
+
+const ViewSelector: FC<{ selectedFeeds: Set }> = ({ selectedFeeds }) => {
+ const { t: tCommon } = useTranslation("common")
+ const { mutate: batchUpdateSubscription } = useBatchUpdateSubscription()
+ const { ask } = useDialog()
+ return views.map((view) => {
+ return (
+ {
+ ask({
+ title: "Confirm",
+ message: `Are you sure you want to move these feeds to ${tCommon(view.name)}?`,
+ onConfirm: () => {
+ batchUpdateSubscription({
+ feedIdList: Array.from(selectedFeeds),
+ view: view.view,
+ })
+ },
+ })
+ }}
+ >
+ {tCommon(view.name)}
+
+ )
+ })
+}
+
+const FeedListItem = memo(
+ ({
+ id,
+ selected,
+ onSelect,
+ }: {
+ id: string
+ selected: boolean
+ onSelect: (feedId: string, checked: boolean) => void
+ }) => {
+ const subscription = useSubscriptionByFeedId(id)
+ const feed = useFeedById(id)
+ const isCustomizeName = subscription?.title && feed?.title !== subscription?.title
+ const { t: tCommon } = useTranslation("common")
+
+ if (!subscription) return null
+
+ return (
+
+ )
+ },
+)
+
+const FeedClaimedSection = () => {
const { t } = useTranslation("settings")
const claimedList = useAuthQuery(Queries.feed.claimedList())
+ const numberFormatter = useMemo(() => new Intl.NumberFormat("en-US", {}), [])
+
return (
+
+
{t("feeds.claim")}
+
-
- {claimedList.data?.length ? (
+ {claimedList.isLoading ? (
+
+ ) : !claimedList.data?.length ? (
+
+ ) : null}
+ {claimedList.data?.length ? (
+
@@ -61,7 +333,7 @@ export const SettingFeeds = () => {
- {row.subscriptionCount}
+ {numberFormatter.format(row.subscriptionCount)}
{BigInt(row.tipAmount || 0n)}
@@ -70,14 +342,8 @@ export const SettingFeeds = () => {
))}
- ) : claimedList.isLoading ? (
-
- ) : (
-
- )}
-
+
+ ) : null}
)
diff --git a/apps/desktop/layer/renderer/src/modules/settings/tabs/lists/hooks.tsx b/apps/desktop/layer/renderer/src/modules/settings/tabs/lists/hooks.tsx
new file mode 100644
index 000000000..b4dfcb012
--- /dev/null
+++ b/apps/desktop/layer/renderer/src/modules/settings/tabs/lists/hooks.tsx
@@ -0,0 +1,32 @@
+import type { FeedViewType } from "@follow/constants"
+import { useCallback } from "react"
+import { useTranslation } from "react-i18next"
+
+import { useModalStack } from "~/components/ui/modal/stacked/hooks"
+import { useBatchUpdateSubscription } from "~/hooks/biz/useSubscriptionActions"
+
+import { CategoryCreationModalContent } from "./modals"
+
+export const useCategoryCreationModal = () => {
+ const { t } = useTranslation()
+ const { present } = useModalStack()
+ const { mutate: addFeedsToCategoryMutation } = useBatchUpdateSubscription()
+ return useCallback(
+ (view: FeedViewType, feedIds: string[]) =>
+ present({
+ title: t("sidebar.feed_column.context_menu.title"),
+ content: () => (
+ {
+ addFeedsToCategoryMutation({
+ feedIdList: feedIds,
+ category,
+ view: view!,
+ })
+ }}
+ />
+ ),
+ }),
+ [addFeedsToCategoryMutation, present, t],
+ )
+}
diff --git a/apps/desktop/layer/renderer/src/pages/settings/(settings)/feeds.tsx b/apps/desktop/layer/renderer/src/pages/settings/(settings)/feeds.tsx
index 229c34a70..6467cb1c7 100644
--- a/apps/desktop/layer/renderer/src/pages/settings/(settings)/feeds.tsx
+++ b/apps/desktop/layer/renderer/src/pages/settings/(settings)/feeds.tsx
@@ -9,7 +9,6 @@ export const loader = defineSettingPageData({
icon: iconName,
name: "titles.feeds",
priority,
- hideIf: (ctx) => ctx.isInMASReview,
})
export function Component() {
diff --git a/apps/desktop/layer/renderer/src/store/subscription/hooks.ts b/apps/desktop/layer/renderer/src/store/subscription/hooks.ts
index 9db7d079c..634c12706 100644
--- a/apps/desktop/layer/renderer/src/store/subscription/hooks.ts
+++ b/apps/desktop/layer/renderer/src/store/subscription/hooks.ts
@@ -1,10 +1,11 @@
-import { FeedViewType, viewList } from "@follow/constants"
+import type { FeedViewType } from "@follow/constants"
import { useCallback, useMemo } from "react"
import { useGeneralSettingSelector } from "~/atoms/settings/general"
import { useFeedStore } from "../feed"
import {
+ categoriesSelector,
categoryOpenStateByViewSelector,
feedIdByViewSelector,
feedSubscriptionCountSelector,
@@ -15,7 +16,10 @@ import {
subscriptionByFeedIdSelector,
subscriptionByViewSelector,
subscriptionCategoryExistSelector,
+ subscriptionInboxSelector,
+ subscriptionListSelector,
subscriptionsByFeedIsdSelector,
+ viewWithSubscriptionSelector,
} from "./selector"
import { useSubscriptionStore } from "./store"
@@ -34,26 +38,9 @@ export const useSubscriptionByView = (view: FeedViewType) =>
useCallback((state) => subscriptionByViewSelector(view)(state) || [], [view]),
)
-export const useViewWithSubscription = () =>
- useSubscriptionStore(
- useCallback((state) => {
- return viewList.filter((view) => {
- if (
- view === FeedViewType.Articles ||
- view === FeedViewType.SocialMedia ||
- view === FeedViewType.Pictures ||
- view === FeedViewType.Videos
- ) {
- return true
- } else {
- return state.feedIdByView[view].length > 0
- }
- })
- }, []),
- )
+export const useViewWithSubscription = () => useSubscriptionStore(viewWithSubscriptionSelector)
-export const useCategories = () =>
- useSubscriptionStore(useCallback((state) => state.categories || [], []))
+export const useCategories = () => useSubscriptionStore(categoriesSelector)
export const useCategoriesByView = (view: FeedViewType) =>
useSubscriptionStore(
@@ -129,21 +116,11 @@ export const useAllFeeds = () => {
}
export const useAllLists = () => {
- return useSubscriptionStore(
- useCallback(
- (store) => Object.values(store.data).filter((subscription) => subscription.listId),
- [],
- ),
- )
+ return useSubscriptionStore(subscriptionListSelector)
}
export const useAllInboxes = () => {
- return useSubscriptionStore(
- useCallback(
- (store) => Object.values(store.data).filter((subscription) => subscription.inboxId),
- [],
- ),
- )
+ return useSubscriptionStore(subscriptionInboxSelector)
}
export const useFeedsGroupedData = (view: FeedViewType) => {
diff --git a/apps/desktop/layer/renderer/src/store/subscription/selector.ts b/apps/desktop/layer/renderer/src/store/subscription/selector.ts
index 2d3243203..1bfbc7ee7 100644
--- a/apps/desktop/layer/renderer/src/store/subscription/selector.ts
+++ b/apps/desktop/layer/renderer/src/store/subscription/selector.ts
@@ -1,4 +1,4 @@
-import type { FeedViewType } from "@follow/constants"
+import { FeedViewType, viewList } from "@follow/constants"
import { FEED_COLLECTION_LIST, ROUTE_FEED_IN_FOLDER } from "~/constants"
@@ -56,3 +56,26 @@ export const listSubscriptionCountSelector = (state: State) => state.listIds.siz
export const inboxSubscriptionCountSelector = (state: State) => state.inboxIds.size
export const isSubscribedSelector = (feedId: string) => (state: State) => !!state.data[feedId]
+
+export const subscriptionListSelector = (state: State) =>
+ Object.values(state.data).filter((subscription) => subscription.listId)
+
+export const subscriptionInboxSelector = (store: State) =>
+ Object.values(store.data).filter((subscription) => subscription.inboxId)
+
+export const viewWithSubscriptionSelector = (state: State) => {
+ return viewList.filter((view) => {
+ if (
+ view === FeedViewType.Articles ||
+ view === FeedViewType.SocialMedia ||
+ view === FeedViewType.Pictures ||
+ view === FeedViewType.Videos
+ ) {
+ return true
+ } else {
+ return state.feedIdByView[view].length > 0
+ }
+ })
+}
+
+export const categoriesSelector = (state: State) => state.categories || []
diff --git a/locales/app/en.json b/locales/app/en.json
index ed73585ec..ff90e93d1 100644
--- a/locales/app/en.json
+++ b/locales/app/en.json
@@ -356,7 +356,7 @@
"sidebar.feed_actions.resetting_feed": "Resetting feed...",
"sidebar.feed_actions.unfollow": "Unfollow",
"sidebar.feed_actions.unfollow_feed": "Unfollow Feed",
- "sidebar.feed_actions.unfollow_feed_many": "Unfollow all selected feeds",
+ "sidebar.feed_actions.unfollow_feed_many": "Unfollow All Selected Feeds",
"sidebar.feed_actions.unfollow_feed_many_confirm": "Confirm unfollow all selected feeds?",
"sidebar.feed_actions.unfollow_feed_many_warning": "Warning: This operation will unfollow all selected feeds, and cannot be undone.",
"sidebar.feed_column.context_menu.add_feeds_to_category": "Move to Category",
diff --git a/locales/settings/en.json b/locales/settings/en.json
index c2ec5de01..39267e538 100644
--- a/locales/settings/en.json
+++ b/locales/settings/en.json
@@ -145,8 +145,10 @@
"discoverFilters.filters": "Filters",
"discoverFilters.language": "Language",
"discoverFilters.title": "Discover Filters",
+ "feeds.claim": "Claim Feeds",
"feeds.claimTips": "To claim your feeds and receive tips, right-click on the feed in your subscription list and select Claim.",
"feeds.noFeeds": "No claimed feeds",
+ "feeds.subscription": "Subscribed Feeds",
"feeds.tableHeaders.name": "Name",
"feeds.tableHeaders.subscriptionCount": "Subs",
"feeds.tableHeaders.tipAmount": "Tips",