diff --git a/babel.config.js b/babel.config.js index deb42c7e83e..9adf2e4f163 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,12 @@ -module.exports = { - presets: ['module:@react-native/babel-preset'], - plugins: ['react-native-reanimated/plugin'] +module.exports = function (api) { + const isAndroid = api.caller(c => c.platform === 'android') + + return { + presets: ['module:@react-native/babel-preset'], + plugins: [ + isAndroid + ? './node_modules/r3-hack/node_modules/react-native-reanimated/plugin' + : 'react-native-worklets/plugin' + ] + } } diff --git a/ios/Podfile b/ios/Podfile index e5649896688..5a8846ce969 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -50,7 +50,7 @@ target 'edge' do :path => config[:reactNativePath], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/..", - :fabric_enabled => false, + :fabric_enabled => true, :hermes_enabled => true ) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1875c9c2965..0251138322b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2320,7 +2320,7 @@ PODS: - ZXingObjC - RNReactNativeHapticFeedback (1.14.0): - React-Core - - RNReanimated (3.19.0): + - RNReanimated (4.0.2): - DoubleConversion - glog - hermes-engine @@ -2343,10 +2343,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.19.0) - - RNReanimated/worklets (= 3.19.0) + - RNReanimated/reanimated (= 4.0.2) + - RNWorklets - Yoga - - RNReanimated/reanimated (3.19.0): + - RNReanimated/reanimated (4.0.2): - DoubleConversion - glog - hermes-engine @@ -2369,9 +2369,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 3.19.0) + - RNReanimated/reanimated/apple (= 4.0.2) + - RNWorklets - Yoga - - RNReanimated/reanimated/apple (3.19.0): + - RNReanimated/reanimated/apple (4.0.2): - DoubleConversion - glog - hermes-engine @@ -2394,8 +2395,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNWorklets - Yoga - - RNReanimated/worklets (3.19.0): + - RNScreens (4.11.1): - DoubleConversion - glog - hermes-engine @@ -2412,15 +2414,16 @@ PODS: - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-RCTImage - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/worklets/apple (= 3.19.0) + - RNScreens/common (= 4.11.1) - Yoga - - RNReanimated/worklets/apple (3.19.0): + - RNScreens/common (4.11.1): - DoubleConversion - glog - hermes-engine @@ -2437,6 +2440,7 @@ PODS: - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-RCTImage - React-renderercss - React-rendererdebug - React-utils @@ -2444,7 +2448,9 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (4.11.1): + - RNSecureRandom (1.0.1): + - React + - RNSentry (6.14.0): - DoubleConversion - glog - hermes-engine @@ -2461,16 +2467,15 @@ PODS: - React-jsi - React-NativeModulesApple - React-RCTFabric - - React-RCTImage - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.11.1) + - Sentry/HybridSDK (= 8.50.2) - Yoga - - RNScreens/common (4.11.1): + - RNShare (12.0.11): - DoubleConversion - glog - hermes-engine @@ -2487,7 +2492,6 @@ PODS: - React-jsi - React-NativeModulesApple - React-RCTFabric - - React-RCTImage - React-renderercss - React-rendererdebug - React-utils @@ -2495,9 +2499,12 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSecureRandom (1.0.1): + - RNSound (0.11.0): - React - - RNSentry (6.14.0): + - RNSound/Core (= 0.11.0) + - RNSound/Core (0.11.0): + - React + - RNStoreReview (0.4.3): - DoubleConversion - glog - hermes-engine @@ -2520,9 +2527,8 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - Sentry/HybridSDK (= 8.50.2) - Yoga - - RNShare (12.0.11): + - RNSVG (15.12.0): - DoubleConversion - glog - hermes-engine @@ -2545,13 +2551,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNSVG/common (= 15.12.0) - Yoga - - RNSound (0.11.0): - - React - - RNSound/Core (= 0.11.0) - - RNSound/Core (0.11.0): - - React - - RNStoreReview (0.4.3): + - RNSVG/common (15.12.0): - DoubleConversion - glog - hermes-engine @@ -2575,7 +2577,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.12.0): + - RNVectorIcons (10.1.0): - DoubleConversion - glog - hermes-engine @@ -2598,9 +2600,8 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.12.0) - Yoga - - RNSVG/common (15.12.0): + - RNWorklets (0.4.1): - DoubleConversion - glog - hermes-engine @@ -2623,8 +2624,34 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNWorklets/worklets (= 0.4.1) - Yoga - - RNVectorIcons (10.1.0): + - RNWorklets/worklets (0.4.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNWorklets/worklets/apple (= 0.4.1) + - Yoga + - RNWorklets/worklets/apple (0.4.1): - DoubleConversion - glog - hermes-engine @@ -2934,6 +2961,7 @@ DEPENDENCIES: - RNStoreReview (from `../node_modules/react-native-store-review`) - RNSVG (from `../node_modules/react-native-svg`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) + - RNWorklets (from `../node_modules/react-native-worklets`) - VisionCamera (from `../node_modules/react-native-vision-camera`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -3243,6 +3271,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-svg" RNVectorIcons: :path: "../node_modules/react-native-vector-icons" + RNWorklets: + :path: "../node_modules/react-native-worklets" VisionCamera: :path: "../node_modules/react-native-vision-camera" Yoga: @@ -3392,7 +3422,7 @@ SPEC CHECKSUMS: RNPermissions: 60b23d827dc7a4b2e347356fc20206184676637c RNQrGenerator: 95feb09b4b2ed65f3a4afe795a20beed4b134ab5 RNReactNativeHapticFeedback: 8364333ca888b1b7ec9d2daf04b010ee5436366e - RNReanimated: 35a5a59798fe9a9a61259146be9fe8b0b602ab39 + RNReanimated: 24b8a5b54c499ac783c2c299aad4712002c03bb1 RNScreens: 90b905d545a5ebbe976985702b8a39e3475727b2 RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc RNSentry: 33bc24cea8f87ab26521a9330549a82c703b5db4 @@ -3401,6 +3431,7 @@ SPEC CHECKSUMS: RNStoreReview: 8b47d208282c23296d2466a488a8d9ca1979d79b RNSVG: 45e3c3210465e75ab6374c9f746179e75d76ce48 RNVectorIcons: f1bc9e04b6f67ec09ea54e6f092e75a9e205c1d7 + RNWorklets: 32e63979c33291a69d4d8541d4425ab91763320e SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d Sentry: d95f5f3b32d01324b3e27d3c52747005302cc026 @@ -3426,6 +3457,6 @@ SPEC CHECKSUMS: ZIPFoundation: b1f0de4eed33e74a676f76e12559ab6b75990197 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 -PODFILE CHECKSUM: 2ab3d4f06447f2252ef672833166c9c33f3e8475 +PODFILE CHECKSUM: 8c119d1d2fda8d2ceda1c186a5415d0c7473469a COCOAPODS: 1.16.2 diff --git a/metro.config.js b/metro.config.js index 939f4ee12a8..95fff748522 100644 --- a/metro.config.js +++ b/metro.config.js @@ -2,6 +2,7 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config') const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config') +const r3Paths = require('r3-hack') const defaultConfig = getDefaultConfig(__dirname) const { assetExts, sourceExts } = defaultConfig.resolver @@ -19,6 +20,31 @@ const config = { ) }, resolver: { + resolveRequest(context, moduleName, platform) { + if (platform === 'android') { + // Use Reanimated 3 on Android: + const filePath = r3Paths[moduleName] + if (filePath != null) { + return { type: 'sourceFile', filePath } + } + + // Ensure we aren't missing any reanimated 3 -> 4 mappings: + if ( + moduleName.startsWith('react-native-reanimated') || + moduleName.startsWith('react-native-worklets') + ) { + console.log( + `Could not find "${moduleName}". Please update r3-hack to include it.` + ) + return { type: 'empty' } + } + } + + // Otherwise use the normal Metro resolution: + return context.resolveRequest(context, moduleName, platform) + }, + + // From react-native-svg-transformer: assetExts: assetExts.filter(ext => ext !== 'svg'), sourceExts: [...sourceExts, 'svg'] } diff --git a/package.json b/package.json index 20a24b08d8a..72b5f7f1d70 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@react-native-firebase/messaging": "^20.5.0", "@react-native-picker/picker": "^2.4.0", "@react-navigation/bottom-tabs": "^6.5.4", - "@react-navigation/drawer": "^6.5.8", + "@react-navigation/drawer": "^6.7.2", "@react-navigation/elements": "^1.3.14", "@react-navigation/native": "^6.1.3", "@react-navigation/stack": "^6.3.12", @@ -118,6 +118,7 @@ "posthog-react-native": "^2.8.1", "prompts": "^2.4.2", "qrcode-generator": "^1.4.4", + "r3-hack": "./scripts/r3-hack", "react": "19.0.0", "react-native": "0.79.2", "react-native-airship": "^0.2.12", @@ -150,7 +151,7 @@ "react-native-performance": "^5.1.4", "react-native-permissions": "^4.1.5", "react-native-piratechain": "^0.5.12", - "react-native-reanimated": "^3.18.0", + "react-native-reanimated": "^4.0.2", "react-native-reorderable-list": "^0.5.0", "react-native-safari-view": "^2.1.0", "react-native-safe-area-context": "^5.5.1", @@ -165,6 +166,7 @@ "react-native-vision-camera": "^4.7.1", "react-native-webview": "^13.15.0", "react-native-wheel-picker-android": "^2.0.6", + "react-native-worklets": "^0.4.1", "react-native-zano": "^0.2.1", "react-native-zcash": "^0.9.10", "react-redux": "^8.1.1", @@ -264,5 +266,10 @@ }, "engines": { "node": ">=18" + }, + "reanimated": { + "staticFeatureFlags": { + "DISABLE_COMMIT_PAUSING_MECHANISM": true + } } } diff --git a/patches/@react-navigation+drawer+6.7.2.patch b/patches/@react-navigation+drawer+6.7.2.patch new file mode 100644 index 00000000000..663d5904502 --- /dev/null +++ b/patches/@react-navigation+drawer+6.7.2.patch @@ -0,0 +1,157 @@ +diff --git a/node_modules/@react-navigation/drawer/src/views/DrawerView.tsx b/node_modules/@react-navigation/drawer/src/views/DrawerView.tsx +index 94770c4..9e091d3 100644 +--- a/node_modules/@react-navigation/drawer/src/views/DrawerView.tsx ++++ b/node_modules/@react-navigation/drawer/src/views/DrawerView.tsx +@@ -76,8 +76,7 @@ function DrawerViewBase({ + Platform.OS === 'android' || + Platform.OS === 'ios', + // Reanimated 2 is not configured +- // @ts-expect-error: the type definitions are incomplete +- useLegacyImplementation = !Reanimated.isConfigured?.(), ++ useLegacyImplementation = false, + }: Props) { + // Reanimated v3 dropped legacy v1 syntax + const legacyImplemenationNotAvailable = +diff --git a/node_modules/@react-navigation/drawer/src/views/modern/Drawer.tsx b/node_modules/@react-navigation/drawer/src/views/modern/Drawer.tsx +index 57af20c..77aca6a 100644 +--- a/node_modules/@react-navigation/drawer/src/views/modern/Drawer.tsx ++++ b/node_modules/@react-navigation/drawer/src/views/modern/Drawer.tsx +@@ -11,20 +11,15 @@ import { + import Animated, { + interpolate, + runOnJS, +- useAnimatedGestureHandler, + useAnimatedStyle, + useDerivedValue, + useSharedValue, + withSpring, + } from 'react-native-reanimated'; ++import { Gesture, GestureDetector } from 'react-native-gesture-handler'; + + import type { DrawerProps } from '../../types'; + import DrawerProgressContext from '../../utils/DrawerProgressContext'; +-import { +- GestureState, +- PanGestureHandler, +- PanGestureHandlerGestureEvent, +-} from '../GestureHandler'; + import Overlay from './Overlay'; + + const SWIPE_DISTANCE_MINIMUM = 5; +@@ -152,7 +147,7 @@ export default function Drawer({ + const touchStartX = useSharedValue(0); + const touchX = useSharedValue(0); + const translationX = useSharedValue(getDrawerTranslationX(open)); +- const gestureState = useSharedValue(GestureState.UNDETERMINED); ++ const gestureState = useSharedValue(false); + + const toggleDrawer = React.useCallback( + ({ open, isUserInitiated, velocity }: ToggleOptions) => { +@@ -192,41 +187,31 @@ export default function Drawer({ + [open, toggleDrawer] + ); + +- const onGestureEvent = useAnimatedGestureHandler< +- PanGestureHandlerGestureEvent, +- { startX: number; hasCalledOnStart: boolean } +- >({ +- onStart: (event, ctx) => { +- ctx.hasCalledOnStart = false; +- ctx.startX = translationX.value; +- gestureState.value = event.state; +- touchStartX.value = event.x; +- }, +- onActive: (event, ctx) => { ++ const gesture = Gesture.Pan(). ++ activeOffsetX([-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]). ++ failOffsetY([-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]). ++ hitSlop(hitSlop). ++ enabled(drawerType !== 'permanent' && swipeEnabled). ++ onStart(event => { ++ gestureState.value = true; ++ touchStartX.value = event.x ++ runOnJS(onGestureStart)(); ++ }). ++ onChange(event => { + touchX.value = event.x; +- translationX.value = ctx.startX + event.translationX; +- gestureState.value = event.state; +- +- // onStart will _always_ be called, even when the activation +- // criteria isn't met yet. This makes sure onGestureStart is only +- // called when the criteria is really met. +- if (!ctx.hasCalledOnStart) { +- ctx.hasCalledOnStart = true; +- runOnJS(onGestureStart)(); +- } +- }, +- onEnd: (event) => { +- gestureState.value = event.state; +- ++ translationX.value = event.translationX; ++ }). ++ onEnd((event) => { ++ gestureState.value = false; + const nextOpen = + (Math.abs(event.translationX) > SWIPE_DISTANCE_MINIMUM && + Math.abs(event.translationX) > swipeVelocityThreshold) || +- Math.abs(event.translationX) > swipeDistanceThreshold ++ Math.abs(event.translationX) > swipeDistanceThreshold + ? drawerPosition === 'left' + ? // If swiped to right, open the drawer, otherwise close it +- (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 ++ (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 + : // If swiped to left, open the drawer, otherwise close it +- (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 ++ (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 + : open; + + toggleDrawer({ +@@ -234,11 +219,10 @@ export default function Drawer({ + isUserInitiated: true, + velocity: event.velocityX, + }); +- }, +- onFinish: () => { ++ }).onFinalize(() => { + runOnJS(onGestureFinish)(); +- }, +- }); ++ }) ++ + + const translateX = useDerivedValue(() => { + // Comment stolen from react-native-gesture-handler/DrawerLayout +@@ -267,7 +251,7 @@ export default function Drawer({ + // + // This is used only when drawerType is "front" + const touchDistance = +- drawerType === 'front' && gestureState.value === GestureState.ACTIVE ++ drawerType === 'front' && gestureState.value + ? minmax( + drawerPosition === 'left' + ? touchStartX.value - drawerWidth +@@ -344,13 +328,7 @@ export default function Drawer({ + + return ( + +- + {/* Immediate child of gesture handler needs to be an Animated.View */} + + +- ++ + + ); + } diff --git a/patches/react-native+0.79.2.patch b/patches/react-native+0.79.2.patch new file mode 100644 index 00000000000..69291f444fd --- /dev/null +++ b/patches/react-native+0.79.2.patch @@ -0,0 +1,133 @@ +diff --git a/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +index 522ec57..e6ad1df 100644 +--- a/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp ++++ b/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +@@ -22,6 +22,10 @@ + + namespace facebook::react { + ++namespace { ++const int MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING = 3; ++} // namespace ++ + using CommitStatus = ShadowTree::CommitStatus; + using CommitMode = ShadowTree::CommitMode; + +@@ -207,7 +211,8 @@ void ShadowTree::setCommitMode(CommitMode commitMode) const { + auto revision = ShadowTreeRevision{}; + + { +- std::unique_lock lock(commitMutex_); ++ ShadowTree::UniqueLock lock = uniqueCommitLock(); ++ + if (commitMode_ == commitMode) { + return; + } +@@ -224,7 +229,7 @@ void ShadowTree::setCommitMode(CommitMode commitMode) const { + } + + CommitMode ShadowTree::getCommitMode() const { +- std::shared_lock lock(commitMutex_); ++ SharedLock lock = sharedCommitLock(); + return commitMode_; + } + +@@ -238,17 +243,17 @@ CommitStatus ShadowTree::commit( + const CommitOptions& commitOptions) const { + [[maybe_unused]] int attempts = 0; + +- while (true) { +- attempts++; +- ++ while (attempts < MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING) { + auto status = tryCommit(transaction, commitOptions); + if (status != CommitStatus::Failed) { + return status; + } ++ attempts++; ++ } + +- // After multiple attempts, we failed to commit the transaction. +- // Something internally went terribly wrong. +- react_native_assert(attempts < 1024); ++ { ++ std::unique_lock lock(commitMutexRecursive_); ++ return tryCommit(transaction, commitOptions); + } + } + +@@ -266,7 +271,7 @@ CommitStatus ShadowTree::tryCommit( + + { + // Reading `currentRevision_` in shared manner. +- std::shared_lock lock(commitMutex_); ++ SharedLock lock = sharedCommitLock(); + commitMode = commitMode_; + oldRevision = currentRevision_; + } +@@ -307,7 +312,7 @@ CommitStatus ShadowTree::tryCommit( + + { + // Updating `currentRevision_` in unique manner if it hasn't changed. +- std::unique_lock lock(commitMutex_); ++ UniqueLock lock = uniqueCommitLock(); + + if (currentRevision_.number != oldRevision.number) { + return CommitStatus::Failed; +@@ -345,7 +350,7 @@ CommitStatus ShadowTree::tryCommit( + } + + ShadowTreeRevision ShadowTree::getCurrentRevision() const { +- std::shared_lock lock(commitMutex_); ++ SharedLock lock = sharedCommitLock(); + return currentRevision_; + } + +@@ -392,4 +397,12 @@ void ShadowTree::notifyDelegatesOfUpdates() const { + delegate_.shadowTreeDidFinishTransaction(mountingCoordinator_, true); + } + ++inline ShadowTree::UniqueLock ShadowTree::uniqueCommitLock() const { ++ return std::unique_lock{commitMutexRecursive_}; ++} ++ ++inline ShadowTree::SharedLock ShadowTree::sharedCommitLock() const { ++ return std::unique_lock{commitMutexRecursive_}; ++} ++ + } // namespace facebook::react +diff --git a/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h b/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h +index ae9d005..c3bef0b 100644 +--- a/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h ++++ b/node_modules/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h +@@ -8,6 +8,8 @@ + #pragma once + + #include ++#include ++#include + + #include + #include +@@ -139,10 +141,21 @@ class ShadowTree final { + const SurfaceId surfaceId_; + const ShadowTreeDelegate& delegate_; + mutable std::shared_mutex commitMutex_; ++ mutable std::recursive_mutex commitMutexRecursive_; + mutable CommitMode commitMode_{ + CommitMode::Normal}; // Protected by `commitMutex_`. + mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`. + std::shared_ptr mountingCoordinator_; ++ ++ using UniqueLock = std::variant< ++ std::unique_lock, ++ std::unique_lock>; ++ using SharedLock = std::variant< ++ std::shared_lock, ++ std::unique_lock>; ++ ++ inline UniqueLock uniqueCommitLock() const; ++ inline SharedLock sharedCommitLock() const; + }; + + } // namespace facebook::react diff --git a/react-native.config.js b/react-native.config.js index a1348c7ef4b..10ca5143757 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -4,6 +4,23 @@ module.exports = { platforms: { ios: null } + }, + + // We want Reanimated 3 on Android: + 'react-native-reanimated': { + platforms: { + android: { + sourceDir: + '../node_modules/r3-hack/node_modules/react-native-reanimated/android' + } + } + }, + + // We don't want Reanimated 4 worklets on Android: + 'react-native-worklets': { + platforms: { + android: null + } } } } diff --git a/scripts/r3-hack/README.md b/scripts/r3-hack/README.md new file mode 100644 index 00000000000..dcda549350c --- /dev/null +++ b/scripts/r3-hack/README.md @@ -0,0 +1,17 @@ +# Reanimated Hack + +Here is the problem: + +- iOS: We need to run Reanimated 4 on the new achitecture to get decent performance. +- Android: We cannot use the new architecture yet (there are unsolved performance problems), so we need to use Reanimated 3. + +How can we have two versions of a native library at once? Well... + +- Require Reanimated 4 normally through package.json. +- Require Reanimated 3 indirectly through this r3-hack shim package. +- Use react-native.config.js to override the native module with Reanimated 3. +- Tell babel.config.js to use the right plugin based on platform. +- Hack metro.config.js to resolve 'react-native-reanimated' to + this package's exported paths on Android. + +Super sketchy, but it works! diff --git a/scripts/r3-hack/index.js b/scripts/r3-hack/index.js new file mode 100644 index 00000000000..2ecc6710b0c --- /dev/null +++ b/scripts/r3-hack/index.js @@ -0,0 +1,14 @@ +// The various react-native-reanimated entry points the app uses, +// but resolved to our internal copy of Reanimated 3: +module.exports = { + // Reanimated itself: + 'react-native-reanimated': require.resolve('react-native-reanimated'), + + // react-native-keyboard-controller reaches into our internals: + 'react-native-reanimated/src/core': require.resolve( + 'react-native-reanimated/src/core.ts' + ), + + // Some functions like `runOnJs` have moved, so put them back: + 'react-native-worklets': require.resolve('react-native-reanimated') +} diff --git a/scripts/r3-hack/package.json b/scripts/r3-hack/package.json new file mode 100644 index 00000000000..8dcec0a2ac5 --- /dev/null +++ b/scripts/r3-hack/package.json @@ -0,0 +1,12 @@ +{ + "name": "r3-hack", + "version": "0.0.1", + "description": "Makes it possible to have Reanimated 3 alongside Reanimated 4", + "main": "index.js", + "files": [ + "index.js" + ], + "dependencies": { + "react-native-reanimated": "^3.19.1" + } +} diff --git a/src/__tests__/components/__snapshots__/AccountSyncBar.test.tsx.snap b/src/__tests__/components/__snapshots__/AccountSyncBar.test.tsx.snap index 383343151f0..7c62619eca7 100644 --- a/src/__tests__/components/__snapshots__/AccountSyncBar.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/AccountSyncBar.test.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`ProgressBar should render with loading props 1`] = ` `; diff --git a/src/__tests__/components/__snapshots__/BalanceCard.test.tsx.snap b/src/__tests__/components/__snapshots__/BalanceCard.test.tsx.snap index 8eeba2197bd..971859c2d28 100644 --- a/src/__tests__/components/__snapshots__/BalanceCard.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/BalanceCard.test.tsx.snap @@ -257,6 +257,7 @@ exports[`BalanceCard should render with loading props 1`] = ` } } jestInlineStyle={{}} + nativeID="0" style={ [ { @@ -651,6 +652,7 @@ exports[`BalanceCard should render with loading props 1`] = ` } } jestInlineStyle={{}} + nativeID="1" style={ [ { @@ -1022,6 +1024,7 @@ exports[`BalanceCard should render with loading props 1`] = ` } } jestInlineStyle={{}} + nativeID="2" style={ [ { diff --git a/src/__tests__/components/__snapshots__/FilledTextInput.test.tsx.snap b/src/__tests__/components/__snapshots__/FilledTextInput.test.tsx.snap index e31f4b53330..65fd00ffcd2 100644 --- a/src/__tests__/components/__snapshots__/FilledTextInput.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/FilledTextInput.test.tsx.snap @@ -120,6 +120,7 @@ exports[`FilledTextInput should render with some props 1`] = ` }, ] } + nativeID="1" scale={0} style={ [ @@ -153,6 +154,7 @@ exports[`FilledTextInput should render with some props 1`] = ` } } jestInlineStyle={{}} + nativeID="2" style={ [ { @@ -196,6 +198,7 @@ exports[`FilledTextInput should render with some props 1`] = ` }, ] } + nativeID="3" style={ [ { @@ -247,6 +250,7 @@ exports[`FilledTextInput should render with some props 1`] = ` }, ] } + nativeID="4" shift={1} style={ [ @@ -299,6 +303,7 @@ exports[`FilledTextInput should render with some props 1`] = ` }, ] } + nativeID="5" scale={1} shift={1} style={ @@ -369,6 +374,7 @@ exports[`FilledTextInput should render with some props 1`] = ` keyboardType="default" maxLength={11} multiline={true} + nativeID="6" numberOfLines={20} onBlur={[Function]} onChangeText={[Function]} @@ -465,6 +471,7 @@ exports[`FilledTextInput should render with some props 1`] = ` }, ] } + nativeID="7" scale={22} style={ [ @@ -498,6 +505,7 @@ exports[`FilledTextInput should render with some props 1`] = ` } } jestInlineStyle={{}} + nativeID="8" style={ [ { @@ -538,6 +546,7 @@ exports[`FilledTextInput should render with some props 1`] = ` {}, ] } + nativeID="9" noLayoutFlow={false} style={ [ diff --git a/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap b/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap index 61b94289a39..96fbdcd99c9 100644 --- a/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/MenuTabs.test.tsx.snap @@ -31,6 +31,7 @@ exports[`MenuTabs should render with loading props 1`] = ` }, ] } + nativeID="0" pointerEvents="box-none" shiftY={0} style={ @@ -83,6 +84,7 @@ exports[`MenuTabs should render with loading props 1`] = ` }, ] } + nativeID="1" openRatio={1} pointerEvents="none" style={ @@ -173,6 +175,7 @@ exports[`MenuTabs should render with loading props 1`] = ` }, ] } + nativeID="2" openRatio={1} style={ [ diff --git a/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap index 4611b1a14ec..ea6c5f9c554 100644 --- a/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap +++ b/src/__tests__/modals/__snapshots__/AccelerateTxModal.test.tsx.snap @@ -102,6 +102,7 @@ exports[`AccelerateTxModalComponent should render with loading props 1`] = ` }, ] } + nativeID="1" style={ [ { @@ -466,6 +467,7 @@ exports[`AccelerateTxModalComponent should render with loading props 1`] = ` }, ] } + nativeID="2" style={ [ { @@ -485,9 +487,6 @@ exports[`AccelerateTxModalComponent should render with loading props 1`] = ` /> @@ -1517,6 +1524,7 @@ exports[`SendScene2 1 spendTarget 1`] = ` "reduceMotionV": "system", } } + nativeID="14" > @@ -3514,6 +3529,7 @@ exports[`SendScene2 1 spendTarget with info tiles 1`] = ` "reduceMotionV": "system", } } + nativeID="25" > @@ -5505,6 +5529,7 @@ exports[`SendScene2 2 spendTargets 1`] = ` "reduceMotionV": "system", } } + nativeID="37" > @@ -7098,6 +7130,7 @@ exports[`SendScene2 2 spendTargets hide tiles 1`] = ` "reduceMotionV": "system", } } + nativeID="48" > @@ -8667,6 +8707,7 @@ exports[`SendScene2 2 spendTargets hide tiles 2`] = ` "reduceMotionV": "system", } } + nativeID="59" > @@ -10018,6 +10065,7 @@ exports[`SendScene2 2 spendTargets hide tiles 3`] = ` "reduceMotionV": "system", } } + nativeID="69" > @@ -11763,6 +11819,7 @@ exports[`SendScene2 2 spendTargets lock tiles 1`] = ` "reduceMotionV": "system", } } + nativeID="82" > @@ -13466,6 +13531,7 @@ exports[`SendScene2 2 spendTargets lock tiles 2`] = ` "reduceMotionV": "system", } } + nativeID="94" > @@ -15103,6 +15177,7 @@ exports[`SendScene2 2 spendTargets lock tiles 3`] = ` "reduceMotionV": "system", } } + nativeID="106" > , @@ -857,6 +863,7 @@ exports[`SwapCreateScene should render with loading props 1`] = ` }, ] } + nativeID="6" onLayout={[Function]} style={ [ diff --git a/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap index a0062fd7fc3..0d148ce54c7 100644 --- a/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap @@ -327,6 +327,7 @@ exports[`SwapSuccessSceneComponent should render with loading props 1`] = ` { +const AnimatedDigit: React.FC = props => { const { animationDuration, digit, easing, textStyle, index, numberHeight } = props const animY = useSharedValue(0) @@ -98,12 +98,14 @@ const AnimatedDigit = (props: AnimatedDigitProps): React.ReactElement => { ) const styles = getStyles(useTheme()) - if (!isIntegerDigit(digit)) { - animY.value = withTiming(0, { duration: animationDuration, easing }) - } else { - const height = -1 * (numberHeight * Number(digit)) - animY.value = withTiming(height, { duration: animationDuration, easing }) - } + React.useEffect(() => { + if (!isIntegerDigit(digit)) { + animY.value = withTiming(0, { duration: animationDuration, easing }) + } else { + const height = -1 * (numberHeight * Number(digit)) + animY.value = withTiming(height, { duration: animationDuration, easing }) + } + }, [animY, animationDuration, digit, easing, numberHeight]) const animStyle = useAnimatedStyle(() => { return { diff --git a/src/components/common/CrossFade.tsx b/src/components/common/CrossFade.tsx index f2efeb651a3..73a3e0ff129 100644 --- a/src/components/common/CrossFade.tsx +++ b/src/components/common/CrossFade.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import { StyleSheet } from 'react-native' import Animated, { - runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' +import { runOnJS } from 'react-native-worklets' interface Props { // The props.key of the visible child, or undefined to hide everything: diff --git a/src/components/common/EdgeTouchableHighlight.ts b/src/components/common/EdgeTouchableHighlight.ts deleted file mode 100644 index ac7f82ba2c8..00000000000 --- a/src/components/common/EdgeTouchableHighlight.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TouchableHighlight } from 'react-native' - -import { withExtendedTouchable } from '../hoc/withExtendedTouchable' - -export const EdgeTouchableHighlight = withExtendedTouchable(TouchableHighlight) diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx index 3b7075a44e7..96f09293934 100644 --- a/src/components/common/SceneWrapper.tsx +++ b/src/components/common/SceneWrapper.tsx @@ -11,12 +11,13 @@ import { useKeyboardHandler, useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller' -import Reanimated, { runOnJS, useAnimatedStyle } from 'react-native-reanimated' +import Reanimated, { useAnimatedStyle } from 'react-native-reanimated' import { type EdgeInsets, useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context' +import { runOnJS } from 'react-native-worklets' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { diff --git a/src/components/modals/CategoryModal.tsx b/src/components/modals/CategoryModal.tsx index 3aa550d701c..c8ee35ec30f 100644 --- a/src/components/modals/CategoryModal.tsx +++ b/src/components/modals/CategoryModal.tsx @@ -19,7 +19,7 @@ import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' import { scale } from '../../util/scaling' import { MinimalButton } from '../buttons/MinimalButton' -import { EdgeTouchableHighlight } from '../common/EdgeTouchableHighlight' +import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' import { DividerLine } from '../themed/DividerLine' import { EdgeText } from '../themed/EdgeText' @@ -125,27 +125,25 @@ export function CategoryModal(props: Props) { const keyExtractor = useHandler((row: CategoryRow) => row.raw) const renderRow: ListRenderItem = useHandler(({ item }) => ( - { await handleCategoryUpdate(item.raw) }} > - <> - - - {item.display} - - {item.new ? ( - - + - - ) : null} + + + {item.display} - - - + {item.new ? ( + + + + + ) : null} + + + )) return ( diff --git a/src/components/modals/EdgeModal.tsx b/src/components/modals/EdgeModal.tsx index 0f70444f9c3..d46bd29535e 100644 --- a/src/components/modals/EdgeModal.tsx +++ b/src/components/modals/EdgeModal.tsx @@ -14,12 +14,12 @@ import { } from 'react-native-gesture-handler' import { cacheStyles } from 'react-native-patina' import Animated, { - runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import AntDesignIcon from 'react-native-vector-icons/AntDesign' +import { runOnJS } from 'react-native-worklets' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { useHandler } from '../../hooks/useHandler' diff --git a/src/components/notification/NotificationCard.tsx b/src/components/notification/NotificationCard.tsx index 3d734ab7ed3..2d0b78f40f4 100644 --- a/src/components/notification/NotificationCard.tsx +++ b/src/components/notification/NotificationCard.tsx @@ -4,12 +4,12 @@ import FastImage from 'react-native-fast-image' import { ShadowedView } from 'react-native-fast-shadow' import { cacheStyles } from 'react-native-patina' import Animated, { - runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import AntDesignIcon from 'react-native-vector-icons/AntDesign' +import { runOnJS } from 'react-native-worklets' import { useHandler } from '../../hooks/useHandler' import { getThemedIconUri } from '../../util/CdnUris' @@ -103,7 +103,7 @@ const NotificationCardComponent = (props: Props) => { - {/* Android font scaling is too aggressive. + {/* Android font scaling is too aggressive. Android prioritizes font shrinking much more before trying to add newlines, while iOS prioritizes newlines before shrinking text. We already use smaller text here so we shouldn't shrink it diff --git a/src/components/progress-indicators/AccountSyncBar.tsx b/src/components/progress-indicators/AccountSyncBar.tsx index b49b3ce8128..7c463d71d99 100644 --- a/src/components/progress-indicators/AccountSyncBar.tsx +++ b/src/components/progress-indicators/AccountSyncBar.tsx @@ -1,5 +1,11 @@ import React from 'react' -import { Animated, Easing, View } from 'react-native' +import { View } from 'react-native' +import Animated, { + interpolate, + useAnimatedStyle, + useSharedValue, + withTiming +} from 'react-native-reanimated' import { useAccountSyncRatio } from '../../hooks/useAccountSyncRatio' import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' @@ -14,18 +20,12 @@ export const AccountSyncBar = () => { // Calculate the average progress: const progress = useAccountSyncRatio() + const animation = useSharedValue(progress) // Animation state: const [isProgressVisible, setIsProgressVisible] = React.useState( progress !== 100 ) - const animation = React.useRef(new Animated.Value(progress)).current - - const widthInterpolated = animation.interpolate({ - inputRange: [0, 1], - outputRange: ['10%', '100%'], - extrapolate: 'clamp' - }) React.useEffect(() => { if (progress === 1) { @@ -38,20 +38,24 @@ export const AccountSyncBar = () => { // Added wallets, resynced wallets, unpaused wallets, etc. setIsProgressVisible(true) - Animated.timing(animation, { - duration: 1500, - easing: Easing.ease, - toValue: progress, - useNativeDriver: false - }).start() + animation.value = withTiming(progress, { duration: 1500 }) } }, [animation, progress]) - if (!isProgressVisible) return null + const widthStyle = useAnimatedStyle(() => ({ + transform: [ + { + scaleX: interpolate(animation.value, [0, 1], [0.1, 1], { + extrapolateRight: 'clamp' + }) + } + ] + })) + if (!isProgressVisible) return null return ( - + ) } @@ -67,6 +71,7 @@ const getStyles = cacheStyles((theme: Theme) => ({ top: 0, right: 0, height: 3, - backgroundColor: theme.walletProgressIconFill + backgroundColor: theme.walletProgressIconFill, + transformOrigin: 'left center' } })) diff --git a/src/components/scenes/GettingStartedScene.tsx b/src/components/scenes/GettingStartedScene.tsx index 605082bbcbe..1288270a702 100644 --- a/src/components/scenes/GettingStartedScene.tsx +++ b/src/components/scenes/GettingStartedScene.tsx @@ -5,7 +5,6 @@ import Animated, { Extrapolation, interpolate, interpolateColor, - runOnJS, type SharedValue, useAnimatedReaction, useAnimatedStyle, @@ -16,6 +15,7 @@ import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context' +import { runOnJS } from 'react-native-worklets' import edgeLogoIcon from '../../assets/images/edgeLogo/Edge_logo_Icon_L.png' import uspImage0 from '../../assets/images/gettingStarted/usp0.png' diff --git a/src/components/settings/SettingsRow.tsx b/src/components/settings/SettingsRow.tsx index 5c80112ef43..2aa04d999ed 100644 --- a/src/components/settings/SettingsRow.tsx +++ b/src/components/settings/SettingsRow.tsx @@ -7,7 +7,7 @@ import Animated, { } from 'react-native-reanimated' import { usePendingPress } from '../../hooks/usePendingPress' -import { EdgeTouchableHighlight } from '../common/EdgeTouchableHighlight' +import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { styled } from '../hoc/styled' import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' @@ -54,37 +54,34 @@ const SettingsRowComponent = (props: Props) => { const [pending, handlePress] = usePendingPress(onPress) return ( - - <> - {children} - - {label} - - - - - - {right} - - - + {children} + + {label} + + + + + + {right} + + ) } diff --git a/src/components/themed/Carousel.tsx b/src/components/themed/Carousel.tsx index 87a732dc9ab..52e2bc7bb20 100644 --- a/src/components/themed/Carousel.tsx +++ b/src/components/themed/Carousel.tsx @@ -3,7 +3,6 @@ import { useEffect } from 'react' import { type LayoutChangeEvent, Pressable, View } from 'react-native' import { Gesture, GestureDetector } from 'react-native-gesture-handler' import Animated, { - runOnJS, type SharedValue, useAnimatedStyle, useSharedValue, @@ -11,6 +10,7 @@ import Animated, { withTiming } from 'react-native-reanimated' import { useSafeAreaFrame } from 'react-native-safe-area-context' +import { runOnJS } from 'react-native-worklets' import { useState } from '../../types/reactHooks' import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' diff --git a/src/components/themed/FlipInput2.tsx b/src/components/themed/FlipInput2.tsx index 7ac7989eca1..6aeaf40a542 100644 --- a/src/components/themed/FlipInput2.tsx +++ b/src/components/themed/FlipInput2.tsx @@ -13,7 +13,6 @@ import Animated, { Easing, interpolate, interpolateColor, - runOnJS, type SharedValue, useAnimatedRef, useAnimatedStyle, @@ -22,6 +21,7 @@ import Animated, { withDelay, withTiming } from 'react-native-reanimated' +import { runOnJS } from 'react-native-worklets' import { useHandler } from '../../hooks/useHandler' import { useNumericInputProps } from '../../hooks/useNumericInputProps' diff --git a/src/components/themed/SafeSlider.tsx b/src/components/themed/SafeSlider.tsx index 35dacc6182d..9030fde87cc 100644 --- a/src/components/themed/SafeSlider.tsx +++ b/src/components/themed/SafeSlider.tsx @@ -1,15 +1,14 @@ import * as React from 'react' import { ActivityIndicator, StyleSheet, View } from 'react-native' -import { PanGestureHandler } from 'react-native-gesture-handler' +import { Gesture, GestureDetector } from 'react-native-gesture-handler' import Animated, { Easing, - runOnJS, - useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import Entypo from 'react-native-vector-icons/Entypo' +import { runOnJS } from 'react-native-worklets' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' @@ -23,7 +22,6 @@ const COMPLETE_POINT: number = 3 interface Props { onSlidingComplete: (reset: () => void) => Promise | void parentStyle?: any - completePoint?: number width?: number // Disabled logic: @@ -31,13 +29,12 @@ interface Props { disabled: boolean } -export const SafeSlider = (props: Props) => { +export const SafeSlider: React.FC = props => { const { disabledText, - disabled, + disabled = false, onSlidingComplete, - parentStyle, - completePoint = COMPLETE_POINT + parentStyle } = props const theme = useTheme() @@ -51,10 +48,9 @@ export const SafeSlider = (props: Props) => { const sliderDisabled = disabled || completed const sliderText = !sliderDisabled ? lstrings.send_confirmation_slide_to_confirm - : disabledText || lstrings.select_exchange_amount_short + : disabledText ?? lstrings.select_exchange_amount_short const translateX = useSharedValue(upperBound) - const isSliding = useSharedValue(false) const resetSlider = useHandler(() => { translateX.value = withTiming(upperBound, { @@ -63,45 +59,36 @@ export const SafeSlider = (props: Props) => { }) setCompleted(false) }) - const complete = () => { + const handleComplete = (): void => { triggerHaptic('impactMedium') onSlidingComplete(() => { resetSlider() - })?.catch(err => { + })?.catch((err: unknown) => { showError(err) }) setCompleted(true) } - const onGestureEvent = useAnimatedGestureHandler({ - onStart: (_, ctx: { offsetX: number }) => { - if (!sliderDisabled) ctx.offsetX = translateX.value - }, - onActive: (event, ctx) => { - if (!sliderDisabled) { - isSliding.value = true - translateX.value = clamp( - event.translationX + ctx.offsetX, - 0, - upperBound - ) - } - }, - onEnd: () => { - if (!sliderDisabled) { - isSliding.value = false - - if (translateX.value < completePoint) { - runOnJS(complete)() - } else { - translateX.value = withTiming(upperBound, { - duration: 500, - easing: Easing.inOut(Easing.exp) - }) - } + const gesture = Gesture.Pan() + .enabled(!sliderDisabled) + .onChange(event => { + translateX.value = Math.max( + 0, + // We start at `upperBound` and translate left, + // so `event.translationX` should be negative: + upperBound + Math.min(0, event.translationX) + ) + }) + .onEnd(event => { + if (translateX.value < COMPLETE_POINT) { + runOnJS(handleComplete)() + } else { + translateX.value = withTiming(upperBound, { + duration: 500, + easing: Easing.inOut(Easing.exp) + }) } - } - }) + }) const scrollTranslationStyle = useAnimatedStyle(() => { return { transform: [{ translateX: translateX.value }] } @@ -124,7 +111,7 @@ export const SafeSlider = (props: Props) => { > - + { size={theme.rem(1.5)} /> - + {completed ? ( { ) } -type Clamp = (value: number, lowerBound: number, upperBound: number) => number - -const clamp: Clamp = (value, lowerBound, upperBound) => { - 'worklet' - return Math.min(Math.max(lowerBound, value), upperBound) -} - const getStyles = cacheStyles((theme: Theme) => ({ sliderContainer: { alignItems: 'center' diff --git a/src/components/themed/Slider.tsx b/src/components/themed/Slider.tsx index e33df514cb2..7d60bc7b472 100644 --- a/src/components/themed/Slider.tsx +++ b/src/components/themed/Slider.tsx @@ -1,72 +1,58 @@ import * as React from 'react' import { ActivityIndicator, StyleSheet, View } from 'react-native' -import { PanGestureHandler } from 'react-native-gesture-handler' +import { Gesture, GestureDetector } from 'react-native-gesture-handler' import Animated, { Easing, - runOnJS, - useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import Entypo from 'react-native-vector-icons/Entypo' +import { runOnJS } from 'react-native-worklets' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { triggerHaptic } from '../../util/haptic' import { showError } from '../services/AirshipInstance' -import { - cacheStyles, - type Theme, - type ThemeProps, - withTheme -} from '../services/ThemeContext' +import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' import { EdgeText } from './EdgeText' const COMPLETE_POINT: number = 3 -interface OwnProps { +interface Props { onSlidingComplete: (reset: () => void) => Promise | void parentStyle?: any showSpinner?: boolean - completePoint?: number width?: number - // Reset logic: - reset?: boolean - // Disabled logic: disabledText?: string disabled: boolean } -type Props = OwnProps & ThemeProps - -export const SliderComponent = (props: Props) => { +export const Slider: React.FC = props => { const { disabledText, - disabled, - reset, - showSpinner, + disabled = false, + showSpinner = false, onSlidingComplete, - parentStyle, - completePoint = COMPLETE_POINT, - theme, - width = props.theme.confirmationSliderWidth + parentStyle } = props + + const theme = useTheme() const styles = getStyles(theme) const { confirmationSliderThumbWidth } = theme const [completed, setCompleted] = React.useState(false) + const { width = theme.confirmationSliderWidth } = props const upperBound = width - theme.confirmationSliderThumbWidth const widthStyle = { width } const sliderDisabled = disabled || showSpinner const sliderText = !sliderDisabled ? lstrings.send_confirmation_slide_to_confirm - : disabledText || lstrings.select_exchange_amount_short + : disabledText ?? lstrings.select_exchange_amount_short const translateX = useSharedValue(upperBound) - const isSliding = useSharedValue(false) const resetSlider = useHandler(() => { translateX.value = withTiming(upperBound, { @@ -75,45 +61,36 @@ export const SliderComponent = (props: Props) => { }) setCompleted(false) }) - const complete = () => { + const handleComplete = (): void => { triggerHaptic('impactMedium') onSlidingComplete(() => { resetSlider() - })?.catch(err => { + })?.catch((err: unknown) => { showError(err) }) setCompleted(true) } - const onGestureEvent = useAnimatedGestureHandler({ - onStart: (_, ctx: { offsetX: number }) => { - if (!sliderDisabled) ctx.offsetX = translateX.value - }, - onActive: (event, ctx) => { - if (!sliderDisabled) { - isSliding.value = true - translateX.value = clamp( - event.translationX + ctx.offsetX, - 0, - upperBound - ) - } - }, - onEnd: () => { - if (!sliderDisabled) { - isSliding.value = false - - if (translateX.value < completePoint) { - runOnJS(complete)() - } else { - translateX.value = withTiming(upperBound, { - duration: 500, - easing: Easing.inOut(Easing.exp) - }) - } + const gesture = Gesture.Pan() + .enabled(!sliderDisabled) + .onChange(event => { + translateX.value = Math.max( + 0, + // We start at `upperBound` and translate left, + // so `event.translationX` should be negative: + upperBound + Math.min(0, event.translationX) + ) + }) + .onEnd(event => { + if (translateX.value < COMPLETE_POINT) { + runOnJS(handleComplete)() + } else { + translateX.value = withTiming(upperBound, { + duration: 500, + easing: Easing.inOut(Easing.exp) + }) } - } - }) + }) const scrollTranslationStyle = useAnimatedStyle(() => { return { transform: [{ translateX: translateX.value }] } @@ -127,12 +104,10 @@ export const SliderComponent = (props: Props) => { // Reset slider state conditions: React.useEffect(() => { - // Reset prop set by parent - if (reset) resetSlider() // Completed prop set by parent and no longer showing spinner - else if (completed && !showSpinner) resetSlider() + if (completed && !showSpinner) resetSlider() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [resetSlider, reset, showSpinner]) + }, [resetSlider, showSpinner]) return ( @@ -145,7 +120,7 @@ export const SliderComponent = (props: Props) => { > - + { size={theme.rem(1.5)} /> - + {showSpinner ? ( { ) } -type Clamp = (value: number, lowerBound: number, upperBound: number) => number - -const clamp: Clamp = (value, lowerBound, upperBound) => { - 'worklet' - return Math.min(Math.max(lowerBound, value), upperBound) -} - const getStyles = cacheStyles((theme: Theme) => ({ sliderContainer: { alignItems: 'center' @@ -241,5 +209,3 @@ const getStyles = cacheStyles((theme: Theme) => ({ zIndex: 1 } })) - -export const Slider = withTheme(SliderComponent) diff --git a/src/components/themed/SwipeableRow.tsx b/src/components/themed/SwipeableRow.tsx index a73cdef1a21..1cf2d3216b8 100644 --- a/src/components/themed/SwipeableRow.tsx +++ b/src/components/themed/SwipeableRow.tsx @@ -8,7 +8,6 @@ import { import { Gesture, GestureDetector } from 'react-native-gesture-handler' import Animated, { type AnimationCallback, - runOnJS, type SharedValue, useAnimatedStyle, useDerivedValue, @@ -16,6 +15,7 @@ import Animated, { withSpring, withTiming } from 'react-native-reanimated' +import { runOnJS } from 'react-native-worklets' import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' diff --git a/src/components/themed/ThemedButtons.tsx b/src/components/themed/ThemedButtons.tsx index 7176317a32b..5620dce4fc3 100644 --- a/src/components/themed/ThemedButtons.tsx +++ b/src/components/themed/ThemedButtons.tsx @@ -10,7 +10,6 @@ import { sidesToMargin, sidesToPadding } from '../../util/sides' -import { EdgeTouchableHighlight } from '../common/EdgeTouchableHighlight' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { showError } from '../services/AirshipInstance' import { type Theme, useTheme } from '../services/ThemeContext' @@ -71,17 +70,15 @@ export function Radio(props: RadioButtonProps) { return ( - {children} - + ) } diff --git a/src/state/SceneFooterState.tsx b/src/state/SceneFooterState.tsx index 6d1e370958f..419e48ab06d 100644 --- a/src/state/SceneFooterState.tsx +++ b/src/state/SceneFooterState.tsx @@ -2,11 +2,11 @@ import { useIsFocused } from '@react-navigation/native' import { useCallback, useEffect, useMemo } from 'react' import { Platform } from 'react-native' import { - runOnJS, useAnimatedReaction, useSharedValue, withTiming } from 'react-native-reanimated' +import { runOnJS } from 'react-native-worklets' import type { SceneWrapperInfo } from '../components/common/SceneWrapper' import { useSharedEvent } from '../hooks/useSharedEvent' diff --git a/src/state/SceneScrollState.tsx b/src/state/SceneScrollState.tsx index 2daff8cb7a3..ac3b9726a9f 100644 --- a/src/state/SceneScrollState.tsx +++ b/src/state/SceneScrollState.tsx @@ -6,7 +6,6 @@ import { useAnimatedReaction, useAnimatedScrollHandler, useSharedValue, - useWorkletCallback, withTiming } from 'react-native-reanimated' @@ -179,16 +178,19 @@ export const useSceneScrollWorkletHandler = () => { ) // Define the handleScroll function as a worklet using useWorkletCallback - const handleScroll = useWorkletCallback((event: NativeScrollEvent) => { - 'worklet' - if (!isFocused) return - - const y = event.contentOffset.y - if (scrollY.value !== y) { - localScrollY.value = y - scrollY.value = y - } - }, []) + const handleScroll = useCallback( + (event: NativeScrollEvent) => { + 'worklet' + if (!isFocused.value) return + + const y = event.contentOffset.y + if (scrollY.value !== y) { + localScrollY.value = y + scrollY.value = y + } + }, + [isFocused, localScrollY, scrollY] + ) return handleScroll } diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts index b256bf41062..3a5fbf20eab 100644 --- a/src/theme/variables/edgeDark.ts +++ b/src/theme/variables/edgeDark.ts @@ -364,7 +364,6 @@ export const edgeDark: Theme = { // Settings Row settingsRowBackground: palette.transparent, - settingsRowPressed: palette.transparent, settingsRowHeaderFont: palette.QuicksandMedium, settingsRowHeaderFontSizeRem: 1, settingsRowSubHeader: palette.transparent, diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts index bd685abd56d..53eef4350b4 100644 --- a/src/theme/variables/edgeLight.ts +++ b/src/theme/variables/edgeLight.ts @@ -316,7 +316,6 @@ export const edgeLight: Theme = { // Settings Row settingsRowBackground: palette.white, - settingsRowPressed: palette.transparent, settingsRowHeaderFont: palette.QuicksandMedium, settingsRowHeaderFontSizeRem: 1, settingsRowSubHeader: palette.transparent, diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts index d2f207c98c3..1f47c6b750f 100644 --- a/src/theme/variables/testDark.ts +++ b/src/theme/variables/testDark.ts @@ -358,7 +358,6 @@ export const testDark: Theme = { // Settings Row settingsRowBackground: palette.transparent, - settingsRowPressed: palette.transparent, settingsRowHeaderFont: palette.QuicksandMedium, settingsRowHeaderFontSizeRem: 1, settingsRowSubHeader: palette.transparent, diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts index b38fb5daddc..b7c6d557c76 100644 --- a/src/theme/variables/testLight.ts +++ b/src/theme/variables/testLight.ts @@ -316,7 +316,6 @@ export const testLight: Theme = { // Settings Row settingsRowBackground: palette.white, - settingsRowPressed: palette.transparent, settingsRowHeaderFont: palette.QuicksandMedium, settingsRowHeaderFontSizeRem: 1, settingsRowSubHeader: palette.transparent, diff --git a/src/types/Theme.ts b/src/types/Theme.ts index 3a45c429642..d9042a50603 100644 --- a/src/types/Theme.ts +++ b/src/types/Theme.ts @@ -156,7 +156,6 @@ export interface Theme { // Settings Row settingsRowBackground: string - settingsRowPressed: string settingsRowHeaderFont: string settingsRowHeaderFontSizeRem: number diff --git a/yarn.lock b/yarn.lock index d4b83771932..fc08b6d6810 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1063,7 +1063,20 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" + integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.0" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.0" + debug "^4.3.1" + +"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== @@ -1133,12 +1146,7 @@ bfs-path "^1.0.2" cross-fetch "^3.1.5" -"@chain-registry/types@^2.0.28", "@chain-registry/types@^2.0.30": - version "2.0.30" - resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-2.0.30.tgz#08c7d5d02b04e0049ae42e3c701862c1cfd7d87a" - integrity sha512-5bcDLnsNhJ5lB7f9StIOebo+0Rf6C5sA1CtCQ4OtmNJVhqouIWLDvnQngJBbyiknYd3JPskI/HyP6AZ6P3GF3g== - -"@chain-registry/types@^2.0.42": +"@chain-registry/types@^2.0.28", "@chain-registry/types@^2.0.30", "@chain-registry/types@^2.0.42": version "2.0.42" resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-2.0.42.tgz#9d86692fcc400d13246ab08085ff40ac040566a7" integrity sha512-ozPgnok3kmtABQSUH4UussJdpygrdwA0uQJdnDhiVPczQ6OH5UQSvpX5UXDfNiutRkTztp7h/TA1DSQQbiQo1g== @@ -4421,63 +4429,63 @@ nullthrows "^1.1.1" "@react-navigation/bottom-tabs@^6.5.4": - version "6.5.4" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.5.4.tgz#f123705fbc093de900c37501c6ab9ab646c04d50" - integrity sha512-C2Tf+SsO9zc+p/MKUkILso8Dw4KTIscXPi7YhdepyZbM8ZYMGfnZKzffGjLFlLhQXtsFQQuHMelD8sIStTkgyQ== + version "6.6.1" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.6.1.tgz#589edc9c8fbf652c485b3c37d344faafe3cd4cc4" + integrity sha512-9oD4cypEBjPuaMiu9tevWGiQ4w/d6l3HNhcJ1IjXZ24xvYDSs0mqjUcdt8SWUolCvRrYc/DmNBLlT83bk0bHTw== dependencies: - "@react-navigation/elements" "^1.3.14" + "@react-navigation/elements" "^1.3.31" color "^4.2.3" warn-once "^0.1.0" -"@react-navigation/core@^6.4.6": - version "6.4.6" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.6.tgz#b0738667dec5927b01c4c496c2f4c73ef8a5e4dd" - integrity sha512-6zaAgUT5k4vhJlddUk2l52RZyMkMelHdrRv1cL57ALi2RZzERdgmbiMKhJerxFLn9S8E3PUe8vwxHzjHOZKG4w== +"@react-navigation/core@^6.4.17": + version "6.4.17" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.17.tgz#f277a196b578c8a456efcc563d1c9bd87eb4ab04" + integrity sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg== dependencies: - "@react-navigation/routers" "^6.1.6" + "@react-navigation/routers" "^6.1.9" escape-string-regexp "^4.0.0" nanoid "^3.1.23" query-string "^7.1.3" react-is "^16.13.0" - use-latest-callback "^0.1.5" + use-latest-callback "^0.2.1" -"@react-navigation/drawer@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-6.5.8.tgz#1bab9f761099fefd5a3e7080851076d0b4310dac" - integrity sha512-24okwc1gVJex+NMwau1QfxIRUDfFvdbnwFS/N0bCUXDVu8bqsMeYA00C3xwCpvdRmANbBH4rFRniNbJLAvD4Jg== +"@react-navigation/drawer@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-6.7.2.tgz#3c85298c73af915f3cd5e6ca26a1a26d61010256" + integrity sha512-o4g2zgTZa2+oLd+8V33etrSM38KIqu8S/zCBTsdsHUoQyVE7JNRiv3Qgq/jMvEb8PZCqWmm7jHItcgzrBuwyOQ== dependencies: - "@react-navigation/elements" "^1.3.14" + "@react-navigation/elements" "^1.3.31" color "^4.2.3" warn-once "^0.1.0" -"@react-navigation/elements@^1.3.14": - version "1.3.14" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.14.tgz#ee9ef7f57e0877700ebb359947728b255bbb9c65" - integrity sha512-RBbPhYq+KNFPAkWPaHB9gypq0jTGp/0fkMwRLToJ8jkLtWG4LV+JoQ/erFQnVARkR3Q807n0VnES15EYP4ITMQ== +"@react-navigation/elements@^1.3.14", "@react-navigation/elements@^1.3.31": + version "1.3.31" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.31.tgz#28dd802a0787bb03fc0e5be296daf1804dbebbcf" + integrity sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ== "@react-navigation/native@^6.1.3": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.3.tgz#06064c5e49c417a3dbe210b3f00841beefd326c5" - integrity sha512-DB5FyG6aqGfcjjVozljF5NEkjWaSymIbQHfWwsjL0YrvC1gfc7E53QXDOjxZ/wfbCo8qZs8RIC/LAgclP2YK/w== + version "6.1.18" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.18.tgz#338fa9afa2c89feec1d3eac41c963840d8d6f106" + integrity sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg== dependencies: - "@react-navigation/core" "^6.4.6" + "@react-navigation/core" "^6.4.17" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" nanoid "^3.1.23" -"@react-navigation/routers@^6.1.6": - version "6.1.6" - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.6.tgz#f57f2a73855d329255aa225fdad75ae8e7700c6d" - integrity sha512-Z5DeCW3pUvMafbU9Cjy1qJYC2Bvl8iy3+PfsB0DsAwQ6zZ3WAXW5FTMX4Gb9H+Jg6qHWGbMFFwlYpS3UJ3tlVQ== +"@react-navigation/routers@^6.1.9": + version "6.1.9" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.9.tgz#73f5481a15a38e36592a0afa13c3c064b9f90bed" + integrity sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA== dependencies: nanoid "^3.1.23" "@react-navigation/stack@^6.3.12": - version "6.3.12" - resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.12.tgz#8e3779efd0b2ad015f62b89193b9eacecb41fab2" - integrity sha512-sDclO/EflZUbfxZZlz5fk3A0wfegeCOEKGznP9n53s+JrDJL2Dc24Gufg56ENLNRX3CiE1KzsaM4SQLJYf/zsA== + version "6.4.1" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.4.1.tgz#a158350637f5298292202ce854e5c5c9688f23f9" + integrity sha512-upMEHOKMtuMu4c9gmoPlO/JqI6mDlSqwXg1aXKOTQLXAF8H5koOLRfrmi7AkdiE9A7lDXWUAZoGuD9O88cYvDQ== dependencies: - "@react-navigation/elements" "^1.3.14" + "@react-navigation/elements" "^1.3.31" color "^4.2.3" warn-once "^0.1.0" @@ -7951,14 +7959,7 @@ chai-as-promised@^7.1.1: dependencies: check-error "^1.0.2" -chain-registry@^2.0.30: - version "2.0.30" - resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-2.0.30.tgz#d99c5e71e48480f7e2d225228c75ea1adb7e3b40" - integrity sha512-EGwgdjXYVU9oBCKOpXImAEvJIC4zP6C3Efl/Jw5llNUchCuy1zilgzhuj802Sf+Y/f2DITfF1CjNfSty4eeYNg== - dependencies: - "@chain-registry/types" "^2.0.30" - -chain-registry@^2.0.37: +chain-registry@^2.0.30, chain-registry@^2.0.37: version "2.0.42" resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-2.0.42.tgz#9ece4cbbf335c1bdb725e2573aae9dfe0442c88a" integrity sha512-Magth+y5yLEvyKLgOMJBkxtvaXufyE8/0KhLfyKZfG2Fqy+R9AuKoffr99n8ViSSAXf0kZCYDMZ8bh27P3Vo3A== @@ -15802,6 +15803,11 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +r3-hack@./scripts/r3-hack: + version "0.0.1" + dependencies: + react-native-reanimated "^3.19.1" + radix3@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.1.0.tgz#9745df67a49c522e94a33d0a93cf743f104b6e0d" @@ -16035,7 +16041,7 @@ react-native-is-edge-to-edge@1.1.7: resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz#28947688f9fafd584e73a4f935ea9603bd9b1939" integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w== -react-native-is-edge-to-edge@^1.1.6, react-native-is-edge-to-edge@^1.1.7: +react-native-is-edge-to-edge@^1.1.6, react-native-is-edge-to-edge@^1.1.7, react-native-is-edge-to-edge@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== @@ -16106,10 +16112,10 @@ react-native-piratechain@^0.5.12: dependencies: rfc4648 "^1.3.0" -react-native-reanimated@^3.18.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.19.0.tgz#3413dd8865eec549642a4a63cd4e5354a25043c7" - integrity sha512-FNfqLuPuVHsW9KcsZtnJqIPlMQvuySnSFJXgSt9fVDPqptbSUkiAF6MthUwd4Mxt05hCRcbV+T65CENgVS5iCg== +react-native-reanimated@^3.19.1: + version "3.19.1" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.19.1.tgz#761f731bfb5dc67724ca7bf7bce90a6da975d753" + integrity sha512-ILL0FSNzSVIg6WuawrsMBvNxk2yJFiTUcahimXDAeNiE/09eagVUlHhYWXAAmH0umvAOafBaGjO7YfBhUrf5ZQ== dependencies: "@babel/plugin-transform-arrow-functions" "^7.0.0-0" "@babel/plugin-transform-class-properties" "^7.0.0-0" @@ -16124,6 +16130,14 @@ react-native-reanimated@^3.18.0: invariant "^2.2.4" react-native-is-edge-to-edge "1.1.7" +react-native-reanimated@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.0.2.tgz#c986bff36ce90cd1df83506d6fb5d62db6f2d3b7" + integrity sha512-RVD/jeTWrkloRVJmTAEtgXtihEnfA7aO6SgoaTBy3jbU1zblE3Oztq0ktVO5q/rxl96l9w2+6LLcuZ6N8D7aKQ== + dependencies: + react-native-is-edge-to-edge "^1.2.1" + semver "7.7.2" + react-native-reorderable-list@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/react-native-reorderable-list/-/react-native-reorderable-list-0.5.0.tgz#5f85360d68988fdd350cea720b0201f413329101" @@ -16232,6 +16246,22 @@ react-native-wheel-picker-android@^2.0.6: dependencies: moment "^2.22.0" +react-native-worklets@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.4.1.tgz#563d39160195101d9cf236b54b383d6950508263" + integrity sha512-QXAMZ8jz0sLEoNrc3ej050z6Sd+UJ/Gef4SACeMuoLRinwHIy4uel7XtMPJZMqKhFerkwXZ7Ips5vIjnNyPDBA== + dependencies: + "@babel/plugin-transform-arrow-functions" "^7.0.0-0" + "@babel/plugin-transform-class-properties" "^7.0.0-0" + "@babel/plugin-transform-classes" "^7.0.0-0" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0" + "@babel/plugin-transform-optional-chaining" "^7.0.0-0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0-0" + "@babel/plugin-transform-template-literals" "^7.0.0-0" + "@babel/plugin-transform-unicode-regex" "^7.0.0-0" + "@babel/preset-typescript" "^7.16.7" + convert-source-map "^2.0.0" + react-native-zano@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/react-native-zano/-/react-native-zano-0.2.1.tgz#9e46eca9e2a2549326b90a0f6be6bf91cc2b1e42" @@ -16983,6 +17013,11 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= +semver@7.7.2, semver@^7.1.3, semver@^7.3.2, semver@^7.3.5, semver@^7.5.2, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -16993,11 +17028,6 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3, semver@^7.3.2, semver@^7.3.5, semver@^7.5.2, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.2: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== - send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -17675,7 +17705,16 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17775,7 +17814,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17789,6 +17828,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -18784,10 +18830,10 @@ use-context-selector@^2.0.0: resolved "https://registry.yarnpkg.com/use-context-selector/-/use-context-selector-2.0.0.tgz#3b5dafec7aa947c152d4f0aa7f250e99a205df3d" integrity sha512-owfuSmUNd3eNp3J9CdDl0kMgfidV+MkDvHPpvthN5ThqM+ibMccNE0k+Iq7TWC6JPFvGZqanqiGCuQx6DyV24g== -use-latest-callback@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.5.tgz#a4a836c08fa72f6608730b5b8f4bbd9c57c04f51" - integrity sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ== +use-latest-callback@^0.2.1: + version "0.2.4" + resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.4.tgz#35c0f028f85a3f4cf025b06011110e87cc18f57e" + integrity sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg== use-sync-external-store@^1.0.0: version "1.2.0" @@ -19488,7 +19534,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -19506,6 +19552,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"