fix(mobile): removeClippedSubviews=false on grids — stop thumbnails vanishing on scroll

Android FlatList defaults removeClippedSubviews=true, which detaches off-viewport
subviews; expo-image frequently fails to re-render them when they scroll back in →
blank thumbnails (bug-report f181d382 2026-06-07, recurring). Disable on all heavy
image grids: scene grids (Scenes/Site/Studio/Tag/Performer) + movie poster grids.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jtrzupek 2026-06-08 10:18:48 +02:00
parent d4b89f16e3
commit 940d4872e3
6 changed files with 14 additions and 0 deletions

View file

@ -133,6 +133,9 @@ export function MoviesScreen() {
data={items}
numColumns={NUM_COLS}
keyExtractor={(m) => m.id}
// Android removeClippedSubviews=true blankuje plakaty po scrollu (expo-image
// nie re-renderuje odpiętych) — ten sam bug co "znikające miniaturki" scen.
removeClippedSubviews={false}
renderItem={({ item }) => (
<MoviePosterCard
movie={item}

View file

@ -273,6 +273,10 @@ export function PerformerScenesScreen() {
data={sortedScenes}
keyExtractor={(s) => s.id}
numColumns={2}
// Android default removeClippedSubviews=true odpina miniaturki poza
// viewportem i expo-image często nie re-renderuje ich po powrocie →
// "miniaturki znikają przy scrollu" (bug-report f181d382 2026-06-07).
removeClippedSubviews={false}
renderItem={({ item }) => (
<SceneTile scene={item} seenSince={seenSince} secondLine="studio" />
)}
@ -347,6 +351,7 @@ export function PerformerScenesScreen() {
key="movies-list"
data={movies}
numColumns={MOVIE_COLS}
removeClippedSubviews={false}
keyExtractor={(m) => m.id}
renderItem={({ item }) => {
// Defensive: jeśli backend kiedyś wyśle pojedynczy malformed movie,

View file

@ -129,6 +129,9 @@ export function ScenesScreen() {
data={items}
keyExtractor={(s) => s.id}
numColumns={2}
// Android removeClippedSubviews=true (default) blankuje miniaturki po scrollu —
// expo-image nie re-renderuje odpiętych subview. Bug-report "znikają miniaturki".
removeClippedSubviews={false}
renderItem={({ item }) => <SceneTile scene={item} />}
columnWrapperStyle={styles.gridRow}
ListHeaderComponent={!debouncedQ && activeCount === 0 ? <ContinueWatchingRail /> : null}

View file

@ -113,6 +113,7 @@ export function SiteScenesScreen() {
data={items}
keyExtractor={(s) => s.id}
numColumns={2}
removeClippedSubviews={false}
renderItem={({ item }) => <SceneTile scene={item} secondLine="studio" />}
columnWrapperStyle={styles.gridRow}
refreshing={isRefetching}

View file

@ -138,6 +138,7 @@ export function StudioScenesScreen() {
data={sortedItems}
keyExtractor={(s) => s.id}
numColumns={2}
removeClippedSubviews={false}
renderItem={({ item }) => (
<SceneTile scene={item} seenSince={seenSince} secondLine="performers" />
)}

View file

@ -50,6 +50,7 @@ export function TagScenesScreen() {
data={data?.items ?? []}
keyExtractor={(s) => s.id}
numColumns={2}
removeClippedSubviews={false}
renderItem={({ item }) => <SceneTile scene={item} secondLine="studio" />}
columnWrapperStyle={styles.gridRow}
refreshing={isRefetching}