forked from Archives/Athou_commafeed
use a single call to useContextMenu as recommended in the docs
This commit is contained in:
@@ -95,6 +95,7 @@ export const Constants = {
|
|||||||
},
|
},
|
||||||
dom: {
|
dom: {
|
||||||
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
||||||
|
entryContextMenuId: (entry: Entry) => entry.id,
|
||||||
},
|
},
|
||||||
browserExtensionUrl: "https://github.com/Athou/commafeed-browser-extension",
|
browserExtensionUrl: "https://github.com/Athou/commafeed-browser-extension",
|
||||||
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
||||||
|
|||||||
@@ -30,17 +30,16 @@ export const calculatePlaceholderSize = ({ width, height, maxWidth }: { width?:
|
|||||||
export const scrollToWithCallback = ({ options, onScrollEnded }: { options: ScrollToOptions; onScrollEnded: () => void }) => {
|
export const scrollToWithCallback = ({ options, onScrollEnded }: { options: ScrollToOptions; onScrollEnded: () => void }) => {
|
||||||
const offset = (options.top ?? 0).toFixed()
|
const offset = (options.top ?? 0).toFixed()
|
||||||
|
|
||||||
const onScroll = () => {
|
const onScroll = throttle(100, () => {
|
||||||
if (window.scrollY.toFixed() === offset) {
|
if (window.scrollY.toFixed() === offset) {
|
||||||
window.removeEventListener("scroll", throttleListener)
|
window.removeEventListener("scroll", onScroll)
|
||||||
onScrollEnded()
|
onScrollEnded()
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
const throttleListener = throttle(100, onScroll)
|
window.addEventListener("scroll", onScroll)
|
||||||
window.addEventListener("scroll", throttleListener)
|
|
||||||
|
|
||||||
// scrollTo does not trigger if there's nothing to do, trigger it manually
|
// scrollTo does not trigger if there's nothing to do, trigger it manually
|
||||||
throttleListener()
|
onScroll()
|
||||||
|
|
||||||
window.scrollTo(options)
|
window.scrollTo(options)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { useBrowserExtension } from "hooks/useBrowserExtension"
|
|||||||
import { useMousetrap } from "hooks/useMousetrap"
|
import { useMousetrap } from "hooks/useMousetrap"
|
||||||
import { useViewMode } from "hooks/useViewMode"
|
import { useViewMode } from "hooks/useViewMode"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
|
import { useContextMenu } from "react-contexify"
|
||||||
import InfiniteScroll from "react-infinite-scroller"
|
import InfiniteScroll from "react-infinite-scroller"
|
||||||
import { throttle } from "throttle-debounce"
|
import { throttle } from "throttle-debounce"
|
||||||
import { FeedEntry } from "./FeedEntry"
|
import { FeedEntry } from "./FeedEntry"
|
||||||
@@ -59,6 +60,15 @@ export function FeedEntries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contextMenu = useContextMenu()
|
||||||
|
const headerRightClicked = (entry: ExpendableEntry, event: React.MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
contextMenu.show({
|
||||||
|
id: Constants.dom.entryContextMenuId(entry),
|
||||||
|
event,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const bodyClicked = (entry: ExpendableEntry) => {
|
const bodyClicked = (entry: ExpendableEntry) => {
|
||||||
if (viewMode !== "expanded") return
|
if (viewMode !== "expanded") return
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -73,8 +83,15 @@ export function FeedEntries() {
|
|||||||
|
|
||||||
const swipedRight = (entry: ExpendableEntry) => dispatch(markEntry({ entry, read: !entry.read }))
|
const swipedRight = (entry: ExpendableEntry) => dispatch(markEntry({ entry, read: !entry.read }))
|
||||||
|
|
||||||
|
// close context menu on scroll
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = () => {
|
const listener = throttle(100, () => contextMenu.hideAll())
|
||||||
|
window.addEventListener("scroll", listener)
|
||||||
|
return () => window.removeEventListener("scroll", listener)
|
||||||
|
}, [contextMenu])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = throttle(100, () => {
|
||||||
if (viewMode !== "expanded") return
|
if (viewMode !== "expanded") return
|
||||||
if (scrollingToEntry) return
|
if (scrollingToEntry) return
|
||||||
|
|
||||||
@@ -96,11 +113,10 @@ export function FeedEntries() {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
const throttledListener = throttle(100, listener)
|
window.addEventListener("scroll", listener)
|
||||||
window.addEventListener("scroll", throttledListener)
|
return () => window.removeEventListener("scroll", listener)
|
||||||
return () => window.removeEventListener("scroll", throttledListener)
|
}, [dispatch, contextMenu, entries, viewMode, scrollMarks, scrollingToEntry])
|
||||||
}, [dispatch, entries, viewMode, scrollMarks, scrollingToEntry])
|
|
||||||
|
|
||||||
useMousetrap("r", () => dispatch(reloadEntries()))
|
useMousetrap("r", () => dispatch(reloadEntries()))
|
||||||
useMousetrap("j", () =>
|
useMousetrap("j", () =>
|
||||||
@@ -278,6 +294,7 @@ export function FeedEntries() {
|
|||||||
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
||||||
maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined}
|
maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined}
|
||||||
onHeaderClick={event => headerClicked(entry, event)}
|
onHeaderClick={event => headerClicked(entry, event)}
|
||||||
|
onHeaderRightClick={event => headerRightClicked(entry, event)}
|
||||||
onBodyClick={() => bodyClicked(entry)}
|
onBodyClick={() => bodyClicked(entry)}
|
||||||
onSwipedRight={() => swipedRight(entry)}
|
onSwipedRight={() => swipedRight(entry)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import React from "react"
|
|||||||
import { useSwipeable } from "react-swipeable"
|
import { useSwipeable } from "react-swipeable"
|
||||||
import { FeedEntryBody } from "./FeedEntryBody"
|
import { FeedEntryBody } from "./FeedEntryBody"
|
||||||
import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader"
|
import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader"
|
||||||
import { FeedEntryContextMenu, useFeedEntryContextMenu } from "./FeedEntryContextMenu"
|
import { FeedEntryContextMenu } from "./FeedEntryContextMenu"
|
||||||
import { FeedEntryFooter } from "./FeedEntryFooter"
|
import { FeedEntryFooter } from "./FeedEntryFooter"
|
||||||
import { FeedEntryHeader } from "./FeedEntryHeader"
|
import { FeedEntryHeader } from "./FeedEntryHeader"
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ interface FeedEntryProps {
|
|||||||
showSelectionIndicator: boolean
|
showSelectionIndicator: boolean
|
||||||
maxWidth?: number
|
maxWidth?: number
|
||||||
onHeaderClick: (e: React.MouseEvent) => void
|
onHeaderClick: (e: React.MouseEvent) => void
|
||||||
|
onHeaderRightClick: (e: React.MouseEvent) => void
|
||||||
onBodyClick: (e: React.MouseEvent) => void
|
onBodyClick: (e: React.MouseEvent) => void
|
||||||
onSwipedRight: () => void
|
onSwipedRight: () => void
|
||||||
}
|
}
|
||||||
@@ -90,8 +91,6 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
onSwipedRight: props.onSwipedRight,
|
onSwipedRight: props.onSwipedRight,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { onContextMenu } = useFeedEntryContextMenu(props.entry)
|
|
||||||
|
|
||||||
let paddingX: MantineNumberSize = "xs"
|
let paddingX: MantineNumberSize = "xs"
|
||||||
if (viewMode === "title" || viewMode === "cozy") paddingX = 6
|
if (viewMode === "title" || viewMode === "cozy") paddingX = 6
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
onClick={props.onHeaderClick}
|
onClick={props.onHeaderClick}
|
||||||
onAuxClick={props.onHeaderClick}
|
onAuxClick={props.onHeaderClick}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={props.onHeaderRightClick}
|
||||||
>
|
>
|
||||||
<Box px={paddingX} py={paddingY} {...swipeHandlers}>
|
<Box px={paddingX} py={paddingY} {...swipeHandlers}>
|
||||||
{compactHeader && <FeedEntryCompactHeader entry={props.entry} />}
|
{compactHeader && <FeedEntryCompactHeader entry={props.entry} />}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { createStyles, Group } from "@mantine/core"
|
import { createStyles, Group } from "@mantine/core"
|
||||||
|
import { Constants } from "app/constants"
|
||||||
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
||||||
import { redirectToFeed } from "app/slices/redirect"
|
import { redirectToFeed } from "app/slices/redirect"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { Entry } from "app/types"
|
import { Entry } from "app/types"
|
||||||
import { truncate } from "app/utils"
|
import { truncate } from "app/utils"
|
||||||
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useEffect } from "react"
|
import { Item, Menu, Separator } from "react-contexify"
|
||||||
import { Item, Menu, Separator, useContextMenu } from "react-contexify"
|
|
||||||
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
|
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
|
||||||
import { throttle } from "throttle-debounce"
|
|
||||||
|
|
||||||
interface FeedEntryContextMenuProps {
|
interface FeedEntryContextMenuProps {
|
||||||
entry: Entry
|
entry: Entry
|
||||||
@@ -28,8 +27,6 @@ const useStyles = createStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const menuId = (entry: Entry) => entry.id
|
|
||||||
|
|
||||||
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
||||||
const { classes, theme } = useStyles()
|
const { classes, theme } = useStyles()
|
||||||
const sourceType = useAppSelector(state => state.entries.source.type)
|
const sourceType = useAppSelector(state => state.entries.source.type)
|
||||||
@@ -37,7 +34,7 @@ export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
|||||||
const { openLinkInBackgroundTab } = useBrowserExtension()
|
const { openLinkInBackgroundTab } = useBrowserExtension()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu id={menuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
|
<Menu id={Constants.dom.entryContextMenuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
|
||||||
<Item
|
<Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open(props.entry.url, "_blank", "noreferrer")
|
window.open(props.entry.url, "_blank", "noreferrer")
|
||||||
@@ -101,27 +98,3 @@ export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
|||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFeedEntryContextMenu(entry: Entry) {
|
|
||||||
const contextMenu = useContextMenu({
|
|
||||||
id: menuId(entry),
|
|
||||||
})
|
|
||||||
|
|
||||||
const onContextMenu = (event: React.MouseEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
contextMenu.show({
|
|
||||||
event,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// close context menu on scroll
|
|
||||||
useEffect(() => {
|
|
||||||
const listener = () => contextMenu.hideAll()
|
|
||||||
const throttledListener = throttle(100, listener)
|
|
||||||
|
|
||||||
window.addEventListener("scroll", throttledListener)
|
|
||||||
return () => window.removeEventListener("scroll", throttledListener)
|
|
||||||
}, [contextMenu])
|
|
||||||
|
|
||||||
return { onContextMenu }
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user