diff --git a/src/renderer/src/components/ui/media.tsx b/src/renderer/src/components/ui/media.tsx index bb0aa22b0..864ab6005 100644 --- a/src/renderer/src/components/ui/media.tsx +++ b/src/renderer/src/components/ui/media.tsx @@ -104,6 +104,7 @@ const MediaImpl: FC = ({ { onContextMenu: (e) => { e.stopPropagation() + e.preventDefault() props.onContextMenu?.(e) showNativeMenu( [ diff --git a/src/renderer/src/modules/entry-column/EntryColumnShortcutHandler.tsx b/src/renderer/src/modules/entry-column/EntryColumnShortcutHandler.tsx new file mode 100644 index 000000000..96e1cbf27 --- /dev/null +++ b/src/renderer/src/modules/entry-column/EntryColumnShortcutHandler.tsx @@ -0,0 +1,98 @@ +import { useMainContainerElement } from "@renderer/atoms/dom" +import { shortcuts } from "@renderer/constants/shortcuts" +import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry" +import { useRouteEntryId } from "@renderer/hooks/biz/useRouteParams" +import { useRefValue } from "@renderer/hooks/common" +import type { FC } from "react" +import { memo, useLayoutEffect, useState } from "react" +import { useHotkeys } from "react-hotkeys-hook" +import type { VirtuosoHandle } from "react-virtuoso" + +export const EntryColumnShortcutHandler: FC<{ + refetch: () => void + data: readonly string[] + virtuosoRef: React.RefObject +}> = memo(({ data, refetch, virtuosoRef }) => { + const dataRef = useRefValue(data!) + + useHotkeys( + shortcuts.entries.refetch.key, + () => { + refetch() + }, + { scopes: ["home"] }, + ) + const currentEntryIdRef = useRefValue(useRouteEntryId()) + + const navigate = useNavigateEntry() + + const $mainContainer = useMainContainerElement() + const [enabledArrowKey, setEnabledArrowKey] = useState(false) + + // Enable arrow key navigation shortcuts only when focus is on entryContent or entryList, + // entryList shortcuts should not be triggered in the feed col + useLayoutEffect(() => { + if (!$mainContainer) return + const handler = () => { + const target = document.activeElement + const isFocusIn = + $mainContainer.contains(target) || $mainContainer === target + + setEnabledArrowKey(isFocusIn) + } + + handler() + // NOTE: focusin event will bubble to the document + document.addEventListener("focusin", handler) + return () => { + document.removeEventListener("focusin", handler) + } + }, [$mainContainer]) + + useHotkeys( + shortcuts.entries.next.key, + () => { + const data = dataRef.current + const currentActiveEntryIndex = data.indexOf( + currentEntryIdRef.current || "", + ) + + const nextIndex = Math.min(currentActiveEntryIndex + 1, data.length - 1) + + virtuosoRef.current?.scrollIntoView?.({ + index: nextIndex, + }) + const nextId = data![nextIndex] + + navigate({ + entryId: nextId, + }) + }, + { scopes: ["home"], enabled: enabledArrowKey }, + ) + useHotkeys( + shortcuts.entries.previous.key, + () => { + const data = dataRef.current + const currentActiveEntryIndex = data.indexOf( + currentEntryIdRef.current || "", + ) + + const nextIndex = + currentActiveEntryIndex === -1 ? + data.length - 1 : + Math.max(0, currentActiveEntryIndex - 1) + + virtuosoRef.current?.scrollIntoView?.({ + index: nextIndex, + }) + const nextId = data![nextIndex] + + navigate({ + entryId: nextId, + }) + }, + { scopes: ["home"], enabled: enabledArrowKey }, + ) + return null +}) diff --git a/src/renderer/src/modules/entry-column/hooks.ts b/src/renderer/src/modules/entry-column/hooks.ts index c73fd21f2..0ef6ac26a 100644 --- a/src/renderer/src/modules/entry-column/hooks.ts +++ b/src/renderer/src/modules/entry-column/hooks.ts @@ -1,6 +1,5 @@ import { useGeneralSettingKey } from "@renderer/atoms/settings/general" import { views } from "@renderer/constants" -import { shortcuts } from "@renderer/constants/shortcuts" import { useRouteParamsSelector, useRouteParms, @@ -10,7 +9,6 @@ import { entries, useEntries } from "@renderer/queries/entries" import { entryActions, useEntryIdsByFeedIdOrView } from "@renderer/store/entry" import { useFolderFeedsByFeedId } from "@renderer/store/subscription" import { useCallback, useEffect, useMemo, useRef, useState } from "react" -import { useHotkeys } from "react-hotkeys-hook" import type { ListRange } from "react-virtuoso" import { useDebounceCallback } from "usehooks-ts" @@ -125,14 +123,6 @@ export const useEntriesByView = ({ onReset }: { onReset?: () => void }) => { const entryIds = remoteEntryIds || currentEntries - useHotkeys( - shortcuts.entries.refetch.key, - () => { - query.refetch() - }, - { scopes: ["home"] }, - ) - // in unread only entries only can grow the data, but not shrink // so we memo this previous data to avoid the flicker const prevEntryIdsRef = useRef(entryIds) diff --git a/src/renderer/src/modules/entry-column/index.tsx b/src/renderer/src/modules/entry-column/index.tsx index 412ae8afc..ec6330d93 100644 --- a/src/renderer/src/modules/entry-column/index.tsx +++ b/src/renderer/src/modules/entry-column/index.tsx @@ -1,4 +1,3 @@ -import { useMainContainerElement } from "@renderer/atoms/dom" import { setGeneralSetting, useGeneralSettingKey, @@ -27,10 +26,8 @@ import { import { shortcuts } from "@renderer/constants/shortcuts" import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry" import { - useRouteEntryId, useRouteParms, } from "@renderer/hooks/biz/useRouteParams" -import { useRefValue } from "@renderer/hooks/common" import { useIsOnline } from "@renderer/hooks/common/useIsOnline" import { apiClient } from "@renderer/lib/api-fetch" import { cn, getEntriesParams, getOS, isBizId } from "@renderer/lib/utils" @@ -48,11 +45,9 @@ import { forwardRef, useCallback, useEffect, - useLayoutEffect, useRef, useState, } from "react" -import { useHotkeys } from "react-hotkeys-hook" import type { ScrollSeekConfiguration, VirtuosoHandle, @@ -60,6 +55,7 @@ import type { } from "react-virtuoso" import { Virtuoso, VirtuosoGrid } from "react-virtuoso" +import { EntryColumnShortcutHandler } from "./EntryColumnShortcutHandler" import { useEntriesByView, useEntryMarkReadHandler } from "./hooks" import { EntryItem, @@ -204,7 +200,11 @@ export function EntryColumn() { /> ) : ( - + )} @@ -430,81 +430,10 @@ const EmptyList = forwardRef>( const EntryList: FC< VirtuosoProps & { virtuosoRef: React.RefObject + + refetch: () => void } -> = ({ virtuosoRef, ...virtuosoOptions }) => { - const dataRef = useRefValue(virtuosoOptions.data!) - const currentEntryIdRef = useRefValue(useRouteEntryId()) - - const navigate = useNavigateEntry() - - const $mainContainer = useMainContainerElement() - const [enabledArrowKey, setEnabledArrowKey] = useState(false) - - // Enable arrow key navigation shortcuts only when focus is on entryContent or entryList, - // entryList shortcuts should not be triggered in the feed col - useLayoutEffect(() => { - if (!$mainContainer) return - const handler = () => { - const target = document.activeElement - const isFocusIn = - $mainContainer.contains(target) || $mainContainer === target - - setEnabledArrowKey(isFocusIn) - } - - handler() - // NOTE: focusin event will bubble to the document - document.addEventListener("focusin", handler) - return () => { - document.removeEventListener("focusin", handler) - } - }, [$mainContainer]) - - useHotkeys( - shortcuts.entries.next.key, - () => { - const data = dataRef.current - const currentActiveEntryIndex = data.indexOf( - currentEntryIdRef.current || "", - ) - - const nextIndex = Math.min(currentActiveEntryIndex + 1, data.length - 1) - - virtuosoRef.current?.scrollIntoView?.({ - index: nextIndex, - }) - const nextId = data![nextIndex] - - navigate({ - entryId: nextId, - }) - }, - { scopes: ["home"], enabled: enabledArrowKey }, - ) - useHotkeys( - shortcuts.entries.previous.key, - () => { - const data = dataRef.current - const currentActiveEntryIndex = data.indexOf( - currentEntryIdRef.current || "", - ) - - const nextIndex = - currentActiveEntryIndex === -1 ? - data.length - 1 : - Math.max(0, currentActiveEntryIndex - 1) - - virtuosoRef.current?.scrollIntoView?.({ - index: nextIndex, - }) - const nextId = data![nextIndex] - - navigate({ - entryId: nextId, - }) - }, - { scopes: ["home"], enabled: enabledArrowKey }, - ) +> = ({ virtuosoRef, refetch, ...virtuosoOptions }) => { // Prevent scroll list move when press up/down key, the up/down key should be taken over by the shortcut key we defined. const handleKeyDown: React.KeyboardEventHandler = useCallback( (e) => { @@ -515,10 +444,17 @@ const EntryList: FC< [], ) return ( - + <> + + + ) }