feat(subscription-column): add view tab context menu

This commit is contained in:
DIYgod 2025-10-24 17:31:21 +08:00
parent 75f5c725ac
commit 60dcd42f16
No known key found for this signature in database
6 changed files with 123 additions and 14 deletions

View File

@ -7,12 +7,16 @@ import type { FC } from "react"
import { startTransition, useCallback } from "react"
import { useTranslation } from "react-i18next"
import { useUISettingKey } from "~/atoms/settings/ui"
import { MenuItemText, useShowContextMenu } from "~/atoms/context-menu"
import { setUISetting, useUISettingKey } from "~/atoms/settings/ui"
import { FocusablePresets } from "~/components/common/Focusable"
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
import { parseView, useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
import { useTimelineList } from "~/hooks/biz/useTimelineList"
import { useContextMenu } from "~/hooks/common/useContextMenu"
import { resetSelectedFeedIds } from "./atom"
import { useShowTimelineTabsSettingsModal } from "./TimelineTabsSettingsModal"
export function SubscriptionTabButton({
timelineId,
@ -25,35 +29,125 @@ export function SubscriptionTabButton({
const isActive = activeTimelineId === timelineId
const navigate = useNavigateEntry()
const navigateToTimeline = useCallback(
(nextTimelineId: string) => {
navigate({
timelineId: nextTimelineId,
feedId: null,
entryId: null,
})
resetSelectedFeedIds()
},
[navigate],
)
const setActive = useCallback(() => {
navigate({
timelineId,
feedId: null,
entryId: null,
})
resetSelectedFeedIds()
}, [navigate, timelineId])
navigateToTimeline(timelineId)
}, [navigateToTimeline, timelineId])
const view = parseView(timelineId)
if (view === FeedViewType.All) {
return <ViewAllSwitchButton isActive={isActive} setActive={setActive} shortcut={shortcut} />
return (
<ViewAllSwitchButton
timelineId={timelineId}
isActive={isActive}
setActive={setActive}
shortcut={shortcut}
navigateToTimeline={navigateToTimeline}
/>
)
} else if (typeof view === "number") {
return (
<ViewSwitchButton view={view} isActive={isActive} setActive={setActive} shortcut={shortcut} />
<ViewSwitchButton
view={view}
timelineId={timelineId}
isActive={isActive}
setActive={setActive}
shortcut={shortcut}
navigateToTimeline={navigateToTimeline}
/>
)
}
}
const useSubscriptionTabContextMenu = ({
timelineId,
isActive,
navigateToTimeline,
}: {
timelineId: string
isActive: boolean
navigateToTimeline: (timelineId: string) => void
}) => {
const { t } = useTranslation()
const showContextMenu = useShowContextMenu()
const showTimelineTabsSettingsModal = useShowTimelineTabsSettingsModal()
const visibleTimelineList = useTimelineList({ withAll: true, visible: true })
const hiddenTimelineList = useTimelineList({ withAll: true, hidden: true })
const canHide = visibleTimelineList.filter((id) => id !== timelineId).length > 0
const handleHide = useCallback(() => {
if (!canHide) return
const nextVisible = visibleTimelineList.filter((id) => id !== timelineId)
const nextHidden = hiddenTimelineList.filter((id) => id !== timelineId).concat(timelineId)
setUISetting("timelineTabs", {
visible: nextVisible,
hidden: nextHidden,
})
if (isActive) {
const currentIndex = visibleTimelineList.indexOf(timelineId)
const fallbackTimelineId =
nextVisible[currentIndex] ?? nextVisible[currentIndex - 1] ?? nextVisible[0]
if (fallbackTimelineId) {
navigateToTimeline(fallbackTimelineId)
}
}
}, [canHide, hiddenTimelineList, isActive, navigateToTimeline, timelineId, visibleTimelineList])
const contextMenuProps = useContextMenu({
onContextMenu: async (event) => {
event.preventDefault()
event.stopPropagation()
await showContextMenu(
[
new MenuItemText({
label: t("sidebar.timeline_tabs.hide_tab"),
click: handleHide,
disabled: !canHide,
}),
new MenuItemText({
label: t("sidebar.timeline_tabs.customize"),
click: showTimelineTabsSettingsModal,
}),
],
event,
)
},
})
return contextMenuProps
}
const ViewAllSwitchButton: FC<{
timelineId: string
isActive: boolean
setActive: () => void
shortcut: string
}> = ({ isActive, setActive, shortcut }) => {
navigateToTimeline: (timelineId: string) => void
}> = ({ timelineId, isActive, setActive, shortcut, navigateToTimeline }) => {
const unreadByView = useUnreadByView(FeedViewType.All)
const { t } = useTranslation()
const showSidebarUnreadCount = useUISettingKey("sidebarShowUnreadCount")
const item = getView(FeedViewType.All)
const contextMenuProps = useSubscriptionTabContextMenu({
timelineId,
isActive,
navigateToTimeline,
})
return (
<ActionButton
@ -66,6 +160,7 @@ const ViewAllSwitchButton: FC<{
"flex h-11 w-8 shrink-0 grow flex-col items-center gap-1 text-[1.375rem]",
ELECTRON ? "hover:!bg-theme-item-hover" : "",
)}
{...contextMenuProps}
onClick={(e) => {
startTransition(() => {
setActive()
@ -92,10 +187,12 @@ const ViewAllSwitchButton: FC<{
const ViewSwitchButton: FC<{
view: FeedViewType
timelineId: string
isActive: boolean
setActive: () => void
shortcut: string
}> = ({ view, isActive, setActive, shortcut }) => {
navigateToTimeline: (timelineId: string) => void
}> = ({ view, timelineId, isActive, setActive, shortcut, navigateToTimeline }) => {
const unreadByView = useUnreadByView(view)
const { t } = useTranslation()
const showSidebarUnreadCount = useUISettingKey("sidebarShowUnreadCount")
@ -107,6 +204,11 @@ const ViewSwitchButton: FC<{
view: item.view,
},
})
const contextMenuProps = useSubscriptionTabContextMenu({
timelineId,
isActive,
navigateToTimeline,
})
return (
<ActionButton
@ -121,6 +223,7 @@ const ViewSwitchButton: FC<{
ELECTRON ? "hover:!bg-theme-item-hover" : "",
isOver && "border-orange-400 bg-orange-400/60",
)}
{...contextMenuProps}
onClick={(e) => {
startTransition(() => {
setActive()

View File

@ -228,7 +228,7 @@ export const useShowTimelineTabsSettingsModal = () => {
return useCallback(() => {
present({
id: "timeline-tabs-settings",
title: "Customize Timeline Tabs",
title: "Customize View Tabs",
content: () => <TimelineTabsSettings />,
overlay: true,
clickOutsideToDismiss: true,

View File

@ -392,6 +392,8 @@
"sidebar.feed_column.context_menu.rename_category_success": "Category renamed successfully",
"sidebar.feed_column.context_menu.title": "Move to New Category",
"sidebar.select_sort_method": "Select a sort method",
"sidebar.timeline_tabs.customize": "Customize View Tabs...",
"sidebar.timeline_tabs.hide_tab": "Hide this view",
"signin.continue_with": "Continue with {{provider}}",
"signin.sign_in_to": "Sign in to",
"signin.sign_up_to": "Sign up to",

View File

@ -387,6 +387,8 @@
"sidebar.feed_column.context_menu.rename_category_success": "カテゴリーのリネームに成功しました",
"sidebar.feed_column.context_menu.title": "新規カテゴリーに移動する",
"sidebar.select_sort_method": "並べ替え方法を選択",
"sidebar.timeline_tabs.customize": "ビュータブをカスタマイズ...",
"sidebar.timeline_tabs.hide_tab": "このビューを非表示にする",
"signin.continue_with": "{{provider}} で続ける",
"signin.sign_in_to": "サインイン",
"signin.sign_up_to": "サインアップ",

View File

@ -390,6 +390,8 @@
"sidebar.feed_column.context_menu.rename_category_success": "分类重命名成功",
"sidebar.feed_column.context_menu.title": "移动至新分类",
"sidebar.select_sort_method": "选择排序方法",
"sidebar.timeline_tabs.customize": "自定义视图标签...",
"sidebar.timeline_tabs.hide_tab": "隐藏此视图",
"signin.continue_with": "使用 {{provider}} 登录",
"signin.sign_in_to": "登录",
"signin.sign_up_to": "注册",

View File

@ -129,7 +129,7 @@
"appearance.custom_font": "Custom Font",
"appearance.customization.title": "Customization",
"appearance.customize_sub_tabs.description": "Customize the subscription tabs to your liking.",
"appearance.customize_sub_tabs.label": "Customize Subscription Tabs",
"appearance.customize_sub_tabs.label": "Customize View Tabs",
"appearance.customize_toolbar.description": "Customize the entry content toolbar to your liking.",
"appearance.customize_toolbar.label": "Customize Toolbar",
"appearance.date_format.description": "Adjust the display date format.",