From cd5cd02535b4c924976d40d20d53f7b817991416 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 30 Jan 2026 17:28:42 +0200 Subject: [PATCH] video player gif presentation style --- .../VideoEmbed/GifPresentationControls.tsx | 85 +++++++++++++++++++ .../VideoEmbedInner/VideoEmbedInnerNative.tsx | 41 ++++++--- .../VideoEmbedInner/VideoEmbedInnerWeb.tsx | 1 + .../web-controls/VideoControls.tsx | 13 +++ .../Post/Embed/VideoEmbed/index.web.tsx | 1 + 5 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx diff --git a/src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx b/src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx new file mode 100644 index 0000000000..885044056b --- /dev/null +++ b/src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx @@ -0,0 +1,85 @@ +import {Pressable, StyleSheet, View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {atoms as a, useTheme} from '#/alf' +import {Fill} from '#/components/Fill' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' +import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' + +export function GifPresentationControls({ + onPress, + isPlaying, + isLoading, +}: { + onPress: () => void + isPlaying: boolean + isLoading?: boolean +}) { + const {_} = useLingui() + const t = useTheme() + + return ( + <> + + {isLoading ? ( + + + + ) : !isPlaying ? ( + + ) : undefined} + + {!isPlaying && ( + + )} + + + GIF + + + + ) +} + +const styles = StyleSheet.create({ + gifBadgeContainer: { + backgroundColor: 'rgba(0, 0, 0, 0.75)', + borderRadius: 6, + paddingHorizontal: 6, + paddingVertical: 3, + position: 'absolute', + right: 5, + bottom: 5, + zIndex: 2, + }, + gifBadge: { + color: 'white', + fontSize: 7, + fontWeight: '600', + }, +}) diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx index ecc36dc33b..bbcf72238b 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx @@ -15,6 +15,7 @@ import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' +import {GifPresentationControls} from '../GifPresentationControls' import {TimeIndicator} from './TimeIndicator' export function VideoEmbedInnerNative({ @@ -50,6 +51,8 @@ export function VideoEmbedInnerNative({ throw new Error(error) } + const isGif = embed.presentation === 'gif' + return ( - { - videoRef.current?.enterFullscreen(true) - }} - toggleMuted={() => { - videoRef.current?.toggleMuted() - }} - togglePlayback={() => { - videoRef.current?.togglePlayback() - }} - isPlaying={isPlaying} - timeRemaining={timeRemaining} - /> + {isGif ? ( + { + videoRef.current?.togglePlayback() + }} + isPlaying={isPlaying} + isLoading={false} + /> + ) : ( + { + videoRef.current?.enterFullscreen(true) + }} + toggleMuted={() => { + videoRef.current?.toggleMuted() + }} + togglePlayback={() => { + videoRef.current?.togglePlayback() + }} + isPlaying={isPlaying} + timeRemaining={timeRemaining} + /> + )} ) } -function VideoControls({ +function VideoPresentationControls({ enterFullscreen, toggleMuted, togglePlayback, diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx index add2a7afb4..6ae7ac5eb6 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx @@ -99,6 +99,7 @@ export function VideoEmbedInnerWeb({ onScreen={onScreen} fullscreenRef={containerRef} hasSubtitleTrack={hasSubtitleTrack} + isGif={embed.presentation === 'gif'} /> diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx index d918d3f99c..e7c7514a7f 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx @@ -27,6 +27,7 @@ import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' import {IS_WEB_MOBILE_IOS, IS_WEB_TOUCH_DEVICE} from '#/env' +import {GifPresentationControls} from '../../GifPresentationControls' import {TimeIndicator} from '../TimeIndicator' import {ControlButton} from './ControlButton' import {Scrubber} from './Scrubber' @@ -44,6 +45,7 @@ export function Controls({ fullscreenRef, hlsLoading, hasSubtitleTrack, + isGif, }: { videoRef: React.RefObject hlsRef: React.RefObject @@ -55,6 +57,7 @@ export function Controls({ fullscreenRef: React.RefObject hlsLoading: boolean hasSubtitleTrack: boolean + isGif: boolean }) { const { play, @@ -287,6 +290,16 @@ export function Controls({ ((focused || autoplayDisabled) && !playing) || (interactingViaKeypress ? hasFocus : hovered) + if (isGif) { + return ( + + ) + } + return (