From 51eea863651c5506e80f13e1a9bd70e00fbf4060 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 12:05:23 +0100 Subject: [PATCH 01/14] Initial work on updating reanimated --- package-lock.json | 9 +- package.json | 2 +- .../AttachmentCarousel/Pager/index.tsx | 7 +- .../useCarouselContextEvents.ts | 9 +- .../AttachmentViewPdf/index.android.tsx | 24 +-- .../BaseAutoCompleteSuggestions.tsx | 31 ++-- .../AvatarCropModal/AvatarCropModal.tsx | 149 ++++++++---------- src/components/FloatingActionButton.tsx | 17 +- src/components/OpacityView.tsx | 9 +- src/components/SpacerView.tsx | 11 +- 10 files changed, 128 insertions(+), 140 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2f927adf13f..cf21c105a4ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,7 +104,7 @@ "react-native-plaid-link-sdk": "11.11.0", "react-native-qrcode-svg": "6.3.11", "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0", - "react-native-reanimated": "3.15.1", + "react-native-reanimated": "^3.16.1", "react-native-release-profiler": "^0.2.1", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.10.9", @@ -35626,9 +35626,10 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.1.tgz", - "integrity": "sha512-DbBeUUExtJ1x1nfE94I8qgDgWjq5ztM3IO/+XFO+agOkPeVpBs5cRnxHfJKrjqJ2MgwhJOUDmtHxo+tDsoeitg==", + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.1.tgz", + "integrity": "sha512-Wnbo7toHZ6kPLAD8JWKoKCTfNoqYOMW5vUEP76Rr4RBmJCrdXj6oauYP0aZnZq8NCbiP5bwwu7+RECcWtoetnQ==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", diff --git a/package.json b/package.json index 9620386b5560..6b22e123c174 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "react-native-plaid-link-sdk": "11.11.0", "react-native-qrcode-svg": "6.3.11", "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0", - "react-native-reanimated": "3.15.1", + "react-native-reanimated": "^3.16.1", "react-native-release-profiler": "^0.2.1", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.10.9", diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx index 6fe32050b6d5..68a12c4cd76e 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx @@ -70,14 +70,13 @@ function AttachmentCarouselPager( const pageScrollHandler = usePageScrollHandler((e) => { 'worklet'; - // eslint-disable-next-line react-compiler/react-compiler - activePage.value = e.position; - isPagerScrolling.value = e.offset !== 0; + activePage.set(e.position); + isPagerScrolling.set(e.offset !== 0); }, []); useEffect(() => { setActivePageIndex(initialPage); - activePage.value = initialPage; + activePage.set(initialPage); }, [activePage, initialPage]); /** The `pagerItems` object that passed down to the context. Later used to detect current page, whether it's a single image gallery etc. */ diff --git a/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts b/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts index 1c54d7841347..3311f6476194 100644 --- a/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts +++ b/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts @@ -35,12 +35,11 @@ function useCarouselContextEvents(setShouldShowArrows: (show?: SetStateAction { - if (!isScrollEnabled.value) { + if (!isScrollEnabled.get()) { return; } onRequestToggleArrows(); - }, [isScrollEnabled.value, onRequestToggleArrows]); + }, [isScrollEnabled, onRequestToggleArrows]); return {handleTap, handleScaleChange, scale, isScrollEnabled}; } diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx index c6e7984b793f..c756345664cc 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx @@ -32,32 +32,32 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) { const Pan = Gesture.Pan() .manualActivation(true) .onTouchesMove((evt) => { - if (offsetX.value !== 0 && offsetY.value !== 0 && isScrollEnabled && scale.value === 1) { - const translateX = Math.abs((evt.allTouches.at(0)?.absoluteX ?? 0) - offsetX.value); - const translateY = Math.abs((evt.allTouches.at(0)?.absoluteY ?? 0) - offsetY.value); - const allowEnablingScroll = !isPanGestureActive.value || isScrollEnabled.value; + if (offsetX.get() !== 0 && offsetY.get() !== 0 && isScrollEnabled && scale.get() === 1) { + const translateX = Math.abs((evt.allTouches.at(0)?.absoluteX ?? 0) - offsetX.get()); + const translateY = Math.abs((evt.allTouches.at(0)?.absoluteY ?? 0) - offsetY.get()); + const allowEnablingScroll = !isPanGestureActive.get() || isScrollEnabled.get(); // if the value of X is greater than Y and the pdf is not zoomed in, // enable the pager scroll so that the user // can swipe to the next attachment otherwise disable it. if (translateX > translateY && translateX > SCROLL_THRESHOLD && allowEnablingScroll) { // eslint-disable-next-line react-compiler/react-compiler - isScrollEnabled.value = true; + isScrollEnabled.set(true); } else if (translateY > SCROLL_THRESHOLD) { - isScrollEnabled.value = false; + isScrollEnabled.set(false); } } - isPanGestureActive.value = true; - offsetX.value = evt.allTouches.at(0)?.absoluteX ?? 0; - offsetY.value = evt.allTouches.at(0)?.absoluteY ?? 0; + isPanGestureActive.set(true); + offsetX.set(evt.allTouches.at(0)?.absoluteX ?? 0); + offsetY.set(evt.allTouches.at(0)?.absoluteY ?? 0); }) .onTouchesUp(() => { - isPanGestureActive.value = false; + isPanGestureActive.set(false); if (!isScrollEnabled) { return; } - isScrollEnabled.value = scale.value === 1; + isScrollEnabled.set(scale.get() === 1); }); const Content = useMemo( @@ -69,7 +69,7 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) { // The react-native-pdf's onScaleChanged event will sometimes give us scale values of e.g. 0.99... instead of 1, // even though we're not pinching/zooming // Rounding the scale value to 2 decimal place fixes this issue, since pinching will still be possible but very small pinches are ignored. - scale.value = Math.round(newScale * 1e2) / 1e2; + scale.set(Math.round(newScale * 1e2) / 1e2); }} /> ), diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index 2d22a2560bb0..abc221ed646a 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -50,24 +50,27 @@ function BaseAutoCompleteSuggestions({ const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length; const animatedStyles = useAnimatedStyle(() => ({ - opacity: fadeInOpacity.value, - ...StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value), + opacity: fadeInOpacity.get(), + ...StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.get()), })); useEffect(() => { if (measuredHeightOfSuggestionRows === prevRowHeightRef.current) { - // eslint-disable-next-line react-compiler/react-compiler - fadeInOpacity.value = withTiming(1, { - duration: 70, - easing: Easing.inOut(Easing.ease), - }); - rowHeight.value = measuredHeightOfSuggestionRows; + fadeInOpacity.set( + withTiming(1, { + duration: 70, + easing: Easing.inOut(Easing.ease), + }), + ); + rowHeight.set(measuredHeightOfSuggestionRows); } else { - fadeInOpacity.value = 1; - rowHeight.value = withTiming(measuredHeightOfSuggestionRows, { - duration: 100, - easing: Easing.bezier(0.25, 0.1, 0.25, 1), - }); + fadeInOpacity.set(1); + rowHeight.set( + withTiming(measuredHeightOfSuggestionRows, { + duration: 100, + easing: Easing.bezier(0.25, 0.1, 0.25, 1), + }), + ); } prevRowHeightRef.current = measuredHeightOfSuggestionRows; @@ -103,7 +106,7 @@ function BaseAutoCompleteSuggestions({ renderItem={renderItem} keyExtractor={keyExtractor} removeClippedSubviews={false} - showsVerticalScrollIndicator={innerHeight > rowHeight.value} + showsVerticalScrollIndicator={innerHeight > rowHeight.get()} extraData={[highlightedSuggestionIndex, renderSuggestionMenuItem]} /> diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx index dca0d08d11d5..a3c4cdca78bf 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.tsx +++ b/src/components/AvatarCropModal/AvatarCropModal.tsx @@ -97,16 +97,16 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose // Changes the modal state values to initial const resetState = useCallback(() => { - originalImageWidth.value = CONST.AVATAR_CROP_MODAL.INITIAL_SIZE; - originalImageHeight.value = CONST.AVATAR_CROP_MODAL.INITIAL_SIZE; - translateY.value = 0; - translateX.value = 0; - scale.value = CONST.AVATAR_CROP_MODAL.MIN_SCALE; - rotation.value = 0; - translateSlider.value = 0; - prevMaxOffsetX.value = 0; - prevMaxOffsetY.value = 0; - isLoading.value = false; + originalImageWidth.set(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); + originalImageHeight.set(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); + translateY.set(0); + translateX.set(0); + scale.set(CONST.AVATAR_CROP_MODAL.MIN_SCALE); + rotation.set(0); + translateSlider.set(0); + prevMaxOffsetX.set(0); + prevMaxOffsetY.set(0); + isLoading.set(false); setImageContainerSize(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); setSliderContainerSize(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE); setIsImageContainerInitialized(false); @@ -123,12 +123,11 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose ImageSize.getSize(imageUri).then(({width, height, rotation: orginalRotation}) => { // On Android devices ImageSize library returns also rotation parameter. if (orginalRotation === 90 || orginalRotation === 270) { - // eslint-disable-next-line react-compiler/react-compiler - originalImageHeight.value = width; - originalImageWidth.value = height; + originalImageHeight.set(width); + originalImageWidth.set(height); } else { - originalImageHeight.value = height; - originalImageWidth.value = width; + originalImageHeight.set(height); + originalImageWidth.set(width); } setIsImageInitialized(true); @@ -136,8 +135,8 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose // Because the reanimated library has some internal optimizations, // sometimes when the modal is hidden styles of the image and slider might not be updated. // To trigger the update we need to slightly change the following values: - translateSlider.value += 0.01; - rotation.value += 360; + translateSlider.set((value) => value + 0.01); + rotation.set((value) => value + 360); }); }, [imageUri, originalImageHeight, originalImageWidth, rotation, translateSlider]); @@ -150,15 +149,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose * Returns current image size taking into account scale and rotation. */ const getDisplayedImageSize = useWorkletCallback(() => { - let height = imageContainerSize * scale.value; - let width = imageContainerSize * scale.value; + let height = imageContainerSize * scale.get(); + let width = imageContainerSize * scale.get(); // Since the smaller side will be always equal to the imageContainerSize multiplied by scale, // another side can be calculated with aspect ratio. - if (originalImageWidth.value > originalImageHeight.value) { - width *= originalImageWidth.value / originalImageHeight.value; + if (originalImageWidth.get() > originalImageHeight.get()) { + width *= originalImageWidth.get() / originalImageHeight.get(); } else { - height *= originalImageHeight.value / originalImageWidth.value; + height *= originalImageHeight.get() / originalImageWidth.get(); } return {height, width}; @@ -172,10 +171,10 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose const {height, width} = getDisplayedImageSize(); const maxOffsetX = (width - imageContainerSize) / 2; const maxOffsetY = (height - imageContainerSize) / 2; - translateX.value = clamp(offsetX, [maxOffsetX * -1, maxOffsetX]); - translateY.value = clamp(offsetY, [maxOffsetY * -1, maxOffsetY]); - prevMaxOffsetX.value = maxOffsetX; - prevMaxOffsetY.value = maxOffsetY; + translateX.set(clamp(offsetX, [maxOffsetX * -1, maxOffsetX])); + translateY.set(clamp(offsetY, [maxOffsetY * -1, maxOffsetY])); + prevMaxOffsetX.set(maxOffsetX); + prevMaxOffsetY.set(maxOffsetY); }, [imageContainerSize, scale, clamp], ); @@ -190,8 +189,8 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose * and updates image's offset. */ const panGesture = Gesture.Pan().onChange((event) => { - const newX = translateX.value + event.changeX; - const newY = translateY.value + event.changeY; + const newX = translateX.get() + event.changeX; + const newY = translateY.get() + event.changeY; updateImageOffset(newX, newY); }); @@ -200,7 +199,7 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose // when the browser window is resized. useEffect(() => { // If no panning has happened and the value is 0, do an early return. - if (!prevMaxOffsetX.value && !prevMaxOffsetY.value) { + if (!prevMaxOffsetX.get() && !prevMaxOffsetY.get()) { return; } const {height, width} = getDisplayedImageSize(); @@ -210,14 +209,14 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose // Since interpolation is expensive, we only want to do it if // image has been panned across X or Y axis by the user. if (prevMaxOffsetX) { - translateX.value = interpolate(translateX.value, [prevMaxOffsetX.value * -1, prevMaxOffsetX.value], [maxOffsetX * -1, maxOffsetX]); + translateX.set(interpolate(translateX.get(), [prevMaxOffsetX.get() * -1, prevMaxOffsetX.get()], [maxOffsetX * -1, maxOffsetX])); } if (prevMaxOffsetY) { - translateY.value = interpolate(translateY.value, [prevMaxOffsetY.value * -1, prevMaxOffsetY.value], [maxOffsetY * -1, maxOffsetY]); + translateY.set(interpolate(translateY.get(), [prevMaxOffsetY.get() * -1, prevMaxOffsetY.get()], [maxOffsetY * -1, maxOffsetY])); } - prevMaxOffsetX.value = maxOffsetX; - prevMaxOffsetY.value = maxOffsetY; + prevMaxOffsetX.set(maxOffsetX); + prevMaxOffsetY.set(maxOffsetY); }, [getDisplayedImageSize, imageContainerSize, prevMaxOffsetX, prevMaxOffsetY, translateX, translateY]); /** @@ -228,65 +227,72 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose onBegin: () => { 'worklet'; - isPressableEnabled.value = false; + isPressableEnabled.set(false); }, onChange: (event: GestureUpdateEvent) => { 'worklet'; - const newSliderValue = clamp(translateSlider.value + event.changeX, [0, sliderContainerSize]); + const newSliderValue = clamp(translateSlider.get() + event.changeX, [0, sliderContainerSize]); const newScale = newScaleValue(newSliderValue, sliderContainerSize); - const differential = newScale / scale.value; + const differential = newScale / scale.get(); - scale.value = newScale; - translateSlider.value = newSliderValue; + scale.set(newScale); + translateSlider.set(newSliderValue); - const newX = translateX.value * differential; - const newY = translateY.value * differential; + const newX = translateX.get() * differential; + const newY = translateY.get() * differential; updateImageOffset(newX, newY); }, onFinalize: () => { 'worklet'; - isPressableEnabled.value = true; + isPressableEnabled.set(true); }, }; // This effect is needed to prevent the incorrect position of // the slider's knob when the window's layout changes useEffect(() => { - translateSlider.value = interpolate(scale.value, [CONST.AVATAR_CROP_MODAL.MIN_SCALE, CONST.AVATAR_CROP_MODAL.MAX_SCALE], [0, sliderContainerSize]); - }, [scale.value, sliderContainerSize, translateSlider]); + translateSlider.set(interpolate(scale.get(), [CONST.AVATAR_CROP_MODAL.MIN_SCALE, CONST.AVATAR_CROP_MODAL.MAX_SCALE], [0, sliderContainerSize])); + }, [scale, sliderContainerSize, translateSlider]); // Rotates the image by changing the rotation value by 90 degrees // and updating the position so the image remains in the same place after rotation const rotateImage = useCallback(() => { - rotation.value -= 90; + rotation.set((value) => value - 90); + + const oldX = translateX.get(); + const oldY = translateY.get(); + const oldHeight = originalImageHeight.get(); + const oldWidth = originalImageWidth.get(); // Rotating 2d coordinates by applying the formula (x, y) → (-y, x). - [translateX.value, translateY.value] = [translateY.value, translateX.value * -1]; + translateX.set(oldY); + translateY.set(oldX * -1); // Since we rotated the image by 90 degrees, now width becomes height and vice versa. - [originalImageHeight.value, originalImageWidth.value] = [originalImageWidth.value, originalImageHeight.value]; - }, [originalImageHeight.value, originalImageWidth.value, rotation, translateX.value, translateY.value]); + originalImageHeight.set(oldWidth); + originalImageWidth.set(oldHeight); + }, [originalImageHeight, originalImageWidth, rotation, translateX, translateY]); // Crops an image that was provided in the imageUri prop, using the current position/scale // then calls onSave and onClose callbacks const cropAndSaveImage = useCallback(() => { - if (isLoading.value) { + if (isLoading.get()) { return; } - isLoading.value = true; - const smallerSize = Math.min(originalImageHeight.value, originalImageWidth.value); - const size = smallerSize / scale.value; - const imageCenterX = originalImageWidth.value / 2; - const imageCenterY = originalImageHeight.value / 2; + isLoading.set(true); + const smallerSize = Math.min(originalImageHeight.get(), originalImageWidth.get()); + const size = smallerSize / scale.get(); + const imageCenterX = originalImageWidth.get() / 2; + const imageCenterY = originalImageHeight.get() / 2; const apothem = size / 2; // apothem for squares is equals to half of it size // Since the translate value is only a distance from the image center, we are able to calculate // the originX and the originY - start coordinates of cropping view. - const originX = imageCenterX - apothem - (translateX.value / imageContainerSize / scale.value) * smallerSize; - const originY = imageCenterY - apothem - (translateY.value / imageContainerSize / scale.value) * smallerSize; + const originX = imageCenterX - apothem - (translateX.get() / imageContainerSize / scale.get()) * smallerSize; + const originY = imageCenterY - apothem - (translateY.get() / imageContainerSize / scale.get()) * smallerSize; const crop = { height: size, @@ -301,29 +307,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose const name = isSvg ? 'fileName.png' : imageName; const type = isSvg ? 'image/png' : imageType; - cropOrRotateImage(imageUri, [{rotate: rotation.value % 360}, {crop}], {compress: 1, name, type}) + cropOrRotateImage(imageUri, [{rotate: rotation.get() % 360}, {crop}], {compress: 1, name, type}) .then((newImage) => { onClose?.(); onSave?.(newImage); }) .catch(() => { - isLoading.value = false; + isLoading.set(false); }); - }, [ - imageUri, - imageName, - imageType, - onClose, - onSave, - originalImageHeight.value, - originalImageWidth.value, - scale.value, - translateX.value, - imageContainerSize, - translateY.value, - rotation.value, - isLoading, - ]); + }, [isLoading, originalImageHeight, originalImageWidth, scale, translateX, imageContainerSize, translateY, imageType, imageName, imageUri, rotation, onClose, onSave]); const sliderOnPress = (locationX: number) => { // We are using the worklet directive here and running on the UI thread to ensure the Reanimated @@ -331,17 +323,16 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose 'worklet'; - if (!locationX || !isPressableEnabled.value) { + if (!locationX || !isPressableEnabled.get()) { return; } const newSliderValue = clamp(locationX, [0, sliderContainerSize]); const newScale = newScaleValue(newSliderValue, sliderContainerSize); - // eslint-disable-next-line react-compiler/react-compiler - translateSlider.value = newSliderValue; - const differential = newScale / scale.value; - scale.value = newScale; - const newX = translateX.value * differential; - const newY = translateY.value * differential; + translateSlider.set(newSliderValue); + const differential = newScale / scale.get(); + scale.set(newScale); + const newX = translateX.get() * differential; + const newY = translateY.get() * differential; updateImageOffset(newX, newY); }; diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index ecf72f89134b..3c831301db8b 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -60,18 +60,19 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo const buttonRef = ref; useEffect(() => { - // eslint-disable-next-line react-compiler/react-compiler - sharedValue.value = withTiming(isActive ? 1 : 0, { - duration: 340, - easing: Easing.inOut(Easing.ease), - }); + sharedValue.set( + withTiming(isActive ? 1 : 0, { + duration: 340, + easing: Easing.inOut(Easing.ease), + }), + ); }, [isActive, sharedValue]); const animatedStyle = useAnimatedStyle(() => { - const backgroundColor = interpolateColor(sharedValue.value, [0, 1], [success, buttonDefaultBG]); + const backgroundColor = interpolateColor(sharedValue.get(), [0, 1], [success, buttonDefaultBG]); return { - transform: [{rotate: `${sharedValue.value * 135}deg`}], + transform: [{rotate: `${sharedValue.get() * 135}deg`}], backgroundColor, borderRadius, }; @@ -79,7 +80,7 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo const animatedProps = useAnimatedProps( () => { - const fill = interpolateColor(sharedValue.value, [0, 1], [textLight, textDark]); + const fill = interpolateColor(sharedValue.get(), [0, 1], [textLight, textDark]); return { fill, diff --git a/src/components/OpacityView.tsx b/src/components/OpacityView.tsx index f4884fd3c0f8..6c7aa26d05ba 100644 --- a/src/components/OpacityView.tsx +++ b/src/components/OpacityView.tsx @@ -44,16 +44,11 @@ function OpacityView({ }: OpacityViewProps) { const opacity = useSharedValue(1); const opacityStyle = useAnimatedStyle(() => ({ - opacity: opacity.value, + opacity: opacity.get(), })); React.useEffect(() => { - if (shouldDim) { - // eslint-disable-next-line react-compiler/react-compiler - opacity.value = withTiming(dimmingValue, {duration: dimAnimationDuration}); - } else { - opacity.value = withTiming(1, {duration: dimAnimationDuration}); - } + opacity.set(withTiming(shouldDim ? dimmingValue : 1, {duration: dimAnimationDuration})); }, [shouldDim, dimmingValue, opacity, dimAnimationDuration]); return ( diff --git a/src/components/SpacerView.tsx b/src/components/SpacerView.tsx index bb762da1226b..3ca874e8f260 100644 --- a/src/components/SpacerView.tsx +++ b/src/components/SpacerView.tsx @@ -22,9 +22,9 @@ function SpacerView({shouldShow, style}: SpacerViewProps) { const prevShouldShow = usePrevious(shouldShow); const duration = CONST.ANIMATED_TRANSITION; const animatedStyles = useAnimatedStyle(() => ({ - borderBottomWidth: withTiming(borderBottomWidth.value, {duration}), - marginTop: withTiming(marginVertical.value, {duration}), - marginBottom: withTiming(marginVertical.value, {duration}), + borderBottomWidth: withTiming(borderBottomWidth.get(), {duration}), + marginTop: withTiming(marginVertical.get(), {duration}), + marginBottom: withTiming(marginVertical.get(), {duration}), })); React.useEffect(() => { @@ -35,9 +35,8 @@ function SpacerView({shouldShow, style}: SpacerViewProps) { marginVertical: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL, borderBottomWidth: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH, }; - // eslint-disable-next-line react-compiler/react-compiler - marginVertical.value = values.marginVertical; - borderBottomWidth.value = values.borderBottomWidth; + marginVertical.set(values.marginVertical); + borderBottomWidth.set(values.borderBottomWidth); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we only need to trigger when shouldShow prop is changed }, [shouldShow, prevShouldShow]); From f473d06bf18cc97b09e64821e716d6441fc29211 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 12:51:05 +0100 Subject: [PATCH 02/14] Fix patches --- ...ive-reanimated+3.15.1+003+fixNullViewTag.patch | 15 --------------- ...native-reanimated+3.16.1+001+hybrid-app.patch} | 4 ++-- ...imated+3.16.1+002+dontWhitelistTextProp.patch} | 0 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch rename patches/{react-native-reanimated+3.15.1+001+hybrid-app.patch => react-native-reanimated+3.16.1+001+hybrid-app.patch} (91%) rename patches/{react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch => react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch} (100%) diff --git a/patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch b/patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch deleted file mode 100644 index ca982c6f8036..000000000000 --- a/patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/node_modules/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx b/node_modules/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx -index 577b4a7..c60f0f8 100644 ---- a/node_modules/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx -+++ b/node_modules/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx -@@ -481,7 +481,9 @@ export function createAnimatedComponent( - ? (ref as HTMLElement) - : findNodeHandle(ref as Component); - -- this._componentViewTag = tag as number; -+ if (tag !== null) { -+ this._componentViewTag = tag as number; -+ } - - const { layout, entering, exiting, sharedTransitionTag } = this.props; - if ( diff --git a/patches/react-native-reanimated+3.15.1+001+hybrid-app.patch b/patches/react-native-reanimated+3.16.1+001+hybrid-app.patch similarity index 91% rename from patches/react-native-reanimated+3.15.1+001+hybrid-app.patch rename to patches/react-native-reanimated+3.16.1+001+hybrid-app.patch index 3b40360d5860..835df1f034a9 100644 --- a/patches/react-native-reanimated+3.15.1+001+hybrid-app.patch +++ b/patches/react-native-reanimated+3.16.1+001+hybrid-app.patch @@ -1,9 +1,9 @@ diff --git a/node_modules/react-native-reanimated/scripts/reanimated_utils.rb b/node_modules/react-native-reanimated/scripts/reanimated_utils.rb -index af0935f..ccd2a9e 100644 +index 9fc7b15..e453d84 100644 --- a/node_modules/react-native-reanimated/scripts/reanimated_utils.rb +++ b/node_modules/react-native-reanimated/scripts/reanimated_utils.rb @@ -17,7 +17,11 @@ def find_config() - :react_native_common_dir => nil, + :react_native_reanimated_dir_from_pods_root => nil, } - react_native_node_modules_dir = File.join(File.dirname(`cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('react-native/package.json')"`), '..') diff --git a/patches/react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch b/patches/react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch similarity index 100% rename from patches/react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch rename to patches/react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch From 4ac36b19b9d92c8a16679beb819e572f06f75354 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 12:52:08 +0100 Subject: [PATCH 03/14] Improve performance of rotateImage --- .../AvatarCropModal/AvatarCropModal.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx index a3c4cdca78bf..44ae528c425a 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.tsx +++ b/src/components/AvatarCropModal/AvatarCropModal.tsx @@ -260,20 +260,17 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose // Rotates the image by changing the rotation value by 90 degrees // and updating the position so the image remains in the same place after rotation const rotateImage = useCallback(() => { - rotation.set((value) => value - 90); + runOnUI(() => { + rotation.set((value) => value - 90); - const oldX = translateX.get(); - const oldY = translateY.get(); - const oldHeight = originalImageHeight.get(); - const oldWidth = originalImageWidth.get(); + const oldTranslateX = translateX.get(); + translateX.set(translateY.get()); + translateY.set(oldTranslateX * -1); - // Rotating 2d coordinates by applying the formula (x, y) → (-y, x). - translateX.set(oldY); - translateY.set(oldX * -1); - - // Since we rotated the image by 90 degrees, now width becomes height and vice versa. - originalImageHeight.set(oldWidth); - originalImageWidth.set(oldHeight); + const oldOriginalImageHeight = originalImageHeight.get(); + originalImageHeight.set(originalImageWidth.get()); + originalImageWidth.set(oldOriginalImageHeight); + })(); }, [originalImageHeight, originalImageWidth, rotation, translateX, translateY]); // Crops an image that was provided in the imageUri prop, using the current position/scale From 0667cf722d040636d7b1b1570a105e98cae15381 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 13:26:05 +0100 Subject: [PATCH 04/14] Continue updating reanimated usage --- .../AvatarCropModal/AvatarCropModal.tsx | 26 ++- .../CustomStatusBarAndBackground/index.tsx | 14 +- src/components/MultiGestureCanvas/index.tsx | 84 +++++----- .../MultiGestureCanvas/usePanGesture.ts | 158 ++++++++++-------- 4 files changed, 157 insertions(+), 125 deletions(-) diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx index 44ae528c425a..3ff9ccc4e3f8 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.tsx +++ b/src/components/AvatarCropModal/AvatarCropModal.tsx @@ -4,7 +4,7 @@ import type {LayoutChangeEvent} from 'react-native'; import {Gesture, GestureHandlerRootView} from 'react-native-gesture-handler'; import type {GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler'; import ImageSize from 'react-native-image-size'; -import {interpolate, runOnUI, useSharedValue, useWorkletCallback} from 'react-native-reanimated'; +import {interpolate, runOnUI, useSharedValue} from 'react-native-reanimated'; import Button from '@components/Button'; import HeaderGap from '@components/HeaderGap'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -143,12 +143,18 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose /** * Validates that value is within the provided mix/max range. */ - const clamp = useWorkletCallback((value: number, [min, max]) => interpolate(value, [min, max], [min, max], 'clamp'), []); + const clamp = useCallback((value: number, [min, max]: [number, number]) => { + 'worklet'; + + return interpolate(value, [min, max], [min, max], 'clamp'); + }, []); /** * Returns current image size taking into account scale and rotation. */ - const getDisplayedImageSize = useWorkletCallback(() => { + const getDisplayedImageSize = useCallback(() => { + 'worklet'; + let height = imageContainerSize * scale.get(); let width = imageContainerSize * scale.get(); @@ -161,13 +167,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose } return {height, width}; - }, [imageContainerSize, scale]); + }, [imageContainerSize, originalImageHeight, originalImageWidth, scale]); /** * Validates the offset to prevent overflow, and updates the image offset. */ - const updateImageOffset = useWorkletCallback( + const updateImageOffset = useCallback( (offsetX: number, offsetY: number) => { + 'worklet'; + const {height, width} = getDisplayedImageSize(); const maxOffsetX = (width - imageContainerSize) / 2; const maxOffsetY = (height - imageContainerSize) / 2; @@ -176,13 +184,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose prevMaxOffsetX.set(maxOffsetX); prevMaxOffsetY.set(maxOffsetY); }, - [imageContainerSize, scale, clamp], + [getDisplayedImageSize, imageContainerSize, translateX, clamp, translateY, prevMaxOffsetX, prevMaxOffsetY], ); - const newScaleValue = useWorkletCallback((newSliderValue: number, containerSize: number) => { + const newScaleValue = useCallback((newSliderValue: number, containerSize: number) => { + 'worklet'; + const {MAX_SCALE, MIN_SCALE} = CONST.AVATAR_CROP_MODAL; return (newSliderValue / containerSize) * (MAX_SCALE - MIN_SCALE) + MIN_SCALE; - }); + }, []); /** * Calculates new x & y image translate value on image panning diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index ec52f07d211c..ac1fc77dff96 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -44,14 +44,14 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack const statusBarAnimation = useSharedValue(0); useAnimatedReaction( - () => statusBarAnimation.value, + () => statusBarAnimation.get(), (current, previous) => { // Do not run if either of the animated value is null // or previous animated value is greater than or equal to the current one if (previous === null || current === null || current <= previous) { return; } - const backgroundColor = interpolateColor(statusBarAnimation.value, [0, 1], [prevStatusBarBackgroundColor.value, statusBarBackgroundColor.value]); + const backgroundColor = interpolateColor(statusBarAnimation.get(), [0, 1], [prevStatusBarBackgroundColor.get(), statusBarBackgroundColor.get()]); runOnJS(updateStatusBarAppearance)({backgroundColor}); }, ); @@ -92,8 +92,8 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack currentScreenBackgroundColor = backgroundColorFromRoute || pageTheme.backgroundColor; } - prevStatusBarBackgroundColor.value = statusBarBackgroundColor.value; - statusBarBackgroundColor.value = currentScreenBackgroundColor; + prevStatusBarBackgroundColor.set(statusBarBackgroundColor.get()); + statusBarBackgroundColor.set(currentScreenBackgroundColor); const callUpdateStatusBarAppearance = () => { updateStatusBarAppearance({statusBarStyle: newStatusBarStyle}); @@ -101,8 +101,8 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack }; const callUpdateStatusBarBackgroundColor = () => { - statusBarAnimation.value = 0; - statusBarAnimation.value = withDelay(300, withTiming(1)); + statusBarAnimation.set(0); + statusBarAnimation.set(withDelay(300, withTiming(1))); }; // Don't update the status bar style if it's the same as the current one, to prevent flashing. @@ -121,7 +121,7 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack callUpdateStatusBarAppearance(); } - if (currentScreenBackgroundColor !== theme.appBG || prevStatusBarBackgroundColor.value !== theme.appBG) { + if (currentScreenBackgroundColor !== theme.appBG || prevStatusBarBackgroundColor.get() !== theme.appBG) { callUpdateStatusBarBackgroundColor(); } }, diff --git a/src/components/MultiGestureCanvas/index.tsx b/src/components/MultiGestureCanvas/index.tsx index ff9566839d59..fda3ee9bea60 100644 --- a/src/components/MultiGestureCanvas/index.tsx +++ b/src/components/MultiGestureCanvas/index.tsx @@ -1,12 +1,12 @@ import type {ForwardedRef} from 'react'; -import React, {useEffect, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; import type {GestureType} from 'react-native-gesture-handler'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import type {GestureRef} from 'react-native-gesture-handler/lib/typescript/handlers/gestures/gesture'; import type PagerView from 'react-native-pager-view'; import type {SharedValue} from 'react-native-reanimated'; -import Animated, {cancelAnimation, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue, useWorkletCallback, withSpring} from 'react-native-reanimated'; +import Animated, {cancelAnimation, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring} from 'react-native-reanimated'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; @@ -92,7 +92,7 @@ function MultiGestureCanvas({ // Adding together zoom scale and the initial scale to fit the content into the canvas // Using the minimum content scale, so that the image is not bigger than the canvas // and not smaller than needed to fit - const totalScale = useDerivedValue(() => zoomScale.value * minContentScale, [minContentScale]); + const totalScale = useDerivedValue(() => zoomScale.get() * minContentScale, [minContentScale]); const panTranslateX = useSharedValue(0); const panTranslateY = useSharedValue(0); @@ -110,44 +110,50 @@ function MultiGestureCanvas({ /** * Stops any currently running decay animation from panning */ - const stopAnimation = useWorkletCallback(() => { + const stopAnimation = useCallback(() => { + 'worklet'; + cancelAnimation(offsetX); cancelAnimation(offsetY); - }); + }, [offsetX, offsetY]); /** * Resets the canvas to the initial state and animates back smoothly */ - const reset = useWorkletCallback((animated: boolean, callback?: () => void) => { - stopAnimation(); - - // eslint-disable-next-line react-compiler/react-compiler - offsetX.value = 0; - offsetY.value = 0; - pinchScale.value = 1; - - if (animated) { - panTranslateX.value = withSpring(0, SPRING_CONFIG); - panTranslateY.value = withSpring(0, SPRING_CONFIG); - pinchTranslateX.value = withSpring(0, SPRING_CONFIG); - pinchTranslateY.value = withSpring(0, SPRING_CONFIG); - zoomScale.value = withSpring(1, SPRING_CONFIG, callback); - - return; - } - - panTranslateX.value = 0; - panTranslateY.value = 0; - pinchTranslateX.value = 0; - pinchTranslateY.value = 0; - zoomScale.value = 1; - - if (callback === undefined) { - return; - } - - callback(); - }); + const reset = useCallback( + (animated: boolean, callback?: () => void) => { + 'worklet'; + + stopAnimation(); + + offsetX.set(0); + offsetY.set(0); + pinchScale.set(1); + + if (animated) { + panTranslateX.set(withSpring(0, SPRING_CONFIG)); + panTranslateY.set(withSpring(0, SPRING_CONFIG)); + pinchTranslateX.set(withSpring(0, SPRING_CONFIG)); + pinchTranslateY.set(withSpring(0, SPRING_CONFIG)); + zoomScale.set(withSpring(1, SPRING_CONFIG, callback)); + + return; + } + + panTranslateX.set(0); + panTranslateY.set(0); + pinchTranslateX.set(0); + pinchTranslateY.set(0); + zoomScale.set(1); + + if (callback === undefined) { + return; + } + + callback(); + }, + [offsetX, offsetY, panTranslateX, panTranslateY, pinchScale, pinchTranslateX, pinchTranslateY, stopAnimation, zoomScale], + ); const {singleTapGesture: baseSingleTapGesture, doubleTapGesture} = useTapGestures({ canvasSize, @@ -164,6 +170,7 @@ function MultiGestureCanvas({ onTap, shouldDisableTransformationGestures, }); + // eslint-disable-next-line react-compiler/react-compiler const singleTapGesture = baseSingleTapGesture.requireExternalGestureToFail(doubleTapGesture, panGestureRef); const panGestureSimultaneousList = useMemo( @@ -186,6 +193,7 @@ function MultiGestureCanvas({ onSwipeDown, }) .simultaneousWithExternalGesture(...panGestureSimultaneousList) + // eslint-disable-next-line react-compiler/react-compiler .withRef(panGestureRef); const pinchGesture = usePinchGesture({ @@ -217,8 +225,8 @@ function MultiGestureCanvas({ // Animate the x and y position of the content within the canvas based on all of the gestures const animatedStyles = useAnimatedStyle(() => { - const x = pinchTranslateX.value + panTranslateX.value + offsetX.value; - const y = pinchTranslateY.value + panTranslateY.value + offsetY.value; + const x = pinchTranslateX.get() + panTranslateX.get() + offsetX.get(); + const y = pinchTranslateY.get() + panTranslateY.get() + offsetY.get(); return { transform: [ @@ -228,7 +236,7 @@ function MultiGestureCanvas({ { translateY: y, }, - {scale: totalScale.value}, + {scale: totalScale.get()}, ], // Hide the image if the size is not ready yet opacity: contentSizeProp?.width ? 1 : 0, diff --git a/src/components/MultiGestureCanvas/usePanGesture.ts b/src/components/MultiGestureCanvas/usePanGesture.ts index b31e310055ae..bd29a18cc46c 100644 --- a/src/components/MultiGestureCanvas/usePanGesture.ts +++ b/src/components/MultiGestureCanvas/usePanGesture.ts @@ -1,8 +1,9 @@ /* eslint-disable no-param-reassign */ +import {useCallback} from 'react'; import {Dimensions} from 'react-native'; import type {PanGesture} from 'react-native-gesture-handler'; import {Gesture} from 'react-native-gesture-handler'; -import {runOnJS, useDerivedValue, useSharedValue, useWorkletCallback, withDecay, withSpring} from 'react-native-reanimated'; +import {runOnJS, useDerivedValue, useSharedValue, withDecay, withSpring} from 'react-native-reanimated'; import * as Browser from '@libs/Browser'; import {SPRING_CONFIG} from './constants'; import type {MultiGestureCanvasVariables} from './types'; @@ -47,8 +48,8 @@ const usePanGesture = ({ onSwipeDown, }: UsePanGestureProps): PanGesture => { // The content size after fitting it to the canvas and zooming - const zoomedContentWidth = useDerivedValue(() => contentSize.width * totalScale.value, [contentSize.width]); - const zoomedContentHeight = useDerivedValue(() => contentSize.height * totalScale.value, [contentSize.height]); + const zoomedContentWidth = useDerivedValue(() => contentSize.width * totalScale.get(), [contentSize.width]); + const zoomedContentHeight = useDerivedValue(() => contentSize.height * totalScale.get(), [contentSize.height]); // Used to track previous touch position for the "swipe down to close" gesture const previousTouch = useSharedValue<{x: number; y: number} | null>(null); @@ -61,140 +62,153 @@ const usePanGesture = ({ const isMobileBrowser = Browser.isMobile(); // Disable "swipe down to close" gesture when content is bigger than the canvas - const enableSwipeDownToClose = useDerivedValue(() => canvasSize.height < zoomedContentHeight.value, [canvasSize.height]); + const enableSwipeDownToClose = useDerivedValue(() => canvasSize.height < zoomedContentHeight.get(), [canvasSize.height]); // Calculates bounds of the scaled content // Can we pan left/right/up/down // Can be used to limit gesture or implementing tension effect - const getBounds = useWorkletCallback(() => { + const getBounds = useCallback(() => { + 'worklet'; + let horizontalBoundary = 0; let verticalBoundary = 0; - if (canvasSize.width < zoomedContentWidth.value) { - horizontalBoundary = Math.abs(canvasSize.width - zoomedContentWidth.value) / 2; + if (canvasSize.width < zoomedContentWidth.get()) { + horizontalBoundary = Math.abs(canvasSize.width - zoomedContentWidth.get()) / 2; } - if (canvasSize.height < zoomedContentHeight.value) { - verticalBoundary = Math.abs(zoomedContentHeight.value - canvasSize.height) / 2; + if (canvasSize.height < zoomedContentHeight.get()) { + verticalBoundary = Math.abs(zoomedContentHeight.get() - canvasSize.height) / 2; } const horizontalBoundaries = {min: -horizontalBoundary, max: horizontalBoundary}; const verticalBoundaries = {min: -verticalBoundary, max: verticalBoundary}; const clampedOffset = { - x: MultiGestureCanvasUtils.clamp(offsetX.value, horizontalBoundaries.min, horizontalBoundaries.max), - y: MultiGestureCanvasUtils.clamp(offsetY.value, verticalBoundaries.min, verticalBoundaries.max), + x: MultiGestureCanvasUtils.clamp(offsetX.get(), horizontalBoundaries.min, horizontalBoundaries.max), + y: MultiGestureCanvasUtils.clamp(offsetY.get(), verticalBoundaries.min, verticalBoundaries.max), }; // If the horizontal/vertical offset is the same after clamping to the min/max boundaries, the content is within the boundaries - const isInHoriztontalBoundary = clampedOffset.x === offsetX.value; - const isInVerticalBoundary = clampedOffset.y === offsetY.value; + const isInHorizontalBoundary = clampedOffset.x === offsetX.get(); + const isInVerticalBoundary = clampedOffset.y === offsetY.get(); return { horizontalBoundaries, verticalBoundaries, clampedOffset, - isInHoriztontalBoundary, + isInHorizontalBoundary, isInVerticalBoundary, }; - }, [canvasSize.width, canvasSize.height]); + }, [canvasSize.width, canvasSize.height, zoomedContentWidth, zoomedContentHeight, offsetX, offsetY]); // We want to smoothly decay/end the gesture by phasing out the pan animation // In case the content is outside of the boundaries of the canvas, // we need to move the content back into the boundaries - const finishPanGesture = useWorkletCallback(() => { + const finishPanGesture = useCallback(() => { + 'worklet'; + // If the content is centered within the canvas, we don't need to run any animations - if (offsetX.value === 0 && offsetY.value === 0 && panTranslateX.value === 0 && panTranslateY.value === 0) { + if (offsetX.get() === 0 && offsetY.get() === 0 && panTranslateX.get() === 0 && panTranslateY.get() === 0) { return; } - const {clampedOffset, isInHoriztontalBoundary, isInVerticalBoundary, horizontalBoundaries, verticalBoundaries} = getBounds(); + const {clampedOffset, isInHorizontalBoundary, isInVerticalBoundary, horizontalBoundaries, verticalBoundaries} = getBounds(); // If the content is within the horizontal/vertical boundaries of the canvas, we can smoothly phase out the animation // If not, we need to snap back to the boundaries - if (isInHoriztontalBoundary) { + if (isInHorizontalBoundary) { // If the (absolute) velocity is 0, we don't need to run an animation - if (Math.abs(panVelocityX.value) !== 0) { + if (Math.abs(panVelocityX.get()) !== 0) { // Phase out the pan animation // eslint-disable-next-line react-compiler/react-compiler - offsetX.value = withDecay({ - velocity: panVelocityX.value, - clamp: [horizontalBoundaries.min, horizontalBoundaries.max], - deceleration: PAN_DECAY_DECELARATION, - rubberBandEffect: false, - }); + offsetX.set( + withDecay({ + velocity: panVelocityX.get(), + clamp: [horizontalBoundaries.min, horizontalBoundaries.max], + deceleration: PAN_DECAY_DECELARATION, + rubberBandEffect: false, + }), + ); } } else { // Animated back to the boundary - offsetX.value = withSpring(clampedOffset.x, SPRING_CONFIG); + offsetX.set(withSpring(clampedOffset.x, SPRING_CONFIG)); } if (isInVerticalBoundary) { // If the (absolute) velocity is 0, we don't need to run an animation - if (Math.abs(panVelocityY.value) !== 0) { + if (Math.abs(panVelocityY.get()) !== 0) { // Phase out the pan animation - offsetY.value = withDecay({ - velocity: panVelocityY.value, - clamp: [verticalBoundaries.min, verticalBoundaries.max], - deceleration: PAN_DECAY_DECELARATION, - }); + offsetY.set( + withDecay({ + velocity: panVelocityY.get(), + clamp: [verticalBoundaries.min, verticalBoundaries.max], + deceleration: PAN_DECAY_DECELARATION, + }), + ); } } else { - const finalTranslateY = offsetY.value + panVelocityY.value * 0.2; - - if (finalTranslateY > SNAP_POINT && zoomScale.value <= 1) { - offsetY.value = withSpring(SNAP_POINT_HIDDEN, SPRING_CONFIG, () => { - isSwipingDownToClose.value = false; - - if (onSwipeDown) { - runOnJS(onSwipeDown)(); - } - }); + const finalTranslateY = offsetY.get() + panVelocityY.get() * 0.2; + + if (finalTranslateY > SNAP_POINT && zoomScale.get() <= 1) { + offsetY.set( + withSpring(SNAP_POINT_HIDDEN, SPRING_CONFIG, () => { + isSwipingDownToClose.set(false); + + if (onSwipeDown) { + runOnJS(onSwipeDown)(); + } + }), + ); } else { // Animated back to the boundary - offsetY.value = withSpring(clampedOffset.y, SPRING_CONFIG, () => { - isSwipingDownToClose.value = false; - }); + offsetY.set( + withSpring(clampedOffset.y, SPRING_CONFIG, () => { + isSwipingDownToClose.set(false); + }), + ); } } // Reset velocity variables after we finished the pan gesture - panVelocityX.value = 0; - panVelocityY.value = 0; - }); + panVelocityX.set(0); + panVelocityY.set(0); + }, [getBounds, isSwipingDownToClose, offsetX, offsetY, onSwipeDown, panTranslateX, panTranslateY, panVelocityX, panVelocityY, zoomScale]); const panGesture = Gesture.Pan() .manualActivation(true) .averageTouches(true) .onTouchesUp(() => { - previousTouch.value = null; + previousTouch.set(null); }) .onTouchesMove((evt, state) => { // We only allow panning when the content is zoomed in - if (zoomScale.value > 1 && !shouldDisableTransformationGestures.value) { + if (zoomScale.get() > 1 && !shouldDisableTransformationGestures.get()) { state.activate(); } // TODO: this needs tuning to work properly - if (!shouldDisableTransformationGestures.value && zoomScale.value === 1 && previousTouch.value !== null) { - const velocityX = Math.abs((evt.allTouches.at(0)?.x ?? 0) - previousTouch.value.x); - const velocityY = (evt.allTouches.at(0)?.y ?? 0) - previousTouch.value.y; + const previousTouchValue = previousTouch.get(); + if (!shouldDisableTransformationGestures.get() && zoomScale.get() === 1 && previousTouchValue !== null) { + const velocityX = Math.abs((evt.allTouches.at(0)?.x ?? 0) - previousTouchValue.x); + const velocityY = (evt.allTouches.at(0)?.y ?? 0) - previousTouchValue.y; if (Math.abs(velocityY) > velocityX && velocityY > 20) { state.activate(); - isSwipingDownToClose.value = true; - previousTouch.value = null; + isSwipingDownToClose.set(true); + previousTouch.set(null); return; } } - if (previousTouch.value === null) { - previousTouch.value = { + if (previousTouchValue === null) { + previousTouch.set({ x: evt.allTouches.at(0)?.x ?? 0, y: evt.allTouches.at(0)?.y ?? 0, - }; + }); } }) .onStart(() => { @@ -207,31 +221,31 @@ const usePanGesture = ({ return; } - panVelocityX.value = evt.velocityX; - panVelocityY.value = evt.velocityY; + panVelocityX.set(evt.velocityX); + panVelocityY.set(evt.velocityY); - if (!isSwipingDownToClose.value) { - if (!isMobileBrowser || (isMobileBrowser && zoomScale.value !== 1)) { - panTranslateX.value += evt.changeX; + if (!isSwipingDownToClose.get()) { + if (!isMobileBrowser || (isMobileBrowser && zoomScale.get() !== 1)) { + panTranslateX.set((value) => value + evt.changeX); } } - if (enableSwipeDownToClose.value || isSwipingDownToClose.value) { - panTranslateY.value += evt.changeY; + if (enableSwipeDownToClose.get() || isSwipingDownToClose.get()) { + panTranslateY.set((value) => value + evt.changeY); } }) .onEnd(() => { // Add pan translation to total offset and reset gesture variables - offsetX.value += panTranslateX.value; - offsetY.value += panTranslateY.value; + offsetX.set((value) => value + panTranslateX.get()); + offsetY.set((value) => value + panTranslateY.get()); // Reset pan gesture variables - panTranslateX.value = 0; - panTranslateY.value = 0; - previousTouch.value = null; + panTranslateX.set(0); + panTranslateY.set(0); + previousTouch.set(null); // If we are swiping (in the pager), we don't want to return to boundaries - if (shouldDisableTransformationGestures.value) { + if (shouldDisableTransformationGestures.get()) { return; } From 73c358f73962f392493c082b880579523c4b0ff7 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 14:40:45 +0100 Subject: [PATCH 05/14] Finish migrating to new reanimated --- .../MultiGestureCanvas/usePinchGesture.ts | 88 ++++++++++--------- .../MultiGestureCanvas/useTapGestures.ts | 23 +++-- .../ReportActionItem/ReportPreview.tsx | 19 ++-- .../AnimatedSettlementButton.tsx | 44 +++++----- .../SplashScreenHider/index.native.tsx | 31 ++++--- .../VideoPlayer/BaseVideoPlayer.tsx | 7 +- .../VideoPlayerControls/ProgressBar/index.tsx | 11 ++- .../VolumeButton/index.tsx | 15 ++-- .../VideoPlayerContexts/VolumeContext.tsx | 7 +- src/hooks/useAnimatedHighlightStyle/index.ts | 35 ++++---- src/pages/Search/SearchPageBottomTab.tsx | 15 ++-- .../report/AnimatedEmptyStateBackground.tsx | 9 +- .../home/report/FloatingMessageCounter.tsx | 6 +- .../ComposerWithSuggestions.tsx | 13 ++- .../ReportActionCompose.tsx | 7 +- .../report/ReportActionItemMessageEdit.tsx | 14 ++- .../step/IOURequestStepScan/index.native.tsx | 13 ++- .../BackgroundImage/index.ios.tsx | 13 +-- 18 files changed, 182 insertions(+), 188 deletions(-) diff --git a/src/components/MultiGestureCanvas/usePinchGesture.ts b/src/components/MultiGestureCanvas/usePinchGesture.ts index 46a5e28e5732..7c517ad2f30b 100644 --- a/src/components/MultiGestureCanvas/usePinchGesture.ts +++ b/src/components/MultiGestureCanvas/usePinchGesture.ts @@ -1,8 +1,8 @@ /* eslint-disable no-param-reassign */ -import {useEffect, useState} from 'react'; +import {useCallback, useEffect, useState} from 'react'; import type {PinchGesture} from 'react-native-gesture-handler'; import {Gesture} from 'react-native-gesture-handler'; -import {runOnJS, useAnimatedReaction, useSharedValue, useWorkletCallback, withSpring} from 'react-native-reanimated'; +import {runOnJS, useAnimatedReaction, useSharedValue, withSpring} from 'react-native-reanimated'; import {SPRING_CONFIG, ZOOM_RANGE_BOUNCE_FACTORS} from './constants'; import type {MultiGestureCanvasVariables} from './types'; @@ -61,16 +61,16 @@ const usePinchGesture = ({ return; } - runOnJS(onScaleChanged)(zoomScale.value); + runOnJS(onScaleChanged)(zoomScale.get()); }; // Update the total (pinch) translation based on the regular pinch + bounce useAnimatedReaction( - () => [pinchTranslateX.value, pinchTranslateY.value, pinchBounceTranslateX.value, pinchBounceTranslateY.value], + () => [pinchTranslateX.get(), pinchTranslateY.get(), pinchBounceTranslateX.get(), pinchBounceTranslateY.get()], ([translateX, translateY, bounceX, bounceY]) => { // eslint-disable-next-line react-compiler/react-compiler - totalPinchTranslateX.value = translateX + bounceX; - totalPinchTranslateY.value = translateY + bounceY; + totalPinchTranslateX.set(translateX + bounceX); + totalPinchTranslateY.set(translateY + bounceY); }, ); @@ -78,12 +78,16 @@ const usePinchGesture = ({ * Calculates the adjusted focal point of the pinch gesture, * based on the canvas size and the current offset */ - const getAdjustedFocal = useWorkletCallback( - (focalX: number, focalY: number) => ({ - x: focalX - (canvasSize.width / 2 + offsetX.value), - y: focalY - (canvasSize.height / 2 + offsetY.value), - }), - [canvasSize.width, canvasSize.height], + const getAdjustedFocal = useCallback( + (focalX: number, focalY: number) => { + 'worklet'; + + return { + x: focalX - (canvasSize.width / 2 + offsetX.get()), + y: focalY - (canvasSize.height / 2 + offsetY.get()), + }; + }, + [canvasSize.width, canvasSize.height, offsetX, offsetY], ); // The pinch gesture is disabled when we release one of the fingers @@ -101,7 +105,7 @@ const usePinchGesture = ({ // The first argument is not used, but must be defined .onTouchesDown((_evt, state) => { // We don't want to activate pinch gesture when we are swiping in the pager - if (!shouldDisableTransformationGestures.value) { + if (!shouldDisableTransformationGestures.get()) { return; } @@ -112,8 +116,8 @@ const usePinchGesture = ({ // Set the origin focal point of the pinch gesture at the start of the gesture const adjustedFocal = getAdjustedFocal(evt.focalX, evt.focalY); - pinchOrigin.x.value = adjustedFocal.x; - pinchOrigin.y.value = adjustedFocal.y; + pinchOrigin.x.set(adjustedFocal.x); + pinchOrigin.y.set(adjustedFocal.y); }) .onChange((evt) => { // Disable the pinch gesture if one finger is released, @@ -123,58 +127,58 @@ const usePinchGesture = ({ return; } - const newZoomScale = pinchScale.value * evt.scale; - + const newZoomScale = pinchScale.get() * evt.scale; + const zoomScaleValue = zoomScale.get(); // Limit the zoom scale to zoom range including bounce range - if (zoomScale.value >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScale.value <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) { - zoomScale.value = newZoomScale; - currentPinchScale.value = evt.scale; + if (zoomScaleValue >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScaleValue <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) { + zoomScale.set(newZoomScale); + currentPinchScale.set(evt.scale); triggerScaleChangedEvent(); } // Calculate new pinch translation const adjustedFocal = getAdjustedFocal(evt.focalX, evt.focalY); - const newPinchTranslateX = adjustedFocal.x + currentPinchScale.value * pinchOrigin.x.value * -1; - const newPinchTranslateY = adjustedFocal.y + currentPinchScale.value * pinchOrigin.y.value * -1; + const newPinchTranslateX = adjustedFocal.x + currentPinchScale.get() * pinchOrigin.x.get() * -1; + const newPinchTranslateY = adjustedFocal.y + currentPinchScale.get() * pinchOrigin.y.get() * -1; // If the zoom scale is within the zoom range, we perform the regular pinch translation // Otherwise it means that we are "overzoomed" or "underzoomed", so we need to bounce back - if (zoomScale.value >= zoomRange.min && zoomScale.value <= zoomRange.max) { - pinchTranslateX.value = newPinchTranslateX; - pinchTranslateY.value = newPinchTranslateY; + if (zoomScaleValue >= zoomRange.min && zoomScaleValue <= zoomRange.max) { + pinchTranslateX.set(newPinchTranslateX); + pinchTranslateY.set(newPinchTranslateY); } else { // Store x and y translation that is produced while bouncing // so we can revert the bounce once pinch gesture is released - pinchBounceTranslateX.value = newPinchTranslateX - pinchTranslateX.value; - pinchBounceTranslateY.value = newPinchTranslateY - pinchTranslateY.value; + pinchBounceTranslateX.set(newPinchTranslateX - pinchTranslateX.get()); + pinchBounceTranslateY.set(newPinchTranslateY - pinchTranslateY.get()); } }) .onEnd(() => { // Add pinch translation to total offset and reset gesture variables - offsetX.value += pinchTranslateX.value; - offsetY.value += pinchTranslateY.value; - pinchTranslateX.value = 0; - pinchTranslateY.value = 0; - currentPinchScale.value = 1; + offsetX.set((value) => value + pinchTranslateX.get()); + offsetY.set((value) => value + pinchTranslateY.get()); + pinchTranslateX.set(0); + pinchTranslateY.set(0); + currentPinchScale.set(1); // If the content was "overzoomed" or "underzoomed", we need to bounce back with an animation - if (pinchBounceTranslateX.value !== 0 || pinchBounceTranslateY.value !== 0) { - pinchBounceTranslateX.value = withSpring(0, SPRING_CONFIG); - pinchBounceTranslateY.value = withSpring(0, SPRING_CONFIG); + if (pinchBounceTranslateX.get() !== 0 || pinchBounceTranslateY.get() !== 0) { + pinchBounceTranslateX.set(withSpring(0, SPRING_CONFIG)); + pinchBounceTranslateY.set(withSpring(0, SPRING_CONFIG)); } - if (zoomScale.value < zoomRange.min) { + if (zoomScale.get() < zoomRange.min) { // If the zoom scale is less than the minimum zoom scale, we need to set the zoom scale to the minimum - pinchScale.value = zoomRange.min; - zoomScale.value = withSpring(zoomRange.min, SPRING_CONFIG, triggerScaleChangedEvent); - } else if (zoomScale.value > zoomRange.max) { + pinchScale.set(zoomRange.min); + zoomScale.set(withSpring(zoomRange.min, SPRING_CONFIG, triggerScaleChangedEvent)); + } else if (zoomScale.get() > zoomRange.max) { // If the zoom scale is higher than the maximum zoom scale, we need to set the zoom scale to the maximum - pinchScale.value = zoomRange.max; - zoomScale.value = withSpring(zoomRange.max, SPRING_CONFIG, triggerScaleChangedEvent); + pinchScale.set(zoomRange.max); + zoomScale.set(withSpring(zoomRange.max, SPRING_CONFIG, triggerScaleChangedEvent)); } else { // Otherwise, we just update the pinch scale offset - pinchScale.value = zoomScale.value; + pinchScale.set(zoomScale.get()); triggerScaleChangedEvent(); } }); diff --git a/src/components/MultiGestureCanvas/useTapGestures.ts b/src/components/MultiGestureCanvas/useTapGestures.ts index e4bb02bd5d34..a918310d2862 100644 --- a/src/components/MultiGestureCanvas/useTapGestures.ts +++ b/src/components/MultiGestureCanvas/useTapGestures.ts @@ -1,8 +1,8 @@ /* eslint-disable no-param-reassign */ -import {useMemo} from 'react'; +import {useCallback, useMemo} from 'react'; import type {TapGesture} from 'react-native-gesture-handler'; import {Gesture} from 'react-native-gesture-handler'; -import {runOnJS, useWorkletCallback, withSpring} from 'react-native-reanimated'; +import {runOnJS, withSpring} from 'react-native-reanimated'; import {DOUBLE_TAP_SCALE, SPRING_CONFIG} from './constants'; import type {MultiGestureCanvasVariables} from './types'; import * as MultiGestureCanvasUtils from './utils'; @@ -46,7 +46,7 @@ const useTapGestures = ({ // On double tap the content should be zoomed to fill, but at least zoomed by DOUBLE_TAP_SCALE const doubleTapScale = useMemo(() => Math.max(DOUBLE_TAP_SCALE, maxContentScale / minContentScale), [maxContentScale, minContentScale]); - const zoomToCoordinates = useWorkletCallback( + const zoomToCoordinates = useCallback( (focalX: number, focalY: number, callback: () => void) => { 'worklet'; @@ -111,19 +111,18 @@ const useTapGestures = ({ offsetAfterZooming.y = 0; } - // eslint-disable-next-line react-compiler/react-compiler - offsetX.value = withSpring(offsetAfterZooming.x, SPRING_CONFIG); - offsetY.value = withSpring(offsetAfterZooming.y, SPRING_CONFIG); - zoomScale.value = withSpring(doubleTapScale, SPRING_CONFIG, callback); - pinchScale.value = doubleTapScale; + offsetX.set(withSpring(offsetAfterZooming.x, SPRING_CONFIG)); + offsetY.set(withSpring(offsetAfterZooming.y, SPRING_CONFIG)); + zoomScale.set(withSpring(doubleTapScale, SPRING_CONFIG, callback)); + pinchScale.set(doubleTapScale); }, - [scaledContentWidth, scaledContentHeight, canvasSize, doubleTapScale], + [stopAnimation, canvasSize.width, canvasSize.height, scaledContentWidth, scaledContentHeight, doubleTapScale, offsetX, offsetY, zoomScale, pinchScale], ); const doubleTapGesture = Gesture.Tap() // The first argument is not used, but must be defined .onTouchesDown((_evt, state) => { - if (!shouldDisableTransformationGestures.value) { + if (!shouldDisableTransformationGestures.get()) { return; } @@ -137,13 +136,13 @@ const useTapGestures = ({ 'worklet'; if (onScaleChanged != null) { - runOnJS(onScaleChanged)(zoomScale.value); + runOnJS(onScaleChanged)(zoomScale.get()); } }; // If the content is already zoomed, we want to reset the zoom, // otherwise we want to zoom in - if (zoomScale.value > 1) { + if (zoomScale.get() > 1) { reset(true, triggerScaleChangedEvent); } else { zoomToCoordinates(evt.x, evt.y, triggerScaleChangedEvent); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 9067f1abb11a..684e922e2dc6 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -135,7 +135,7 @@ function ReportPreview({ const iouSettled = ReportUtils.isSettled(iouReportID) || action?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; const previewMessageOpacity = useSharedValue(1); const previewMessageStyle = useAnimatedStyle(() => ({ - opacity: previewMessageOpacity.value, + opacity: previewMessageOpacity.get(), })); const checkMarkScale = useSharedValue(iouSettled ? 1 : 0); @@ -425,11 +425,11 @@ function ReportPreview({ return; } - // eslint-disable-next-line react-compiler/react-compiler - previewMessageOpacity.value = withTiming(0.75, {duration: CONST.ANIMATION_PAID_DURATION / 2}, () => { - // eslint-disable-next-line react-compiler/react-compiler - previewMessageOpacity.value = withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION / 2}); - }); + previewMessageOpacity.set( + withTiming(0.75, {duration: CONST.ANIMATION_PAID_DURATION / 2}, () => { + previewMessageOpacity.set(withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION / 2})); + }), + ); // We only want to animate the text when the text changes // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [previewMessage, previewMessageOpacity]); @@ -439,12 +439,7 @@ function ReportPreview({ return; } - if (isPaidAnimationRunning) { - // eslint-disable-next-line react-compiler/react-compiler - checkMarkScale.value = withDelay(CONST.ANIMATION_PAID_CHECKMARK_DELAY, withSpring(1, {duration: CONST.ANIMATION_PAID_DURATION})); - } else { - checkMarkScale.value = 1; - } + checkMarkScale.set(isPaidAnimationRunning ? withDelay(CONST.ANIMATION_PAID_CHECKMARK_DELAY, withSpring(1, {duration: CONST.ANIMATION_PAID_DURATION})) : 1); }, [isPaidAnimationRunning, iouSettled, checkMarkScale]); return ( diff --git a/src/components/SettlementButton/AnimatedSettlementButton.tsx b/src/components/SettlementButton/AnimatedSettlementButton.tsx index 5de528d741a2..65c2fd2f493b 100644 --- a/src/components/SettlementButton/AnimatedSettlementButton.tsx +++ b/src/components/SettlementButton/AnimatedSettlementButton.tsx @@ -23,20 +23,20 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is const height = useSharedValue(variables.componentSizeNormal); const buttonMarginTop = useSharedValue(styles.expenseAndReportPreviewTextButtonContainer.gap); const buttonStyles = useAnimatedStyle(() => ({ - transform: [{scale: buttonScale.value}], - opacity: buttonOpacity.value, + transform: [{scale: buttonScale.get()}], + opacity: buttonOpacity.get(), })); const paymentCompleteTextStyles = useAnimatedStyle(() => ({ - transform: [{scale: paymentCompleteTextScale.value}], - opacity: paymentCompleteTextOpacity.value, + transform: [{scale: paymentCompleteTextScale.get()}], + opacity: paymentCompleteTextOpacity.get(), position: 'absolute', alignSelf: 'center', })); const containerStyles = useAnimatedStyle(() => ({ - height: height.value, + height: height.get(), justifyContent: 'center', overflow: 'hidden', - marginTop: buttonMarginTop.value, + marginTop: buttonMarginTop.get(), })); const buttonDisabledStyle = isPaidAnimationRunning ? { @@ -46,13 +46,12 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is : undefined; const resetAnimation = useCallback(() => { - // eslint-disable-next-line react-compiler/react-compiler - buttonScale.value = 1; - buttonOpacity.value = 1; - paymentCompleteTextScale.value = 0; - paymentCompleteTextOpacity.value = 1; - height.value = variables.componentSizeNormal; - buttonMarginTop.value = styles.expenseAndReportPreviewTextButtonContainer.gap; + buttonScale.set(1); + buttonOpacity.set(1); + paymentCompleteTextScale.set(0); + paymentCompleteTextOpacity.set(1); + height.set(variables.componentSizeNormal); + buttonMarginTop.set(styles.expenseAndReportPreviewTextButtonContainer.gap); }, [buttonScale, buttonOpacity, paymentCompleteTextScale, paymentCompleteTextOpacity, height, buttonMarginTop, styles.expenseAndReportPreviewTextButtonContainer.gap]); useEffect(() => { @@ -60,19 +59,20 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is resetAnimation(); return; } - // eslint-disable-next-line react-compiler/react-compiler - buttonScale.value = withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}); - buttonOpacity.value = withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}); - paymentCompleteTextScale.value = withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION}); + buttonScale.set(withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); + buttonOpacity.set(withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); + paymentCompleteTextScale.set(withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION})); // Wait for the above animation + 1s delay before hiding the component const totalDelay = CONST.ANIMATION_PAID_DURATION + CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY; - height.value = withDelay( - totalDelay, - withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()), + height.set( + withDelay( + totalDelay, + withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()), + ), ); - buttonMarginTop.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); - paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})); + buttonMarginTop.set(withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}))); + paymentCompleteTextOpacity.set(withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}))); }, [isPaidAnimationRunning, onAnimationFinish, buttonOpacity, buttonScale, height, paymentCompleteTextOpacity, paymentCompleteTextScale, buttonMarginTop, resetAnimation]); return ( diff --git a/src/components/SplashScreenHider/index.native.tsx b/src/components/SplashScreenHider/index.native.tsx index 7c579519c926..d41770fb7f52 100644 --- a/src/components/SplashScreenHider/index.native.tsx +++ b/src/components/SplashScreenHider/index.native.tsx @@ -17,10 +17,10 @@ function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashS const scale = useSharedValue(1); const opacityStyle = useAnimatedStyle(() => ({ - opacity: opacity.value, + opacity: opacity.get(), })); const scaleStyle = useAnimatedStyle(() => ({ - transform: [{scale: scale.value}], + transform: [{scale: scale.get()}], })); const hideHasBeenCalled = useRef(false); @@ -34,19 +34,22 @@ function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashS hideHasBeenCalled.current = true; BootSplash.hide().then(() => { - // eslint-disable-next-line react-compiler/react-compiler - scale.value = withTiming(0, { - duration: 200, - easing: Easing.back(2), - }); + scale.set( + withTiming(0, { + duration: 200, + easing: Easing.back(2), + }), + ); - opacity.value = withTiming( - 0, - { - duration: 250, - easing: Easing.out(Easing.ease), - }, - () => runOnJS(onHide)(), + opacity.set( + withTiming( + 0, + { + duration: 250, + easing: Easing.out(Easing.ease), + }, + () => runOnJS(onHide)(), + ), ); }); }, [opacity, scale, onHide]); diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 012537b75108..7fd26a177b0c 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -78,7 +78,7 @@ function BaseVideoPlayer({ const [controlStatusState, setControlStatusState] = useState(controlsStatus); const controlsOpacity = useSharedValue(1); const controlsAnimatedStyle = useAnimatedStyle(() => ({ - opacity: controlsOpacity.value, + opacity: controlsOpacity.get(), })); const videoPlayerRef = useRef(null); @@ -106,8 +106,7 @@ function BaseVideoPlayer({ }, [isCurrentlyURLSet, isPlaying, pauseVideo, playVideo, updateCurrentlyPlayingURL, url, videoResumeTryNumberRef]); const hideControl = useCallback(() => { - // eslint-disable-next-line react-compiler/react-compiler - controlsOpacity.value = withTiming(0, {duration: 500}, () => runOnJS(setControlStatusState)(CONST.VIDEO_PLAYER.CONTROLS_STATUS.HIDE)); + controlsOpacity.set(withTiming(0, {duration: 500}, () => runOnJS(setControlStatusState)(CONST.VIDEO_PLAYER.CONTROLS_STATUS.HIDE))); }, [controlsOpacity]); const debouncedHideControl = useMemo(() => debounce(hideControl, 1500), [hideControl]); @@ -144,7 +143,7 @@ function BaseVideoPlayer({ return; } setControlStatusState(CONST.VIDEO_PLAYER.CONTROLS_STATUS.SHOW); - controlsOpacity.value = 1; + controlsOpacity.set(1); }, [controlStatusState, controlsOpacity, hideControl]); const showPopoverMenu = (event?: GestureResponderEvent | KeyboardEvent) => { diff --git a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx index 5d1ea0d85d0b..69273c1444e8 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx +++ b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx @@ -30,13 +30,12 @@ function ProgressBar({duration, position, seekPosition}: ProgressBarProps) { const wasVideoPlayingOnCheck = useSharedValue(false); const onCheckVideoPlaying = (isPlaying: boolean) => { - // eslint-disable-next-line react-compiler/react-compiler - wasVideoPlayingOnCheck.value = isPlaying; + wasVideoPlayingOnCheck.set(isPlaying); }; const progressBarInteraction = (event: GestureUpdateEvent | GestureStateChangeEvent) => { const progress = getProgress(event.x, sliderWidth); - progressWidth.value = progress; + progressWidth.set(progress); runOnJS(seekPosition)((progress * duration) / 100); }; @@ -56,7 +55,7 @@ function ProgressBar({duration, position, seekPosition}: ProgressBarProps) { }) .onFinalize(() => { runOnJS(setIsSliderPressed)(false); - if (!wasVideoPlayingOnCheck.value) { + if (!wasVideoPlayingOnCheck.get()) { return; } runOnJS(playVideo)(); @@ -66,10 +65,10 @@ function ProgressBar({duration, position, seekPosition}: ProgressBarProps) { if (isSliderPressed) { return; } - progressWidth.value = getProgress(position, duration); + progressWidth.set(getProgress(position, duration)); }, [duration, isSliderPressed, position, progressWidth]); - const progressBarStyle: ViewStyle = useAnimatedStyle(() => ({width: `${progressWidth.value}%`})); + const progressBarStyle: ViewStyle = useAnimatedStyle(() => ({width: `${progressWidth.get()}%`})); return ( diff --git a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx index 751bd1b1df26..a6e44607ea62 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx +++ b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx @@ -35,7 +35,7 @@ function VolumeButton({style, small = false}: VolumeButtonProps) { const {translate} = useLocalize(); const {updateVolume, volume} = useVolumeContext(); const [sliderHeight, setSliderHeight] = useState(1); - const [volumeIcon, setVolumeIcon] = useState({icon: getVolumeIcon(volume.value)}); + const [volumeIcon, setVolumeIcon] = useState({icon: getVolumeIcon(volume.get())}); const [isSliderBeingUsed, setIsSliderBeingUsed] = useState(false); const onSliderLayout = useCallback((event: LayoutChangeEvent) => { @@ -45,8 +45,7 @@ function VolumeButton({style, small = false}: VolumeButtonProps) { const changeVolumeOnPan = useCallback( (event: GestureStateChangeEvent | GestureUpdateEvent) => { const val = NumberUtils.roundToTwoDecimalPlaces(1 - event.y / sliderHeight); - // eslint-disable-next-line react-compiler/react-compiler - volume.value = NumberUtils.clamp(val, 0, 1); + volume.set(NumberUtils.clamp(val, 0, 1)); }, [sliderHeight, volume], ); @@ -63,15 +62,15 @@ function VolumeButton({style, small = false}: VolumeButtonProps) { runOnJS(setIsSliderBeingUsed)(false); }); - const progressBarStyle = useAnimatedStyle(() => ({height: `${volume.value * 100}%`})); + const progressBarStyle = useAnimatedStyle(() => ({height: `${volume.get() * 100}%`})); const updateIcon = useCallback((vol: number) => { setVolumeIcon({icon: getVolumeIcon(vol)}); }, []); useDerivedValue(() => { - runOnJS(updateVolume)(volume.value); - runOnJS(updateIcon)(volume.value); + runOnJS(updateVolume)(volume.get()); + runOnJS(updateIcon)(volume.get()); }, [volume]); return ( @@ -95,8 +94,8 @@ function VolumeButton({style, small = false}: VolumeButtonProps) { )} updateVolume(volume.value === 0 ? 1 : 0)} + tooltipText={volume.get() === 0 ? translate('videoPlayer.unmute') : translate('videoPlayer.mute')} + onPress={() => updateVolume(volume.get() === 0 ? 1 : 0)} src={volumeIcon.icon} small={small} shouldForceRenderingTooltipBelow diff --git a/src/components/VideoPlayerContexts/VolumeContext.tsx b/src/components/VideoPlayerContexts/VolumeContext.tsx index f22b524848de..1f7b3bf66551 100644 --- a/src/components/VideoPlayerContexts/VolumeContext.tsx +++ b/src/components/VideoPlayerContexts/VolumeContext.tsx @@ -16,8 +16,7 @@ function VolumeContextProvider({children}: ChildrenProps) { return; } currentVideoPlayerRef.current.setStatusAsync({volume: newVolume, isMuted: newVolume === 0}); - // eslint-disable-next-line react-compiler/react-compiler - volume.value = newVolume; + volume.set(newVolume); }, [currentVideoPlayerRef, volume], ); @@ -28,8 +27,8 @@ function VolumeContextProvider({children}: ChildrenProps) { if (!originalParent) { return; } - updateVolume(volume.value); - }, [originalParent, updateVolume, volume.value]); + updateVolume(volume.get()); + }, [originalParent, updateVolume, volume]); const contextValue = useMemo(() => ({updateVolume, volume}), [updateVolume, volume]); return {children}; diff --git a/src/hooks/useAnimatedHighlightStyle/index.ts b/src/hooks/useAnimatedHighlightStyle/index.ts index e17f30fc60bf..4c9099c7f1ba 100644 --- a/src/hooks/useAnimatedHighlightStyle/index.ts +++ b/src/hooks/useAnimatedHighlightStyle/index.ts @@ -66,9 +66,9 @@ export default function useAnimatedHighlightStyle({ const theme = useTheme(); const highlightBackgroundStyle = useAnimatedStyle(() => ({ - backgroundColor: interpolateColor(repeatableProgress.value, [0, 1], [backgroundColor ?? theme.appBG, highlightColor ?? theme.border]), - height: height ? interpolate(nonRepeatableProgress.value, [0, 1], [0, height]) : 'auto', - opacity: interpolate(nonRepeatableProgress.value, [0, 1], [0, 1]), + backgroundColor: interpolateColor(repeatableProgress.get(), [0, 1], [backgroundColor ?? theme.appBG, highlightColor ?? theme.border]), + height: height ? interpolate(nonRepeatableProgress.get(), [0, 1], [0, height]) : 'auto', + opacity: interpolate(nonRepeatableProgress.get(), [0, 1], [0, 1]), borderRadius, })); @@ -90,19 +90,22 @@ export default function useAnimatedHighlightStyle({ setStartHighlight(false); InteractionManager.runAfterInteractions(() => { runOnJS(() => { - nonRepeatableProgress.value = withDelay( - itemEnterDelay, - withTiming(1, {duration: itemEnterDuration, easing: Easing.inOut(Easing.ease)}, (finished) => { - if (!finished) { - return; - } - - // eslint-disable-next-line react-compiler/react-compiler - repeatableProgress.value = withSequence( - withDelay(highlightStartDelay, withTiming(1, {duration: highlightStartDuration, easing: Easing.inOut(Easing.ease)})), - withDelay(highlightEndDelay, withTiming(0, {duration: highlightEndDuration, easing: Easing.inOut(Easing.ease)})), - ); - }), + nonRepeatableProgress.set( + withDelay( + itemEnterDelay, + withTiming(1, {duration: itemEnterDuration, easing: Easing.inOut(Easing.ease)}, (finished) => { + if (!finished) { + return; + } + + repeatableProgress.set( + withSequence( + withDelay(highlightStartDelay, withTiming(1, {duration: highlightStartDuration, easing: Easing.inOut(Easing.ease)})), + withDelay(highlightEndDelay, withTiming(0, {duration: highlightEndDuration, easing: Easing.inOut(Easing.ease)})), + ), + ); + }), + ), ); })(); }); diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 38efb8eb929f..a34e8a47e0cb 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -37,7 +37,7 @@ function SearchPageBottomTab() { const scrollOffset = useSharedValue(0); const topBarOffset = useSharedValue(variables.searchHeaderHeight); const topBarAnimatedStyle = useAnimatedStyle(() => ({ - top: topBarOffset.value, + top: topBarOffset.get(), })); const scrollHandler = useAnimatedScrollHandler({ @@ -47,15 +47,14 @@ function SearchPageBottomTab() { return; } const currentOffset = contentOffset.y; - const isScrollingDown = currentOffset > scrollOffset.value; - const distanceScrolled = currentOffset - scrollOffset.value; + const isScrollingDown = currentOffset > scrollOffset.get(); + const distanceScrolled = currentOffset - scrollOffset.get(); if (isScrollingDown && contentOffset.y > TOO_CLOSE_TO_TOP_DISTANCE) { - // eslint-disable-next-line react-compiler/react-compiler - topBarOffset.value = clamp(topBarOffset.value - distanceScrolled, variables.minimalTopBarOffset, variables.searchHeaderHeight); + topBarOffset.set(clamp(topBarOffset.get() - distanceScrolled, variables.minimalTopBarOffset, variables.searchHeaderHeight)); } else if (!isScrollingDown && distanceScrolled < 0 && contentOffset.y + layoutMeasurement.height < contentSize.height - TOO_CLOSE_TO_BOTTOM_DISTANCE) { - topBarOffset.value = withTiming(variables.searchHeaderHeight, {duration: ANIMATION_DURATION_IN_MS}); + topBarOffset.set(withTiming(variables.searchHeaderHeight, {duration: ANIMATION_DURATION_IN_MS})); } - scrollOffset.value = currentOffset; + scrollOffset.set(currentOffset); }, }); @@ -113,7 +112,7 @@ function SearchPageBottomTab() { { - topBarOffset.value = withTiming(variables.searchHeaderHeight, {duration: ANIMATION_DURATION_IN_MS}); + topBarOffset.set(withTiming(variables.searchHeaderHeight, {duration: ANIMATION_DURATION_IN_MS})); }} /> diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index d277862ebf2c..56ae27de5ea1 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -35,13 +35,12 @@ function AnimatedEmptyStateBackground() { * We use x and y gyroscope velocity and add it to position offset to move background based on device movements. * Position the phone was in while entering the screen is the initial position for background image. */ - const {x, y} = animatedSensor.sensor.value; + const {x, y} = animatedSensor.sensor.get(); // The x vs y here seems wrong but is the way to make it feel right to the user - // eslint-disable-next-line react-compiler/react-compiler - xOffset.value = clamp(xOffset.value + y * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_X, IMAGE_OFFSET_X); - yOffset.value = clamp(yOffset.value - x * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_Y, IMAGE_OFFSET_Y); + xOffset.set((value) => clamp(value + y * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_X, IMAGE_OFFSET_X)); + yOffset.set((value) => clamp(value - x * CONST.ANIMATION_GYROSCOPE_VALUE, -IMAGE_OFFSET_Y, IMAGE_OFFSET_Y)); return { - transform: [{translateX: withSpring(xOffset.value)}, {translateY: withSpring(yOffset.value, {overshootClamping: true})}, {scale: 1.15}], + transform: [{translateX: withSpring(xOffset.get())}, {translateY: withSpring(yOffset.get(), {overshootClamping: true})}, {scale: 1.15}], }; }, [isReducedMotionEnabled]); diff --git a/src/pages/home/report/FloatingMessageCounter.tsx b/src/pages/home/report/FloatingMessageCounter.tsx index 0d92946e3d66..a196704d2119 100644 --- a/src/pages/home/report/FloatingMessageCounter.tsx +++ b/src/pages/home/report/FloatingMessageCounter.tsx @@ -30,13 +30,13 @@ function FloatingMessageCounter({isActive = false, onClick = () => {}}: Floating const show = useCallback(() => { 'worklet'; - translateY.value = withSpring(MARKER_ACTIVE_TRANSLATE_Y); + translateY.set(withSpring(MARKER_ACTIVE_TRANSLATE_Y)); }, [translateY]); const hide = useCallback(() => { 'worklet'; - translateY.value = withSpring(MARKER_INACTIVE_TRANSLATE_Y); + translateY.set(withSpring(MARKER_INACTIVE_TRANSLATE_Y)); }, [translateY]); useEffect(() => { @@ -49,7 +49,7 @@ function FloatingMessageCounter({isActive = false, onClick = () => {}}: Floating const wrapperStyle = useAnimatedStyle(() => ({ ...styles.floatingMessageCounterWrapper, - transform: [{translateY: translateY.value}], + transform: [{translateY: translateY.get()}], })); return ( diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 1cb70fe6c926..9042c30440f7 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -709,20 +709,19 @@ function ComposerWithSuggestions( useEffect(() => { // We use the tag to store the native ID of the text input. Later, we use it in onSelectionChange to pick up the proper text input data. + tag.set(findNodeHandle(textInputRef.current) ?? -1); + }, [tag]); - tag.value = findNodeHandle(textInputRef.current) ?? -1; - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, []); useFocusedInputHandler( { onSelectionChange: (event) => { 'worklet'; - if (event.target === tag.value) { - cursorPositionValue.value = { + if (event.target === tag.get()) { + cursorPositionValue.set({ x: event.selection.end.x, y: event.selection.end.y, - }; + }); } }, }, @@ -731,7 +730,7 @@ function ComposerWithSuggestions( const measureParentContainerAndReportCursor = useCallback( (callback: MeasureParentContainerAndCursorCallback) => { const {scrollValue} = getScrollPosition({mobileInputScrollPosition, textInputRef}); - const {x: xPosition, y: yPosition} = getCursorPosition({positionOnMobile: cursorPositionValue.value, positionOnWeb: selection}); + const {x: xPosition, y: yPosition} = getCursorPosition({positionOnMobile: cursorPositionValue.get(), positionOnWeb: selection}); measureParentContainer((x, y, width, height) => { callback({ x, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 23b059f2fda2..4673475063c8 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -342,7 +342,7 @@ function ReportActionCompose({ const handleSendMessage = useCallback(() => { 'worklet'; - const clearComposer = composerRefShared.value.clear; + const clearComposer = composerRefShared.get().clear; if (!clearComposer) { throw new Error('The composerRefShared.clear function is not set yet. This should never happen, and indicates a developer error.'); } @@ -468,10 +468,9 @@ function ReportActionCompose({ { composerRef.current = ref ?? undefined; - // eslint-disable-next-line react-compiler/react-compiler - composerRefShared.value = { + composerRefShared.set({ clear: ref?.clear, - }; + }); }} suggestionsRef={suggestionsRef} isNextModalWillOpenRef={isNextModalWillOpenRef} diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fd2dc2d44d4b..8bcf5a462d38 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -417,7 +417,7 @@ function ReportActionItemMessageEdit( const measureParentContainerAndReportCursor = useCallback( (callback: MeasureParentContainerAndCursorCallback) => { const {scrollValue} = getScrollPosition({mobileInputScrollPosition, textInputRef}); - const {x: xPosition, y: yPosition} = getCursorPosition({positionOnMobile: cursorPositionValue.value, positionOnWeb: selection}); + const {x: xPosition, y: yPosition} = getCursorPosition({positionOnMobile: cursorPositionValue.get(), positionOnWeb: selection}); measureContainer((x, y, width, height) => { callback({ x, @@ -429,25 +429,23 @@ function ReportActionItemMessageEdit( }); }); }, - [cursorPositionValue.value, measureContainer, selection], + [cursorPositionValue, measureContainer, selection], ); useEffect(() => { // We use the tag to store the native ID of the text input. Later, we use it in onSelectionChange to pick up the proper text input data. - - // eslint-disable-next-line react-compiler/react-compiler - tag.value = findNodeHandle(textInputRef.current) ?? -1; + tag.set(findNodeHandle(textInputRef.current) ?? -1); }, [tag]); useFocusedInputHandler( { onSelectionChange: (event) => { 'worklet'; - if (event.target === tag.value) { - cursorPositionValue.value = { + if (event.target === tag.get()) { + cursorPositionValue.set({ x: event.selection.end.x, y: event.selection.end.y, - }; + }); } }, }, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index d3f0c9cb496d..61c856c856cf 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -120,8 +120,8 @@ function IOURequestStepScan({ const focusIndicatorPosition = useSharedValue({x: 0, y: 0}); const cameraFocusIndicatorAnimatedStyle = useAnimatedStyle(() => ({ - opacity: focusIndicatorOpacity.value, - transform: [{translateX: focusIndicatorPosition.value.x}, {translateY: focusIndicatorPosition.value.y}, {scale: focusIndicatorScale.value}], + opacity: focusIndicatorOpacity.get(), + transform: [{translateX: focusIndicatorPosition.get().x}, {translateY: focusIndicatorPosition.get().y}, {scale: focusIndicatorScale.get()}], })); const focusCamera = (point: Point) => { @@ -143,11 +143,10 @@ function IOURequestStepScan({ .onStart((ev: {x: number; y: number}) => { const point = {x: ev.x, y: ev.y}; - // eslint-disable-next-line react-compiler/react-compiler - focusIndicatorOpacity.value = withSequence(withTiming(0.8, {duration: 250}), withDelay(1000, withTiming(0, {duration: 250}))); - focusIndicatorScale.value = 2; - focusIndicatorScale.value = withSpring(1, {damping: 10, stiffness: 200}); - focusIndicatorPosition.value = point; + focusIndicatorOpacity.set(withSequence(withTiming(0.8, {duration: 250}), withDelay(1000, withTiming(0, {duration: 250})))); + focusIndicatorScale.set(2); + focusIndicatorScale.set(withSpring(1, {damping: 10, stiffness: 200})); + focusIndicatorPosition.set(point); runOnJS(focusCamera)(point); }); diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.tsx b/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.tsx index 75e0be7e5f7c..fc28dc08fb15 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.tsx +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.tsx @@ -20,14 +20,15 @@ function BackgroundImage({width, transitionDuration, isSmallScreen = false}: Bac const isAnonymous = isAnonymousUser(); const opacity = useSharedValue(0); - const animatedStyle = useAnimatedStyle(() => ({opacity: opacity.value})); + const animatedStyle = useAnimatedStyle(() => ({opacity: opacity.get()})); // This sets the opacity animation for the background image once it has loaded. function setOpacityAnimation() { - // eslint-disable-next-line react-compiler/react-compiler - opacity.value = withTiming(1, { - duration: CONST.MICROSECONDS_PER_MS, - easing: Easing.ease, - }); + opacity.set( + withTiming(1, { + duration: CONST.MICROSECONDS_PER_MS, + easing: Easing.ease, + }), + ); } useEffect(() => { From 70253160c8bc659506327794b52ee853bfab2a08 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Nov 2024 15:03:51 +0100 Subject: [PATCH 06/14] Refactor LoadingBar to use setter methods for animated values --- src/components/LoadingBar.tsx | 75 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/components/LoadingBar.tsx b/src/components/LoadingBar.tsx index 163ffe2aa66b..5650c3fe38ac 100644 --- a/src/components/LoadingBar.tsx +++ b/src/components/LoadingBar.tsx @@ -17,44 +17,49 @@ function LoadingBar({shouldShow}: LoadingBarProps) { useEffect(() => { if (shouldShow) { - // eslint-disable-next-line react-compiler/react-compiler - isVisible.value = true; - left.value = 0; - width.value = 0; - opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); - left.value = withDelay( - CONST.ANIMATED_PROGRESS_BAR_DELAY, - withRepeat( - withSequence( - withTiming(0, {duration: 0}), - withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + isVisible.set(true); + left.set(0); + width.set(0); + opacity.set(withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION})); + left.set( + withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, ), - -1, - false, ), ); - width.value = withDelay( - CONST.ANIMATED_PROGRESS_BAR_DELAY, - withRepeat( - withSequence( - withTiming(0, {duration: 0}), - withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + width.set( + withDelay( + CONST.ANIMATED_PROGRESS_BAR_DELAY, + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, ), - -1, - false, ), ); - } else if (isVisible.value) { - opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { - runOnJS(() => { - isVisible.value = false; - cancelAnimation(left); - cancelAnimation(width); - }); - }); + } else if (isVisible.get()) { + opacity.set( + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { + runOnJS(() => { + isVisible.set(false); + cancelAnimation(left); + cancelAnimation(width); + }); + }), + ); } // we want to update only when shouldShow changes // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -62,20 +67,20 @@ function LoadingBar({shouldShow}: LoadingBarProps) { const animatedIndicatorStyle = useAnimatedStyle(() => { return { - left: `${left.value}%`, - width: `${width.value}%`, + left: `${left.get()}%`, + width: `${width.get()}%`, }; }); const animatedContainerStyle = useAnimatedStyle(() => { return { - opacity: opacity.value, + opacity: opacity.get(), }; }); return ( - {isVisible.value ? : null} + {isVisible.get() ? : null} ); } From 69e7aa9ac0073b4de30efbbde2b27bbc5513b60e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 15:51:09 +0100 Subject: [PATCH 07/14] Update patches --- ...animated+3.16.1+003+include-missing-header.patch | 13 ------------- ...t-native-reanimated+3.16.2+001+hybrid-app.patch} | 0 ...animated+3.16.2+002+dontWhitelistTextProp.patch} | 0 3 files changed, 13 deletions(-) delete mode 100644 patches/react-native-reanimated+3.16.1+003+include-missing-header.patch rename patches/{react-native-reanimated+3.16.1+001+hybrid-app.patch => react-native-reanimated+3.16.2+001+hybrid-app.patch} (100%) rename patches/{react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch => react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch} (100%) diff --git a/patches/react-native-reanimated+3.16.1+003+include-missing-header.patch b/patches/react-native-reanimated+3.16.1+003+include-missing-header.patch deleted file mode 100644 index 80244991a890..000000000000 --- a/patches/react-native-reanimated+3.16.1+003+include-missing-header.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp b/node_modules/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp -index 475ec7a..832fb06 100644 ---- a/node_modules/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp -+++ b/node_modules/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp -@@ -32,6 +32,8 @@ - - #ifdef RCT_NEW_ARCH_ENABLED - #include -+#include -+#include - #endif // RCT_NEW_ARCH_ENABLED - - // Standard `__cplusplus` macro reference: \ No newline at end of file diff --git a/patches/react-native-reanimated+3.16.1+001+hybrid-app.patch b/patches/react-native-reanimated+3.16.2+001+hybrid-app.patch similarity index 100% rename from patches/react-native-reanimated+3.16.1+001+hybrid-app.patch rename to patches/react-native-reanimated+3.16.2+001+hybrid-app.patch diff --git a/patches/react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch b/patches/react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch similarity index 100% rename from patches/react-native-reanimated+3.16.1+002+dontWhitelistTextProp.patch rename to patches/react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch From 3ae2acee7bbb189b36a35e706a0b80ba42b42b9f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 15:53:05 +0100 Subject: [PATCH 08/14] Bump podfile --- ios/Podfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 01aefab63378..2f596dc65d40 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2503,7 +2503,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated (3.16.1): + - RNReanimated (3.16.2): - DoubleConversion - glog - hermes-engine @@ -2523,10 +2523,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.16.1) - - RNReanimated/worklets (= 3.16.1) + - RNReanimated/reanimated (= 3.16.2) + - RNReanimated/worklets (= 3.16.2) - Yoga - - RNReanimated/reanimated (3.16.1): + - RNReanimated/reanimated (3.16.2): - DoubleConversion - glog - hermes-engine @@ -2546,9 +2546,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 3.16.1) + - RNReanimated/reanimated/apple (= 3.16.2) - Yoga - - RNReanimated/reanimated/apple (3.16.1): + - RNReanimated/reanimated/apple (3.16.2): - DoubleConversion - glog - hermes-engine @@ -2569,7 +2569,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated/worklets (3.16.1): + - RNReanimated/worklets (3.16.2): - DoubleConversion - glog - hermes-engine @@ -3291,7 +3291,7 @@ SPEC CHECKSUMS: rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4 RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28 RNReactNativeHapticFeedback: 73756a3477a5a622fa16862a3ab0d0fc5e5edff5 - RNReanimated: 2d728bad3a69119be89c3431ee0ccda026ecffdc + RNReanimated: 3aad205448de01d8b780118a429995ca6c438e51 RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2 RNShare: bd4fe9b95d1ee89a200778cc0753ebe650154bb0 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 From 3e5e1f3625f5e413acf8a0bd6d208ab8d1629044 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 21 Nov 2024 12:34:13 +0100 Subject: [PATCH 09/14] bum .value to .get() and .set() in reanimated sharedValue --- .../Attachments/AttachmentCarousel/Pager/index.tsx | 2 +- src/components/Attachments/AttachmentCarousel/index.tsx | 8 ++++---- .../AttachmentViewPdf/BaseAttachmentViewPdf.tsx | 2 +- src/components/AvatarCropModal/ImageCropView.tsx | 8 ++++---- src/components/MultiGestureCanvas/index.tsx | 4 ++-- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- src/components/VideoPlayer/BaseVideoPlayer.tsx | 2 +- .../VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx index 6863d839a616..68d6591c0df6 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx @@ -105,7 +105,7 @@ function AttachmentCarouselPager( ); const animatedProps = useAnimatedProps(() => ({ - scrollEnabled: isScrollEnabled.value, + scrollEnabled: isScrollEnabled.get(), })); /** diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 3a7540f65055..f169416f1812 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -253,18 +253,18 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi Gesture.Pan() .enabled(canUseTouchScreen) .onUpdate(({translationX}) => { - if (!isScrollEnabled.value) { + if (!isScrollEnabled.get()) { return; } if (translationX !== 0) { - isPagerScrolling.value = true; + isPagerScrolling.set(true); } scrollTo(scrollRef, page * cellWidth - translationX, 0, false); }) .onEnd(({translationX, velocityX}) => { - if (!isScrollEnabled.value) { + if (!isScrollEnabled.get()) { return; } @@ -281,7 +281,7 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi newIndex = Math.min(attachments.length - 1, Math.max(0, page + delta)); } - isPagerScrolling.value = false; + isPagerScrolling.set(false); scrollTo(scrollRef, newIndex * cellWidth, 0, true); }) // eslint-disable-next-line react-compiler/react-compiler diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx index 7c1d350fc307..4da481809b46 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx @@ -58,7 +58,7 @@ function BaseAttachmentViewPdf({ onPressProp(event); } - if (attachmentCarouselPagerContext !== null && isScrollEnabled?.value) { + if (attachmentCarouselPagerContext !== null && isScrollEnabled?.get()) { attachmentCarouselPagerContext.onTap(); } }, diff --git a/src/components/AvatarCropModal/ImageCropView.tsx b/src/components/AvatarCropModal/ImageCropView.tsx index 5bfb0d5f6557..1f11986a99f9 100644 --- a/src/components/AvatarCropModal/ImageCropView.tsx +++ b/src/components/AvatarCropModal/ImageCropView.tsx @@ -59,12 +59,12 @@ function ImageCropView({imageUri = '', containerSize = 0, panGesture = Gesture.P const imageStyle = useAnimatedStyle(() => { 'worklet'; - const height = originalImageHeight.value; - const width = originalImageWidth.value; + const height = originalImageHeight.get(); + const width = originalImageWidth.get(); const aspectRatio = height > width ? height / width : width / height; - const rotate = interpolate(rotation.value, [0, 360], [0, 360]); + const rotate = interpolate(rotation.get(), [0, 360], [0, 360]); return { - transform: [{translateX: translateX.value}, {translateY: translateY.value}, {scale: scale.value * aspectRatio}, {rotate: `${rotate}deg`}], + transform: [{translateX: translateX.get()}, {translateY: translateY.get()}, {scale: scale.get() * aspectRatio}, {rotate: `${rotate}deg`}], }; }, [originalImageHeight, originalImageWidth, rotation, translateX, translateY, scale]); diff --git a/src/components/MultiGestureCanvas/index.tsx b/src/components/MultiGestureCanvas/index.tsx index 6c925f568be8..7e5c5d2365d8 100644 --- a/src/components/MultiGestureCanvas/index.tsx +++ b/src/components/MultiGestureCanvas/index.tsx @@ -116,13 +116,13 @@ function MultiGestureCanvas({ const offsetY = useSharedValue(0); useAnimatedReaction( - () => isSwipingDownToClose.value, + () => isSwipingDownToClose.get(), (current) => { if (!isUsedInCarousel) { return; } // eslint-disable-next-line react-compiler/react-compiler, no-param-reassign - isPagerScrollEnabled.value = !current; + isPagerScrollEnabled.set(!current); }, ); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 52092a5662ef..61a05360d2a4 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -145,7 +145,7 @@ function ReportPreview({ const thumbsUpScale = useSharedValue(isApproved ? 1 : 0.25); const thumbsUpStyle = useAnimatedStyle(() => ({ ...styles.defaultCheckmarkWrapper, - transform: [{scale: thumbsUpScale.value}], + transform: [{scale: thumbsUpScale.get()}], })); const moneyRequestComment = action?.childLastMoneyRequestComment ?? ''; @@ -468,7 +468,7 @@ function ReportPreview({ return; } - thumbsUpScale.value = withSpring(1, {duration: 200}); + thumbsUpScale.set(withSpring(1, {duration: 200})); }, [isApproved, thumbsUpScale]); return ( diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index ce397f4c2ec7..140d3f5eccc4 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -206,7 +206,7 @@ function BaseVideoPlayer({ if (status.didJustFinish) { setIsEnded(status.didJustFinish && !status.isLooping); setControlStatusState(CONST.VIDEO_PLAYER.CONTROLS_STATUS.SHOW); - controlsOpacity.value = 1; + controlsOpacity.set(1); } else if (status.isPlaying && isEnded) { setIsEnded(false); } diff --git a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx index 3d0e02da92b5..97e837658a3e 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx +++ b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx @@ -56,7 +56,7 @@ function ProgressBar({duration, position, seekPosition}: ProgressBarProps) { }) .onFinalize(() => { setIsSliderPressed(false); - if (!wasVideoPlayingOnCheck.value) { + if (!wasVideoPlayingOnCheck.get()) { return; } playVideo(); From 83a30caa4a12d5cd0cf3cdd5abc5f5ae38446ced Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 10:31:22 +0100 Subject: [PATCH 10/14] Bump patches --- ....patch => react-native-reanimated+3.16.3+001+hybrid-app.patch} | 0 ...eact-native-reanimated+3.16.3+002+dontWhitelistTextProp.patch} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native-reanimated+3.16.2+001+hybrid-app.patch => react-native-reanimated+3.16.3+001+hybrid-app.patch} (100%) rename patches/{react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch => react-native-reanimated+3.16.3+002+dontWhitelistTextProp.patch} (100%) diff --git a/patches/react-native-reanimated+3.16.2+001+hybrid-app.patch b/patches/react-native-reanimated+3.16.3+001+hybrid-app.patch similarity index 100% rename from patches/react-native-reanimated+3.16.2+001+hybrid-app.patch rename to patches/react-native-reanimated+3.16.3+001+hybrid-app.patch diff --git a/patches/react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch b/patches/react-native-reanimated+3.16.3+002+dontWhitelistTextProp.patch similarity index 100% rename from patches/react-native-reanimated+3.16.2+002+dontWhitelistTextProp.patch rename to patches/react-native-reanimated+3.16.3+002+dontWhitelistTextProp.patch From 640bceec4284ba2827680388cd642559f934c91d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 10:32:04 +0100 Subject: [PATCH 11/14] Bump pods --- ios/Podfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4f970b21c3f5..1f1c87db2176 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2503,7 +2503,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated (3.16.2): + - RNReanimated (3.16.3): - DoubleConversion - glog - hermes-engine @@ -2523,10 +2523,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.16.2) - - RNReanimated/worklets (= 3.16.2) + - RNReanimated/reanimated (= 3.16.3) + - RNReanimated/worklets (= 3.16.3) - Yoga - - RNReanimated/reanimated (3.16.2): + - RNReanimated/reanimated (3.16.3): - DoubleConversion - glog - hermes-engine @@ -2546,9 +2546,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 3.16.2) + - RNReanimated/reanimated/apple (= 3.16.3) - Yoga - - RNReanimated/reanimated/apple (3.16.2): + - RNReanimated/reanimated/apple (3.16.3): - DoubleConversion - glog - hermes-engine @@ -2569,7 +2569,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated/worklets (3.16.2): + - RNReanimated/worklets (3.16.3): - DoubleConversion - glog - hermes-engine @@ -3291,7 +3291,7 @@ SPEC CHECKSUMS: rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4 RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28 RNReactNativeHapticFeedback: 73756a3477a5a622fa16862a3ab0d0fc5e5edff5 - RNReanimated: 3aad205448de01d8b780118a429995ca6c438e51 + RNReanimated: 03ba2447d5a7789e2843df2ee05108d93b6441d6 RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2 RNShare: bd4fe9b95d1ee89a200778cc0753ebe650154bb0 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 From 4d2a3b7626d4f7addea68dfe92098a77f0d452ac Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 10:58:21 +0100 Subject: [PATCH 12/14] Migrate all leftover shared values to new api --- src/components/AvatarCropModal/Slider.tsx | 2 +- .../KeyboardAvoidingView/index.android.tsx | 52 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/components/AvatarCropModal/Slider.tsx b/src/components/AvatarCropModal/Slider.tsx index bac581da25e6..2f8a8fb6ef53 100644 --- a/src/components/AvatarCropModal/Slider.tsx +++ b/src/components/AvatarCropModal/Slider.tsx @@ -34,7 +34,7 @@ function Slider({sliderValue, gestureCallbacks}: SliderProps) { 'worklet'; return { - transform: [{translateX: sliderValue.value}], + transform: [{translateX: sliderValue.get()}], }; }); diff --git a/src/components/KeyboardAvoidingView/index.android.tsx b/src/components/KeyboardAvoidingView/index.android.tsx index e8eb79d18bbd..4d758511d7ad 100644 --- a/src/components/KeyboardAvoidingView/index.android.tsx +++ b/src/components/KeyboardAvoidingView/index.android.tsx @@ -9,8 +9,8 @@ const useKeyboardAnimation = () => { const {reanimated} = useKeyboardContext(); // calculate it only once on mount, to avoid `SharedValue` reads during a render - const [initialHeight] = useState(() => -reanimated.height.value); - const [initialProgress] = useState(() => reanimated.progress.value); + const [initialHeight] = useState(() => -reanimated.height.get()); + const [initialProgress] = useState(() => reanimated.progress.get()); const heightWhenOpened = useSharedValue(initialHeight); const height = useSharedValue(initialHeight); @@ -22,22 +22,20 @@ const useKeyboardAnimation = () => { onStart: (e) => { 'worklet'; - progress.value = e.progress; - height.value = e.height; + progress.set(e.progress); + height.set(e.height); if (e.height > 0) { - // eslint-disable-next-line react-compiler/react-compiler - isClosed.value = false; - heightWhenOpened.value = e.height; + isClosed.set(false); + heightWhenOpened.set(e.height); } }, onEnd: (e) => { 'worklet'; - isClosed.value = e.height === 0; - - height.value = e.height; - progress.value = e.progress; + isClosed.set(e.height === 0); + height.set(e.height); + progress.set(e.progress); }, }, [], @@ -63,7 +61,7 @@ const defaultLayout: LayoutRectangle = { const KeyboardAvoidingView = forwardRef>( ({behavior, children, contentContainerStyle, enabled = true, keyboardVerticalOffset = 0, style, onLayout: onLayoutProps, ...props}, ref) => { const initialFrame = useSharedValue(null); - const frame = useDerivedValue(() => initialFrame.value ?? defaultLayout); + const frame = useDerivedValue(() => initialFrame.get() ?? defaultLayout); const keyboard = useKeyboardAnimation(); const {height: screenHeight} = useSafeAreaFrame(); @@ -71,21 +69,21 @@ const KeyboardAvoidingView = forwardRef { 'worklet'; - const keyboardY = screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset; + const keyboardY = screenHeight - keyboard.heightWhenOpened.get() - keyboardVerticalOffset; - return Math.max(frame.value.y + frame.value.height - keyboardY, 0); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [screenHeight, keyboardVerticalOffset]); + return Math.max(frame.get().y + frame.get().height - keyboardY, 0); + }, [screenHeight, keyboard.heightWhenOpened, keyboardVerticalOffset, frame]); - const onLayoutWorklet = useCallback((layout: LayoutRectangle) => { - 'worklet'; + const onLayoutWorklet = useCallback( + (layout: LayoutRectangle) => { + 'worklet'; - if (keyboard.isClosed.value || initialFrame.value === null) { - // eslint-disable-next-line react-compiler/react-compiler - initialFrame.value = layout; - } - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, []); + if (keyboard.isClosed.get() || initialFrame.get() === null) { + initialFrame.set(layout); + } + }, + [initialFrame, keyboard.isClosed], + ); const onLayout = useCallback>( (e) => { runOnUI(onLayoutWorklet)(e.nativeEvent.layout); @@ -95,14 +93,14 @@ const KeyboardAvoidingView = forwardRef { - const bottom = interpolate(keyboard.progress.value, [0, 1], [0, relativeKeyboardHeight()]); + const bottom = interpolate(keyboard.progress.get(), [0, 1], [0, relativeKeyboardHeight()]); const bottomHeight = enabled ? bottom : 0; switch (behavior) { case 'height': - if (!keyboard.isClosed.value) { + if (!keyboard.isClosed.get()) { return { - height: frame.value.height - bottomHeight, + height: frame.get().height - bottomHeight, flex: 0, }; } From f631cb96b84a1469ed9ae2d9fcafb211cfba6536 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 11:24:03 +0100 Subject: [PATCH 13/14] Add manual depracation to shared value property --- src/types/modules/react-native-reanimated.d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/types/modules/react-native-reanimated.d.ts diff --git a/src/types/modules/react-native-reanimated.d.ts b/src/types/modules/react-native-reanimated.d.ts new file mode 100644 index 000000000000..7786a904786f --- /dev/null +++ b/src/types/modules/react-native-reanimated.d.ts @@ -0,0 +1,12 @@ +import type {SharedValue as ReanimatedSharedValue} from 'react-native-reanimated'; + +// Remove this augmentation once `value` property is deprecated in reanimated +declare module 'react-native-reanimated' { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface SharedValue extends ReanimatedSharedValue { + /** + * @deprecated Use the new `.get()` and `.set(value)` methods instead. + */ + value: Value; + } +} From 954560c214bc90f9223a4fff3c24a3a549371400 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Tue, 26 Nov 2024 12:55:24 +0100 Subject: [PATCH 14/14] apply Tomek suggestions --- src/components/MultiGestureCanvas/usePanGesture.ts | 2 +- src/components/MultiGestureCanvas/usePinchGesture.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/MultiGestureCanvas/usePanGesture.ts b/src/components/MultiGestureCanvas/usePanGesture.ts index bd29a18cc46c..f2f33aa87e7e 100644 --- a/src/components/MultiGestureCanvas/usePanGesture.ts +++ b/src/components/MultiGestureCanvas/usePanGesture.ts @@ -204,7 +204,7 @@ const usePanGesture = ({ } } - if (previousTouchValue === null) { + if (previousTouch.get() === null) { previousTouch.set({ x: evt.allTouches.at(0)?.x ?? 0, y: evt.allTouches.at(0)?.y ?? 0, diff --git a/src/components/MultiGestureCanvas/usePinchGesture.ts b/src/components/MultiGestureCanvas/usePinchGesture.ts index 7c517ad2f30b..7f5cecc4e949 100644 --- a/src/components/MultiGestureCanvas/usePinchGesture.ts +++ b/src/components/MultiGestureCanvas/usePinchGesture.ts @@ -128,9 +128,8 @@ const usePinchGesture = ({ } const newZoomScale = pinchScale.get() * evt.scale; - const zoomScaleValue = zoomScale.get(); // Limit the zoom scale to zoom range including bounce range - if (zoomScaleValue >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScaleValue <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) { + if (zoomScale.get() >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScale.get() <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) { zoomScale.set(newZoomScale); currentPinchScale.set(evt.scale); @@ -144,7 +143,7 @@ const usePinchGesture = ({ // If the zoom scale is within the zoom range, we perform the regular pinch translation // Otherwise it means that we are "overzoomed" or "underzoomed", so we need to bounce back - if (zoomScaleValue >= zoomRange.min && zoomScaleValue <= zoomRange.max) { + if (zoomScale.get() >= zoomRange.min && zoomScale.get() <= zoomRange.max) { pinchTranslateX.set(newPinchTranslateX); pinchTranslateY.set(newPinchTranslateY); } else {