diff --git a/packages/react-native/Libraries/Image/ImageSource.d.ts b/packages/react-native/Libraries/Image/ImageSource.d.ts index 1b7d51729b5..2d47b6ba194 100644 --- a/packages/react-native/Libraries/Image/ImageSource.d.ts +++ b/packages/react-native/Libraries/Image/ImageSource.d.ts @@ -51,7 +51,7 @@ export interface ImageURISource { * to a URL load request, no attempt is made to load the data from the originating source, * and the load is considered to have failed. * - * @platform ios (for `force-cache` and `only-if-cached`) + * @platform ios (for `force-cache`) */ cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached' | undefined; /** diff --git a/packages/react-native/Libraries/Image/ImageSource.js b/packages/react-native/Libraries/Image/ImageSource.js index a489de2ff07..1698c945b0b 100644 --- a/packages/react-native/Libraries/Image/ImageSource.js +++ b/packages/react-native/Libraries/Image/ImageSource.js @@ -66,7 +66,7 @@ export interface ImageURISource { * to a URL load request, no attempt is made to load the data from the originating source, * and the load is considered to have failed. * - * @platform ios (for `force-cache` and `only-if-cached`) + * @platform ios (for `force-cache`) */ +cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached'); diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index e6594e97579..9349fd21d89 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3379,6 +3379,7 @@ public final class com/facebook/react/modules/fresco/FrescoModule$Companion { public final class com/facebook/react/modules/fresco/ImageCacheControl : java/lang/Enum { public static final field DEFAULT Lcom/facebook/react/modules/fresco/ImageCacheControl; + public static final field ONLY_IF_CACHED Lcom/facebook/react/modules/fresco/ImageCacheControl; public static final field RELOAD Lcom/facebook/react/modules/fresco/ImageCacheControl; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/modules/fresco/ImageCacheControl; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ImageCacheControl.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ImageCacheControl.kt index 689edb1d6f0..acb04ea7beb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ImageCacheControl.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ImageCacheControl.kt @@ -15,4 +15,11 @@ public enum class ImageCacheControl { * be used to satisfy a URL load request. */ RELOAD, + /** + * The existing cache data will be used to satisfy a request, regardless of its age or expiration + * date. If there is no existing data in the cache corresponding to a URL load request, no attempt + * is made to load the data from the originating source, and the load is considered to have + * failed. + */ + ONLY_IF_CACHED, } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.kt index 49717cfa51a..502f220a090 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.kt @@ -43,6 +43,9 @@ internal class ReactOkHttpNetworkFetcher(private val okHttpClient: OkHttpClient) ImageCacheControl.RELOAD -> { cacheControlBuilder.noCache() } + ImageCacheControl.ONLY_IF_CACHED -> { + cacheControlBuilder.onlyIfCached() + } ImageCacheControl.DEFAULT -> { // No-op } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt index b384b5e2f2e..5365d689f5f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt @@ -41,6 +41,7 @@ import com.facebook.imagepipeline.image.ImageInfo import com.facebook.imagepipeline.postprocessors.IterativeBoxBlurPostProcessor import com.facebook.imagepipeline.request.BasePostprocessor import com.facebook.imagepipeline.request.ImageRequest +import com.facebook.imagepipeline.request.ImageRequest.RequestLevel import com.facebook.imagepipeline.request.ImageRequestBuilder import com.facebook.imagepipeline.request.Postprocessor import com.facebook.react.bridge.ReactContext @@ -311,10 +312,18 @@ public class ReactImageView( null, "default" -> ImageCacheControl.DEFAULT "reload" -> ImageCacheControl.RELOAD + "only-if-cached" -> ImageCacheControl.ONLY_IF_CACHED else -> ImageCacheControl.DEFAULT } } + private fun computeRequestLevel(cacheControl: ImageCacheControl): RequestLevel { + return when (cacheControl) { + ImageCacheControl.ONLY_IF_CACHED -> RequestLevel.DISK_CACHE + else -> RequestLevel.FULL_FETCH + } + } + public fun setDefaultSource(name: String?) { val newDefaultDrawable = instance.getResourceDrawable(context, name) if (defaultImageDrawable != newDefaultDrawable) { @@ -426,6 +435,7 @@ public class ReactImageView( val imageSource = this.imageSource ?: return val uri = imageSource.uri val cacheControl = imageSource.cacheControl + val requestLevel = computeRequestLevel(cacheControl) val postprocessorList = mutableListOf() iterativeBoxBlurPostProcessor?.let { postprocessorList.add(it) } @@ -445,6 +455,7 @@ public class ReactImageView( .setResizeOptions(resizeOptions) .setAutoRotateEnabled(true) .setProgressiveRenderingEnabled(progressiveRenderingEnabled) + .setLowestPermittedRequestLevel(requestLevel) if (resizeMethod == ImageResizeMethod.NONE) { imageRequestBuilder.setDownsampleOverride(DownsampleMode.NEVER) diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 814c7312ea7..81e42ad8adb 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -659,6 +659,20 @@ function CacheControlAndroidExample(): React.Node { key={reload} /> + + + Only-if-cached + + console.log(e.nativeEvent.error)} + /> + @@ -1091,9 +1105,10 @@ exports.examples = [ }, { title: 'Cache Policy', - description: ('First image will be loaded and will be cached. ' + - 'Second image is the same but will be reloaded if re-rendered ' + - 'as the cache policy is set to reload.': string), + description: `- First image will be loaded and cached. +- Second image is the same but will be reloaded if re-rendered as the cache policy is set to reload. +- Third image will never be loaded as the cache policy is set to only-if-cached and the image has not been loaded before. + `, render: function (): React.Node { return ; },