From eb4bdefbfe9044cb7cfdadf8c2eef5ede1edaa2b Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 3 Jun 2026 13:56:03 -0400 Subject: [PATCH] Reduce iOS memory pressure: lightbox windowing, memory warning handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Lightbox pager: only mount ±1 neighbor of the current page so a multi-image post no longer decodes every fullsize image up front. - App: respond to iOS memoryWarning events by clearing expo-image's in-memory cache, giving the GC a stronger hint before jetsam. - GIF picker: priority="low" + recyclingKey on item Image. Minor; the picker's masonry grid still needs proper row virtualization. Hypothesis-driven against APP-D9 breadcrumb patterns; needs TestFlight validation before claiming any rate reduction. --- src/App.tsx | 12 +++++++ src/components/Lightbox/pager/ImagePager.tsx | 34 ++++++++++--------- .../gifPicker/components/GifPickerItem.tsx | 2 ++ 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d41f28bdb5..922c1345d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,14 @@ import '#/logger/sentry/setup' import {Fragment, useEffect, useState} from 'react' +import {DeviceEventEmitter} from 'react-native' import {GestureHandlerRootView} from 'react-native-gesture-handler' import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller' import { initialWindowMetrics, SafeAreaProvider, } from 'react-native-safe-area-context' +import {Image} from 'expo-image' import * as ScreenOrientation from 'expo-screen-orientation' import * as SplashScreen from 'expo-splash-screen' import * as SystemUI from 'expo-system-ui' @@ -141,6 +143,16 @@ function InnerApp() { }) }, [l]) + // Respond to iOS memory warnings before the OS jetsams us. expo-image trims + // its own cache on warning, but explicitly clearing it gives the GC a stronger + // hint and frees decoded bitmaps no longer in active use. + useEffect(() => { + const sub = DeviceEventEmitter.addListener('memoryWarning', () => { + void Image.clearMemoryCache() + }) + return () => sub.remove() + }, []) + return ( diff --git a/src/components/Lightbox/pager/ImagePager.tsx b/src/components/Lightbox/pager/ImagePager.tsx index c960490e91..e4c49aebb5 100644 --- a/src/components/Lightbox/pager/ImagePager.tsx +++ b/src/components/Lightbox/pager/ImagePager.tsx @@ -415,22 +415,24 @@ function ImageView({ style={styles.pager}> {images.map((imageSrc, i) => ( - + {Math.abs(i - imageIndex) <= 1 ? ( + + ) : null} ))} diff --git a/src/features/gifPicker/components/GifPickerItem.tsx b/src/features/gifPicker/components/GifPickerItem.tsx index e307e064a3..4d42225eb4 100644 --- a/src/features/gifPicker/components/GifPickerItem.tsx +++ b/src/features/gifPicker/components/GifPickerItem.tsx @@ -52,6 +52,8 @@ export function GifPickerItem({ accessibilityLabel={gif.title} accessibilityHint="" cachePolicy="none" + priority="low" + recyclingKey={gif.id} accessibilityIgnoresInvertColors /> )}