Add support for new-tab clicks on feeds (#4462)
parent
59f49bef68
commit
90ec22a674
|
@ -12,7 +12,8 @@ import {
|
||||||
isExternalUrl,
|
isExternalUrl,
|
||||||
linkRequiresWarning,
|
linkRequiresWarning,
|
||||||
} from '#/lib/strings/url-helpers'
|
} from '#/lib/strings/url-helpers'
|
||||||
import {isNative, isWeb} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
|
import {shouldClickOpenNewTab} from '#/platform/urls'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
import {useOpenLink} from '#/state/preferences/in-app-browser'
|
import {useOpenLink} from '#/state/preferences/in-app-browser'
|
||||||
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
|
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
|
||||||
|
@ -116,16 +117,7 @@ export function useLink({
|
||||||
if (isExternal) {
|
if (isExternal) {
|
||||||
openLink(href)
|
openLink(href)
|
||||||
} else {
|
} else {
|
||||||
/**
|
const shouldOpenInNewTab = shouldClickOpenNewTab(e)
|
||||||
* A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
|
|
||||||
* of @ts-ignore below.
|
|
||||||
*/
|
|
||||||
const event = e as any
|
|
||||||
const isMiddleClick = isWeb && event.button === 1
|
|
||||||
const isMetaKey =
|
|
||||||
isWeb &&
|
|
||||||
(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
|
|
||||||
const shouldOpenInNewTab = isMetaKey || isMiddleClick
|
|
||||||
|
|
||||||
if (isBskyDownloadUrl(href)) {
|
if (isBskyDownloadUrl(href)) {
|
||||||
shareUrl(BSKY_DOWNLOAD_URL)
|
shareUrl(BSKY_DOWNLOAD_URL)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {Linking} from 'react-native'
|
import {GestureResponderEvent, Linking} from 'react-native'
|
||||||
|
|
||||||
import {isNative, isWeb} from './detection'
|
import {isNative, isWeb} from './detection'
|
||||||
|
|
||||||
export async function getInitialURL(): Promise<string | undefined> {
|
export async function getInitialURL(): Promise<string | undefined> {
|
||||||
|
@ -23,3 +24,15 @@ export function clearHash() {
|
||||||
window.location.hash = ''
|
window.location.hash = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldClickOpenNewTab(e: GestureResponderEvent) {
|
||||||
|
/**
|
||||||
|
* A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
|
||||||
|
* of @ts-ignore below.
|
||||||
|
*/
|
||||||
|
const event = e as any
|
||||||
|
const isMiddleClick = isWeb && event.button === 1
|
||||||
|
const isMetaKey =
|
||||||
|
isWeb && (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
|
||||||
|
return isMetaKey || isMiddleClick
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
import {
|
||||||
|
Linking,
|
||||||
|
Pressable,
|
||||||
|
StyleProp,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
ViewStyle,
|
||||||
|
} from 'react-native'
|
||||||
import {AtUri} from '@atproto/api'
|
import {AtUri} from '@atproto/api'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Plural, Trans} from '@lingui/macro'
|
import {msg, Plural, Trans} from '@lingui/macro'
|
||||||
|
@ -26,6 +33,7 @@ import {RichText} from '#/components/RichText'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import hairlineWidth = StyleSheet.hairlineWidth
|
import hairlineWidth = StyleSheet.hairlineWidth
|
||||||
|
import {shouldClickOpenNewTab} from '#/platform/urls'
|
||||||
|
|
||||||
export function FeedSourceCard({
|
export function FeedSourceCard({
|
||||||
feedUri,
|
feedUri,
|
||||||
|
@ -203,18 +211,31 @@ export function FeedSourceCardLoaded({
|
||||||
style,
|
style,
|
||||||
{borderTopWidth: hideTopBorder ? 0 : hairlineWidth},
|
{borderTopWidth: hideTopBorder ? 0 : hairlineWidth},
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={e => {
|
||||||
|
const shouldOpenInNewTab = shouldClickOpenNewTab(e)
|
||||||
if (feed.type === 'feed') {
|
if (feed.type === 'feed') {
|
||||||
|
if (shouldOpenInNewTab) {
|
||||||
|
Linking.openURL(
|
||||||
|
`/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
navigation.push('ProfileFeed', {
|
navigation.push('ProfileFeed', {
|
||||||
name: feed.creatorDid,
|
name: feed.creatorDid,
|
||||||
rkey: new AtUri(feed.uri).rkey,
|
rkey: new AtUri(feed.uri).rkey,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
} else if (feed.type === 'list') {
|
} else if (feed.type === 'list') {
|
||||||
|
if (shouldOpenInNewTab) {
|
||||||
|
Linking.openURL(
|
||||||
|
`/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
navigation.push('ProfileList', {
|
navigation.push('ProfileList', {
|
||||||
name: feed.creatorDid,
|
name: feed.creatorDid,
|
||||||
rkey: new AtUri(feed.uri).rkey,
|
rkey: new AtUri(feed.uri).rkey,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
key={feed.uri}>
|
key={feed.uri}>
|
||||||
<View style={[styles.headerContainer, a.align_center]}>
|
<View style={[styles.headerContainer, a.align_center]}>
|
||||||
|
|
Loading…
Reference in New Issue