@onekeyfe/react-native-scroll-guard 3.0.96 → 3.0.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,34 +15,52 @@ import com.margelo.nitro.scrollguard.views.HybridScrollGuardStateUpdater
15
15
  * is a FrameLayout that wraps child scrollable views, we need a ViewGroupManager.
16
16
  */
17
17
  class ScrollGuardViewGroupManager : ViewGroupManager<ScrollGuardFrameLayout>() {
18
- private val views = hashMapOf<View, HybridScrollGuard>()
18
+ private class HybridViewHolder(
19
+ val hybridView: HybridScrollGuard,
20
+ var lastState: StateWrapper? = null,
21
+ )
22
+
23
+ private val views = hashMapOf<View, HybridViewHolder>()
19
24
 
20
25
  override fun getName(): String = "ScrollGuard"
21
26
 
22
27
  override fun createViewInstance(reactContext: ThemedReactContext): ScrollGuardFrameLayout {
23
28
  val hybridView = HybridScrollGuard(reactContext)
24
29
  val view = hybridView.view as ScrollGuardFrameLayout
25
- views[view] = hybridView
30
+ views[view] = HybridViewHolder(hybridView)
26
31
  return view
27
32
  }
28
33
 
29
34
  override fun onDropViewInstance(view: ScrollGuardFrameLayout) {
35
+ views[view]?.lastState = null
30
36
  super.onDropViewInstance(view)
31
37
  views.remove(view)
32
38
  }
33
39
 
40
+ override fun prepareToRecycleView(
41
+ reactContext: ThemedReactContext,
42
+ view: ScrollGuardFrameLayout,
43
+ ): ScrollGuardFrameLayout? {
44
+ views[view]?.lastState = null
45
+ return super.prepareToRecycleView(reactContext, view)
46
+ }
47
+
34
48
  override fun updateState(
35
49
  view: ScrollGuardFrameLayout,
36
50
  props: ReactStylesDiffMap,
37
51
  stateWrapper: StateWrapper
38
52
  ): Any? {
39
- val hybridView = views[view]
53
+ val holder = views[view]
40
54
  ?: throw Error("Couldn't find view $view in local views table!")
55
+ val hybridView = holder.hybridView
56
+ val oldState = holder.lastState
57
+ val newState = stateWrapper
41
58
 
42
59
  hybridView.beforeUpdate()
43
- HybridScrollGuardStateUpdater.updateViewProps(hybridView, stateWrapper)
60
+ HybridScrollGuardStateUpdater.updateViewProps(hybridView, newState, oldState)
44
61
  hybridView.afterUpdate()
62
+ holder.lastState = newState
45
63
 
46
- return super.updateState(view, props, stateWrapper)
64
+ return super.updateState(view, props, newState)
47
65
  }
48
66
  }
@@ -15,41 +15,59 @@ namespace margelo::nitro::scrollguard::views {
15
15
  using namespace facebook;
16
16
  using ConcreteStateData = react::ConcreteState<HybridScrollGuardState>;
17
17
 
18
- void JHybridScrollGuardStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
19
- jni::alias_ref<JHybridScrollGuardSpec::JavaPart> javaView,
20
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface) {
21
- std::shared_ptr<JHybridScrollGuardSpec> hybridView = javaView->getJHybridScrollGuardSpec();
22
-
18
+ std::shared_ptr<const HybridScrollGuardProps> JHybridScrollGuardStateUpdater::getPropsFromStateWrapper(
19
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper) {
20
+ if (stateWrapper.get() == nullptr) {
21
+ return nullptr;
22
+ }
23
23
  // Get concrete StateWrapperImpl from passed StateWrapper interface object
24
- jobject rawStateWrapper = stateWrapperInterface.get();
25
- if (!stateWrapperInterface->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
- throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
24
+ jobject rawStateWrapper = stateWrapper.get();
25
+ if (!stateWrapper->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
+ throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
27
+ }
28
+ auto stateWrapperImpl = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
+ static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)
30
+ };
31
+ std::shared_ptr<const react::State> state = stateWrapperImpl->cthis()->getState();
32
+ if (state == nullptr) {
33
+ return nullptr;
27
34
  }
28
- auto stateWrapper = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
- static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)};
30
- std::shared_ptr<const react::State> state = stateWrapper->cthis()->getState();
31
35
  auto concreteState = std::static_pointer_cast<const ConcreteStateData>(state);
32
36
  const HybridScrollGuardState& data = concreteState->getData();
33
- const std::shared_ptr<HybridScrollGuardProps>& props = data.getProps();
37
+ const std::shared_ptr<const HybridScrollGuardProps>& props = data.getProps();
34
38
  if (props == nullptr) [[unlikely]] {
35
- // Props aren't set yet!
36
39
  throw std::runtime_error("HybridScrollGuardState's data doesn't contain any props!");
37
40
  }
41
+ return props;
42
+ }
43
+
44
+ void JHybridScrollGuardStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
45
+ jni::alias_ref<JHybridScrollGuardSpec::JavaPart> javaView,
46
+ jni::alias_ref<JStateWrapper::javaobject> newState,
47
+ jni::alias_ref<JStateWrapper::javaobject> oldState) {
48
+ std::shared_ptr<JHybridScrollGuardSpec> hybridView = javaView->getJHybridScrollGuardSpec();
49
+ std::shared_ptr<const HybridScrollGuardProps> newProps = getPropsFromStateWrapper(newState);
50
+ std::shared_ptr<const HybridScrollGuardProps> oldProps = getPropsFromStateWrapper(oldState);
51
+ if (newProps == nullptr) [[unlikely]] {
52
+ throw std::runtime_error("Current StateWrapper doesn't contain any props!");
53
+ }
38
54
 
39
- // Update all props if they are dirty
40
- if (props->direction.isDirty) {
41
- hybridView->setDirection(props->direction.value);
42
- props->direction.isDirty = false;
55
+ // Update only props that differ from the previous State snapshot.
56
+ if (oldProps == nullptr
57
+ ? newProps->direction.isProvided()
58
+ : !newProps->direction.hasSameValue(oldProps->direction)) {
59
+ hybridView->setDirection(newProps->direction.get());
43
60
  }
44
61
 
45
62
  // Update hybridRef if it changed
46
- if (props->hybridRef.isDirty) {
63
+ if (oldProps == nullptr
64
+ ? newProps->hybridRef.isProvided()
65
+ : !newProps->hybridRef.hasSameValue(oldProps->hybridRef)) {
47
66
  // hybridRef changed - call it with new this
48
- const auto& maybeFunc = props->hybridRef.value;
67
+ const auto& maybeFunc = newProps->hybridRef.get();
49
68
  if (maybeFunc.has_value()) {
50
69
  maybeFunc.value()(hybridView);
51
70
  }
52
- props->hybridRef.isDirty = false;
53
71
  }
54
72
  }
55
73
 
@@ -31,7 +31,12 @@ public:
31
31
  public:
32
32
  static void updateViewProps(jni::alias_ref<jni::JClass> /* class */,
33
33
  jni::alias_ref<JHybridScrollGuardSpec::JavaPart> view,
34
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface);
34
+ jni::alias_ref<JStateWrapper::javaobject> newState,
35
+ jni::alias_ref<JStateWrapper::javaobject> oldState);
36
+
37
+ private:
38
+ static std::shared_ptr<const HybridScrollGuardProps> getPropsFromStateWrapper(
39
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper);
35
40
 
36
41
  public:
37
42
  static void registerNatives() {
@@ -20,6 +20,14 @@ import com.margelo.nitro.scrollguard.*
20
20
  * Represents the React Native `ViewManager` for the "ScrollGuard" Nitro HybridView.
21
21
  */
22
22
  public class HybridScrollGuardManager: SimpleViewManager<View>() {
23
+ /**
24
+ * Represents the View and its last state snapshot (mutable)
25
+ */
26
+ private class HybridViewHolder(
27
+ val hybridView: HybridScrollGuard,
28
+ var lastState: StateWrapper? = null,
29
+ )
30
+
23
31
  init {
24
32
  if (RecyclableView::class.java.isAssignableFrom(HybridScrollGuard::class.java)) {
25
33
  // Enable view recycling
@@ -34,33 +42,41 @@ public class HybridScrollGuardManager: SimpleViewManager<View>() {
34
42
  override fun createViewInstance(reactContext: ThemedReactContext): View {
35
43
  val hybridView = HybridScrollGuard(reactContext)
36
44
  val view = hybridView.view
37
- view.setTag(associated_hybrid_view_tag, hybridView)
45
+ view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView))
38
46
  return view
39
47
  }
40
48
 
41
49
  override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
42
- val hybridView = getHybridView(view)
50
+ val holder = getHybridViewHolder(view)
43
51
  ?: throw Error("Couldn't find view $view in local views table!")
52
+ val hybridView = holder.hybridView
53
+ val oldState = holder.lastState
54
+ val newState = stateWrapper
44
55
 
45
56
  // 1. Update each prop individually
46
57
  hybridView.beforeUpdate()
47
- HybridScrollGuardStateUpdater.updateViewProps(hybridView, stateWrapper)
58
+ HybridScrollGuardStateUpdater.updateViewProps(hybridView, newState, oldState)
48
59
  hybridView.afterUpdate()
60
+ holder.lastState = newState
49
61
 
50
62
  // 2. Continue in base View props
51
- return super.updateState(view, props, stateWrapper)
63
+ return super.updateState(view, props, newState)
52
64
  }
53
65
 
54
66
  override fun onDropViewInstance(view: View) {
55
- val hybridView = getHybridView(view)
56
- hybridView?.onDropView()
67
+ val holder = getHybridViewHolder(view)
68
+ holder?.lastState = null
69
+ holder?.hybridView?.onDropView()
57
70
  return super.onDropViewInstance(view)
58
71
  }
59
72
 
60
73
  protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? {
61
- super.prepareToRecycleView(reactContext, view)
62
- val hybridView = getHybridView(view)
74
+ val preparedView = super.prepareToRecycleView(reactContext, view)
75
+ ?: return null
76
+ val holder = getHybridViewHolder(preparedView)
63
77
  ?: return null
78
+ val hybridView = holder.hybridView
79
+ holder.lastState = null
64
80
 
65
81
  @Suppress("USELESS_IS_CHECK")
66
82
  if (hybridView is RecyclableView) {
@@ -74,7 +90,7 @@ public class HybridScrollGuardManager: SimpleViewManager<View>() {
74
90
  }
75
91
  }
76
92
 
77
- private fun getHybridView(view: View): HybridScrollGuard? {
78
- return view.getTag(associated_hybrid_view_tag) as? HybridScrollGuard
93
+ private fun getHybridViewHolder(view: View): HybridViewHolder? {
94
+ return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder
79
95
  }
80
96
  }
@@ -14,10 +14,10 @@ internal class HybridScrollGuardStateUpdater {
14
14
  companion object {
15
15
  /**
16
16
  * Updates the props for [view] through C++.
17
- * The [state] prop is expected to contain [view]'s props as wrapped Fabric state.
17
+ * The [newState] prop is expected to contain [view]'s props as wrapped Fabric state.
18
18
  */
19
19
  @Suppress("KotlinJniMissingFunction")
20
20
  @JvmStatic
21
- external fun updateViewProps(view: HybridScrollGuardSpec, state: StateWrapper)
21
+ external fun updateViewProps(view: HybridScrollGuardSpec, newState: StateWrapper, oldState: StateWrapper?)
22
22
  }
23
23
  }
@@ -37,6 +37,7 @@ using namespace margelo::nitro::scrollguard::views;
37
37
 
38
38
  @implementation HybridScrollGuardComponent {
39
39
  std::shared_ptr<HybridScrollGuardSpecSwift> _hybridView;
40
+ BOOL _didDropView;
40
41
  }
41
42
 
42
43
  + (void) load {
@@ -50,6 +51,7 @@ using namespace margelo::nitro::scrollguard::views;
50
51
 
51
52
  - (instancetype) init {
52
53
  if (self = [super init]) {
54
+ _props = HybridScrollGuardShadowNode::defaultSharedProps();
53
55
  std::shared_ptr<HybridScrollGuardSpec> hybridView = ScrollGuard::ScrollGuardAutolinking::createScrollGuard();
54
56
  _hybridView = std::dynamic_pointer_cast<HybridScrollGuardSpecSwift>(hybridView);
55
57
  [self updateView];
@@ -69,35 +71,55 @@ using namespace margelo::nitro::scrollguard::views;
69
71
  [self setContentView:view];
70
72
  }
71
73
 
74
+ - (void) notifyOnDropView {
75
+ // A recycled component can later be invalidated. Notify only once per mount.
76
+ if (_didDropView) {
77
+ return;
78
+ }
79
+ ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
80
+ swiftPart.onDropView();
81
+ _didDropView = YES;
82
+ }
83
+
72
84
  - (void) updateProps:(const std::shared_ptr<const react::Props>&)props
73
85
  oldProps:(const std::shared_ptr<const react::Props>&)oldProps {
86
+ // A props update marks a newly mounted or still-active component.
87
+ _didDropView = NO;
88
+
74
89
  // 1. Downcast props
75
- const auto& newViewPropsConst = *std::static_pointer_cast<HybridScrollGuardProps const>(props);
76
- auto& newViewProps = const_cast<HybridScrollGuardProps&>(newViewPropsConst);
90
+ const auto& newViewProps = *std::static_pointer_cast<const HybridScrollGuardProps>(props);
91
+ const auto* oldViewProps = static_cast<const HybridScrollGuardProps*>(oldProps.get());
77
92
  ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
78
93
 
79
- // 2. Update each prop individually
80
- swiftPart.beforeUpdate();
81
-
82
- // direction: optional
83
- if (newViewProps.direction.isDirty) {
84
- swiftPart.setDirection(newViewProps.direction.value);
85
- newViewProps.direction.isDirty = false;
86
- }
87
-
88
- swiftPart.afterUpdate();
94
+ // 2. Update only props that differ from the previous Props snapshot.
95
+ const bool hasTransactionPropChanges = oldViewProps == nullptr
96
+ ? newViewProps.hasAnyProvidedProps()
97
+ : !newViewProps.hasSameProps(*oldViewProps);
98
+ if (hasTransactionPropChanges) {
99
+ swiftPart.beforeUpdate();
100
+
101
+ // direction: optional
102
+ if (oldViewProps == nullptr
103
+ ? newViewProps.direction.isProvided()
104
+ : !newViewProps.direction.hasSameValue(oldViewProps->direction)) {
105
+ swiftPart.setDirection(newViewProps.direction.get());
106
+ }
89
107
 
90
- // 3. Update hybridRef if it changed
91
- if (newViewProps.hybridRef.isDirty) {
92
- // hybridRef changed - call it with new this
93
- const auto& maybeFunc = newViewProps.hybridRef.value;
94
- if (maybeFunc.has_value()) {
95
- maybeFunc.value()(_hybridView);
108
+ // Update hybridRef if it changed
109
+ if (oldViewProps == nullptr
110
+ ? newViewProps.hybridRef.isProvided()
111
+ : !newViewProps.hybridRef.hasSameValue(oldViewProps->hybridRef)) {
112
+ // hybridRef changed - call it with new this
113
+ const auto& maybeFunc = newViewProps.hybridRef.get();
114
+ if (maybeFunc.has_value()) {
115
+ maybeFunc.value()(_hybridView);
116
+ }
96
117
  }
97
- newViewProps.hybridRef.isDirty = false;
118
+
119
+ swiftPart.afterUpdate();
98
120
  }
99
121
 
100
- // 4. Continue in base class
122
+ // 3. Continue in base class
101
123
  [super updateProps:props oldProps:oldProps];
102
124
  }
103
125
 
@@ -106,6 +128,7 @@ using namespace margelo::nitro::scrollguard::views;
106
128
  }
107
129
 
108
130
  - (void)prepareForRecycle {
131
+ [self notifyOnDropView];
109
132
  [super prepareForRecycle];
110
133
  ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
111
134
  swiftPart.maybePrepareForRecycle();
@@ -113,8 +136,7 @@ using namespace margelo::nitro::scrollguard::views;
113
136
 
114
137
  #ifdef ENABLE_RCT_COMPONENT_VIEW_INVALIDATE
115
138
  - (void)invalidate {
116
- ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
117
- swiftPart.onDropView();
139
+ [self notifyOnDropView];
118
140
  [super invalidate];
119
141
  }
120
142
  #endif
@@ -7,45 +7,21 @@
7
7
 
8
8
  #include "HybridScrollGuardComponent.hpp"
9
9
 
10
- #include <string>
11
- #include <exception>
12
- #include <utility>
13
- #include <NitroModules/NitroDefines.hpp>
14
- #include <NitroModules/JSIConverter.hpp>
15
- #include <NitroModules/PropNameIDCache.hpp>
16
- #include <react/renderer/core/RawValue.h>
17
- #include <react/renderer/core/ShadowNode.h>
18
- #include <react/renderer/core/ComponentDescriptor.h>
19
- #include <react/renderer/components/view/ViewProps.h>
10
+ #include <NitroModules/NitroHash.hpp>
11
+ #include <NitroModules/ReactProp.hpp>
20
12
 
21
13
  namespace margelo::nitro::scrollguard::views {
22
14
 
15
+ using namespace facebook;
16
+
23
17
  extern const char HybridScrollGuardComponentName[] = "ScrollGuard";
24
18
 
25
19
  HybridScrollGuardProps::HybridScrollGuardProps(const react::PropsParserContext& context,
26
20
  const HybridScrollGuardProps& sourceProps,
27
21
  const react::RawProps& rawProps):
28
22
  react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
29
- direction([&]() -> CachedProp<std::optional<ScrollGuardDirection>> {
30
- try {
31
- const react::RawValue* rawValue = rawProps.at("direction", nullptr, nullptr);
32
- if (rawValue == nullptr) return sourceProps.direction;
33
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
34
- return CachedProp<std::optional<ScrollGuardDirection>>::fromRawValue(*runtime, value, sourceProps.direction);
35
- } catch (const std::exception& exc) {
36
- throw std::runtime_error(std::string("ScrollGuard.direction: ") + exc.what());
37
- }
38
- }()),
39
- hybridRef([&]() -> CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> {
40
- try {
41
- const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr);
42
- if (rawValue == nullptr) return sourceProps.hybridRef;
43
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
44
- return CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, PropNameIDCache::get(*runtime, "f")), sourceProps.hybridRef);
45
- } catch (const std::exception& exc) {
46
- throw std::runtime_error(std::string("ScrollGuard.hybridRef: ") + exc.what());
47
- }
48
- }()) { }
23
+ direction(nitro::ReactProp<std::optional<ScrollGuardDirection>>::fromRawValue("ScrollGuard", "direction", rawProps, sourceProps.direction)),
24
+ hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>>::fromRawValue("ScrollGuard", "hybridRef", rawProps, sourceProps.hybridRef)) { }
49
25
 
50
26
  bool HybridScrollGuardProps::filterObjectKeys(const std::string& propName) {
51
27
  switch (hashString(propName)) {
@@ -55,29 +31,4 @@ namespace margelo::nitro::scrollguard::views {
55
31
  }
56
32
  }
57
33
 
58
- HybridScrollGuardComponentDescriptor::HybridScrollGuardComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
59
- : ConcreteComponentDescriptor(parameters,
60
- react::RawPropsParser()) {}
61
-
62
- std::shared_ptr<const react::Props> HybridScrollGuardComponentDescriptor::cloneProps(const react::PropsParserContext& context,
63
- const std::shared_ptr<const react::Props>& props,
64
- react::RawProps rawProps) const {
65
- // 1. Prepare raw props parser
66
- rawProps.parse(rawPropsParser_);
67
- // 2. Copy props with Nitro's cached copy constructor
68
- return HybridScrollGuardShadowNode::Props(context, /* & */ rawProps, props);
69
- }
70
-
71
- #ifdef ANDROID
72
- void HybridScrollGuardComponentDescriptor::adopt(react::ShadowNode& shadowNode) const {
73
- // This is called immediately after `ShadowNode` is created, cloned or in progress.
74
- // On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++.
75
- auto& concreteShadowNode = static_cast<HybridScrollGuardShadowNode&>(shadowNode);
76
- const std::shared_ptr<const HybridScrollGuardProps>& constProps = concreteShadowNode.getConcreteSharedProps();
77
- const std::shared_ptr<HybridScrollGuardProps>& props = std::const_pointer_cast<HybridScrollGuardProps>(constProps);
78
- HybridScrollGuardState state{props};
79
- concreteShadowNode.setStateData(std::move(state));
80
- }
81
- #endif
82
-
83
34
  } // namespace margelo::nitro::scrollguard::views
@@ -7,14 +7,15 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <optional>
11
- #include <NitroModules/NitroDefines.hpp>
12
- #include <NitroModules/NitroHash.hpp>
13
- #include <NitroModules/CachedProp.hpp>
14
- #include <react/renderer/core/ConcreteComponentDescriptor.h>
15
- #include <react/renderer/core/PropsParserContext.h>
10
+ #include <NitroModules/ReactProp.hpp>
11
+ #include <NitroModules/ViewComponentDescriptor.hpp>
12
+ #include <NitroModules/ViewPropsHolderState.hpp>
16
13
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
17
14
  #include <react/renderer/components/view/ViewProps.h>
15
+ #include <react/renderer/core/PropsParserContext.h>
16
+ #include <react/renderer/core/RawProps.h>
17
+
18
+ #include <string>
18
19
 
19
20
  #include "ScrollGuardDirection.hpp"
20
21
  #include <optional>
@@ -42,8 +43,20 @@ namespace margelo::nitro::scrollguard::views {
42
43
  const react::RawProps& rawProps);
43
44
 
44
45
  public:
45
- CachedProp<std::optional<ScrollGuardDirection>> direction;
46
- CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> hybridRef;
46
+ nitro::ReactProp<std::optional<ScrollGuardDirection>> direction;
47
+ nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> hybridRef;
48
+
49
+ [[nodiscard]]
50
+ bool hasSameProps(const HybridScrollGuardProps& other) const noexcept {
51
+ return direction.hasSameValue(other.direction) &&
52
+ hybridRef.hasSameValue(other.hybridRef);
53
+ }
54
+
55
+ [[nodiscard]]
56
+ bool hasAnyProvidedProps() const noexcept {
57
+ return direction.isProvided() ||
58
+ hybridRef.isProvided();
59
+ }
47
60
 
48
61
  private:
49
62
  static bool filterObjectKeys(const std::string& propName);
@@ -52,32 +65,7 @@ namespace margelo::nitro::scrollguard::views {
52
65
  /**
53
66
  * State for the "ScrollGuard" View.
54
67
  */
55
- class HybridScrollGuardState final {
56
- public:
57
- HybridScrollGuardState() = default;
58
- explicit HybridScrollGuardState(const std::shared_ptr<HybridScrollGuardProps>& props):
59
- _props(props) {}
60
-
61
- public:
62
- [[nodiscard]]
63
- const std::shared_ptr<HybridScrollGuardProps>& getProps() const {
64
- return _props;
65
- }
66
-
67
- public:
68
- #ifdef ANDROID
69
- HybridScrollGuardState(const HybridScrollGuardState& /* previousState */, folly::dynamic /* data */) {}
70
- folly::dynamic getDynamic() const {
71
- throw std::runtime_error("HybridScrollGuardState does not support folly!");
72
- }
73
- react::MapBuffer getMapBuffer() const {
74
- throw std::runtime_error("HybridScrollGuardState does not support MapBuffer!");
75
- };
76
- #endif
77
-
78
- private:
79
- std::shared_ptr<HybridScrollGuardProps> _props;
80
- };
68
+ using HybridScrollGuardState = nitro::ViewPropsHolderState<HybridScrollGuardProps>;
81
69
 
82
70
  /**
83
71
  * The Shadow Node for the "ScrollGuard" View.
@@ -90,21 +78,7 @@ namespace margelo::nitro::scrollguard::views {
90
78
  /**
91
79
  * The Component Descriptor for the "ScrollGuard" View.
92
80
  */
93
- class HybridScrollGuardComponentDescriptor final: public react::ConcreteComponentDescriptor<HybridScrollGuardShadowNode> {
94
- public:
95
- explicit HybridScrollGuardComponentDescriptor(const react::ComponentDescriptorParameters& parameters);
96
-
97
- public:
98
- /**
99
- * A faster path for cloning props - reuses the caching logic from `HybridScrollGuardProps`.
100
- */
101
- std::shared_ptr<const react::Props> cloneProps(const react::PropsParserContext& context,
102
- const std::shared_ptr<const react::Props>& props,
103
- react::RawProps rawProps) const override;
104
- #ifdef ANDROID
105
- void adopt(react::ShadowNode& shadowNode) const override;
106
- #endif
107
- };
81
+ using HybridScrollGuardComponentDescriptor = nitro::ViewComponentDescriptor<HybridScrollGuardShadowNode>;
108
82
 
109
83
  /* The actual view for "ScrollGuard" needs to be implemented in platform-specific code. */
110
84
 
@@ -15,41 +15,59 @@ namespace margelo::nitro::scrollguard::views {
15
15
  using namespace facebook;
16
16
  using ConcreteStateData = react::ConcreteState<HybridScrollGuardState>;
17
17
 
18
- void JHybridScrollGuardStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
19
- jni::alias_ref<JHybridScrollGuardSpec::JavaPart> javaView,
20
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface) {
21
- std::shared_ptr<JHybridScrollGuardSpec> hybridView = javaView->getJHybridScrollGuardSpec();
22
-
18
+ std::shared_ptr<const HybridScrollGuardProps> JHybridScrollGuardStateUpdater::getPropsFromStateWrapper(
19
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper) {
20
+ if (stateWrapper.get() == nullptr) {
21
+ return nullptr;
22
+ }
23
23
  // Get concrete StateWrapperImpl from passed StateWrapper interface object
24
- jobject rawStateWrapper = stateWrapperInterface.get();
25
- if (!stateWrapperInterface->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
- throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
24
+ jobject rawStateWrapper = stateWrapper.get();
25
+ if (!stateWrapper->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
+ throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
27
+ }
28
+ auto stateWrapperImpl = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
+ static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)
30
+ };
31
+ std::shared_ptr<const react::State> state = stateWrapperImpl->cthis()->getState();
32
+ if (state == nullptr) {
33
+ return nullptr;
27
34
  }
28
- auto stateWrapper = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
- static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)};
30
- std::shared_ptr<const react::State> state = stateWrapper->cthis()->getState();
31
35
  auto concreteState = std::static_pointer_cast<const ConcreteStateData>(state);
32
36
  const HybridScrollGuardState& data = concreteState->getData();
33
- const std::shared_ptr<HybridScrollGuardProps>& props = data.getProps();
37
+ const std::shared_ptr<const HybridScrollGuardProps>& props = data.getProps();
34
38
  if (props == nullptr) [[unlikely]] {
35
- // Props aren't set yet!
36
39
  throw std::runtime_error("HybridScrollGuardState's data doesn't contain any props!");
37
40
  }
41
+ return props;
42
+ }
43
+
44
+ void JHybridScrollGuardStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
45
+ jni::alias_ref<JHybridScrollGuardSpec::JavaPart> javaView,
46
+ jni::alias_ref<JStateWrapper::javaobject> newState,
47
+ jni::alias_ref<JStateWrapper::javaobject> oldState) {
48
+ std::shared_ptr<JHybridScrollGuardSpec> hybridView = javaView->getJHybridScrollGuardSpec();
49
+ std::shared_ptr<const HybridScrollGuardProps> newProps = getPropsFromStateWrapper(newState);
50
+ std::shared_ptr<const HybridScrollGuardProps> oldProps = getPropsFromStateWrapper(oldState);
51
+ if (newProps == nullptr) [[unlikely]] {
52
+ throw std::runtime_error("Current StateWrapper doesn't contain any props!");
53
+ }
38
54
 
39
- // Update all props if they are dirty
40
- if (props->direction.isDirty) {
41
- hybridView->setDirection(props->direction.value);
42
- props->direction.isDirty = false;
55
+ // Update only props that differ from the previous State snapshot.
56
+ if (oldProps == nullptr
57
+ ? newProps->direction.isProvided()
58
+ : !newProps->direction.hasSameValue(oldProps->direction)) {
59
+ hybridView->setDirection(newProps->direction.get());
43
60
  }
44
61
 
45
62
  // Update hybridRef if it changed
46
- if (props->hybridRef.isDirty) {
63
+ if (oldProps == nullptr
64
+ ? newProps->hybridRef.isProvided()
65
+ : !newProps->hybridRef.hasSameValue(oldProps->hybridRef)) {
47
66
  // hybridRef changed - call it with new this
48
- const auto& maybeFunc = props->hybridRef.value;
67
+ const auto& maybeFunc = newProps->hybridRef.get();
49
68
  if (maybeFunc.has_value()) {
50
69
  maybeFunc.value()(hybridView);
51
70
  }
52
- props->hybridRef.isDirty = false;
53
71
  }
54
72
  }
55
73
 
@@ -31,7 +31,12 @@ public:
31
31
  public:
32
32
  static void updateViewProps(jni::alias_ref<jni::JClass> /* class */,
33
33
  jni::alias_ref<JHybridScrollGuardSpec::JavaPart> view,
34
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface);
34
+ jni::alias_ref<JStateWrapper::javaobject> newState,
35
+ jni::alias_ref<JStateWrapper::javaobject> oldState);
36
+
37
+ private:
38
+ static std::shared_ptr<const HybridScrollGuardProps> getPropsFromStateWrapper(
39
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper);
35
40
 
36
41
  public:
37
42
  static void registerNatives() {
@@ -20,6 +20,14 @@ import com.margelo.nitro.scrollguard.*
20
20
  * Represents the React Native `ViewManager` for the "ScrollGuard" Nitro HybridView.
21
21
  */
22
22
  public class HybridScrollGuardManager: SimpleViewManager<View>() {
23
+ /**
24
+ * Represents the View and its last state snapshot (mutable)
25
+ */
26
+ private class HybridViewHolder(
27
+ val hybridView: HybridScrollGuard,
28
+ var lastState: StateWrapper? = null,
29
+ )
30
+
23
31
  init {
24
32
  if (RecyclableView::class.java.isAssignableFrom(HybridScrollGuard::class.java)) {
25
33
  // Enable view recycling
@@ -34,33 +42,41 @@ public class HybridScrollGuardManager: SimpleViewManager<View>() {
34
42
  override fun createViewInstance(reactContext: ThemedReactContext): View {
35
43
  val hybridView = HybridScrollGuard(reactContext)
36
44
  val view = hybridView.view
37
- view.setTag(associated_hybrid_view_tag, hybridView)
45
+ view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView))
38
46
  return view
39
47
  }
40
48
 
41
49
  override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
42
- val hybridView = getHybridView(view)
50
+ val holder = getHybridViewHolder(view)
43
51
  ?: throw Error("Couldn't find view $view in local views table!")
52
+ val hybridView = holder.hybridView
53
+ val oldState = holder.lastState
54
+ val newState = stateWrapper
44
55
 
45
56
  // 1. Update each prop individually
46
57
  hybridView.beforeUpdate()
47
- HybridScrollGuardStateUpdater.updateViewProps(hybridView, stateWrapper)
58
+ HybridScrollGuardStateUpdater.updateViewProps(hybridView, newState, oldState)
48
59
  hybridView.afterUpdate()
60
+ holder.lastState = newState
49
61
 
50
62
  // 2. Continue in base View props
51
- return super.updateState(view, props, stateWrapper)
63
+ return super.updateState(view, props, newState)
52
64
  }
53
65
 
54
66
  override fun onDropViewInstance(view: View) {
55
- val hybridView = getHybridView(view)
56
- hybridView?.onDropView()
67
+ val holder = getHybridViewHolder(view)
68
+ holder?.lastState = null
69
+ holder?.hybridView?.onDropView()
57
70
  return super.onDropViewInstance(view)
58
71
  }
59
72
 
60
73
  protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? {
61
- super.prepareToRecycleView(reactContext, view)
62
- val hybridView = getHybridView(view)
74
+ val preparedView = super.prepareToRecycleView(reactContext, view)
75
+ ?: return null
76
+ val holder = getHybridViewHolder(preparedView)
63
77
  ?: return null
78
+ val hybridView = holder.hybridView
79
+ holder.lastState = null
64
80
 
65
81
  @Suppress("USELESS_IS_CHECK")
66
82
  if (hybridView is RecyclableView) {
@@ -74,7 +90,7 @@ public class HybridScrollGuardManager: SimpleViewManager<View>() {
74
90
  }
75
91
  }
76
92
 
77
- private fun getHybridView(view: View): HybridScrollGuard? {
78
- return view.getTag(associated_hybrid_view_tag) as? HybridScrollGuard
93
+ private fun getHybridViewHolder(view: View): HybridViewHolder? {
94
+ return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder
79
95
  }
80
96
  }
@@ -14,10 +14,10 @@ internal class HybridScrollGuardStateUpdater {
14
14
  companion object {
15
15
  /**
16
16
  * Updates the props for [view] through C++.
17
- * The [state] prop is expected to contain [view]'s props as wrapped Fabric state.
17
+ * The [newState] prop is expected to contain [view]'s props as wrapped Fabric state.
18
18
  */
19
19
  @Suppress("KotlinJniMissingFunction")
20
20
  @JvmStatic
21
- external fun updateViewProps(view: HybridScrollGuardSpec, state: StateWrapper)
21
+ external fun updateViewProps(view: HybridScrollGuardSpec, newState: StateWrapper, oldState: StateWrapper?)
22
22
  }
23
23
  }
@@ -37,6 +37,7 @@ using namespace margelo::nitro::scrollguard::views;
37
37
 
38
38
  @implementation HybridScrollGuardComponent {
39
39
  std::shared_ptr<HybridScrollGuardSpecSwift> _hybridView;
40
+ BOOL _didDropView;
40
41
  }
41
42
 
42
43
  + (void) load {
@@ -50,6 +51,7 @@ using namespace margelo::nitro::scrollguard::views;
50
51
 
51
52
  - (instancetype) init {
52
53
  if (self = [super init]) {
54
+ _props = HybridScrollGuardShadowNode::defaultSharedProps();
53
55
  std::shared_ptr<HybridScrollGuardSpec> hybridView = ScrollGuard::ScrollGuardAutolinking::createScrollGuard();
54
56
  _hybridView = std::dynamic_pointer_cast<HybridScrollGuardSpecSwift>(hybridView);
55
57
  [self updateView];
@@ -69,35 +71,55 @@ using namespace margelo::nitro::scrollguard::views;
69
71
  [self setContentView:view];
70
72
  }
71
73
 
74
+ - (void) notifyOnDropView {
75
+ // A recycled component can later be invalidated. Notify only once per mount.
76
+ if (_didDropView) {
77
+ return;
78
+ }
79
+ ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
80
+ swiftPart.onDropView();
81
+ _didDropView = YES;
82
+ }
83
+
72
84
  - (void) updateProps:(const std::shared_ptr<const react::Props>&)props
73
85
  oldProps:(const std::shared_ptr<const react::Props>&)oldProps {
86
+ // A props update marks a newly mounted or still-active component.
87
+ _didDropView = NO;
88
+
74
89
  // 1. Downcast props
75
- const auto& newViewPropsConst = *std::static_pointer_cast<HybridScrollGuardProps const>(props);
76
- auto& newViewProps = const_cast<HybridScrollGuardProps&>(newViewPropsConst);
90
+ const auto& newViewProps = *std::static_pointer_cast<const HybridScrollGuardProps>(props);
91
+ const auto* oldViewProps = static_cast<const HybridScrollGuardProps*>(oldProps.get());
77
92
  ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
78
93
 
79
- // 2. Update each prop individually
80
- swiftPart.beforeUpdate();
81
-
82
- // direction: optional
83
- if (newViewProps.direction.isDirty) {
84
- swiftPart.setDirection(newViewProps.direction.value);
85
- newViewProps.direction.isDirty = false;
86
- }
87
-
88
- swiftPart.afterUpdate();
94
+ // 2. Update only props that differ from the previous Props snapshot.
95
+ const bool hasTransactionPropChanges = oldViewProps == nullptr
96
+ ? newViewProps.hasAnyProvidedProps()
97
+ : !newViewProps.hasSameProps(*oldViewProps);
98
+ if (hasTransactionPropChanges) {
99
+ swiftPart.beforeUpdate();
100
+
101
+ // direction: optional
102
+ if (oldViewProps == nullptr
103
+ ? newViewProps.direction.isProvided()
104
+ : !newViewProps.direction.hasSameValue(oldViewProps->direction)) {
105
+ swiftPart.setDirection(newViewProps.direction.get());
106
+ }
89
107
 
90
- // 3. Update hybridRef if it changed
91
- if (newViewProps.hybridRef.isDirty) {
92
- // hybridRef changed - call it with new this
93
- const auto& maybeFunc = newViewProps.hybridRef.value;
94
- if (maybeFunc.has_value()) {
95
- maybeFunc.value()(_hybridView);
108
+ // Update hybridRef if it changed
109
+ if (oldViewProps == nullptr
110
+ ? newViewProps.hybridRef.isProvided()
111
+ : !newViewProps.hybridRef.hasSameValue(oldViewProps->hybridRef)) {
112
+ // hybridRef changed - call it with new this
113
+ const auto& maybeFunc = newViewProps.hybridRef.get();
114
+ if (maybeFunc.has_value()) {
115
+ maybeFunc.value()(_hybridView);
116
+ }
96
117
  }
97
- newViewProps.hybridRef.isDirty = false;
118
+
119
+ swiftPart.afterUpdate();
98
120
  }
99
121
 
100
- // 4. Continue in base class
122
+ // 3. Continue in base class
101
123
  [super updateProps:props oldProps:oldProps];
102
124
  }
103
125
 
@@ -106,6 +128,7 @@ using namespace margelo::nitro::scrollguard::views;
106
128
  }
107
129
 
108
130
  - (void)prepareForRecycle {
131
+ [self notifyOnDropView];
109
132
  [super prepareForRecycle];
110
133
  ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
111
134
  swiftPart.maybePrepareForRecycle();
@@ -113,8 +136,7 @@ using namespace margelo::nitro::scrollguard::views;
113
136
 
114
137
  #ifdef ENABLE_RCT_COMPONENT_VIEW_INVALIDATE
115
138
  - (void)invalidate {
116
- ScrollGuard::HybridScrollGuardSpec_cxx& swiftPart = _hybridView->getSwiftPart();
117
- swiftPart.onDropView();
139
+ [self notifyOnDropView];
118
140
  [super invalidate];
119
141
  }
120
142
  #endif
@@ -7,45 +7,21 @@
7
7
 
8
8
  #include "HybridScrollGuardComponent.hpp"
9
9
 
10
- #include <string>
11
- #include <exception>
12
- #include <utility>
13
- #include <NitroModules/NitroDefines.hpp>
14
- #include <NitroModules/JSIConverter.hpp>
15
- #include <NitroModules/PropNameIDCache.hpp>
16
- #include <react/renderer/core/RawValue.h>
17
- #include <react/renderer/core/ShadowNode.h>
18
- #include <react/renderer/core/ComponentDescriptor.h>
19
- #include <react/renderer/components/view/ViewProps.h>
10
+ #include <NitroModules/NitroHash.hpp>
11
+ #include <NitroModules/ReactProp.hpp>
20
12
 
21
13
  namespace margelo::nitro::scrollguard::views {
22
14
 
15
+ using namespace facebook;
16
+
23
17
  extern const char HybridScrollGuardComponentName[] = "ScrollGuard";
24
18
 
25
19
  HybridScrollGuardProps::HybridScrollGuardProps(const react::PropsParserContext& context,
26
20
  const HybridScrollGuardProps& sourceProps,
27
21
  const react::RawProps& rawProps):
28
22
  react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
29
- direction([&]() -> CachedProp<std::optional<ScrollGuardDirection>> {
30
- try {
31
- const react::RawValue* rawValue = rawProps.at("direction", nullptr, nullptr);
32
- if (rawValue == nullptr) return sourceProps.direction;
33
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
34
- return CachedProp<std::optional<ScrollGuardDirection>>::fromRawValue(*runtime, value, sourceProps.direction);
35
- } catch (const std::exception& exc) {
36
- throw std::runtime_error(std::string("ScrollGuard.direction: ") + exc.what());
37
- }
38
- }()),
39
- hybridRef([&]() -> CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> {
40
- try {
41
- const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr);
42
- if (rawValue == nullptr) return sourceProps.hybridRef;
43
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
44
- return CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, PropNameIDCache::get(*runtime, "f")), sourceProps.hybridRef);
45
- } catch (const std::exception& exc) {
46
- throw std::runtime_error(std::string("ScrollGuard.hybridRef: ") + exc.what());
47
- }
48
- }()) { }
23
+ direction(nitro::ReactProp<std::optional<ScrollGuardDirection>>::fromRawValue("ScrollGuard", "direction", rawProps, sourceProps.direction)),
24
+ hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>>::fromRawValue("ScrollGuard", "hybridRef", rawProps, sourceProps.hybridRef)) { }
49
25
 
50
26
  bool HybridScrollGuardProps::filterObjectKeys(const std::string& propName) {
51
27
  switch (hashString(propName)) {
@@ -55,29 +31,4 @@ namespace margelo::nitro::scrollguard::views {
55
31
  }
56
32
  }
57
33
 
58
- HybridScrollGuardComponentDescriptor::HybridScrollGuardComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
59
- : ConcreteComponentDescriptor(parameters,
60
- react::RawPropsParser()) {}
61
-
62
- std::shared_ptr<const react::Props> HybridScrollGuardComponentDescriptor::cloneProps(const react::PropsParserContext& context,
63
- const std::shared_ptr<const react::Props>& props,
64
- react::RawProps rawProps) const {
65
- // 1. Prepare raw props parser
66
- rawProps.parse(rawPropsParser_);
67
- // 2. Copy props with Nitro's cached copy constructor
68
- return HybridScrollGuardShadowNode::Props(context, /* & */ rawProps, props);
69
- }
70
-
71
- #ifdef ANDROID
72
- void HybridScrollGuardComponentDescriptor::adopt(react::ShadowNode& shadowNode) const {
73
- // This is called immediately after `ShadowNode` is created, cloned or in progress.
74
- // On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++.
75
- auto& concreteShadowNode = static_cast<HybridScrollGuardShadowNode&>(shadowNode);
76
- const std::shared_ptr<const HybridScrollGuardProps>& constProps = concreteShadowNode.getConcreteSharedProps();
77
- const std::shared_ptr<HybridScrollGuardProps>& props = std::const_pointer_cast<HybridScrollGuardProps>(constProps);
78
- HybridScrollGuardState state{props};
79
- concreteShadowNode.setStateData(std::move(state));
80
- }
81
- #endif
82
-
83
34
  } // namespace margelo::nitro::scrollguard::views
@@ -7,14 +7,15 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <optional>
11
- #include <NitroModules/NitroDefines.hpp>
12
- #include <NitroModules/NitroHash.hpp>
13
- #include <NitroModules/CachedProp.hpp>
14
- #include <react/renderer/core/ConcreteComponentDescriptor.h>
15
- #include <react/renderer/core/PropsParserContext.h>
10
+ #include <NitroModules/ReactProp.hpp>
11
+ #include <NitroModules/ViewComponentDescriptor.hpp>
12
+ #include <NitroModules/ViewPropsHolderState.hpp>
16
13
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
17
14
  #include <react/renderer/components/view/ViewProps.h>
15
+ #include <react/renderer/core/PropsParserContext.h>
16
+ #include <react/renderer/core/RawProps.h>
17
+
18
+ #include <string>
18
19
 
19
20
  #include "ScrollGuardDirection.hpp"
20
21
  #include <optional>
@@ -42,8 +43,20 @@ namespace margelo::nitro::scrollguard::views {
42
43
  const react::RawProps& rawProps);
43
44
 
44
45
  public:
45
- CachedProp<std::optional<ScrollGuardDirection>> direction;
46
- CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> hybridRef;
46
+ nitro::ReactProp<std::optional<ScrollGuardDirection>> direction;
47
+ nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridScrollGuardSpec>& /* ref */)>>> hybridRef;
48
+
49
+ [[nodiscard]]
50
+ bool hasSameProps(const HybridScrollGuardProps& other) const noexcept {
51
+ return direction.hasSameValue(other.direction) &&
52
+ hybridRef.hasSameValue(other.hybridRef);
53
+ }
54
+
55
+ [[nodiscard]]
56
+ bool hasAnyProvidedProps() const noexcept {
57
+ return direction.isProvided() ||
58
+ hybridRef.isProvided();
59
+ }
47
60
 
48
61
  private:
49
62
  static bool filterObjectKeys(const std::string& propName);
@@ -52,32 +65,7 @@ namespace margelo::nitro::scrollguard::views {
52
65
  /**
53
66
  * State for the "ScrollGuard" View.
54
67
  */
55
- class HybridScrollGuardState final {
56
- public:
57
- HybridScrollGuardState() = default;
58
- explicit HybridScrollGuardState(const std::shared_ptr<HybridScrollGuardProps>& props):
59
- _props(props) {}
60
-
61
- public:
62
- [[nodiscard]]
63
- const std::shared_ptr<HybridScrollGuardProps>& getProps() const {
64
- return _props;
65
- }
66
-
67
- public:
68
- #ifdef ANDROID
69
- HybridScrollGuardState(const HybridScrollGuardState& /* previousState */, folly::dynamic /* data */) {}
70
- folly::dynamic getDynamic() const {
71
- throw std::runtime_error("HybridScrollGuardState does not support folly!");
72
- }
73
- react::MapBuffer getMapBuffer() const {
74
- throw std::runtime_error("HybridScrollGuardState does not support MapBuffer!");
75
- };
76
- #endif
77
-
78
- private:
79
- std::shared_ptr<HybridScrollGuardProps> _props;
80
- };
68
+ using HybridScrollGuardState = nitro::ViewPropsHolderState<HybridScrollGuardProps>;
81
69
 
82
70
  /**
83
71
  * The Shadow Node for the "ScrollGuard" View.
@@ -90,21 +78,7 @@ namespace margelo::nitro::scrollguard::views {
90
78
  /**
91
79
  * The Component Descriptor for the "ScrollGuard" View.
92
80
  */
93
- class HybridScrollGuardComponentDescriptor final: public react::ConcreteComponentDescriptor<HybridScrollGuardShadowNode> {
94
- public:
95
- explicit HybridScrollGuardComponentDescriptor(const react::ComponentDescriptorParameters& parameters);
96
-
97
- public:
98
- /**
99
- * A faster path for cloning props - reuses the caching logic from `HybridScrollGuardProps`.
100
- */
101
- std::shared_ptr<const react::Props> cloneProps(const react::PropsParserContext& context,
102
- const std::shared_ptr<const react::Props>& props,
103
- react::RawProps rawProps) const override;
104
- #ifdef ANDROID
105
- void adopt(react::ShadowNode& shadowNode) const override;
106
- #endif
107
- };
81
+ using HybridScrollGuardComponentDescriptor = nitro::ViewComponentDescriptor<HybridScrollGuardShadowNode>;
108
82
 
109
83
  /* The actual view for "ScrollGuard" needs to be implemented in platform-specific code. */
110
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-scroll-guard",
3
- "version": "3.0.96",
3
+ "version": "3.0.98",
4
4
  "description": "A native view wrapper that prevents parent scrollable containers (PagerView/ViewPager2) from intercepting child scroll gestures",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -62,17 +62,17 @@
62
62
  "devDependencies": {
63
63
  "@react-native/babel-preset": "0.86.2",
64
64
  "del-cli": "^6.0.0",
65
- "nitrogen": "0.36.5",
65
+ "nitrogen": "0.37.0",
66
66
  "react": "19.2.3",
67
67
  "react-native": "patch:react-native@npm%3A0.86.2#~/.yarn/patches/react-native-npm-0.86.2-c9d546616f.patch",
68
68
  "react-native-builder-bob": "^0.40.13",
69
- "react-native-nitro-modules": "0.36.5",
69
+ "react-native-nitro-modules": "0.37.0",
70
70
  "typescript": "^5.9.2"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "react": "*",
74
74
  "react-native": "*",
75
- "react-native-nitro-modules": "0.36.5"
75
+ "react-native-nitro-modules": "0.37.0"
76
76
  },
77
77
  "packageManager": "yarn@4.11.0",
78
78
  "react-native-builder-bob": {