diff --git a/src/components/AltBadgeWithDialog.tsx b/src/components/AltBadgeWithDialog.tsx
new file mode 100644
index 0000000000..07cf9544f0
--- /dev/null
+++ b/src/components/AltBadgeWithDialog.tsx
@@ -0,0 +1,93 @@
+import {Pressable} from 'react-native'
+import {Trans, useLingui} from '@lingui/react/macro'
+
+import {HITSLOP_20} from '#/lib/constants'
+import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
+import {atoms as a, useTheme} from '#/alf'
+import * as Prompt from '#/components/Prompt'
+import {Text} from '#/components/Typography'
+
+const positionStyles = {
+ 'top-left': {
+ top: a.p_xs.padding,
+ left: a.p_xs.padding,
+ },
+ 'top-right': {
+ top: a.p_xs.padding,
+ right: a.p_xs.padding,
+ },
+ 'bottom-left': {
+ bottom: a.p_xs.padding,
+ left: a.p_xs.padding,
+ },
+ 'bottom-right': {
+ bottom: a.p_xs.padding,
+ right: a.p_xs.padding,
+ },
+}
+
+export function AltBadgeWithDialog({
+ text,
+ position,
+}: {
+ text: string
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
+}) {
+ const t = useTheme()
+ const {t: l} = useLingui()
+ const large = useLargeAltBadgeEnabled()
+ const control = Prompt.usePromptControl()
+
+ const pos = position ? [a.absolute, positionStyles[position]] : {}
+
+ return (
+ <>
+ [
+ a.justify_center,
+ a.rounded_sm,
+ a.p_xs,
+ a.z_10,
+ t.atoms.bg_contrast_25,
+ large && {
+ padding: 6,
+ },
+ {
+ opacity: 0.8,
+ },
+ pos,
+ s.hovered || s.pressed
+ ? [
+ {
+ opacity: 1,
+ },
+ ]
+ : [],
+ ]}>
+
+ ALT
+
+
+
+
+
+
+ Alt Text
+
+ {text}
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/components/Post/Embed/ExternalEmbed/Gif.tsx b/src/components/Post/Embed/ExternalEmbed/Gif.tsx
index 4cf95e0fab..24ea286ada 100644
--- a/src/components/Post/Embed/ExternalEmbed/Gif.tsx
+++ b/src/components/Post/Embed/ExternalEmbed/Gif.tsx
@@ -66,19 +66,7 @@ export function GifEmbed({
a.overflow_hidden,
{backgroundColor: t.palette.black},
]}>
-
+
@@ -64,74 +60,41 @@ export function GifPresentationControls({
]}
/>
)}
-
-
- GIF
-
+
+
+
+ GIF
+
+
+ {altText && }
- {altText && }
>
)
}
-
-function AltBadge({text}: {text: string}) {
- const control = Prompt.usePromptControl()
- const {_} = useLingui()
-
- return (
- <>
-
-
- ALT
-
-
-
-
-
- Alt Text
-
- {text}
-
-
- control.close()}
- cta={_(msg`Close`)}
- color="secondary"
- />
-
-
- >
- )
-}
-
-const styles = StyleSheet.create({
- gifBadgeContainer: {
- backgroundColor: 'rgba(0, 0, 0, 0.75)',
- borderRadius: 6,
- paddingHorizontal: 4,
- paddingVertical: 3,
- position: 'absolute',
- left: 6,
- bottom: 6,
- zIndex: 2,
- },
- altBadgeContainer: {
- backgroundColor: 'rgba(0, 0, 0, 0.75)',
- borderRadius: 6,
- paddingHorizontal: 4,
- paddingVertical: 3,
- position: 'absolute',
- right: 6,
- bottom: 6,
- zIndex: 2,
- },
-})
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx
index 05e8b84581..f38677ea1d 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx
@@ -8,6 +8,7 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_30} from '#/lib/constants'
import {useAutoplayDisabled} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf'
+import {AltBadgeWithDialog} from '#/components/AltBadgeWithDialog'
import {useIsWithinMessage} from '#/components/dms/MessageContext'
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause'
@@ -98,19 +99,24 @@ export function VideoEmbedInnerNative({
altText={embed.alt}
/>
) : (
- {
- videoRef.current?.enterFullscreen(true)
- }}
- toggleMuted={() => {
- videoRef.current?.toggleMuted()
- }}
- togglePlayback={() => {
- videoRef.current?.togglePlayback()
- }}
- isPlaying={isPlaying}
- timeRemaining={timeRemaining}
- />
+ <>
+ {
+ videoRef.current?.enterFullscreen(true)
+ }}
+ toggleMuted={() => {
+ videoRef.current?.toggleMuted()
+ }}
+ togglePlayback={() => {
+ videoRef.current?.togglePlayback()
+ }}
+ isPlaying={isPlaying}
+ timeRemaining={timeRemaining}
+ />
+ {embed.alt && (
+
+ )}
+ >
)}
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
index d56c0b234d..b97317dfbf 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
@@ -7,6 +7,8 @@ import type * as HlsTypes from 'hls.js'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {atoms as a} from '#/alf'
+import {AltBadgeWithDialog} from '#/components/AltBadgeWithDialog'
+import {useFullscreen} from '#/components/hooks/useFullscreen'
import * as BandwidthEstimate from './bandwidth-estimate'
import {Controls} from './web-controls/VideoControls'
@@ -30,6 +32,8 @@ export function VideoEmbedInnerWeb({
const [hlsLoading, setHlsLoading] = useState(false)
const figId = useId()
const {_} = useLingui()
+ const [isFullscreen] = useFullscreen(containerRef)
+ const isGif = embed.presentation === 'gif'
// send error up to error boundary
const [error, setError] = useState(null)
@@ -77,6 +81,9 @@ export function VideoEmbedInnerWeb({
)}
+ {!isFullscreen && !isGif && embed.alt && (
+
+ )}