diff --git a/src/components/dialogs/EmbedConsent.tsx b/src/components/dialogs/EmbedConsent.tsx
new file mode 100644
index 0000000000..c3fefd9f09
--- /dev/null
+++ b/src/components/dialogs/EmbedConsent.tsx
@@ -0,0 +1,119 @@
+import React, {useCallback} from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {
+ type EmbedPlayerSource,
+ embedPlayerSources,
+ externalEmbedLabels,
+} from '#/lib/strings/embed-player'
+import {useSetExternalEmbedPref} from '#/state/preferences'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import * as Dialog from '#/components/Dialog'
+import {Button, ButtonText} from '../Button'
+import {Text} from '../Typography'
+
+export function EmbedConsentDialog({
+ control,
+ source,
+ onAccept,
+}: {
+ control: Dialog.DialogControlProps
+ source: EmbedPlayerSource
+ onAccept: () => void
+}) {
+ const {_} = useLingui()
+ const t = useTheme()
+ const setExternalEmbedPref = useSetExternalEmbedPref()
+ const {gtMobile} = useBreakpoints()
+
+ const onShowAllPress = useCallback(() => {
+ for (const key of embedPlayerSources) {
+ setExternalEmbedPref(key, 'show')
+ }
+ onAccept()
+ control.close()
+ }, [control, onAccept, setExternalEmbedPref])
+
+ const onShowPress = useCallback(() => {
+ setExternalEmbedPref(source, 'show')
+ onAccept()
+ control.close()
+ }, [control, onAccept, setExternalEmbedPref, source])
+
+ const onHidePress = useCallback(() => {
+ setExternalEmbedPref(source, 'hide')
+ control.close()
+ }, [control, setExternalEmbedPref, source])
+
+ return (
+
+
+
+
+
+
+ External Media
+
+
+
+
+
+ This content is hosted by {externalEmbedLabels[source]}. Do you
+ want to enable external media?
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx
index e0bcc2f0fd..cc0f9c8b83 100644
--- a/src/state/modals/index.tsx
+++ b/src/state/modals/index.tsx
@@ -3,7 +3,6 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
-import {EmbedPlayerSource} from '#/lib/strings/embed-player'
import {GalleryModel} from '#/state/models/media/gallery'
import {ImageModel} from '#/state/models/media/image'
import {ThreadgateSetting} from '../queries/threadgate'
@@ -125,12 +124,6 @@ export interface LinkWarningModal {
share?: boolean
}
-export interface EmbedConsentModal {
- name: 'embed-consent'
- source: EmbedPlayerSource
- onAccept: () => void
-}
-
export interface InAppBrowserConsentModal {
name: 'in-app-browser-consent'
href: string
@@ -169,7 +162,6 @@ export type Modal =
// Generic
| LinkWarningModal
- | EmbedConsentModal
| InAppBrowserConsentModal
const ModalContext = React.createContext<{
diff --git a/src/view/com/modals/EmbedConsent.tsx b/src/view/com/modals/EmbedConsent.tsx
deleted file mode 100644
index 9419447288..0000000000
--- 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 (
-
-
- External Media
-
-
-
-
- This content is hosted by {externalEmbedLabels[source]}. Do you want
- to enable external media?
-
-
-
-
-
- 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.
-
-
-
-
-
-
- Enable External Media
-
-
-
-
-
-
-
- Enable {externalEmbedLabels[source]} only
-
-
-
-
-
-
-
- No thanks
-
-
-
-
- )
-}
-
-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 85ffccf12b..6524813015 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 =
- } else if (activeModal?.name === 'embed-consent') {
- snapPoints = EmbedConsentModal.snapPoints
- element =
} else if (activeModal?.name === 'in-app-browser-consent') {
snapPoints = InAppBrowserConsentModal.snapPoints
element =
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 7e5d548ace..f95c748111 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 =
} else if (modal.name === 'link-warning') {
element =
- } else if (modal.name === 'embed-consent') {
- element =
} else {
return null
}
diff --git a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx b/src/view/com/util/post-embeds/ExternalGifEmbed.tsx
index f06c8b794d..b2720752ca 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 (
-
- {(!isPrefetched || !isAnimating) && ( // If we have not loaded or are not animating, show the overlay
-
-
- {!isAnimating || !isPlayerActive ? ( // Play button when not animating or not active
-
- ) : (
- // Activity indicator while gif loads
-
- )}
-
-
- )}
-
+
-
+
+
+ {(!isPrefetched || !isAnimating) && ( // If we have not loaded or are not animating, show the overlay
+
+
+ {!isAnimating || !isPlayerActive ? ( // Play button when not animating or not active
+
+ ) : (
+ // Activity indicator while gif loads
+
+ )}
+
+
+ )}
+
+
+ >
)
}
diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
index cf2db5b333..9fdede877d 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 (
-
+
+
{
- setPlayerActive(true)
- },
- })
+ consentDialogControl.open()
return
}
setPlayerActive(true)
},
- [externalEmbedsPrefs, openModal, params.source],
+ [externalEmbedsPrefs, consentDialogControl, params.source],
)
+ const onAcceptConsent = React.useCallback(() => {
+ setPlayerActive(true)
+ }, [])
+
return (
-
- {link.thumb && (!isPlayerActive || isLoading) && (
-
- )}
-
+
-
-
+
+
+ {link.thumb && (!isPlayerActive || isLoading) && (
+
+ )}
+
+
+
+ >
)
}
@@ -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',