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 <tukon479@gmail.com>
This commit is contained in:
parent
418d99aae9
commit
a5dfea871c
|
|
@ -20,10 +20,10 @@ export const CommandActionButton = ({
|
|||
return (
|
||||
<ActionButton
|
||||
ref={ref}
|
||||
icon={icon}
|
||||
{...rest}
|
||||
tooltip={label.title}
|
||||
tooltipDescription={label.description}
|
||||
{...rest}
|
||||
icon={icon}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
() =>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
<AnimatePresence>{showAction && isWide && <ActionBar entryId={entryId} />}</AnimatePresence>
|
||||
<AnimatePresence>
|
||||
{showAction && isWide && (
|
||||
<ActionBar
|
||||
openContextMenu={() => {
|
||||
const { x, y } = getMousePosition()
|
||||
const mouseEvent = new MouseEvent("contextmenu", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
})
|
||||
contextMenuProps.onContextMenu?.(mouseEvent as unknown as MouseEvent<HTMLElement>)
|
||||
}}
|
||||
entryId={entryId}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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<FollowCommandId>(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 (
|
||||
<m.div
|
||||
initial={{ opacity: 0, scale: 0.9, translateY: "-1/2" }}
|
||||
animate={{ opacity: 1, scale: 1, translateY: "-1/2" }}
|
||||
exit={{ opacity: 0, scale: 0.9, translateY: "-1/2" }}
|
||||
transition={Spring.presets.smooth}
|
||||
<div
|
||||
className={cn(
|
||||
"absolute -right-2 top-0 -translate-y-1/2 rounded-lg border border-gray-200 bg-white/90 p-1 shadow-sm backdrop-blur-sm dark:border-neutral-900 dark:bg-neutral-900",
|
||||
view === FeedViewType.All && "top-1/2",
|
||||
view === FeedViewType.All && "right-1 top-1/2",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
|
|
@ -262,9 +291,31 @@ const ActionBar = ({ entryId }: { entryId: string }) => {
|
|||
}}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<EntryHeaderActions entryId={entryId} view={view} compact />
|
||||
<MoreActions entryId={entryId} view={view} compact hideCustomizeToolbar />
|
||||
{(
|
||||
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) => (
|
||||
<CommandActionButton
|
||||
key={item.id}
|
||||
onClick={item.onClick}
|
||||
size="xs"
|
||||
commandId={item.id}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ActionButton
|
||||
onClick={openContextMenu}
|
||||
size="xs"
|
||||
icon={<i className="i-mingcute-more-1-fill" />}
|
||||
/>
|
||||
</div>
|
||||
</m.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
/>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<ActionButton
|
||||
icon={<i className="i-mingcute-more-1-fill" />}
|
||||
size={compact ? "xs" : "base"}
|
||||
/>
|
||||
<ActionButton icon={<i className="i-mingcute-more-1-fill" />} />
|
||||
</DropdownMenuTrigger>
|
||||
<RootPortal>
|
||||
<DropdownMenuContent>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="relative flex shrink-0 items-center justify-end gap-2">
|
||||
<EntryHeaderActions entryId={entryId} view={view} compact={compact} />
|
||||
<EntryHeaderActions entryId={entryId} view={view} />
|
||||
<MoreActions entryId={entryId} view={view} />
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import type { EntryHeaderProps } from "../types"
|
|||
|
||||
interface EntryHeaderContextValue {
|
||||
entryId: string
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
const EntryHeaderContext = createContext<EntryHeaderContextValue | null>(null)
|
||||
|
|
|
|||
Loading…
Reference in New Issue