diff --git a/bun.lock b/bun.lock index fe790271..5694ed62 100644 --- a/bun.lock +++ b/bun.lock @@ -30,7 +30,7 @@ "react-native-fast-image": "^8.6.3", "react-native-harness": "^1.3.0", "react-native-nitro-image": "workspace:*", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "react-native-nitro-web-image": "workspace:*", "react-native-safe-area-context": "^5.8.0", "react-native-screens": "^4.25.2", @@ -57,10 +57,10 @@ "version": "0.15.1", "devDependencies": { "@types/react": "^19.2.15", - "nitrogen": "0.36.5", + "nitrogen": "0.37.0", "react": "19.2.3", "react-native": "0.85.3", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "typescript": "6.0.3", }, "peerDependencies": { @@ -77,10 +77,10 @@ "version": "0.15.1", "devDependencies": { "@types/react": "^19.2.15", - "nitrogen": "0.36.5", + "nitrogen": "0.37.0", "react": "19.2.3", "react-native": "0.85.3", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "typescript": "6.0.3", }, "peerDependencies": { @@ -1561,7 +1561,7 @@ "new-github-release-url": ["new-github-release-url@2.0.0", "", { "dependencies": { "type-fest": "^2.5.1" } }, "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ=="], - "nitrogen": ["nitrogen@0.36.5", "", { "dependencies": { "chalk": "^5.3.0", "react-native-nitro-modules": "^0.36.5", "ts-morph": "^28.0.0", "yargs": "^18.0.0", "zod": "^4.4.3" }, "bin": { "nitrogen": "lib/index.js" } }, "sha512-PvlHrBVaoKEaD6CCUCG7om8MTvLeh4ZSDxHoRx2YiEWUVXPXrRN9NwMn1gjie9Bd91jHA3dYMF7no9Eme4txMA=="], + "nitrogen": ["nitrogen@0.37.0", "", { "dependencies": { "chalk": "^5.3.0", "react-native-nitro-modules": "^0.37.0", "ts-morph": "^28.0.0", "yargs": "^18.0.0", "zod": "^4.4.3" }, "bin": { "nitrogen": "lib/index.js" } }, "sha512-lrWmvctRyazMLU6y4ThUl6q129wMCOvmQfdtqCeZShgmmh3Nbplm9dSJLN2OZSBnDWA5k+T2P8aiNyjr1yiJAg=="], "nocache": ["nocache@3.0.4", "", {}, "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw=="], @@ -1729,7 +1729,7 @@ "react-native-nitro-image": ["react-native-nitro-image@workspace:packages/react-native-nitro-image"], - "react-native-nitro-modules": ["react-native-nitro-modules@0.36.5", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-SJby84+hovD70JmQ1fq+56tw3oapJeChehUqXA4Gqjm+ktJhMAcj8LGwoAjStSOUwRaK7czbzBPB0Widj+EDoQ=="], + "react-native-nitro-modules": ["react-native-nitro-modules@0.37.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ULS2CBZcdwGw5d4Y35EV/tbCOsNsQOnQ9LC9CehMhDNm5c90lwF/LxRamzKuFpjI547G7teTQgD0peqYSGnP9g=="], "react-native-nitro-web-image": ["react-native-nitro-web-image@workspace:packages/react-native-nitro-web-image"], diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index eca8dcba..79f8f632 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -61,7 +61,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - NitroModules (0.36.5): + - NitroModules (0.37.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2284,7 +2284,7 @@ SPEC CHECKSUMS: hermes-engine: ef561c35b1325a953b54456ddbd27ee0faf01ba4 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 NitroImage: 2f592086cbdecbbaa1c10dd97e0cdd43b1bb0817 - NitroModules: 452230d9b63c6c3f59dd97eeaa27ec3449271d4a + NitroModules: cda827b31eb05c1bfcc0e2bbd9b647a69c6c314f NitroWebImage: ca414f829cae44174179ea0e8ff28df75f40f08e RCTDeprecation: a4c521821fab57cbb125b36effe84d897d0dfa12 RCTRequired: 9f3a7e5645d4bc3f551593de7550bb66ab6e42bc diff --git a/example/package.json b/example/package.json index f5f30da1..7e0add0d 100644 --- a/example/package.json +++ b/example/package.json @@ -19,7 +19,7 @@ "react-native-fast-image": "^8.6.3", "react-native-harness": "^1.3.0", "react-native-nitro-image": "workspace:*", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "react-native-nitro-web-image": "workspace:*", "react-native-safe-area-context": "^5.8.0", "react-native-screens": "^4.25.2" diff --git a/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.cpp b/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.cpp index 81eb6e4c..3a03ad18 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.cpp +++ b/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.cpp @@ -15,49 +15,69 @@ namespace margelo::nitro::image::views { using namespace facebook; using ConcreteStateData = react::ConcreteState; -void JHybridNitroImageViewStateUpdater::updateViewProps(jni::alias_ref /* class */, - jni::alias_ref javaView, - jni::alias_ref stateWrapperInterface) { - std::shared_ptr hybridView = javaView->getJHybridNitroImageViewSpec(); - +std::shared_ptr JHybridNitroImageViewStateUpdater::getPropsFromStateWrapper( + jni::alias_ref stateWrapper) { + if (stateWrapper.get() == nullptr) { + return nullptr; + } // Get concrete StateWrapperImpl from passed StateWrapper interface object - jobject rawStateWrapper = stateWrapperInterface.get(); - if (!stateWrapperInterface->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] { - throw std::runtime_error("StateWrapper is not a StateWrapperImpl"); + jobject rawStateWrapper = stateWrapper.get(); + if (!stateWrapper->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] { + throw std::runtime_error("StateWrapper is not a StateWrapperImpl"); + } + auto stateWrapperImpl = jni::alias_ref{ + static_cast(rawStateWrapper) + }; + std::shared_ptr state = stateWrapperImpl->cthis()->getState(); + if (state == nullptr) { + return nullptr; } - auto stateWrapper = jni::alias_ref{ - static_cast(rawStateWrapper)}; - std::shared_ptr state = stateWrapper->cthis()->getState(); auto concreteState = std::static_pointer_cast(state); const HybridNitroImageViewState& data = concreteState->getData(); - const std::shared_ptr& props = data.getProps(); + const std::shared_ptr& props = data.getProps(); if (props == nullptr) [[unlikely]] { - // Props aren't set yet! throw std::runtime_error("HybridNitroImageViewState's data doesn't contain any props!"); } + return props; +} + +void JHybridNitroImageViewStateUpdater::updateViewProps(jni::alias_ref /* class */, + jni::alias_ref javaView, + jni::alias_ref newState, + jni::alias_ref oldState) { + std::shared_ptr hybridView = javaView->getJHybridNitroImageViewSpec(); + std::shared_ptr newProps = getPropsFromStateWrapper(newState); + std::shared_ptr oldProps = getPropsFromStateWrapper(oldState); + if (newProps == nullptr) [[unlikely]] { + throw std::runtime_error("Current StateWrapper doesn't contain any props!"); + } - // Update all props if they are dirty - if (props->image.isDirty) { - hybridView->setImage(props->image.value); - props->image.isDirty = false; + // Update only props that differ from the previous State snapshot. + if (oldProps == nullptr + ? newProps->image.isProvided() + : !newProps->image.hasSameValue(oldProps->image)) { + hybridView->setImage(newProps->image.get()); } - if (props->resizeMode.isDirty) { - hybridView->setResizeMode(props->resizeMode.value); - props->resizeMode.isDirty = false; + if (oldProps == nullptr + ? newProps->resizeMode.isProvided() + : !newProps->resizeMode.hasSameValue(oldProps->resizeMode)) { + hybridView->setResizeMode(newProps->resizeMode.get()); } - if (props->recyclingKey.isDirty) { - hybridView->setRecyclingKey(props->recyclingKey.value); - props->recyclingKey.isDirty = false; + if (oldProps == nullptr + ? newProps->recyclingKey.isProvided() + : !newProps->recyclingKey.hasSameValue(oldProps->recyclingKey)) { + hybridView->setRecyclingKey(newProps->recyclingKey.get()); } // Update hybridRef if it changed - if (props->hybridRef.isDirty) { + if (oldProps == nullptr + ? newProps->hybridRef.isProvided() + : !newProps->hybridRef.hasSameValue(oldProps->hybridRef)) { // hybridRef changed - call it with new this - const auto& maybeFunc = props->hybridRef.value; + const auto& maybeFunc = newProps->hybridRef.get(); if (maybeFunc.has_value()) { maybeFunc.value()(hybridView); } - props->hybridRef.isDirty = false; } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.hpp b/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.hpp index b37d81e7..fb0edf54 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.hpp +++ b/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.hpp @@ -31,7 +31,12 @@ class JHybridNitroImageViewStateUpdater final: public jni::JavaClass /* class */, jni::alias_ref view, - jni::alias_ref stateWrapperInterface); + jni::alias_ref newState, + jni::alias_ref oldState); + +private: + static std::shared_ptr getPropsFromStateWrapper( + jni::alias_ref stateWrapper); public: static void registerNatives() { diff --git a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt index a0c23213..a59da19b 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt +++ b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt @@ -20,6 +20,14 @@ import com.margelo.nitro.image.* * Represents the React Native `ViewManager` for the "NitroImageView" Nitro HybridView. */ public class HybridNitroImageViewManager: SimpleViewManager() { + /** + * Represents the View and its last state snapshot (mutable) + */ + private class HybridViewHolder( + val hybridView: HybridImageView, + var lastState: StateWrapper? = null, + ) + init { if (RecyclableView::class.java.isAssignableFrom(HybridImageView::class.java)) { // Enable view recycling @@ -34,33 +42,41 @@ public class HybridNitroImageViewManager: SimpleViewManager() { override fun createViewInstance(reactContext: ThemedReactContext): View { val hybridView = HybridImageView(reactContext) val view = hybridView.view - view.setTag(associated_hybrid_view_tag, hybridView) + view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView)) return view } override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? { - val hybridView = getHybridView(view) + val holder = getHybridViewHolder(view) ?: throw Error("Couldn't find view $view in local views table!") + val hybridView = holder.hybridView + val oldState = holder.lastState + val newState = stateWrapper // 1. Update each prop individually hybridView.beforeUpdate() - HybridNitroImageViewStateUpdater.updateViewProps(hybridView, stateWrapper) + HybridNitroImageViewStateUpdater.updateViewProps(hybridView, newState, oldState) hybridView.afterUpdate() + holder.lastState = newState // 2. Continue in base View props - return super.updateState(view, props, stateWrapper) + return super.updateState(view, props, newState) } override fun onDropViewInstance(view: View) { - val hybridView = getHybridView(view) - hybridView?.onDropView() + val holder = getHybridViewHolder(view) + holder?.lastState = null + holder?.hybridView?.onDropView() return super.onDropViewInstance(view) } protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? { - super.prepareToRecycleView(reactContext, view) - val hybridView = getHybridView(view) + val preparedView = super.prepareToRecycleView(reactContext, view) + ?: return null + val holder = getHybridViewHolder(preparedView) ?: return null + val hybridView = holder.hybridView + holder.lastState = null @Suppress("USELESS_IS_CHECK") if (hybridView is RecyclableView) { @@ -74,7 +90,7 @@ public class HybridNitroImageViewManager: SimpleViewManager() { } } - private fun getHybridView(view: View): HybridImageView? { - return view.getTag(associated_hybrid_view_tag) as? HybridImageView + private fun getHybridViewHolder(view: View): HybridViewHolder? { + return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewStateUpdater.kt b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewStateUpdater.kt index a77cb5d9..d010544b 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewStateUpdater.kt +++ b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewStateUpdater.kt @@ -14,10 +14,10 @@ internal class HybridNitroImageViewStateUpdater { companion object { /** * Updates the props for [view] through C++. - * The [state] prop is expected to contain [view]'s props as wrapped Fabric state. + * The [newState] prop is expected to contain [view]'s props as wrapped Fabric state. */ @Suppress("KotlinJniMissingFunction") @JvmStatic - external fun updateViewProps(view: HybridNitroImageViewSpec, state: StateWrapper) + external fun updateViewProps(view: HybridNitroImageViewSpec, newState: StateWrapper, oldState: StateWrapper?) } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm index 176e7b23..34ef3c1e 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm @@ -37,6 +37,7 @@ + (BOOL)shouldBeRecycled; @implementation HybridNitroImageViewComponent { std::shared_ptr _hybridView; + BOOL _didDropView; } + (void) load { @@ -50,6 +51,7 @@ + (void) load { - (instancetype) init { if (self = [super init]) { + _props = HybridNitroImageViewShadowNode::defaultSharedProps(); std::shared_ptr hybridView = NitroImage::NitroImageAutolinking::createNitroImageView(); _hybridView = std::dynamic_pointer_cast(hybridView); [self updateView]; @@ -69,45 +71,67 @@ - (void) updateView { [self setContentView:view]; } +- (void) notifyOnDropView { + // A recycled component can later be invalidated. Notify only once per mount. + if (_didDropView) { + return; + } + NitroImage::HybridNitroImageViewSpec_cxx& swiftPart = _hybridView->getSwiftPart(); + swiftPart.onDropView(); + _didDropView = YES; +} + - (void) updateProps:(const std::shared_ptr&)props oldProps:(const std::shared_ptr&)oldProps { + // A props update marks a newly mounted or still-active component. + _didDropView = NO; + // 1. Downcast props - const auto& newViewPropsConst = *std::static_pointer_cast(props); - auto& newViewProps = const_cast(newViewPropsConst); + const auto& newViewProps = *std::static_pointer_cast(props); + const auto* oldViewProps = static_cast(oldProps.get()); NitroImage::HybridNitroImageViewSpec_cxx& swiftPart = _hybridView->getSwiftPart(); - // 2. Update each prop individually - swiftPart.beforeUpdate(); - - // image: optional - if (newViewProps.image.isDirty) { - swiftPart.setImage(newViewProps.image.value); - newViewProps.image.isDirty = false; - } - // resizeMode: optional - if (newViewProps.resizeMode.isDirty) { - swiftPart.setResizeMode(newViewProps.resizeMode.value); - newViewProps.resizeMode.isDirty = false; - } - // recyclingKey: optional - if (newViewProps.recyclingKey.isDirty) { - swiftPart.setRecyclingKey(newViewProps.recyclingKey.value); - newViewProps.recyclingKey.isDirty = false; - } - - swiftPart.afterUpdate(); + // 2. Update only props that differ from the previous Props snapshot. + const bool hasTransactionPropChanges = oldViewProps == nullptr + ? newViewProps.hasAnyProvidedProps() + : !newViewProps.hasSameProps(*oldViewProps); + if (hasTransactionPropChanges) { + swiftPart.beforeUpdate(); + + // image: optional + if (oldViewProps == nullptr + ? newViewProps.image.isProvided() + : !newViewProps.image.hasSameValue(oldViewProps->image)) { + swiftPart.setImage(newViewProps.image.get()); + } + // resizeMode: optional + if (oldViewProps == nullptr + ? newViewProps.resizeMode.isProvided() + : !newViewProps.resizeMode.hasSameValue(oldViewProps->resizeMode)) { + swiftPart.setResizeMode(newViewProps.resizeMode.get()); + } + // recyclingKey: optional + if (oldViewProps == nullptr + ? newViewProps.recyclingKey.isProvided() + : !newViewProps.recyclingKey.hasSameValue(oldViewProps->recyclingKey)) { + swiftPart.setRecyclingKey(newViewProps.recyclingKey.get()); + } - // 3. Update hybridRef if it changed - if (newViewProps.hybridRef.isDirty) { - // hybridRef changed - call it with new this - const auto& maybeFunc = newViewProps.hybridRef.value; - if (maybeFunc.has_value()) { - maybeFunc.value()(_hybridView); + // Update hybridRef if it changed + if (oldViewProps == nullptr + ? newViewProps.hybridRef.isProvided() + : !newViewProps.hybridRef.hasSameValue(oldViewProps->hybridRef)) { + // hybridRef changed - call it with new this + const auto& maybeFunc = newViewProps.hybridRef.get(); + if (maybeFunc.has_value()) { + maybeFunc.value()(_hybridView); + } } - newViewProps.hybridRef.isDirty = false; + + swiftPart.afterUpdate(); } - // 4. Continue in base class + // 3. Continue in base class [super updateProps:props oldProps:oldProps]; } @@ -116,6 +140,7 @@ + (BOOL)shouldBeRecycled { } - (void)prepareForRecycle { + [self notifyOnDropView]; [super prepareForRecycle]; NitroImage::HybridNitroImageViewSpec_cxx& swiftPart = _hybridView->getSwiftPart(); swiftPart.maybePrepareForRecycle(); @@ -123,8 +148,7 @@ - (void)prepareForRecycle { #ifdef ENABLE_RCT_COMPONENT_VIEW_INVALIDATE - (void)invalidate { - NitroImage::HybridNitroImageViewSpec_cxx& swiftPart = _hybridView->getSwiftPart(); - swiftPart.onDropView(); + [self notifyOnDropView]; [super invalidate]; } #endif diff --git a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.cpp b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.cpp index 7a77d827..a65f0aaf 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.cpp +++ b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.cpp @@ -7,65 +7,23 @@ #include "HybridNitroImageViewComponent.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include namespace margelo::nitro::image::views { + using namespace facebook; + extern const char HybridNitroImageViewComponentName[] = "NitroImageView"; HybridNitroImageViewProps::HybridNitroImageViewProps(const react::PropsParserContext& context, const HybridNitroImageViewProps& sourceProps, const react::RawProps& rawProps): react::ViewProps(context, sourceProps, rawProps, filterObjectKeys), - image([&]() -> CachedProp, std::shared_ptr>>> { - try { - const react::RawValue* rawValue = rawProps.at("image", nullptr, nullptr); - if (rawValue == nullptr) return sourceProps.image; - const auto& [runtime, value] = (std::pair)*rawValue; - return CachedProp, std::shared_ptr>>>::fromRawValue(*runtime, value, sourceProps.image); - } catch (const std::exception& exc) { - throw std::runtime_error(std::string("NitroImageView.image: ") + exc.what()); - } - }()), - resizeMode([&]() -> CachedProp> { - try { - const react::RawValue* rawValue = rawProps.at("resizeMode", nullptr, nullptr); - if (rawValue == nullptr) return sourceProps.resizeMode; - const auto& [runtime, value] = (std::pair)*rawValue; - return CachedProp>::fromRawValue(*runtime, value, sourceProps.resizeMode); - } catch (const std::exception& exc) { - throw std::runtime_error(std::string("NitroImageView.resizeMode: ") + exc.what()); - } - }()), - recyclingKey([&]() -> CachedProp> { - try { - const react::RawValue* rawValue = rawProps.at("recyclingKey", nullptr, nullptr); - if (rawValue == nullptr) return sourceProps.recyclingKey; - const auto& [runtime, value] = (std::pair)*rawValue; - return CachedProp>::fromRawValue(*runtime, value, sourceProps.recyclingKey); - } catch (const std::exception& exc) { - throw std::runtime_error(std::string("NitroImageView.recyclingKey: ") + exc.what()); - } - }()), - hybridRef([&]() -> CachedProp& /* ref */)>>> { - try { - const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr); - if (rawValue == nullptr) return sourceProps.hybridRef; - const auto& [runtime, value] = (std::pair)*rawValue; - return CachedProp& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, PropNameIDCache::get(*runtime, "f")), sourceProps.hybridRef); - } catch (const std::exception& exc) { - throw std::runtime_error(std::string("NitroImageView.hybridRef: ") + exc.what()); - } - }()) { } + image(nitro::ReactProp, std::shared_ptr>>>::fromRawValue("NitroImageView", "image", rawProps, sourceProps.image)), + resizeMode(nitro::ReactProp>::fromRawValue("NitroImageView", "resizeMode", rawProps, sourceProps.resizeMode)), + recyclingKey(nitro::ReactProp>::fromRawValue("NitroImageView", "recyclingKey", rawProps, sourceProps.recyclingKey)), + hybridRef(nitro::ReactProp& /* ref */)>>>::fromRawValue("NitroImageView", "hybridRef", rawProps, sourceProps.hybridRef)) { } bool HybridNitroImageViewProps::filterObjectKeys(const std::string& propName) { switch (hashString(propName)) { @@ -77,29 +35,4 @@ namespace margelo::nitro::image::views { } } - HybridNitroImageViewComponentDescriptor::HybridNitroImageViewComponentDescriptor(const react::ComponentDescriptorParameters& parameters) - : ConcreteComponentDescriptor(parameters, - react::RawPropsParser()) {} - - std::shared_ptr HybridNitroImageViewComponentDescriptor::cloneProps(const react::PropsParserContext& context, - const std::shared_ptr& props, - react::RawProps rawProps) const { - // 1. Prepare raw props parser - rawProps.parse(rawPropsParser_); - // 2. Copy props with Nitro's cached copy constructor - return HybridNitroImageViewShadowNode::Props(context, /* & */ rawProps, props); - } - -#ifdef ANDROID - void HybridNitroImageViewComponentDescriptor::adopt(react::ShadowNode& shadowNode) const { - // This is called immediately after `ShadowNode` is created, cloned or in progress. - // On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++. - auto& concreteShadowNode = static_cast(shadowNode); - const std::shared_ptr& constProps = concreteShadowNode.getConcreteSharedProps(); - const std::shared_ptr& props = std::const_pointer_cast(constProps); - HybridNitroImageViewState state{props}; - concreteShadowNode.setStateData(std::move(state)); - } -#endif - } // namespace margelo::nitro::image::views diff --git a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.hpp b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.hpp index 91248b9e..0f0ab044 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.hpp +++ b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/views/HybridNitroImageViewComponent.hpp @@ -7,14 +7,15 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include +#include +#include + +#include #include #include "HybridImageSpec.hpp" @@ -46,10 +47,26 @@ namespace margelo::nitro::image::views { const react::RawProps& rawProps); public: - CachedProp, std::shared_ptr>>> image; - CachedProp> resizeMode; - CachedProp> recyclingKey; - CachedProp& /* ref */)>>> hybridRef; + nitro::ReactProp, std::shared_ptr>>> image; + nitro::ReactProp> resizeMode; + nitro::ReactProp> recyclingKey; + nitro::ReactProp& /* ref */)>>> hybridRef; + + [[nodiscard]] + bool hasSameProps(const HybridNitroImageViewProps& other) const noexcept { + return image.hasSameValue(other.image) && + resizeMode.hasSameValue(other.resizeMode) && + recyclingKey.hasSameValue(other.recyclingKey) && + hybridRef.hasSameValue(other.hybridRef); + } + + [[nodiscard]] + bool hasAnyProvidedProps() const noexcept { + return image.isProvided() || + resizeMode.isProvided() || + recyclingKey.isProvided() || + hybridRef.isProvided(); + } private: static bool filterObjectKeys(const std::string& propName); @@ -58,32 +75,7 @@ namespace margelo::nitro::image::views { /** * State for the "NitroImageView" View. */ - class HybridNitroImageViewState final { - public: - HybridNitroImageViewState() = default; - explicit HybridNitroImageViewState(const std::shared_ptr& props): - _props(props) {} - - public: - [[nodiscard]] - const std::shared_ptr& getProps() const { - return _props; - } - - public: -#ifdef ANDROID - HybridNitroImageViewState(const HybridNitroImageViewState& /* previousState */, folly::dynamic /* data */) {} - folly::dynamic getDynamic() const { - throw std::runtime_error("HybridNitroImageViewState does not support folly!"); - } - react::MapBuffer getMapBuffer() const { - throw std::runtime_error("HybridNitroImageViewState does not support MapBuffer!"); - }; -#endif - - private: - std::shared_ptr _props; - }; + using HybridNitroImageViewState = nitro::ViewPropsHolderState; /** * The Shadow Node for the "NitroImageView" View. @@ -96,21 +88,7 @@ namespace margelo::nitro::image::views { /** * The Component Descriptor for the "NitroImageView" View. */ - class HybridNitroImageViewComponentDescriptor final: public react::ConcreteComponentDescriptor { - public: - explicit HybridNitroImageViewComponentDescriptor(const react::ComponentDescriptorParameters& parameters); - - public: - /** - * A faster path for cloning props - reuses the caching logic from `HybridNitroImageViewProps`. - */ - std::shared_ptr cloneProps(const react::PropsParserContext& context, - const std::shared_ptr& props, - react::RawProps rawProps) const override; -#ifdef ANDROID - void adopt(react::ShadowNode& shadowNode) const override; -#endif - }; + using HybridNitroImageViewComponentDescriptor = nitro::ViewComponentDescriptor; /* The actual view for "NitroImageView" needs to be implemented in platform-specific code. */ diff --git a/packages/react-native-nitro-image/package.json b/packages/react-native-nitro-image/package.json index a58fb605..5a4ee6d9 100644 --- a/packages/react-native-nitro-image/package.json +++ b/packages/react-native-nitro-image/package.json @@ -58,10 +58,10 @@ }, "devDependencies": { "@types/react": "^19.2.15", - "nitrogen": "0.36.5", + "nitrogen": "0.37.0", "react": "19.2.3", "react-native": "0.85.3", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "typescript": "6.0.3" }, "peerDependencies": { diff --git a/packages/react-native-nitro-web-image/package.json b/packages/react-native-nitro-web-image/package.json index af0cd017..379c9eff 100644 --- a/packages/react-native-nitro-web-image/package.json +++ b/packages/react-native-nitro-web-image/package.json @@ -57,10 +57,10 @@ }, "devDependencies": { "@types/react": "^19.2.15", - "nitrogen": "0.36.5", + "nitrogen": "0.37.0", "react": "19.2.3", "react-native": "0.85.3", - "react-native-nitro-modules": "0.36.5", + "react-native-nitro-modules": "0.37.0", "typescript": "6.0.3" }, "peerDependencies": {