about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/modals/EmbedConsent.tsx154
-rw-r--r--src/view/com/modals/Modal.tsx4
-rw-r--r--src/view/com/modals/Modal.web.tsx39
-rw-r--r--src/view/com/util/post-embeds/ExternalGifEmbed.tsx121
-rw-r--r--src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx86
5 files changed, 131 insertions, 273 deletions
diff --git a/src/view/com/modals/EmbedConsent.tsx b/src/view/com/modals/EmbedConsent.tsx
deleted file mode 100644
index 941944728..000000000
--- a/src/view/com/modals/EmbedConsent.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {LinearGradient} from 'expo-linear-gradient'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import {
-  EmbedPlayerSource,
-  embedPlayerSources,
-  externalEmbedLabels,
-} from '#/lib/strings/embed-player'
-import {useModalControls} from '#/state/modals'
-import {useSetExternalEmbedPref} from '#/state/preferences/external-embeds-prefs'
-import {usePalette} from 'lib/hooks/usePalette'
-import {colors, gradients, s} from 'lib/styles'
-import {Text} from '../util/text/Text'
-import {ScrollView} from './util'
-
-export const snapPoints = [450]
-
-export function Component({
-  onAccept,
-  source,
-}: {
-  onAccept: () => void
-  source: EmbedPlayerSource
-}) {
-  const pal = usePalette('default')
-  const {closeModal} = useModalControls()
-  const {_} = useLingui()
-  const setExternalEmbedPref = useSetExternalEmbedPref()
-  const {isMobile} = useWebMediaQueries()
-
-  const onShowAllPress = React.useCallback(() => {
-    for (const key of embedPlayerSources) {
-      setExternalEmbedPref(key, 'show')
-    }
-    onAccept()
-    closeModal()
-  }, [closeModal, onAccept, setExternalEmbedPref])
-
-  const onShowPress = React.useCallback(() => {
-    setExternalEmbedPref(source, 'show')
-    onAccept()
-    closeModal()
-  }, [closeModal, onAccept, setExternalEmbedPref, source])
-
-  const onHidePress = React.useCallback(() => {
-    setExternalEmbedPref(source, 'hide')
-    closeModal()
-  }, [closeModal, setExternalEmbedPref, source])
-
-  return (
-    <ScrollView
-      testID="embedConsentModal"
-      style={[
-        s.flex1,
-        pal.view,
-        isMobile
-          ? {paddingHorizontal: 20, paddingTop: 10}
-          : {paddingHorizontal: 30},
-      ]}>
-      <Text style={[pal.text, styles.title]}>
-        <Trans>External Media</Trans>
-      </Text>
-
-      <Text style={pal.text}>
-        <Trans>
-          This content is hosted by {externalEmbedLabels[source]}. Do you want
-          to enable external media?
-        </Trans>
-      </Text>
-      <View style={[s.mt10]} />
-      <Text style={pal.textLight}>
-        <Trans>
-          External media may allow websites to collect information about you and
-          your device. No information is sent or requested until you press the
-          "play" button.
-        </Trans>
-      </Text>
-      <View style={[s.mt20]} />
-      <TouchableOpacity
-        testID="enableAllBtn"
-        onPress={onShowAllPress}
-        accessibilityRole="button"
-        accessibilityLabel={_(
-          msg`Show embeds from ${externalEmbedLabels[source]}`,
-        )}
-        accessibilityHint=""
-        onAccessibilityEscape={closeModal}>
-        <LinearGradient
-          colors={[gradients.blueLight.start, gradients.blueLight.end]}
-          start={{x: 0, y: 0}}
-          end={{x: 1, y: 1}}
-          style={[styles.btn]}>
-          <Text style={[s.white, s.bold, s.f18]}>
-            <Trans>Enable External Media</Trans>
-          </Text>
-        </LinearGradient>
-      </TouchableOpacity>
-      <View style={[s.mt10]} />
-      <TouchableOpacity
-        testID="enableSourceBtn"
-        onPress={onShowPress}
-        accessibilityRole="button"
-        accessibilityLabel={_(
-          msg`Never load embeds from ${externalEmbedLabels[source]}`,
-        )}
-        accessibilityHint=""
-        onAccessibilityEscape={closeModal}>
-        <View style={[styles.btn, pal.btn]}>
-          <Text style={[pal.text, s.bold, s.f18]}>
-            <Trans>Enable {externalEmbedLabels[source]} only</Trans>
-          </Text>
-        </View>
-      </TouchableOpacity>
-      <View style={[s.mt10]} />
-      <TouchableOpacity
-        testID="disableSourceBtn"
-        onPress={onHidePress}
-        accessibilityRole="button"
-        accessibilityLabel={_(
-          msg`Never load embeds from ${externalEmbedLabels[source]}`,
-        )}
-        accessibilityHint=""
-        onAccessibilityEscape={closeModal}>
-        <View style={[styles.btn, pal.btn]}>
-          <Text style={[pal.text, s.bold, s.f18]}>
-            <Trans>No thanks</Trans>
-          </Text>
-        </View>
-      </TouchableOpacity>
-    </ScrollView>
-  )
-}
-
-const styles = StyleSheet.create({
-  title: {
-    textAlign: 'center',
-    fontWeight: 'bold',
-    fontSize: 24,
-    marginBottom: 12,
-  },
-  btn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'center',
-    width: '100%',
-    borderRadius: 32,
-    padding: 14,
-    backgroundColor: colors.gray1,
-  },
-})
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 85ffccf12..652481301 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -15,7 +15,6 @@ import * as ChangePasswordModal from './ChangePassword'
 import * as CreateOrEditListModal from './CreateOrEditList'
 import * as DeleteAccountModal from './DeleteAccount'
 import * as EditProfileModal from './EditProfile'
-import * as EmbedConsentModal from './EmbedConsent'
 import * as InAppBrowserConsentModal from './InAppBrowserConsent'
 import * as InviteCodesModal from './InviteCodes'
 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
@@ -116,9 +115,6 @@ export function ModalsContainer() {
   } else if (activeModal?.name === 'link-warning') {
     snapPoints = LinkWarningModal.snapPoints
     element = <LinkWarningModal.Component {...activeModal} />
-  } else if (activeModal?.name === 'embed-consent') {
-    snapPoints = EmbedConsentModal.snapPoints
-    element = <EmbedConsentModal.Component {...activeModal} />
   } else if (activeModal?.name === 'in-app-browser-consent') {
     snapPoints = InAppBrowserConsentModal.snapPoints
     element = <InAppBrowserConsentModal.Component {...activeModal} />
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 7e5d548ac..f95c74811 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -1,33 +1,32 @@
 import React from 'react'
-import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native'
+import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
 
-import {useModals, useModalControls} from '#/state/modals'
+import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
 import type {Modal as ModalIface} from '#/state/modals'
-import * as EditProfileModal from './EditProfile'
+import {useModalControls, useModals} from '#/state/modals'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import * as AddAppPassword from './AddAppPasswords'
+import * as AltTextImageModal from './AltImage'
+import * as ChangeEmailModal from './ChangeEmail'
+import * as ChangeHandleModal from './ChangeHandle'
+import * as ChangePasswordModal from './ChangePassword'
 import * as CreateOrEditListModal from './CreateOrEditList'
-import * as UserAddRemoveLists from './UserAddRemoveLists'
-import * as ListAddUserModal from './ListAddRemoveUsers'
-import * as DeleteAccountModal from './DeleteAccount'
-import * as RepostModal from './Repost'
-import * as SelfLabelModal from './SelfLabel'
-import * as ThreadgateModal from './Threadgate'
 import * as CropImageModal from './crop-image/CropImage.web'
-import * as AltTextImageModal from './AltImage'
+import * as DeleteAccountModal from './DeleteAccount'
 import * as EditImageModal from './EditImage'
-import * as ChangeHandleModal from './ChangeHandle'
+import * as EditProfileModal from './EditProfile'
 import * as InviteCodesModal from './InviteCodes'
-import * as AddAppPassword from './AddAppPasswords'
 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
-import * as VerifyEmailModal from './VerifyEmail'
-import * as ChangeEmailModal from './ChangeEmail'
-import * as ChangePasswordModal from './ChangePassword'
 import * as LinkWarningModal from './LinkWarning'
-import * as EmbedConsentModal from './EmbedConsent'
+import * as ListAddUserModal from './ListAddRemoveUsers'
+import * as RepostModal from './Repost'
+import * as SelfLabelModal from './SelfLabel'
+import * as ThreadgateModal from './Threadgate'
+import * as UserAddRemoveLists from './UserAddRemoveLists'
+import * as VerifyEmailModal from './VerifyEmail'
 
 export function ModalsContainer() {
   const {isModalActive, activeModals} = useModals()
@@ -112,8 +111,6 @@ function Modal({modal}: {modal: ModalIface}) {
     element = <ChangePasswordModal.Component />
   } else if (modal.name === 'link-warning') {
     element = <LinkWarningModal.Component {...modal} />
-  } else if (modal.name === 'embed-consent') {
-    element = <EmbedConsentModal.Component {...modal} />
   } else {
     return null
   }
diff --git a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx b/src/view/com/util/post-embeds/ExternalGifEmbed.tsx
index f06c8b794..b2720752c 100644
--- a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalGifEmbed.tsx
@@ -1,6 +1,4 @@
-import {EmbedPlayerParams, getGifDims} from 'lib/strings/embed-player'
 import React from 'react'
-import {Image, ImageLoadEventData} from 'expo-image'
 import {
   ActivityIndicator,
   GestureResponderEvent,
@@ -9,13 +7,17 @@ import {
   StyleSheet,
   View,
 } from 'react-native'
-import {isIOS, isNative, isWeb} from '#/platform/detection'
+import {Image, ImageLoadEventData} from 'expo-image'
+import {AppBskyEmbedExternal} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {useExternalEmbedsPrefs} from 'state/preferences'
-import {useModalControls} from 'state/modals'
-import {useLingui} from '@lingui/react'
 import {msg} from '@lingui/macro'
-import {AppBskyEmbedExternal} from '@atproto/api'
+import {useLingui} from '@lingui/react'
+
+import {EmbedPlayerParams, getGifDims} from '#/lib/strings/embed-player'
+import {isIOS, isNative, isWeb} from '#/platform/detection'
+import {useExternalEmbedsPrefs} from '#/state/preferences'
+import {useDialogControl} from '#/components/Dialog'
+import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
 
 export function ExternalGifEmbed({
   link,
@@ -25,8 +27,9 @@ export function ExternalGifEmbed({
   params: EmbedPlayerParams
 }) {
   const externalEmbedsPrefs = useExternalEmbedsPrefs()
-  const {openModal} = useModalControls()
+
   const {_} = useLingui()
+  const consentDialogControl = useDialogControl()
 
   const thumbHasLoaded = React.useRef(false)
   const viewWidth = React.useRef(0)
@@ -57,11 +60,7 @@ export function ExternalGifEmbed({
 
       // Show consent if this is the first load
       if (externalEmbedsPrefs?.[params.source] === undefined) {
-        openModal({
-          name: 'embed-consent',
-          source: params.source,
-          onAccept: load,
-        })
+        consentDialogControl.open()
         return
       }
       // If the player isn't active, we want to activate it and prefetch the gif
@@ -84,7 +83,13 @@ export function ExternalGifEmbed({
         }
       })
     },
-    [externalEmbedsPrefs, isPlayerActive, load, openModal, params.source],
+    [
+      consentDialogControl,
+      externalEmbedsPrefs,
+      isPlayerActive,
+      load,
+      params.source,
+    ],
   )
 
   const onLoad = React.useCallback((e: ImageLoadEventData) => {
@@ -98,47 +103,55 @@ export function ExternalGifEmbed({
   }, [])
 
   return (
-    <Pressable
-      style={[
-        {height: imageDims.height},
-        styles.topRadius,
-        styles.gifContainer,
-      ]}
-      onPress={onPlayPress}
-      onLayout={onLayout}
-      accessibilityRole="button"
-      accessibilityHint={_(msg`Plays the GIF`)}
-      accessibilityLabel={_(msg`Play ${link.title}`)}>
-      {(!isPrefetched || !isAnimating) && ( // If we have not loaded or are not animating, show the overlay
-        <View style={[styles.layer, styles.overlayLayer]}>
-          <View style={[styles.overlayContainer, styles.topRadius]}>
-            {!isAnimating || !isPlayerActive ? ( // Play button when not animating or not active
-              <FontAwesomeIcon icon="play" size={42} color="white" />
-            ) : (
-              // Activity indicator while gif loads
-              <ActivityIndicator size="large" color="white" />
-            )}
-          </View>
-        </View>
-      )}
-      <Image
-        source={{
-          uri:
-            !isPrefetched || (isWeb && !isAnimating)
-              ? link.thumb
-              : params.playerUri,
-        }} // Web uses the thumb to control playback
-        style={{flex: 1}}
-        ref={imageRef}
-        onLoad={onLoad}
-        autoplay={isAnimating}
-        contentFit="contain"
-        accessibilityIgnoresInvertColors
-        accessibilityLabel={link.title}
-        accessibilityHint={link.title}
-        cachePolicy={isIOS ? 'disk' : 'memory-disk'} // cant control playback with memory-disk on ios
+    <>
+      <EmbedConsentDialog
+        control={consentDialogControl}
+        source={params.source}
+        onAccept={load}
       />
-    </Pressable>
+
+      <Pressable
+        style={[
+          {height: imageDims.height},
+          styles.topRadius,
+          styles.gifContainer,
+        ]}
+        onPress={onPlayPress}
+        onLayout={onLayout}
+        accessibilityRole="button"
+        accessibilityHint={_(msg`Plays the GIF`)}
+        accessibilityLabel={_(msg`Play ${link.title}`)}>
+        {(!isPrefetched || !isAnimating) && ( // If we have not loaded or are not animating, show the overlay
+          <View style={[styles.layer, styles.overlayLayer]}>
+            <View style={[styles.overlayContainer, styles.topRadius]}>
+              {!isAnimating || !isPlayerActive ? ( // Play button when not animating or not active
+                <FontAwesomeIcon icon="play" size={42} color="white" />
+              ) : (
+                // Activity indicator while gif loads
+                <ActivityIndicator size="large" color="white" />
+              )}
+            </View>
+          </View>
+        )}
+        <Image
+          source={{
+            uri:
+              !isPrefetched || (isWeb && !isAnimating)
+                ? link.thumb
+                : params.playerUri,
+          }} // Web uses the thumb to control playback
+          style={{flex: 1}}
+          ref={imageRef}
+          onLoad={onLoad}
+          autoplay={isAnimating}
+          contentFit="contain"
+          accessibilityIgnoresInvertColors
+          accessibilityLabel={link.title}
+          accessibilityHint={link.title}
+          cachePolicy={isIOS ? 'disk' : 'memory-disk'} // cant control playback with memory-disk on ios
+        />
+      </Pressable>
+    </>
   )
 }
 
diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
index cf2db5b33..9fdede877 100644
--- a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
@@ -13,20 +13,23 @@ import Animated, {
   useAnimatedRef,
   useFrameCallback,
 } from 'react-native-reanimated'
-import {Image} from 'expo-image'
-import {WebView} from 'react-native-webview'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
+import {WebView} from 'react-native-webview'
+import {Image} from 'expo-image'
+import {AppBskyEmbedExternal} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/native'
-import {AppBskyEmbedExternal} from '@atproto/api'
-import {EmbedPlayerParams, getPlayerAspect} from 'lib/strings/embed-player'
+
+import {NavigationProp} from '#/lib/routes/types'
+import {EmbedPlayerParams, getPlayerAspect} from '#/lib/strings/embed-player'
+import {isNative} from '#/platform/detection'
+import {useExternalEmbedsPrefs} from '#/state/preferences'
+import {atoms as a} from '#/alf'
+import {useDialogControl} from '#/components/Dialog'
+import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
 import {EventStopper} from '../EventStopper'
-import {isNative} from 'platform/detection'
-import {NavigationProp} from 'lib/routes/types'
-import {useExternalEmbedsPrefs} from 'state/preferences'
-import {useModalControls} from 'state/modals'
 
 interface ShouldStartLoadRequest {
   url: string
@@ -48,7 +51,7 @@ function PlaceholderOverlay({
   if (isPlayerActive && !isLoading) return null
 
   return (
-    <View style={[styles.layer, styles.overlayLayer]}>
+    <View style={[a.absolute, a.inset_0, styles.overlayLayer]}>
       <Pressable
         accessibilityRole="button"
         accessibilityLabel={_(msg`Play Video`)}
@@ -89,7 +92,7 @@ function Player({
   if (!isPlayerActive) return null
 
   return (
-    <EventStopper style={[styles.layer, styles.playerLayer]}>
+    <EventStopper style={[a.absolute, a.inset_0, styles.playerLayer]}>
       <WebView
         javaScriptEnabled={true}
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
@@ -119,7 +122,7 @@ export function ExternalPlayer({
   const insets = useSafeAreaInsets()
   const windowDims = useWindowDimensions()
   const externalEmbedsPrefs = useExternalEmbedsPrefs()
-  const {openModal} = useModalControls()
+  const consentDialogControl = useDialogControl()
 
   const [isPlayerActive, setPlayerActive] = React.useState(false)
   const [isLoading, setIsLoading] = React.useState(true)
@@ -187,37 +190,47 @@ export function ExternalPlayer({
       event.preventDefault()
 
       if (externalEmbedsPrefs?.[params.source] === undefined) {
-        openModal({
-          name: 'embed-consent',
-          source: params.source,
-          onAccept: () => {
-            setPlayerActive(true)
-          },
-        })
+        consentDialogControl.open()
         return
       }
 
       setPlayerActive(true)
     },
-    [externalEmbedsPrefs, openModal, params.source],
+    [externalEmbedsPrefs, consentDialogControl, params.source],
   )
 
+  const onAcceptConsent = React.useCallback(() => {
+    setPlayerActive(true)
+  }, [])
+
   return (
-    <Animated.View ref={viewRef} collapsable={false} style={[aspect]}>
-      {link.thumb && (!isPlayerActive || isLoading) && (
-        <Image
-          style={[{flex: 1}, styles.topRadius]}
-          source={{uri: link.thumb}}
-          accessibilityIgnoresInvertColors
-        />
-      )}
-      <PlaceholderOverlay
-        isLoading={isLoading}
-        isPlayerActive={isPlayerActive}
-        onPress={onPlayPress}
+    <>
+      <EmbedConsentDialog
+        control={consentDialogControl}
+        source={params.source}
+        onAccept={onAcceptConsent}
       />
-      <Player isPlayerActive={isPlayerActive} params={params} onLoad={onLoad} />
-    </Animated.View>
+
+      <Animated.View ref={viewRef} collapsable={false} style={[aspect]}>
+        {link.thumb && (!isPlayerActive || isLoading) && (
+          <Image
+            style={[a.flex_1, styles.topRadius]}
+            source={{uri: link.thumb}}
+            accessibilityIgnoresInvertColors
+          />
+        )}
+        <PlaceholderOverlay
+          isLoading={isLoading}
+          isPlayerActive={isPlayerActive}
+          onPress={onPlayPress}
+        />
+        <Player
+          isPlayerActive={isPlayerActive}
+          params={params}
+          onLoad={onLoad}
+        />
+      </Animated.View>
+    </>
   )
 }
 
@@ -226,13 +239,6 @@ const styles = StyleSheet.create({
     borderTopLeftRadius: 6,
     borderTopRightRadius: 6,
   },
-  layer: {
-    position: 'absolute',
-    top: 0,
-    left: 0,
-    right: 0,
-    bottom: 0,
-  },
   overlayContainer: {
     flex: 1,
     justifyContent: 'center',