Skip to content

Commit

Permalink
fix: context menu prevent default and shortcut handler
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jul 30, 2024
1 parent 670a45e commit b6042af
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 95 deletions.
1 change: 1 addition & 0 deletions src/renderer/src/components/ui/media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const MediaImpl: FC<MediaProps> = ({
{
onContextMenu: (e) => {
e.stopPropagation()
e.preventDefault()
props.onContextMenu?.(e)
showNativeMenu(
[
Expand Down
Original file line number Diff line number Diff line change
@@ -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<VirtuosoHandle>
}> = 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
})
10 changes: 0 additions & 10 deletions src/renderer/src/modules/entry-column/hooks.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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"

Expand Down Expand Up @@ -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)
Expand Down
106 changes: 21 additions & 85 deletions src/renderer/src/modules/entry-column/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useMainContainerElement } from "@renderer/atoms/dom"
import {
setGeneralSetting,
useGeneralSettingKey,
Expand Down Expand Up @@ -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"
Expand All @@ -48,18 +45,17 @@ import {
forwardRef,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react"
import { useHotkeys } from "react-hotkeys-hook"
import type {
ScrollSeekConfiguration,
VirtuosoHandle,
VirtuosoProps,
} from "react-virtuoso"
import { Virtuoso, VirtuosoGrid } from "react-virtuoso"

import { EntryColumnShortcutHandler } from "./EntryColumnShortcutHandler"
import { useEntriesByView, useEntryMarkReadHandler } from "./hooks"
import {
EntryItem,
Expand Down Expand Up @@ -204,7 +200,11 @@ export function EntryColumn() {
/>
) :
(
<EntryList {...virtuosoOptions} virtuosoRef={virtuosoRef} />
<EntryList
{...virtuosoOptions}
virtuosoRef={virtuosoRef}
refetch={entries.refetch}
/>
)}
</ScrollArea.ScrollArea>
</m.div>
Expand Down Expand Up @@ -430,81 +430,10 @@ const EmptyList = forwardRef<HTMLDivElement, HTMLMotionProps<"div">>(
const EntryList: FC<
VirtuosoProps<string, unknown> & {
virtuosoRef: React.RefObject<VirtuosoHandle>
}
> = ({ 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 },
)
refetch: () => void
}
> = ({ 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<HTMLDivElement> = useCallback(
(e) => {
Expand All @@ -515,10 +444,17 @@ const EntryList: FC<
[],
)
return (
<Virtuoso
onKeyDown={handleKeyDown}
{...virtuosoOptions}
ref={virtuosoRef}
/>
<>
<Virtuoso
onKeyDown={handleKeyDown}
{...virtuosoOptions}
ref={virtuosoRef}
/>
<EntryColumnShortcutHandler
refetch={refetch}
data={virtuosoOptions.data!}
virtuosoRef={virtuosoRef}
/>
</>
)
}

0 comments on commit b6042af

Please sign in to comment.