From a5dfea871cd66298c1bcb4b14599a0c06892565d Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 12 Sep 2025 01:23:28 +0800 Subject: [PATCH] refactor: simplify entry actions handling and improve context menu integration - Removed the 'compact' parameter from several components to streamline the entry actions logic. - Updated the CommandActionButton usage in ActionBar for better context menu interaction. - Enhanced the ActionBar component to handle mouse position for context menu display. - Cleaned up imports and adjusted related components to reflect the changes in props and structure. These modifications aim to improve code clarity and maintainability while ensuring consistent functionality across entry actions. Signed-off-by: Innei --- .../ui/button/CommandActionButton.tsx | 4 +- .../src/hooks/biz/useEntryActions.tsx | 21 +---- .../entry-column/layouts/EntryItemWrapper.tsx | 91 +++++++++++++++---- .../entry-content/actions/header-actions.tsx | 13 +-- .../entry-content/actions/more-actions.tsx | 9 +- .../internal/EntryHeaderActionsContainer.tsx | 4 +- .../entry-header/internal/context.tsx | 1 - 7 files changed, 83 insertions(+), 60 deletions(-) diff --git a/apps/desktop/layer/renderer/src/components/ui/button/CommandActionButton.tsx b/apps/desktop/layer/renderer/src/components/ui/button/CommandActionButton.tsx index 975b6143d..f4440eabf 100644 --- a/apps/desktop/layer/renderer/src/components/ui/button/CommandActionButton.tsx +++ b/apps/desktop/layer/renderer/src/components/ui/button/CommandActionButton.tsx @@ -20,10 +20,10 @@ export const CommandActionButton = ({ return ( ) } diff --git a/apps/desktop/layer/renderer/src/hooks/biz/useEntryActions.tsx b/apps/desktop/layer/renderer/src/hooks/biz/useEntryActions.tsx index 7f7b418cf..adbf2b72c 100644 --- a/apps/desktop/layer/renderer/src/hooks/biz/useEntryActions.tsx +++ b/apps/desktop/layer/renderer/src/hooks/biz/useEntryActions.tsx @@ -214,19 +214,8 @@ export const HIDE_ACTIONS_IN_ENTRY_CONTEXT_MENU = [ COMMAND_ID.settings.customizeToolbar, COMMAND_ID.entry.readability, COMMAND_ID.entry.exportAsPDF, - // Copy - COMMAND_ID.entry.copyTitle, - COMMAND_ID.entry.copyLink, ] -export const useEntryActions = ({ - entryId, - view, - compact, -}: { - entryId: string - view: FeedViewType - compact?: boolean -}) => { +export const useEntryActions = ({ entryId, view }: { entryId: string; view: FeedViewType }) => { const entry = useEntry(entryId, entrySelector) const { isCollection, entryId: routeEntryId } = useRouteParams() const isInCollection = useIsEntryStarred(entryId) @@ -428,7 +417,7 @@ export const useEntryActions = ({ new EntryActionMenuItem({ id: COMMAND_ID.entry.tts, onClick: runCmdFn(COMMAND_ID.entry.tts, [{ entryId }]), - hide: !IN_ELECTRON || compact || !entry.hasContent, + hide: !IN_ELECTRON || !entry.hasContent, shortcut: shortcuts[COMMAND_ID.entry.tts], entryId, }), @@ -437,7 +426,6 @@ export const useEntryActions = ({ onClick: runCmdFn(COMMAND_ID.entry.readability, [{ entryId, entryUrl: entry.url! }]), hide: !!entry.readability || - compact || (view && views.find((v) => v.view === view)?.wideMode) || !entry.url, active: isEntryInReadability, @@ -508,7 +496,6 @@ export const useEntryActions = ({ isShowAITranslationAuto, isShowAITranslationOnce, isCollection, - compact, isEntryInReadability, integrationSettings.customIntegration, integrationSettings.enableCustomIntegration, @@ -520,13 +507,11 @@ export const useEntryActions = ({ export const useSortedEntryActions = ({ entryId, view, - compact, }: { entryId: string view: FeedViewType - compact?: boolean }) => { - const entryActions = useEntryActions({ entryId, view, compact }) + const entryActions = useEntryActions({ entryId, view }) const orderMap = useToolbarOrderMap() const mainAction = useMemo( () => diff --git a/apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx b/apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx index ad6d7bf92..dae371d93 100644 --- a/apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx +++ b/apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx @@ -1,11 +1,12 @@ import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js" -import { Spring } from "@follow/components/constants/spring.js" import { useMobile } from "@follow/components/hooks/useMobile.js" +import { getMousePosition } from "@follow/components/hooks/useMouse.js" +import { ActionButton } from "@follow/components/ui/button/action-button.js" import { FeedViewType, views } from "@follow/constants" import { useEntry } from "@follow/store/entry/hooks" import { unreadSyncService } from "@follow/store/unread/store" import { cn } from "@follow/utils/utils" -import { AnimatePresence, m } from "motion/react" +import { AnimatePresence } from "motion/react" import type { FC, MouseEvent, PropsWithChildren, TouchEvent } from "react" import { useCallback, useEffect, useMemo, useState } from "react" import { useTranslation } from "react-i18next" @@ -20,12 +21,13 @@ import { } from "~/atoms/context-menu" import { useGeneralSettingKey } from "~/atoms/settings/general" import { FocusablePresets } from "~/components/common/Focusable" +import { CommandActionButton } from "~/components/ui/button/CommandActionButton" import { useEntryIsRead } from "~/hooks/biz/useAsRead" import { useContextMenuActionShortCutTrigger } from "~/hooks/biz/useContextMenuActionShortCutTrigger" import { + EntryActionMenuItem, HIDE_ACTIONS_IN_ENTRY_CONTEXT_MENU, useEntryActions, - useSortedEntryActions, } from "~/hooks/biz/useEntryActions" import { useFeature } from "~/hooks/biz/useFeature" import { useFeedActions } from "~/hooks/biz/useFeedActions" @@ -34,8 +36,7 @@ import { getRouteParams, useRouteParams, useRouteParamsSelector } from "~/hooks/ import { useContextMenu } from "~/hooks/common/useContextMenu" import { copyToClipboard } from "~/lib/clipboard" import { COMMAND_ID } from "~/modules/command/commands/id" -import { EntryHeaderActions } from "~/modules/entry-content/actions/header-actions" -import { MoreActions } from "~/modules/entry-content/actions/more-actions" +import type { FollowCommandId } from "~/modules/command/types" export const EntryItemWrapper: FC< { @@ -231,30 +232,58 @@ export const EntryItemWrapper: FC< {...(!isMobile ? { onTouchStart: handleClick } : {})} > {children} - {showAction && isWide && } + + {showAction && isWide && ( + { + const { x, y } = getMousePosition() + const mouseEvent = new MouseEvent("contextmenu", { + bubbles: true, + cancelable: true, + clientX: x, + clientY: y, + }) + contextMenuProps.onContextMenu?.(mouseEvent as unknown as MouseEvent) + }} + entryId={entryId} + /> + )} + ) } -const ActionBar = ({ entryId }: { entryId: string }) => { - const { mainAction: entryActions } = useSortedEntryActions({ - entryId, - view: FeedViewType.SocialMedia, - }) +const SHOW_ACTION_BAR_ACTIONS_ORDER = [ + // Copy + COMMAND_ID.entry.copyTitle, + COMMAND_ID.entry.copyLink, + COMMAND_ID.entry.star, + COMMAND_ID.entry.read, + COMMAND_ID.entry.openInBrowser, + COMMAND_ID.entry.tip, + COMMAND_ID.entry.share, +] as FollowCommandId[] +const SHOW_ACTION_BAR_ACTIONS = new Set(SHOW_ACTION_BAR_ACTIONS_ORDER) + +const ActionBar = ({ + entryId, + openContextMenu, +}: { + entryId: string + openContextMenu: () => void +}) => { const { view } = useRouteParams() + const entryActions = useEntryActions({ entryId, view }) + if (entryActions.length === 0) return null return ( - { e.stopPropagation() @@ -262,9 +291,31 @@ const ActionBar = ({ entryId }: { entryId: string }) => { }} >
- - + {( + entryActions.filter( + (item) => item instanceof EntryActionMenuItem && SHOW_ACTION_BAR_ACTIONS.has(item.id), + ) as EntryActionMenuItem[] + ) + .sort( + (a, b) => + SHOW_ACTION_BAR_ACTIONS_ORDER.indexOf(a.id) - + SHOW_ACTION_BAR_ACTIONS_ORDER.indexOf(b.id), + ) + .map((item) => ( + + ))} + + } + />
-
+ ) } diff --git a/apps/desktop/layer/renderer/src/modules/entry-content/actions/header-actions.tsx b/apps/desktop/layer/renderer/src/modules/entry-content/actions/header-actions.tsx index e96b0f827..5d7616801 100644 --- a/apps/desktop/layer/renderer/src/modules/entry-content/actions/header-actions.tsx +++ b/apps/desktop/layer/renderer/src/modules/entry-content/actions/header-actions.tsx @@ -14,16 +14,8 @@ import { EntryActionDropdownItem, useSortedEntryActions } from "~/hooks/biz/useE import { useCommand } from "~/modules/command/hooks/use-command" import type { FollowCommandId } from "~/modules/command/types" -export const EntryHeaderActions = ({ - entryId, - view, - compact, -}: { - entryId: string - view: FeedViewType - compact?: boolean -}) => { - const { mainAction: actionConfigs } = useSortedEntryActions({ entryId, view, compact }) +export const EntryHeaderActions = ({ entryId, view }: { entryId: string; view: FeedViewType }) => { + const { mainAction: actionConfigs } = useSortedEntryActions({ entryId, view }) return actionConfigs .filter((item) => item instanceof MenuItemText || item instanceof EntryActionDropdownItem) @@ -40,7 +32,6 @@ export const EntryHeaderActions = ({ clickableDisabled={config.disabled} highlightMotion={config.notice} id={`${config.entryId}/${config.id}`} - size={compact ? "xs" : "base"} /> ) diff --git a/apps/desktop/layer/renderer/src/modules/entry-content/actions/more-actions.tsx b/apps/desktop/layer/renderer/src/modules/entry-content/actions/more-actions.tsx index 99acbef40..d7938243e 100644 --- a/apps/desktop/layer/renderer/src/modules/entry-content/actions/more-actions.tsx +++ b/apps/desktop/layer/renderer/src/modules/entry-content/actions/more-actions.tsx @@ -26,12 +26,12 @@ import type { FollowCommandId } from "~/modules/command/types" export const MoreActions = ({ entryId, view, - compact, + hideCustomizeToolbar = false, }: { entryId: string view: FeedViewType - compact?: boolean + hideCustomizeToolbar?: boolean }) => { const { moreAction } = useSortedEntryActions({ entryId, view }) @@ -78,10 +78,7 @@ export const MoreActions = ({ return ( - } - size={compact ? "xs" : "base"} - /> + } /> diff --git a/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderActionsContainer.tsx b/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderActionsContainer.tsx index d27b9053b..03f1e26c5 100644 --- a/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderActionsContainer.tsx +++ b/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderActionsContainer.tsx @@ -7,12 +7,12 @@ import { MoreActions } from "../../../actions/more-actions" import { useEntryHeaderContext } from "./context" function EntryHeaderActionsContainerImpl() { - const { entryId, compact } = useEntryHeaderContext() + const { entryId } = useEntryHeaderContext() const { view } = useRouteParams() return (
- +
) diff --git a/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/context.tsx b/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/context.tsx index 2066a3e19..b96bf0abe 100644 --- a/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/context.tsx +++ b/apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/context.tsx @@ -11,7 +11,6 @@ import type { EntryHeaderProps } from "../types" interface EntryHeaderContextValue { entryId: string - compact?: boolean } const EntryHeaderContext = createContext(null)