Open share sheet when long pressing link (#3317)

* uitextview use library w/ fixes

bump

bump

multiple uitextview fixes

* bump

* Open share sheet on link long press

* rm package manager field

* add link warning to longpress

---------

Co-authored-by: Hailey <me@haileyok.com>
zio/stable
Samuel Newman 2024-04-04 18:37:57 +01:00 committed by GitHub
parent 4d28dcc48f
commit 9f657fbace
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 34 deletions

View File

@ -1,23 +1,24 @@
import React from 'react' import React from 'react'
import {GestureResponderEvent} from 'react-native' import {GestureResponderEvent} from 'react-native'
import {useLinkProps, StackActions} from '@react-navigation/native'
import {sanitizeUrl} from '@braintree/sanitize-url' import {sanitizeUrl} from '@braintree/sanitize-url'
import {StackActions, useLinkProps} from '@react-navigation/native'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {isWeb} from '#/platform/detection'
import {useTheme, web, flatten, TextStyleProp, atoms as a} from '#/alf'
import {Button, ButtonProps} from '#/components/Button'
import {AllNavigatorParams} from '#/lib/routes/types' import {AllNavigatorParams} from '#/lib/routes/types'
import {shareUrl} from '#/lib/sharing'
import { import {
convertBskyAppUrlIfNeeded, convertBskyAppUrlIfNeeded,
isExternalUrl, isExternalUrl,
linkRequiresWarning, linkRequiresWarning,
} from '#/lib/strings/url-helpers' } from '#/lib/strings/url-helpers'
import {isNative, isWeb} from '#/platform/detection'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {router} from '#/routes' import {useOpenLink} from '#/state/preferences/in-app-browser'
import {Text, TextProps} from '#/components/Typography'
import {useOpenLink} from 'state/preferences/in-app-browser'
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped' import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf'
import {Button, ButtonProps} from '#/components/Button'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text, TextProps} from '#/components/Typography'
import {router} from '#/routes'
/** /**
* Only available within a `Link`, since that inherits from `Button`. * Only available within a `Link`, since that inherits from `Button`.
@ -60,6 +61,11 @@ type BaseLinkProps = Pick<
* Web-only attribute. Sets `download` attr on web. * Web-only attribute. Sets `download` attr on web.
*/ */
download?: string download?: string
/**
* Native-only attribute. If true, will open the share sheet on long press.
*/
shareOnLongPress?: boolean
} }
export function useLink({ export function useLink({
@ -68,6 +74,7 @@ export function useLink({
action = 'push', action = 'push',
disableMismatchWarning, disableMismatchWarning,
onPress: outerOnPress, onPress: outerOnPress,
shareOnLongPress,
}: BaseLinkProps & { }: BaseLinkProps & {
displayText: string displayText: string
}) { }) {
@ -157,10 +164,34 @@ export function useLink({
], ],
) )
const handleLongPress = React.useCallback(() => {
const requiresWarning = Boolean(
!disableMismatchWarning &&
displayText &&
isExternal &&
linkRequiresWarning(href, displayText),
)
if (requiresWarning) {
openModal({
name: 'link-warning',
text: displayText,
href: href,
share: true,
})
} else {
shareUrl(href)
}
}, [disableMismatchWarning, displayText, href, isExternal, openModal])
const onLongPress =
isNative && isExternal && shareOnLongPress ? handleLongPress : undefined
return { return {
isExternal, isExternal,
href, href,
onPress, onPress,
onLongPress,
} }
} }
@ -229,16 +260,18 @@ export function InlineLink({
download, download,
selectable, selectable,
label, label,
shareOnLongPress,
...rest ...rest
}: InlineLinkProps) { }: InlineLinkProps) {
const t = useTheme() const t = useTheme()
const stringChildren = typeof children === 'string' const stringChildren = typeof children === 'string'
const {href, isExternal, onPress} = useLink({ const {href, isExternal, onPress, onLongPress} = useLink({
to, to,
displayText: stringChildren ? children : '', displayText: stringChildren ? children : '',
action, action,
disableMismatchWarning, disableMismatchWarning,
onPress: outerOnPress, onPress: outerOnPress,
shareOnLongPress,
}) })
const { const {
state: hovered, state: hovered,
@ -270,6 +303,7 @@ export function InlineLink({
]} ]}
role="link" role="link"
onPress={download ? undefined : onPress} onPress={download ? undefined : onPress}
onLongPress={onLongPress}
onPressIn={onPressIn} onPressIn={onPressIn}
onPressOut={onPressOut} onPressOut={onPressOut}
onFocus={onFocus} onFocus={onFocus}

View File

@ -1,15 +1,15 @@
import React from 'react' import React from 'react'
import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {atoms as a, TextStyleProp, flatten, useTheme, web, native} from '#/alf' import {toShortUrl} from '#/lib/strings/url-helpers'
import {InlineLink} from '#/components/Link'
import {Text, TextProps} from '#/components/Typography'
import {toShortUrl} from 'lib/strings/url-helpers'
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {InlineLink} from '#/components/Link'
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
import {Text, TextProps} from '#/components/Typography'
const WORD_WRAP = {wordWrap: 1} const WORD_WRAP = {wordWrap: 1}
@ -105,7 +105,8 @@ export function RichText({
to={link.uri} to={link.uri}
style={[...styles, {pointerEvents: 'auto'}]} style={[...styles, {pointerEvents: 'auto'}]}
// @ts-ignore TODO // @ts-ignore TODO
dataSet={WORD_WRAP}> dataSet={WORD_WRAP}
shareOnLongPress>
{toShortUrl(segment.text)} {toShortUrl(segment.text)}
</InlineLink>, </InlineLink>,
) )

View File

@ -1,8 +1,9 @@
import {isIOS, isAndroid} from 'platform/detection' import {Share} from 'react-native'
// import * as Sharing from 'expo-sharing' // import * as Sharing from 'expo-sharing'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import * as Toast from '../view/com/util/Toast'
import {Share} from 'react-native' import {isAndroid, isIOS} from 'platform/detection'
import * as Toast from '#/view/com/util/Toast'
/** /**
* This function shares a URL using the native Share API if available, or copies it to the clipboard * This function shares a URL using the native Share API if available, or copies it to the clipboard

View File

@ -122,6 +122,7 @@ export interface LinkWarningModal {
name: 'link-warning' name: 'link-warning'
text: string text: string
href: string href: string
share?: boolean
} }
export interface EmbedConsentModal { export interface EmbedConsentModal {

View File

@ -1,22 +1,32 @@
import React from 'react' import React from 'react'
import {SafeAreaView, StyleSheet, View} from 'react-native' import {SafeAreaView, StyleSheet, View} from 'react-native'
import {ScrollView} from './util'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Text} from '../util/text/Text' import {msg, Trans} from '@lingui/macro'
import {Button} from '../util/forms/Button'
import {s, colors} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {isWeb} from 'platform/detection'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {isPossiblyAUrl, splitApexDomain} from 'lib/strings/url-helpers'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {shareUrl} from '#/lib/sharing'
import {isPossiblyAUrl, splitApexDomain} from '#/lib/strings/url-helpers'
import {colors, s} from '#/lib/styles'
import {isWeb} from '#/platform/detection'
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 {Button} from '#/view/com/util/forms/Button'
import {Text} from '#/view/com/util/text/Text'
import {ScrollView} from './util'
export const snapPoints = ['50%'] export const snapPoints = ['50%']
export function Component({text, href}: {text: string; href: string}) { export function Component({
text,
href,
share,
}: {
text: string
href: string
share?: boolean
}) {
const pal = usePalette('default') const pal = usePalette('default')
const {closeModal} = useModalControls() const {closeModal} = useModalControls()
const {isMobile} = useWebMediaQueries() const {isMobile} = useWebMediaQueries()
@ -26,8 +36,12 @@ export function Component({text, href}: {text: string; href: string}) {
const onPressVisit = () => { const onPressVisit = () => {
closeModal() closeModal()
if (share) {
shareUrl(href)
} else {
openLink(href) openLink(href)
} }
}
return ( return (
<SafeAreaView style={[s.flex1, pal.view]}> <SafeAreaView style={[s.flex1, pal.view]}>
@ -72,9 +86,13 @@ export function Component({text, href}: {text: string; href: string}) {
testID="confirmBtn" testID="confirmBtn"
type="primary" type="primary"
onPress={onPressVisit} onPress={onPressVisit}
accessibilityLabel={_(msg`Visit Site`)} accessibilityLabel={share ? _(msg`Share Link`) : _(msg`Visit Site`)}
accessibilityHint={_(msg`Opens the linked website`)} accessibilityHint={
label={_(msg`Visit Site`)} share
? _(msg`Shares the linked website`)
: _(msg`Opens the linked website`)
}
label={share ? _(msg`Share Link`) : _(msg`Visit Site`)}
labelContainerStyle={{justifyContent: 'center', padding: 4}} labelContainerStyle={{justifyContent: 'center', padding: 4}}
labelStyle={[s.f18]} labelStyle={[s.f18]}
/> />