diff --git a/apps/mobile/src/components/ui/video/VideoPlayer.tsx b/apps/mobile/src/components/ui/video/VideoPlayer.tsx index 4b0347a8c..f368580f5 100644 --- a/apps/mobile/src/components/ui/video/VideoPlayer.tsx +++ b/apps/mobile/src/components/ui/video/VideoPlayer.tsx @@ -21,7 +21,7 @@ export function VideoPlayer({ view: FeedViewType }) { const [isFullScreen, setIsFullScreen] = useState(false) - const viewViewRef = useRef(null) + const videoViewRef = useRef(null) const player = useVideoPlayer(source, (player) => { player.loop = true player.muted = true @@ -31,12 +31,16 @@ export function VideoPlayer({ return ( { - viewViewRef.current?.enterFullscreen() + if (!videoViewRef.current) { + console.warn("VideoView ref is not set") + return + } + videoViewRef.current?.enterFullscreen() player.muted = false }} > - attachment.mime_type?.startsWith("audio/"), + const audioOrVideo = entry?.attachments?.find( + (attachment) => + attachment.mime_type?.startsWith("audio/") || attachment.mime_type?.startsWith("video/"), ) const estimatedMins = useMemo(() => { - const durationInSeconds = formatTimeToSeconds(audio?.duration_in_seconds) + const durationInSeconds = formatTimeToSeconds(audioOrVideo?.duration_in_seconds) return durationInSeconds && Math.floor(durationInSeconds / 60) - }, [audio?.duration_in_seconds]) + }, [audioOrVideo?.duration_in_seconds]) if (!entry) return @@ -135,7 +138,7 @@ export const EntryNormalItem = memo( )} {view !== FeedViewType.Notifications && ( - + )} @@ -146,27 +149,45 @@ export const EntryNormalItem = memo( EntryNormalItem.displayName = "EntryNormalItem" const ThumbnailImage = ({ - view, playingAudioUrl, entry, }: { - view: FeedViewType playingAudioUrl: string | null entry: EntryModel }) => { const feed = useFeed(entry?.feedId as string) const thumbnailRatio = useUISettingKey("thumbnailRatio") - const coverImage = entry?.media?.[0] - const image = coverImage?.url || (view === FeedViewType.Audios ? feed?.image : null) - const blurhash = coverImage?.blurhash + const mediaModel = entry?.media?.find( + (media) => media.type === "photo" || (media.type === "video" && media.preview_image_url), + ) + const image = mediaModel?.type === "photo" ? mediaModel?.url : null // mediaModel?.preview_image_url + const blurhash = mediaModel?.blurhash const audio = entry?.attachments?.find((attachment) => attachment.mime_type?.startsWith("audio/")) const audioState = getAttachmentState(playingAudioUrl ?? undefined, audio) const isPlaying = audioState === "playing" const isLoading = audioState === "loading" - const handlePressPlayAudio = useCallback(() => { + const video = entry?.media?.find((media) => media.type === "video") + const videoViewRef = useRef(null) + const videoPlayer = useVideoPlayer(video?.url ?? "") + const [showVideoNativeControlsForAndroid, setShowVideoNativeControlsForAndroid] = useState(false) + + const handlePressPlay = useCallback(() => { + if (video) { + setShowVideoNativeControlsForAndroid(true) + // Ensure the nativeControls is ready before entering fullscreen for Android + setTimeout(() => { + videoViewRef.current?.enterFullscreen() + }, 0) + if (videoPlayer.playing) { + videoPlayer.pause() + } else { + videoPlayer.play() + } + return + } if (!audio) return if (isLoading) return if (isPlaying) { @@ -179,10 +200,11 @@ const ThumbnailImage = ({ artist: feed?.title, artwork: image, }) - }, [audio, entry?.title, feed?.title, image, isLoading, isPlaying]) + }, [audio, entry?.title, feed?.title, image, isLoading, isPlaying, video, videoPlayer]) + if (!image && !audio && !video) return null return ( - + {image && (thumbnailRatio === "square" ? ( @@ -190,18 +212,39 @@ const ThumbnailImage = ({ ))} + {video && ( + + { + videoPlayer.pause() + setShowVideoNativeControlsForAndroid(false) + }} + /> + + )} + {/* Show feed icon if no image but audio is present */} {audio && !image && } - {audio && ( + {(video || audio) && (