11#include " Animations.hpp"
22#include " StyleResolver.hpp"
3+ #include " Effect.hpp"
34#include < unordered_map>
4- #include < mutex>
55
66namespace reactnativecss ::animations {
77
@@ -15,28 +15,30 @@ namespace reactnativecss::animations {
1515 // Map to store scope-specific computeds: Map<variableScope, Map<name, Computed<AnyMap>>>
1616 static std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<reactnativecss::Computed<std::shared_ptr<AnyMap>>>>> scopedComputeds;
1717
18- static std::mutex keyframesMutex;
19-
2018 void setKeyframes (const std::string &name, const std::shared_ptr<AnyMap> &keyframes) {
21- std::lock_guard< std::mutex> lock (keyframesMutex) ;
19+ std::shared_ptr<reactnativecss::Observable< std::shared_ptr<AnyMap>>> observable ;
2220
2321 // Find or create the observable for this keyframe name
2422 auto it = keyframesObservables.find (name);
2523
2624 if (it != keyframesObservables.end ()) {
27- // Update existing observable
28- it->second ->set (keyframes);
25+ observable = it->second ;
2926 } else {
3027 // Create new observable
31- keyframesObservables[name] = reactnativecss::Observable<std::shared_ptr<AnyMap>>::create (
32- keyframes);
28+ observable = reactnativecss::Observable<std::shared_ptr<AnyMap>>::create (keyframes);
29+ keyframesObservables[name] = observable;
30+ }
31+
32+ // Batch the update to prevent cascade during fast refresh
33+ if (observable) {
34+ reactnativecss::Effect::batch ([&]() {
35+ observable->set (keyframes);
36+ });
3337 }
3438 }
3539
3640 std::shared_ptr<AnyMap> getKeyframes (const std::string &name, const std::string &variableScope,
3741 reactnativecss::Effect::GetProxy &get) {
38- std::lock_guard<std::mutex> lock (keyframesMutex);
39-
4042 // First, ensure the Observable exists for this keyframe name
4143 auto obsIt = keyframesObservables.find (name);
4244 if (obsIt == keyframesObservables.end ()) {
@@ -62,63 +64,73 @@ namespace reactnativecss::animations {
6264
6365 if (computedIt == scopeMap.end ()) {
6466 // Create a new Computed that gets the AnyMap from the observable and processes it
65- auto computed = reactnativecss::Computed<std::shared_ptr<AnyMap>>::create (
66- [observable, variableScope](const std::shared_ptr<AnyMap> &prev,
67- reactnativecss::Effect::GetProxy &get) {
68- // Get the raw keyframes from the observable
69- auto rawKeyframes = get (*observable);
70-
71- // Create a new AnyMap to hold the resolved keyframes
72- auto resolvedKeyframes = AnyMap::make (rawKeyframes->getMap ().size ());
73-
74- // Loop over the entries of the rawKeyframes
75- for (const auto &entry: rawKeyframes->getMap ()) {
76- const std::string &key = entry.first ;
77- const AnyValue &value = entry.second ;
78-
79- // Each value should be an AnyObject, if not skip that entry
80- if (!std::holds_alternative<AnyObject>(value)) {
81- continue ;
67+ // Wrap in a batch to ensure the initial computation doesn't trigger cascades
68+ std::shared_ptr<reactnativecss::Computed<std::shared_ptr<AnyMap>>> computed;
69+
70+ reactnativecss::Effect::batch ([&]() {
71+ computed = reactnativecss::Computed<std::shared_ptr<AnyMap>>::create (
72+ [observable, variableScope](const std::shared_ptr<AnyMap> &prev,
73+ reactnativecss::Effect::GetProxy &get) {
74+ // Get the raw keyframes from the observable
75+ auto rawKeyframes = get (*observable);
76+
77+ // If keyframes are empty, return early to avoid unnecessary processing
78+ if (rawKeyframes->getMap ().empty ()) {
79+ return AnyMap::make ();
8280 }
8381
84- const auto &frameMap = std::get<AnyObject>(value);
82+ // Create a new AnyMap to hold the resolved keyframes
83+ auto resolvedKeyframes = AnyMap::make (rawKeyframes->getMap ().size ());
8584
86- // Create a temporary map to hold resolved frame values
87- std::unordered_map<std::string, AnyValue> resolvedFrameMap;
88- resolvedFrameMap.reserve (frameMap.size ());
85+ // Loop over the entries of the rawKeyframes
86+ for (const auto &entry: rawKeyframes->getMap ()) {
87+ const std::string &key = entry.first ;
88+ const AnyValue &value = entry.second ;
8989
90- // Loop over each entry of the frame and resolve values
91- for ( const auto &frameEntry: frameMap ) {
92- const std::string &frameKey = frameEntry. first ;
93- const AnyValue &frameValue = frameEntry. second ;
90+ // Each value should be an AnyObject, if not skip that entry
91+ if (!std::holds_alternative<AnyObject>(value) ) {
92+ continue ;
93+ }
9494
95- // Resolve the value using StyleResolver
96- AnyValue resolvedValue = margelo::nitro::cssnitro::StyleResolver::resolveStyle (
97- frameValue, variableScope, get
98- );
95+ const auto &frameMap = std::get<AnyObject>(value);
9996
100- resolvedFrameMap[frameKey] = resolvedValue;
101- }
97+ // Create a temporary map to hold resolved frame values
98+ std::unordered_map<std::string, AnyValue> resolvedFrameMap;
99+ resolvedFrameMap.reserve (frameMap.size ());
102100
103- // Apply style mapping to the resolved frame
104- auto transformedFrame = margelo::nitro::cssnitro::StyleResolver::applyStyleMapping (
105- resolvedFrameMap, variableScope, get
106- ) ;
101+ // Loop over each entry of the frame and resolve values
102+ for ( const auto &frameEntry: frameMap) {
103+ const std::string &frameKey = frameEntry. first ;
104+ const AnyValue &frameValue = frameEntry. second ;
107105
108- // Convert the transformed frame back to an AnyObject
109- AnyObject finalFrame;
110- for (const auto &transformedEntry: transformedFrame->getMap ()) {
111- finalFrame[transformedEntry.first ] = transformedEntry.second ;
112- }
106+ // Resolve the value using StyleResolver
107+ AnyValue resolvedValue = margelo::nitro::cssnitro::StyleResolver::resolveStyle (
108+ frameValue, variableScope, get
109+ );
110+
111+ resolvedFrameMap[frameKey] = resolvedValue;
112+ }
113+
114+ // Apply style mapping to the resolved frame (don't process animations to avoid recursion)
115+ auto transformedFrame = margelo::nitro::cssnitro::StyleResolver::applyStyleMapping (
116+ resolvedFrameMap, variableScope, get, false
117+ );
118+
119+ // Convert the transformed frame back to an AnyObject
120+ AnyObject finalFrame;
121+ for (const auto &transformedEntry: transformedFrame->getMap ()) {
122+ finalFrame[transformedEntry.first ] = transformedEntry.second ;
123+ }
113124
114- // Set the resolved and transformed frame in the result
115- resolvedKeyframes->setObject (key, finalFrame);
116- }
125+ // Set the resolved and transformed frame in the result
126+ resolvedKeyframes->setObject (key, finalFrame);
127+ }
117128
118- return resolvedKeyframes;
119- },
120- AnyMap::make ()
121- );
129+ return resolvedKeyframes;
130+ },
131+ AnyMap::make ()
132+ );
133+ });
122134
123135 scopeMap[name] = computed;
124136 computedIt = scopeMap.find (name);
@@ -129,7 +141,6 @@ namespace reactnativecss::animations {
129141 }
130142
131143 void deleteScope (const std::string &name) {
132- std::lock_guard<std::mutex> lock (keyframesMutex);
133144 scopedComputeds.erase (name);
134145 }
135146
0 commit comments