diff --git a/packages/react-native/Libraries/Image/Image.android.js b/packages/react-native/Libraries/Image/Image.android.js index 9ce0947ff9d117..c687b581e871e6 100644 --- a/packages/react-native/Libraries/Image/Image.android.js +++ b/packages/react-native/Libraries/Image/Image.android.js @@ -256,14 +256,14 @@ if (ReactNativeFeatureFlags.reduceDefaultPropsInImage()) { } if (defaultSource_ != null && defaultSource_.uri != null) { - nativeProps.defaultSource = defaultSource_.uri; + nativeProps.defaultSource = defaultSource_; } if ( loadingIndicatorSource_ != null && loadingIndicatorSource_.uri != null ) { - nativeProps.loadingIndicatorSrc = loadingIndicatorSource_.uri; + nativeProps.loadingIndicatorSrc = loadingIndicatorSource_; } if (ariaLabel != null) { @@ -395,9 +395,9 @@ if (ReactNativeFeatureFlags.reduceDefaultPropsInImage()) { /* $FlowFixMe[prop-missing](>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}), - defaultSource: defaultSource ? defaultSource.uri : null, + defaultSource: defaultSource ? defaultSource : null, loadingIndicatorSrc: loadingIndicatorSource - ? loadingIndicatorSource.uri + ? loadingIndicatorSource : null, accessibilityLabel: props['aria-label'] ?? props.accessibilityLabel ?? props.alt, diff --git a/packages/react-native/Libraries/Image/ImageViewNativeComponent.js b/packages/react-native/Libraries/Image/ImageViewNativeComponent.js index d1572c5de53dbb..44165b7eaf7852 100644 --- a/packages/react-native/Libraries/Image/ImageViewNativeComponent.js +++ b/packages/react-native/Libraries/Image/ImageViewNativeComponent.js @@ -40,7 +40,7 @@ type ImageHostComponentProps = Readonly<{ src?: ?ResolvedAssetSource | ?ReadonlyArray>, headers?: ?{[string]: string}, defaultSource?: ?ImageSource | ?string, - loadingIndicatorSrc?: ?string, + loadingIndicatorSrc?: ?ImageSource | ?string, }>; interface NativeCommands { diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index 38878826922722..4dd70dbbfce7aa 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -162,7 +162,9 @@ describe('', () => { root.getRenderedOutput({props: ['defaultSource']}).toJSX(), ).toEqual( , ); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt index ec04d15097fe23..998093159c6c37 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt @@ -133,13 +133,13 @@ public constructor( } @ReactProp(name = "defaultSource") - public fun setDefaultSource(view: ReactImageView, source: String?) { + public fun setDefaultSource(view: ReactImageView, source: ReadableMap?) { view.setDefaultSource(source) } // In JS this is Image.props.loadingIndicatorSource.uri @ReactProp(name = "loadingIndicatorSrc") - public fun setLoadingIndicatorSource(view: ReactImageView, source: String?) { + public fun setLoadingIndicatorSource(view: ReactImageView, source: ReadableMap?) { view.setLoadingIndicatorSource(source) } 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 ecbcc9b3083b24..51339ee7c03790 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 @@ -44,9 +44,11 @@ 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.BuildConfig import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.SoftAssertions import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.common.annotations.VisibleForTesting import com.facebook.react.common.build.ReactBuildConfig @@ -281,29 +283,12 @@ public class ReactImageView( } else if (sources.size() == 1) { // Optimize for the case where we have just one uri, case in which we don't need the sizes val source = checkNotNull(sources.getMap(0)) - val cacheControl = computeCacheControl(source.getString("cache")) - var imageSource = ImageSource(context, source.getString("uri"), cacheControl = cacheControl) - if (Uri.EMPTY == imageSource.uri) { - warnImageSource(source.getString("uri")) - imageSource = getTransparentBitmapImageSource(context) - } + val imageSource = readableMapToImageSource(source, includeSize = false) tmpSources.add(imageSource) } else { for (idx in 0 until sources.size()) { val source = sources.getMap(idx) ?: continue - val cacheControl = computeCacheControl(source.getString("cache")) - var imageSource = - ImageSource( - context, - source.getString("uri"), - source.getDouble("width"), - source.getDouble("height"), - cacheControl, - ) - if (Uri.EMPTY == imageSource.uri) { - warnImageSource(source.getString("uri")) - imageSource = getTransparentBitmapImageSource(context) - } + val imageSource = readableMapToImageSource(source, includeSize = true) tmpSources.add(imageSource) } } @@ -336,16 +321,36 @@ public class ReactImageView( } } - public fun setDefaultSource(name: String?) { - val newDefaultDrawable = ResourceDrawableIdHelper.getResourceDrawable(context, name) + public fun setDefaultSource(source: ReadableMap?) { + var newDefaultDrawable: Drawable? = null + if (source != null) { + val imageSource = readableMapToImageSource(source, false) + SoftAssertions.assertCondition( + !BuildConfig.DEBUG && !imageSource.isResource, + "ReactImageView: Only local resources can be used as default image. Uri: ${imageSource.uri}" + ) + + newDefaultDrawable = ResourceDrawableIdHelper.getResourceDrawable(context, imageSource.source) + } + if (defaultImageDrawable != newDefaultDrawable) { defaultImageDrawable = newDefaultDrawable isDirty = true } } - public fun setLoadingIndicatorSource(name: String?) { - val drawable = ResourceDrawableIdHelper.getResourceDrawable(context, name) + public fun setLoadingIndicatorSource(source: ReadableMap?) { + var drawable: Drawable? = null + if (source != null) { + val imageSource = readableMapToImageSource(source, false) + SoftAssertions.assertCondition( + !BuildConfig.DEBUG && !imageSource.isResource, + "ReactImageView: Only local resources can be used as default image. Uri: ${imageSource.uri}" + ) + + drawable = ResourceDrawableIdHelper.getResourceDrawable(context, imageSource.source) + } + val newLoadingIndicatorSource = drawable?.let { AutoRotateDrawable(it, 1000) } if (loadingImageDrawable != newLoadingIndicatorSource) { loadingImageDrawable = newLoadingIndicatorSource @@ -555,6 +560,29 @@ public class ReactImageView( private val isTiled: Boolean get() = tileMode != TileMode.CLAMP + private fun readableMapToImageSource(source: ReadableMap, includeSize: Boolean = false): ImageSource { + val cacheControl = computeCacheControl(source.getString("cache")) + val uri = source.getString("uri") + var imageSource = if (includeSize) { + ImageSource( + context, + uri, + source.getDouble("width"), + source.getDouble("height"), + cacheControl, + ) + } else { + ImageSource(context, uri, cacheControl = cacheControl) + } + + if (Uri.EMPTY == imageSource.uri) { + warnImageSource(uri) + imageSource = getTransparentBitmapImageSource(context) + } + + return imageSource + } + private fun setSourceImage() { imageSource = null if (sources.isEmpty()) { diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h index 0bae06a4138aa5..0efd42fd312f66 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h @@ -66,9 +66,8 @@ class ImageSource { imageSourceResult["scale"] = scale; folly::dynamic sizeResult = folly::dynamic::object(); - sizeResult["width"] = size.width; - sizeResult["height"] = size.height; - imageSourceResult["size"] = sizeResult; + imageSourceResult["width"] = size.width; + imageSourceResult["height"] = size.height; imageSourceResult["body"] = body; imageSourceResult["method"] = method;