From 45754afc691d88f238c3f57bcd9af7cffe582c22 Mon Sep 17 00:00:00 2001 From: goon-foss Date: Wed, 16 Sep 2026 21:52:51 +0200 Subject: [PATCH] fix(mobile): Edge bez agresywnego kadrowania na foldzie i przy poziomych klipach Zgloszenie Jana (fold): rozlozony ekran jest prawie kwadratowy, a klip byl skalowany do wypelnienia i zostawal z niego srodek. Teraz liczymy, ile by sie ucielo: do 30 % zostaje wypelnienie (pionowy klip na zwyklym telefonie traci ok. 20 % szerokosci i to jest OK), powyzej cale wideo i czarne pasy. Podpis i kolumna przyciskow stoja przy krawedziach ekranu, wiec przy pasach same wchodza na pasy zamiast na wideo. Poster pod wideo dostaje to samo kadrowanie, zeby nie mrugalo przy starcie. Zysk takze na zwyklym telefonie: ok. 19 % klipow RedGifs jest poziomych i do tej pory byly rozdmuchane na caly ekran. Zmierzone na emulatorze przez rozmiar powierzchni wideo (dumpsys activity top): - telefon 1080x2400: pionowy klip 1242x2209 (wypelnienie, jak dotad), poziomy 1080x607 i klip 3:4 1080x1440 (pasy zamiast kadrowania) - rozlozony fold 1768x2208: pionowy klip 1140x1947 z pasami po 314 px, poziomy 1768x1298 Co-Authored-By: Claude Opus 5 --- mobile/src/changelog.ts | 8 ++++++++ mobile/src/components/swipe/ClipCell.tsx | 26 +++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/mobile/src/changelog.ts b/mobile/src/changelog.ts index e0e9340..4c1c6ba 100644 --- a/mobile/src/changelog.ts +++ b/mobile/src/changelog.ts @@ -35,6 +35,14 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + id: '2026-09-16d', + date: 'September 2026', + items: [ + 'Edge on a folding phone or a tablet: clips are no longer cropped down to the middle. When the screen and the clip are shaped too differently, you get the whole clip with black bars instead, and the caption and buttons sit on those bars. Wide clips gain from this on normal phones too.', + ], + minRuntime: '1.4', + }, { id: '2026-09-16c', date: 'September 2026', diff --git a/mobile/src/components/swipe/ClipCell.tsx b/mobile/src/components/swipe/ClipCell.tsx index 7bc7410..19707ec 100644 --- a/mobile/src/components/swipe/ClipCell.tsx +++ b/mobile/src/components/swipe/ClipCell.tsx @@ -22,7 +22,7 @@ import { Image } from 'expo-image'; import type { VideoPlayer } from 'expo-video'; import { VideoView } from 'expo-video'; import React from 'react'; -import { Animated, Pressable, StyleSheet, Text, View } from 'react-native'; +import { Animated, Pressable, StyleSheet, Text, View, useWindowDimensions } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -49,6 +49,26 @@ function ClipCellBase({ item, active, player, muted, liked, onToggleMute, onLike const [ready, setReady] = React.useState(false); const heart = React.useRef(new Animated.Value(0)).current; const insets = useSafeAreaInsets(); + + // Kadrowac czy dolozyc czarne pasy. + // + // `cover` wypelnia ekran i tnie to, co wystaje. Na zwyklym telefonie pionowy klip traci + // tak ze 20 % szerokosci i nikomu to nie przeszkadza, ale na rozlozonym foldzie ekran + // jest prawie kwadratowy i z pionowego klipu zostaje srodek (zgloszenie Jana + // 2026-09-16). To samo dotyczy klipow poziomych, ktorych na RedGifs jest ok. 19 %. + // Liczymy, ile by sie ucielo: do `MAX_CROP` zostaje `cover`, powyzej `contain`, czyli + // cale wideo i pasy po bokach albo u gory. Podpis i kolumna przyciskow stoja przy + // krawedziach ekranu, wiec przy pasach same wchodza na pasy zamiast na wideo. + // `useWindowDimensions` reaguje na zlozenie i obrot, wiec kadr zmienia sie od razu. + const win = useWindowDimensions(); + const fit = React.useMemo<'cover' | 'contain'>(() => { + const MAX_CROP = 1.3; + const clipAspect = clip.width && clip.height ? clip.width / clip.height : 9 / 16; + const screenAspect = win.width / Math.max(win.height, 1); + if (!clipAspect || !screenAspect) return 'cover'; + const crop = Math.max(screenAspect / clipAspect, clipAspect / screenAspect); + return crop > MAX_CROP ? 'contain' : 'cover'; + }, [clip.width, clip.height, win.width, win.height]); const lift = React.useMemo( () => barHidden.interpolate({ @@ -154,14 +174,14 @@ function ClipCellBase({ item, active, player, muted, liked, onToggleMute, onLike {poster ? ( - + ) : null} {active && player ? ( ) : null}