From 1801af02b3481fe7e171023cf1e0594d39531116 Mon Sep 17 00:00:00 2001 From: feruz Date: Sat, 21 Mar 2026 08:25:59 +0200 Subject: [PATCH 1/6] dynamic player size --- .../postHtmlRenderer/postHtmlRenderer.tsx | 19 +++++++- .../videoPlayer/videoPlayerView.tsx | 46 +++++++++++++++---- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/components/postHtmlRenderer/postHtmlRenderer.tsx b/src/components/postHtmlRenderer/postHtmlRenderer.tsx index 8b9e52069f..4771c85288 100644 --- a/src/components/postHtmlRenderer/postHtmlRenderer.tsx +++ b/src/components/postHtmlRenderer/postHtmlRenderer.tsx @@ -440,6 +440,15 @@ export const PostHtmlRenderer = memo( [_minTableColWidth, contentWidth], ); + // Extract thumbnail for 3Speak video orientation detection + const _speakThumbnail = useMemo(() => { + if (metadata?.type === '3speak/video' || metadata?.video?.info?.platform === '3speak') { + const images = metadata?.image; + return Array.isArray(images) ? images[0] : images; + } + return undefined; + }, [metadata]); + // iframe renderer for rendering iframes in body const _iframeRenderer = useCallback( function IframeRenderer(props) { @@ -454,12 +463,18 @@ export const PostHtmlRenderer = memo( }; return ; } else { + const isSpeakEmbed = /3speak\.tv/i.test(iframeProps.source.uri || ''); return ( - + ); } }, - [isComment, handleVideoPress, contentWidth], + [isComment, handleVideoPress, contentWidth, _speakThumbnail], ); const tagsStyles = useMemo( diff --git a/src/components/videoPlayer/videoPlayerView.tsx b/src/components/videoPlayer/videoPlayerView.tsx index b83b974f15..4720c3d0a6 100644 --- a/src/components/videoPlayer/videoPlayerView.tsx +++ b/src/components/videoPlayer/videoPlayerView.tsx @@ -1,5 +1,11 @@ import React, { useState, useRef, useEffect } from 'react'; -import { View, StyleSheet, ActivityIndicator, useWindowDimensions } from 'react-native'; +import { + View, + StyleSheet, + ActivityIndicator, + useWindowDimensions, + Image as RNImage, +} from 'react-native'; import WebView from 'react-native-webview'; import YoutubeIframe, { InitialPlayerParams } from 'react-native-youtube-iframe'; @@ -17,6 +23,8 @@ interface VideoPlayerProps { uri?: string; // prop for youtube player disableAutoplay?: boolean; + // thumbnail URL used to detect portrait video orientation + thumbnailUrl?: string; } const VideoPlayer = ({ @@ -26,6 +34,7 @@ const VideoPlayer = ({ contentWidth, mode, disableAutoplay, + thumbnailUrl, }: VideoPlayerProps) => { const dim = useWindowDimensions(); const videoPlayer = useRef(null); @@ -40,15 +49,33 @@ const VideoPlayer = ({ const lockedOrientation = useAppSelector((state) => state.ui.lockedOrientation); const playerWidth = contentWidth || dim.width; - const defaultHeight = playerWidth * (9 / 16); - const [playerHeight, setPlayerHeight] = useState(defaultHeight); + const [playerHeight, setPlayerHeight] = useState(playerWidth * (9 / 16)); const checkSrcRegex = /(.*?)\.(mp4|webm|ogg)$/gi; const isExtensionType = mode === 'uri' ? uri.match(checkSrcRegex) : false; - // Reset height when URI changes + // Reset height when URI changes; detect portrait from thumbnail if available useEffect(() => { setPlayerHeight(playerWidth * (9 / 16)); - }, [uri, playerWidth]); + + if (thumbnailUrl) { + // Load the thumbnail to detect its aspect ratio + RNImage.getSize( + thumbnailUrl, + (w: number, h: number) => { + if (w > 0 && h > 0) { + const ratio = h / w; + if (ratio > 1.05) { + // Portrait or square — update player height + setPlayerHeight(playerWidth * ratio); + } + } + }, + () => { + // Ignore thumbnail load errors + }, + ); + } + }, [uri, playerWidth, thumbnailUrl]); useEffect(() => { if (isFullScreen) { @@ -210,15 +237,16 @@ const VideoPlayer = ({