Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Handle middle mouse click on feed list items #1469

Merged
merged 5 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib/hooks/useAuxClick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// does nothing in native
export const useAuxClick = () => {}
43 changes: 43 additions & 0 deletions src/lib/hooks/useAuxClick.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {useEffect} from 'react'

// This is the handler for the middle mouse button click on the feed.
// Normally, we would do this via `onAuxClick` handler on each link element
// However, that handler is not supported on react-native-web and there are some
// discrepancies between various browsers (i.e: safari doesn't trigger it and routes through click event)
// So, this temporary alternative is meant to bridge the gap in an efficient way until the support improves.
export const useAuxClick = () => {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
useEffect(() => {
// On the web, it should always be there but in case it gets accidentally included in native builds
const wrapperEl = document?.body

// Safari already handles auxclick event as click+metaKey so we need to avoid doing this there in case it becomes recursive
if (wrapperEl && !isSafari) {
const handleAuxClick = (e: MouseEvent & {target: HTMLElement}) => {
// Only handle the middle mouse button click
// Only handle if the clicked element itself or one of its ancestors is a link
if (
e.button !== 1 ||
e.target.closest('a') ||
e.target.tagName === 'A'
) {
return
}

// On the original element, trigger a click event with metaKey set to true so that it triggers
// the browser's default behavior of opening the link in a new tab
e.target.dispatchEvent(
new MouseEvent('click', {metaKey: true, bubbles: true}),
)
}

// @ts-ignore For web only
wrapperEl.addEventListener('auxclick', handleAuxClick)

return () => {
// @ts-ignore For web only
wrapperEl?.removeEventListener('auxclick', handleAuxClick)
}
}
}, [isSafari])
}
5 changes: 3 additions & 2 deletions src/view/com/util/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const Link = observer(function Link({
}: Props) {
const store = useStores()
const navigation = useNavigation<NavigationProp>()
const anchorHref = asAnchor ? sanitizeUrl(href) : undefined

const onPress = React.useCallback(
(e?: Event) => {
Expand Down Expand Up @@ -94,7 +95,7 @@ export const Link = observer(function Link({
accessibilityRole="link"
{...props}>
{/* @ts-ignore web only -prf */}
<View style={style} href={asAnchor ? sanitizeUrl(href) : undefined}>
<View style={style} href={anchorHref}>
{children ? children : <Text>{title || 'link'}</Text>}
</View>
</TouchableWithoutFeedback>
Expand All @@ -120,7 +121,7 @@ export const Link = observer(function Link({
accessible={accessible}
accessibilityRole="link"
// @ts-ignore web only -prf
href={asAnchor ? sanitizeUrl(href) : undefined}
href={anchorHref}
{...props}>
{children ? children : <Text>{title || 'link'}</Text>}
</Pressable>
Expand Down
2 changes: 2 additions & 0 deletions src/view/shell/index.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types'
import {useAuxClick} from 'lib/hooks/useAuxClick'

const ShellInner = observer(function ShellInnerImpl() {
const store = useStores()
const {isDesktop, isMobile} = useWebMediaQueries()
const navigator = useNavigation<NavigationProp>()
useAuxClick()

useEffect(() => {
navigator.addListener('state', () => {
Expand Down