about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-04-04 18:37:57 +0100
committerGitHub <noreply@github.com>2024-04-04 10:37:57 -0700
commit9f657fbace296aa0a189c46b34ccac33a8ef2d8f (patch)
treef81071e0cbf998117844a720d38b3d336b1876c4 /src
parent4d28dcc48fcdb8c13d86485ad2a5799ec777410e (diff)
downloadvoidsky-9f657fbace296aa0a189c46b34ccac33a8ef2d8f.tar.zst
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>
Diffstat (limited to 'src')
-rw-r--r--src/components/Link.tsx52
-rw-r--r--src/components/RichText.tsx17
-rw-r--r--src/lib/sharing.ts7
-rw-r--r--src/state/modals/index.tsx1
-rw-r--r--src/view/com/modals/LinkWarning.tsx46
5 files changed, 89 insertions, 34 deletions
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index 7d0e83332..1a494626f 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -1,23 +1,24 @@
 import React from 'react'
 import {GestureResponderEvent} from 'react-native'
-import {useLinkProps, StackActions} from '@react-navigation/native'
 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 {shareUrl} from '#/lib/sharing'
 import {
   convertBskyAppUrlIfNeeded,
   isExternalUrl,
   linkRequiresWarning,
 } from '#/lib/strings/url-helpers'
+import {isNative, isWeb} from '#/platform/detection'
 import {useModalControls} from '#/state/modals'
-import {router} from '#/routes'
-import {Text, TextProps} from '#/components/Typography'
-import {useOpenLink} from 'state/preferences/in-app-browser'
+import {useOpenLink} from '#/state/preferences/in-app-browser'
 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`.
@@ -60,6 +61,11 @@ type BaseLinkProps = Pick<
    * Web-only attribute. Sets `download` attr on web.
    */
   download?: string
+
+  /**
+   * Native-only attribute. If true, will open the share sheet on long press.
+   */
+  shareOnLongPress?: boolean
 }
 
 export function useLink({
@@ -68,6 +74,7 @@ export function useLink({
   action = 'push',
   disableMismatchWarning,
   onPress: outerOnPress,
+  shareOnLongPress,
 }: BaseLinkProps & {
   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 {
     isExternal,
     href,
     onPress,
+    onLongPress,
   }
 }
 
@@ -229,16 +260,18 @@ export function InlineLink({
   download,
   selectable,
   label,
+  shareOnLongPress,
   ...rest
 }: InlineLinkProps) {
   const t = useTheme()
   const stringChildren = typeof children === 'string'
-  const {href, isExternal, onPress} = useLink({
+  const {href, isExternal, onPress, onLongPress} = useLink({
     to,
     displayText: stringChildren ? children : '',
     action,
     disableMismatchWarning,
     onPress: outerOnPress,
+    shareOnLongPress,
   })
   const {
     state: hovered,
@@ -270,6 +303,7 @@ export function InlineLink({
       ]}
       role="link"
       onPress={download ? undefined : onPress}
+      onLongPress={onLongPress}
       onPressIn={onPressIn}
       onPressOut={onPressOut}
       onFocus={onFocus}
diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx
index 1a14415cf..0e89e13b7 100644
--- a/src/components/RichText.tsx
+++ b/src/components/RichText.tsx
@@ -1,15 +1,15 @@
 import React from 'react'
-import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api'
-import {useLingui} from '@lingui/react'
+import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
 import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 
-import {atoms as a, TextStyleProp, flatten, useTheme, web, native} from '#/alf'
-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 {toShortUrl} from '#/lib/strings/url-helpers'
 import {isNative} from '#/platform/detection'
+import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf'
 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}
 
@@ -105,7 +105,8 @@ export function RichText({
             to={link.uri}
             style={[...styles, {pointerEvents: 'auto'}]}
             // @ts-ignore TODO
-            dataSet={WORD_WRAP}>
+            dataSet={WORD_WRAP}
+            shareOnLongPress>
             {toShortUrl(segment.text)}
           </InlineLink>,
         )
diff --git a/src/lib/sharing.ts b/src/lib/sharing.ts
index 9f402f873..c50a2734a 100644
--- a/src/lib/sharing.ts
+++ b/src/lib/sharing.ts
@@ -1,8 +1,9 @@
-import {isIOS, isAndroid} from 'platform/detection'
+import {Share} from 'react-native'
 // import * as Sharing from 'expo-sharing'
 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
diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx
index aae4fc52f..e0bcc2f0f 100644
--- a/src/state/modals/index.tsx
+++ b/src/state/modals/index.tsx
@@ -122,6 +122,7 @@ export interface LinkWarningModal {
   name: 'link-warning'
   text: string
   href: string
+  share?: boolean
 }
 
 export interface EmbedConsentModal {
diff --git a/src/view/com/modals/LinkWarning.tsx b/src/view/com/modals/LinkWarning.tsx
index b5ff6700d..bf5bf6d29 100644
--- a/src/view/com/modals/LinkWarning.tsx
+++ b/src/view/com/modals/LinkWarning.tsx
@@ -1,22 +1,32 @@
 import React from 'react'
 import {SafeAreaView, StyleSheet, View} from 'react-native'
-import {ScrollView} from './util'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Text} from '../util/text/Text'
-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 {msg, Trans} from '@lingui/macro'
 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 {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 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 {closeModal} = useModalControls()
   const {isMobile} = useWebMediaQueries()
@@ -26,7 +36,11 @@ export function Component({text, href}: {text: string; href: string}) {
 
   const onPressVisit = () => {
     closeModal()
-    openLink(href)
+    if (share) {
+      shareUrl(href)
+    } else {
+      openLink(href)
+    }
   }
 
   return (
@@ -72,9 +86,13 @@ export function Component({text, href}: {text: string; href: string}) {
             testID="confirmBtn"
             type="primary"
             onPress={onPressVisit}
-            accessibilityLabel={_(msg`Visit Site`)}
-            accessibilityHint={_(msg`Opens the linked website`)}
-            label={_(msg`Visit Site`)}
+            accessibilityLabel={share ? _(msg`Share Link`) : _(msg`Visit Site`)}
+            accessibilityHint={
+              share
+                ? _(msg`Shares the linked website`)
+                : _(msg`Opens the linked website`)
+            }
+            label={share ? _(msg`Share Link`) : _(msg`Visit Site`)}
             labelContainerStyle={{justifyContent: 'center', padding: 4}}
             labelStyle={[s.f18]}
           />