diff --git a/src/Splash.tsx b/src/Splash.tsx index 5a2b18445e..a52b8837d1 100644 --- a/src/Splash.tsx +++ b/src/Splash.tsx @@ -81,40 +81,40 @@ export function Splash(props: React.PropsWithChildren) { return { transform: [ { - scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'), + scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), }, { scale: interpolate( - outroLogo.value, + outroLogo.get(), [0, 0.08, 1], [1, 0.8, 500], 'clamp', ), }, ], - opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } }) const bottomLogoAnimation = useAnimatedStyle(() => { return { - opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } }) const reducedLogoAnimation = useAnimatedStyle(() => { return { transform: [ { - scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'), + scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), }, ], - opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), + opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } }) const logoWrapperAnimation = useAnimatedStyle(() => { return { opacity: interpolate( - outroAppOpacity.value, + outroAppOpacity.get(), [0, 0.1, 0.2, 1], [1, 1, 0, 0], 'clamp', @@ -126,11 +126,11 @@ export function Splash(props: React.PropsWithChildren) { return { transform: [ { - scale: interpolate(outroApp.value, [0, 1], [1.1, 1], 'clamp'), + scale: interpolate(outroApp.get(), [0, 1], [1.1, 1], 'clamp'), }, ], opacity: interpolate( - outroAppOpacity.value, + outroAppOpacity.get(), [0, 0.1, 0.2, 1], [0, 0, 1, 1], 'clamp', @@ -146,29 +146,37 @@ export function Splash(props: React.PropsWithChildren) { if (isReady) { SplashScreen.hideAsync() .then(() => { - intro.value = withTiming( - 1, - {duration: 400, easing: Easing.out(Easing.cubic)}, - async () => { - // set these values to check animation at specific point - // outroLogo.value = 0.1 - // outroApp.value = 0.1 - outroLogo.value = withTiming( - 1, - {duration: 1200, easing: Easing.in(Easing.cubic)}, - () => { - runOnJS(onFinish)() - }, - ) - outroApp.value = withTiming(1, { - duration: 1200, - easing: Easing.inOut(Easing.cubic), - }) - outroAppOpacity.value = withTiming(1, { - duration: 1200, - easing: Easing.in(Easing.cubic), - }) - }, + intro.set(() => + withTiming( + 1, + {duration: 400, easing: Easing.out(Easing.cubic)}, + async () => { + // set these values to check animation at specific point + // outroLogo.set(0.1) + // outroApp.set(0.1) + outroLogo.set(() => + withTiming( + 1, + {duration: 1200, easing: Easing.in(Easing.cubic)}, + () => { + runOnJS(onFinish)() + }, + ), + ) + outroApp.set(() => + withTiming(1, { + duration: 1200, + easing: Easing.inOut(Easing.cubic), + }), + ) + outroAppOpacity.set(() => + withTiming(1, { + duration: 1200, + easing: Easing.in(Easing.cubic), + }), + ) + }, + ), ) }) .catch(() => {}) diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx index e0b3be6373..1495549127 100644 --- a/src/components/Loader.tsx +++ b/src/components/Loader.tsx @@ -17,13 +17,12 @@ export function Loader(props: Props) { const rotation = useSharedValue(0) const animatedStyles = useAnimatedStyle(() => ({ - transform: [{rotate: rotation.value + 'deg'}], + transform: [{rotate: rotation.get() + 'deg'}], })) React.useEffect(() => { - rotation.value = withRepeat( - withTiming(360, {duration: 500, easing: Easing.linear}), - -1, + rotation.set(() => + withRepeat(withTiming(360, {duration: 500, easing: Easing.linear}), -1), ) }, [rotation]) diff --git a/src/components/ProgressGuide/Toast.tsx b/src/components/ProgressGuide/Toast.tsx index 69e0082606..b26c718f87 100644 --- a/src/components/ProgressGuide/Toast.tsx +++ b/src/components/ProgressGuide/Toast.tsx @@ -55,13 +55,15 @@ export const ProgressGuideToast = React.forwardRef< // animate the opacity then set isOpen to false when done const setIsntOpen = () => setIsOpen(false) - opacity.value = withTiming( - 0, - { - duration: 400, - easing: Easing.out(Easing.cubic), - }, - () => runOnJS(setIsntOpen)(), + opacity.set(() => + withTiming( + 0, + { + duration: 400, + easing: Easing.out(Easing.cubic), + }, + () => runOnJS(setIsntOpen)(), + ), ) }, [setIsOpen, opacity]) @@ -71,20 +73,24 @@ export const ProgressGuideToast = React.forwardRef< // animate the vertical translation, the opacity, and the checkmark const playCheckmark = () => animatedCheckRef.current?.play() - opacity.value = 0 - opacity.value = withTiming( - 1, - { - duration: 100, + opacity.set(0) + opacity.set(() => + withTiming( + 1, + { + duration: 100, + easing: Easing.out(Easing.cubic), + }, + () => runOnJS(playCheckmark)(), + ), + ) + translateY.set(0) + translateY.set(() => + withTiming(insets.top + 10, { + duration: 500, easing: Easing.out(Easing.cubic), - }, - () => runOnJS(playCheckmark)(), + }), ) - translateY.value = 0 - translateY.value = withTiming(insets.top + 10, { - duration: 500, - easing: Easing.out(Easing.cubic), - }) // start the countdown timer to autoclose timeoutRef.current = setTimeout(close, visibleDuration || 5e3) @@ -114,8 +120,8 @@ export const ProgressGuideToast = React.forwardRef< }, [winDim.width]) const animatedStyle = useAnimatedStyle(() => ({ - transform: [{translateY: translateY.value}], - opacity: opacity.value, + transform: [{translateY: translateY.get()}], + opacity: opacity.get(), })) return ( diff --git a/src/components/anim/AnimatedCheck.tsx b/src/components/anim/AnimatedCheck.tsx index 7fdfc14cfa..60407274ed 100644 --- a/src/components/anim/AnimatedCheck.tsx +++ b/src/components/anim/AnimatedCheck.tsx @@ -32,21 +32,25 @@ export const AnimatedCheck = React.forwardRef< const checkAnim = useSharedValue(0) const circleAnimatedProps = useAnimatedProps(() => ({ - strokeDashoffset: 166 - circleAnim.value * 166, + strokeDashoffset: 166 - circleAnim.get() * 166, })) const checkAnimatedProps = useAnimatedProps(() => ({ - strokeDashoffset: 48 - 48 * checkAnim.value, + strokeDashoffset: 48 - 48 * checkAnim.get(), })) const play = React.useCallback( (cb?: () => void) => { - circleAnim.value = 0 - checkAnim.value = 0 + circleAnim.set(0) + checkAnim.set(0) - circleAnim.value = withTiming(1, {duration: 500, easing: Easing.linear}) - checkAnim.value = withDelay( - 500, - withTiming(1, {duration: 300, easing: Easing.linear}, cb), + circleAnim.set(() => + withTiming(1, {duration: 500, easing: Easing.linear}), + ) + checkAnim.set(() => + withDelay( + 500, + withTiming(1, {duration: 300, easing: Easing.linear}, cb), + ), ) }, [circleAnim, checkAnim], diff --git a/src/components/dms/ActionsWrapper.tsx b/src/components/dms/ActionsWrapper.tsx index b77516e7b7..a087fed3fb 100644 --- a/src/components/dms/ActionsWrapper.tsx +++ b/src/components/dms/ActionsWrapper.tsx @@ -34,7 +34,7 @@ export function ActionsWrapper({ const scale = useSharedValue(1) const animatedStyle = useAnimatedStyle(() => ({ - transform: [{scale: scale.value}], + transform: [{scale: scale.get()}], })) const open = React.useCallback(() => { @@ -46,7 +46,7 @@ export function ActionsWrapper({ const shrink = React.useCallback(() => { 'worklet' cancelAnimation(scale) - scale.value = withTiming(1, {duration: 200}) + scale.set(() => withTiming(1, {duration: 200})) }, [scale]) const doubleTapGesture = Gesture.Tap() @@ -58,11 +58,13 @@ export function ActionsWrapper({ const pressAndHoldGesture = Gesture.LongPress() .onStart(() => { 'worklet' - scale.value = withTiming(1.05, {duration: 200}, finished => { - if (!finished) return - runOnJS(open)() - shrink() - }) + scale.set(() => + withTiming(1.05, {duration: 200}, finished => { + if (!finished) return + runOnJS(open)() + shrink() + }), + ) }) .onTouchesUp(shrink) .onTouchesMove(shrink) diff --git a/src/components/dms/ChatEmptyPill.tsx b/src/components/dms/ChatEmptyPill.tsx index ffd022f560..042c3ad76b 100644 --- a/src/components/dms/ChatEmptyPill.tsx +++ b/src/components/dms/ChatEmptyPill.tsx @@ -42,12 +42,12 @@ export function ChatEmptyPill() { const onPressIn = React.useCallback(() => { if (isWeb) return - scale.value = withTiming(1.075, {duration: 100}) + scale.set(() => withTiming(1.075, {duration: 100})) }, [scale]) const onPressOut = React.useCallback(() => { if (isWeb) return - scale.value = withTiming(1, {duration: 100}) + scale.set(() => withTiming(1, {duration: 100})) }, [scale]) const onPress = React.useCallback(() => { @@ -61,7 +61,7 @@ export function ChatEmptyPill() { }, [playHaptic, prompts.length]) const animatedStyle = useAnimatedStyle(() => ({ - transform: [{scale: scale.value}], + transform: [{scale: scale.get()}], })) return ( diff --git a/src/components/dms/NewMessagesPill.tsx b/src/components/dms/NewMessagesPill.tsx index 2f7ff8f4b0..e3bc0c1f86 100644 --- a/src/components/dms/NewMessagesPill.tsx +++ b/src/components/dms/NewMessagesPill.tsx @@ -35,12 +35,12 @@ export function NewMessagesPill({ const onPressIn = React.useCallback(() => { if (isWeb) return - scale.value = withTiming(1.075, {duration: 100}) + scale.set(() => withTiming(1.075, {duration: 100})) }, [scale]) const onPressOut = React.useCallback(() => { if (isWeb) return - scale.value = withTiming(1, {duration: 100}) + scale.set(() => withTiming(1, {duration: 100})) }, [scale]) const onPress = React.useCallback(() => { @@ -49,7 +49,7 @@ export function NewMessagesPill({ }, [onPressInner, playHaptic]) const animatedStyle = useAnimatedStyle(() => ({ - transform: [{scale: scale.value}], + transform: [{scale: scale.get()}], })) return ( diff --git a/src/lib/custom-animations/GestureActionView.tsx b/src/lib/custom-animations/GestureActionView.tsx index 79e9db8a91..ba6952a81d 100644 --- a/src/lib/custom-animations/GestureActionView.tsx +++ b/src/lib/custom-animations/GestureActionView.tsx @@ -61,7 +61,7 @@ export function GestureActionView({ const clampedTransX = useDerivedValue(() => { const min = actions.leftFirst ? -MAX_WIDTH : 0 const max = actions.rightFirst ? MAX_WIDTH : 0 - return clamp(transX.value, min, max) + return clamp(transX.get(), min, max) }) const iconScale = useSharedValue(1) @@ -75,21 +75,23 @@ export function GestureActionView({ return } - iconScale.value = withSequence( - withTiming(1.2, {duration: 175}), - withTiming(1, {duration: 100}), + iconScale.set(() => + withSequence( + withTiming(1.2, {duration: 175}), + withTiming(1, {duration: 100}), + ), ) } useAnimatedReaction( () => transX, () => { - if (transX.value === 0) { + if (transX.get() === 0) { runOnJS(setActiveAction)(null) - } else if (transX.value < 0) { + } else if (transX.get() < 0) { if ( actions.leftSecond && - transX.value <= -actions.leftSecond.threshold + transX.get() <= -actions.leftSecond.threshold ) { if (activeAction !== 'leftSecond') { runOnJS(setActiveAction)('leftSecond') @@ -97,10 +99,10 @@ export function GestureActionView({ } else if (activeAction !== 'leftFirst') { runOnJS(setActiveAction)('leftFirst') } - } else if (transX.value > 0) { + } else if (transX.get() > 0) { if ( actions.rightSecond && - transX.value > actions.rightSecond.threshold + transX.get() > actions.rightSecond.threshold ) { if (activeAction !== 'rightSecond') { runOnJS(setActiveAction)('rightSecond') @@ -119,44 +121,44 @@ export function GestureActionView({ .activeOffsetY([-200, 200]) .onStart(() => { 'worklet' - isActive.value = true + isActive.set(true) }) .onChange(e => { 'worklet' - transX.value = e.translationX + transX.set(e.translationX) if (e.translationX < 0) { // Left side if (actions.leftSecond) { if ( e.translationX <= -actions.leftSecond.threshold && - !hitSecond.value + !hitSecond.get() ) { runPopAnimation() runOnJS(haptic)() - hitSecond.value = true + hitSecond.set(true) } else if ( - hitSecond.value && + hitSecond.get() && e.translationX > -actions.leftSecond.threshold ) { runPopAnimation() - hitSecond.value = false + hitSecond.set(false) } } - if (!hitSecond.value && actions.leftFirst) { + if (!hitSecond.get() && actions.leftFirst) { if ( e.translationX <= -actions.leftFirst.threshold && - !hitFirst.value + !hitFirst.get() ) { runPopAnimation() runOnJS(haptic)() - hitFirst.value = true + hitFirst.set(true) } else if ( - hitFirst.value && + hitFirst.get() && e.translationX > -actions.leftFirst.threshold ) { - hitFirst.value = false + hitFirst.set(false) } } } else if (e.translationX > 0) { @@ -164,33 +166,33 @@ export function GestureActionView({ if (actions.rightSecond) { if ( e.translationX >= actions.rightSecond.threshold && - !hitSecond.value + !hitSecond.get() ) { runPopAnimation() runOnJS(haptic)() - hitSecond.value = true + hitSecond.set(true) } else if ( - hitSecond.value && + hitSecond.get() && e.translationX < actions.rightSecond.threshold ) { runPopAnimation() - hitSecond.value = false + hitSecond.set(false) } } - if (!hitSecond.value && actions.rightFirst) { + if (!hitSecond.get() && actions.rightFirst) { if ( e.translationX >= actions.rightFirst.threshold && - !hitFirst.value + !hitFirst.get() ) { runPopAnimation() runOnJS(haptic)() - hitFirst.value = true + hitFirst.set(true) } else if ( - hitFirst.value && + hitFirst.get() && e.translationX < actions.rightFirst.threshold ) { - hitFirst.value = false + hitFirst.set(false) } } } @@ -198,29 +200,29 @@ export function GestureActionView({ .onEnd(e => { 'worklet' if (e.translationX < 0) { - if (hitSecond.value && actions.leftSecond) { + if (hitSecond.get() && actions.leftSecond) { runOnJS(actions.leftSecond.action)() - } else if (hitFirst.value && actions.leftFirst) { + } else if (hitFirst.get() && actions.leftFirst) { runOnJS(actions.leftFirst.action)() } } else if (e.translationX > 0) { - if (hitSecond.value && actions.rightSecond) { + if (hitSecond.get() && actions.rightSecond) { runOnJS(actions.rightSecond.action)() - } else if (hitSecond.value && actions.rightFirst) { + } else if (hitSecond.get() && actions.rightFirst) { runOnJS(actions.rightFirst.action)() } } - transX.value = withTiming(0, {duration: 200}) - hitFirst.value = false - hitSecond.value = false - isActive.value = false + transX.set(() => withTiming(0, {duration: 200})) + hitFirst.set(false) + hitSecond.set(false) + isActive.set(false) }) const composedGesture = Gesture.Simultaneous(panGesture) const animatedSliderStyle = useAnimatedStyle(() => { return { - transform: [{translateX: clampedTransX.value}], + transform: [{translateX: clampedTransX.get()}], } }) @@ -274,7 +276,7 @@ export function GestureActionView({ const animatedBackgroundStyle = useAnimatedStyle(() => { return { backgroundColor: interpolateColor( - clampedTransX.value, + clampedTransX.get(), interpolation.inputRange, // @ts-expect-error - Weird type expected by reanimated, but this is okay interpolation.outputRange, @@ -283,10 +285,10 @@ export function GestureActionView({ }) const animatedIconStyle = useAnimatedStyle(() => { - const absTransX = Math.abs(clampedTransX.value) + const absTransX = Math.abs(clampedTransX.get()) return { opacity: interpolate(absTransX, [0, 75], [0.15, 1]), - transform: [{scale: iconScale.value}], + transform: [{scale: iconScale.get()}], } }) diff --git a/src/lib/custom-animations/PressableScale.tsx b/src/lib/custom-animations/PressableScale.tsx index 4737b9ea32..1e776546d3 100644 --- a/src/lib/custom-animations/PressableScale.tsx +++ b/src/lib/custom-animations/PressableScale.tsx @@ -2,7 +2,6 @@ import React from 'react' import {Pressable, PressableProps, StyleProp, ViewStyle} from 'react-native' import Animated, { cancelAnimation, - runOnJS, useAnimatedStyle, useReducedMotion, useSharedValue, @@ -32,27 +31,25 @@ export function PressableScale({ const scale = useSharedValue(1) const animatedStyle = useAnimatedStyle(() => ({ - transform: [{scale: scale.value}], + transform: [{scale: scale.get()}], })) return ( { - 'worklet' if (onPressIn) { - runOnJS(onPressIn)(e) + onPressIn(e) } cancelAnimation(scale) - scale.value = withTiming(targetScale, {duration: 100}) + scale.set(() => withTiming(targetScale, {duration: 100})) }} onPressOut={e => { - 'worklet' if (onPressOut) { - runOnJS(onPressOut)(e) + onPressOut(e) } cancelAnimation(scale) - scale.value = withTiming(1, {duration: 100}) + scale.set(() => withTiming(1, {duration: 100})) }} style={[!reducedMotion && animatedStyle, style]} {...rest}> diff --git a/src/lib/hooks/useMinimalShellTransform.ts b/src/lib/hooks/useMinimalShellTransform.ts index 6787767553..6f16fa0f95 100644 --- a/src/lib/hooks/useMinimalShellTransform.ts +++ b/src/lib/hooks/useMinimalShellTransform.ts @@ -10,15 +10,16 @@ export function useMinimalShellHeaderTransform() { const {headerHeight} = useShellLayout() const headerTransform = useAnimatedStyle(() => { + const headerModeValue = headerMode.get() return { - pointerEvents: headerMode.value === 0 ? 'auto' : 'none', - opacity: Math.pow(1 - headerMode.value, 2), + pointerEvents: headerModeValue === 0 ? 'auto' : 'none', + opacity: Math.pow(1 - headerModeValue, 2), transform: [ { translateY: interpolate( - headerMode.value, + headerModeValue, [0, 1], - [0, -headerHeight.value], + [0, -headerHeight.get()], ), }, ], @@ -33,15 +34,16 @@ export function useMinimalShellFooterTransform() { const {footerHeight} = useShellLayout() const footerTransform = useAnimatedStyle(() => { + const footerModeValue = footerMode.get() return { - pointerEvents: footerMode.value === 0 ? 'auto' : 'none', - opacity: Math.pow(1 - footerMode.value, 2), + pointerEvents: footerModeValue === 0 ? 'auto' : 'none', + opacity: Math.pow(1 - footerModeValue, 2), transform: [ { translateY: interpolate( - footerMode.value, + footerModeValue, [0, 1], - [0, footerHeight.value], + [0, footerHeight.get()], ), }, ], @@ -58,7 +60,7 @@ export function useMinimalShellFabTransform() { return { transform: [ { - translateY: interpolate(footerMode.value, [0, 1], [-44, 0]), + translateY: interpolate(footerMode.get(), [0, 1], [-44, 0]), }, ], } diff --git a/src/screens/Messages/components/MessageInput.tsx b/src/screens/Messages/components/MessageInput.tsx index 8edad6272e..85509211b1 100644 --- a/src/screens/Messages/components/MessageInput.tsx +++ b/src/screens/Messages/components/MessageInput.tsx @@ -108,22 +108,22 @@ export function MessageInput({ const measurement = measure(inputRef) if (!measurement) return - const max = windowHeight - -keyboardHeight.value - topInset - 150 + const max = windowHeight - -keyboardHeight.get() - topInset - 150 const availableSpace = max - measurement.height - maxHeight.value = max - isInputScrollable.value = availableSpace < 30 + maxHeight.set(max) + isInputScrollable.set(availableSpace < 30) }, }, [windowHeight, topInset], ) const animatedStyle = useAnimatedStyle(() => ({ - maxHeight: maxHeight.value, + maxHeight: maxHeight.get(), })) const animatedProps = useAnimatedProps(() => ({ - scrollEnabled: isInputScrollable.value, + scrollEnabled: isInputScrollable.get(), })) return ( diff --git a/src/screens/Messages/components/MessagesList.tsx b/src/screens/Messages/components/MessagesList.tsx index 9db4f07b60..9f67929a34 100644 --- a/src/screens/Messages/components/MessagesList.tsx +++ b/src/screens/Messages/components/MessagesList.tsx @@ -145,7 +145,7 @@ export function MessagesList({ (_: number, height: number) => { // Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the // previous off whenever we add new content to the previous offset whenever we add new content to the list. - if (isWeb && isAtTop.value && hasScrolled) { + if (isWeb && isAtTop.get() && hasScrolled) { flatListRef.current?.scrollToOffset({ offset: height - prevContentHeight.current, animated: false, @@ -153,7 +153,7 @@ export function MessagesList({ } // This number _must_ be the height of the MaybeLoader component - if (height > 50 && isAtBottom.value) { + if (height > 50 && isAtBottom.get()) { // If the size of the content is changing by more than the height of the screen, then we don't // want to scroll further than the start of all the new content. Since we are storing the previous offset, // we can just scroll the user to that offset and add a little bit of padding. We'll also show the pill @@ -161,7 +161,7 @@ export function MessagesList({ if ( didBackground.current && hasScrolled && - height - prevContentHeight.current > layoutHeight.value - 50 && + height - prevContentHeight.current > layoutHeight.get() - 50 && convoState.items.length - prevItemCount.current > 1 ) { flatListRef.current?.scrollToOffset({ @@ -209,7 +209,7 @@ export function MessagesList({ ) const onStartReached = useCallback(() => { - if (hasScrolled && prevContentHeight.current > layoutHeight.value) { + if (hasScrolled && prevContentHeight.current > layoutHeight.get()) { convoState.fetchMessageHistory() } }, [convoState, hasScrolled, layoutHeight]) @@ -217,18 +217,18 @@ export function MessagesList({ const onScroll = React.useCallback( (e: ReanimatedScrollEvent) => { 'worklet' - layoutHeight.value = e.layoutMeasurement.height + layoutHeight.set(e.layoutMeasurement.height) const bottomOffset = e.contentOffset.y + e.layoutMeasurement.height // Most apps have a little bit of space the user can scroll past while still automatically scrolling ot the bottom // when a new message is added, hence the 100 pixel offset - isAtBottom.value = e.contentSize.height - 100 < bottomOffset - isAtTop.value = e.contentOffset.y <= 1 + isAtBottom.set(e.contentSize.height - 100 < bottomOffset) + isAtTop.set(e.contentOffset.y <= 1) if ( newMessagesPill.show && (e.contentOffset.y > newMessagesPill.startContentOffset + 200 || - isAtBottom.value) + isAtBottom.get()) ) { runOnJS(setNewMessagesPill)({ show: false, @@ -256,28 +256,28 @@ export function MessagesList({ // Immediate updates - like opening the emoji picker - will have a duration of zero. In those cases, we should // just update the height here instead of having the `onMove` event do it (that event will not fire!) if (e.duration === 0) { - layoutScrollWithoutAnimation.value = true - keyboardHeight.value = e.height + layoutScrollWithoutAnimation.set(true) + keyboardHeight.set(e.height) } else { - keyboardIsOpening.value = true + keyboardIsOpening.set(true) } }, onMove: e => { 'worklet' - keyboardHeight.value = e.height + keyboardHeight.set(e.height) if (e.height > bottomOffset) { scrollTo(flatListRef, 0, 1e7, false) } }, onEnd: () => { 'worklet' - keyboardIsOpening.value = false + keyboardIsOpening.set(false) }, }) const animatedListStyle = useAnimatedStyle(() => ({ marginBottom: - keyboardHeight.value > bottomOffset ? keyboardHeight.value : bottomOffset, + keyboardHeight.get() > bottomOffset ? keyboardHeight.get() : bottomOffset, })) // -- Message sending @@ -363,13 +363,13 @@ export function MessagesList({ // -- List layout changes (opening emoji keyboard, etc.) const onListLayout = React.useCallback( (e: LayoutChangeEvent) => { - layoutHeight.value = e.nativeEvent.layout.height + layoutHeight.set(e.nativeEvent.layout.height) - if (isWeb || !keyboardIsOpening.value) { + if (isWeb || !keyboardIsOpening.get()) { flatListRef.current?.scrollToEnd({ - animated: !layoutScrollWithoutAnimation.value, + animated: !layoutScrollWithoutAnimation.get(), }) - layoutScrollWithoutAnimation.value = false + layoutScrollWithoutAnimation.set(false) } }, [ diff --git a/src/screens/Profile/Header/GrowableAvatar.tsx b/src/screens/Profile/Header/GrowableAvatar.tsx index 20ac14892d..dab69f9552 100644 --- a/src/screens/Profile/Header/GrowableAvatar.tsx +++ b/src/screens/Profile/Header/GrowableAvatar.tsx @@ -45,7 +45,7 @@ function GrowableAvatarInner({ const animatedStyle = useAnimatedStyle(() => ({ transform: [ { - scale: interpolate(scrollY.value, [-150, 0], [1.2, 1], { + scale: interpolate(scrollY.get(), [-150, 0], [1.2, 1], { extrapolateRight: Extrapolation.CLAMP, }), }, diff --git a/src/screens/Profile/Header/GrowableBanner.tsx b/src/screens/Profile/Header/GrowableBanner.tsx index 144b7cd2de..7f5a3cd6e4 100644 --- a/src/screens/Profile/Header/GrowableBanner.tsx +++ b/src/screens/Profile/Header/GrowableBanner.tsx @@ -66,7 +66,7 @@ function GrowableBannerInner({ const animatedStyle = useAnimatedStyle(() => ({ transform: [ { - scale: interpolate(scrollY.value, [-150, 0], [2, 1], { + scale: interpolate(scrollY.get(), [-150, 0], [2, 1], { extrapolateRight: Extrapolation.CLAMP, }), }, @@ -76,7 +76,7 @@ function GrowableBannerInner({ const animatedBlurViewProps = useAnimatedProps(() => { return { intensity: interpolate( - scrollY.value, + scrollY.get(), [-300, -65, -15], [50, 40, 0], Extrapolation.CLAMP, @@ -85,16 +85,17 @@ function GrowableBannerInner({ }) const animatedSpinnerStyle = useAnimatedStyle(() => { + const scrollYValue = scrollY.get() return { - display: scrollY.value < 0 ? 'flex' : 'none', + display: scrollYValue < 0 ? 'flex' : 'none', opacity: interpolate( - scrollY.value, + scrollYValue, [-60, -15], [1, 0], Extrapolation.CLAMP, ), transform: [ - {translateY: interpolate(scrollY.value, [-150, 0], [-75, 0])}, + {translateY: interpolate(scrollYValue, [-150, 0], [-75, 0])}, {rotate: '90deg'}, ], } @@ -103,7 +104,7 @@ function GrowableBannerInner({ const animatedBackButtonStyle = useAnimatedStyle(() => ({ transform: [ { - translateY: interpolate(scrollY.value, [-150, 60], [-150, 60], { + translateY: interpolate(scrollY.get(), [-150, 60], [-150, 60], { extrapolateRight: Extrapolation.CLAMP, }), }, @@ -168,7 +169,7 @@ function useShouldAnimateSpinner({ const stickyIsOverscrolled = useStickyToggle(isOverscrolled, 10) useAnimatedReaction( - () => scrollY.value < -5, + () => scrollY.get() < -5, (value, prevValue) => { if (value !== prevValue) { runOnJS(setIsOverscrolled)(value) diff --git a/src/state/shell/minimal-mode.tsx b/src/state/shell/minimal-mode.tsx index 3f1cebdf0f..00547ee3e0 100644 --- a/src/state/shell/minimal-mode.tsx +++ b/src/state/shell/minimal-mode.tsx @@ -44,13 +44,17 @@ export function Provider({children}: React.PropsWithChildren<{}>) { 'worklet' // Cancel any existing animation cancelAnimation(headerMode) - headerMode.value = withSpring(v ? 1 : 0, { - overshootClamping: true, - }) + headerMode.set(() => + withSpring(v ? 1 : 0, { + overshootClamping: true, + }), + ) cancelAnimation(footerMode) - footerMode.value = withSpring(v ? 1 : 0, { - overshootClamping: true, - }) + footerMode.set(() => + withSpring(v ? 1 : 0, { + overshootClamping: true, + }), + ) }, [headerMode, footerMode], ) diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index a581cb79e2..1b2692c51a 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -1265,12 +1265,12 @@ function useScrollTracker({ const contentHeight = useSharedValue(0) const hasScrolledToTop = useDerivedValue(() => - withTiming(contentOffset.value === 0 ? 1 : 0), + withTiming(contentOffset.get() === 0 ? 1 : 0), ) const hasScrolledToBottom = useDerivedValue(() => withTiming( - contentHeight.value - contentOffset.value - 5 <= scrollViewHeight.value + contentHeight.get() - contentOffset.get() - 5 <= scrollViewHeight.get() ? 1 : 0, ), @@ -1288,11 +1288,11 @@ function useScrollTracker({ }) => { 'worklet' if (typeof newContentHeight === 'number') - contentHeight.value = Math.floor(newContentHeight) + contentHeight.set(Math.floor(newContentHeight)) if (typeof newContentOffset === 'number') - contentOffset.value = Math.floor(newContentOffset) + contentOffset.set(Math.floor(newContentOffset)) if (typeof newScrollViewHeight === 'number') - scrollViewHeight.value = Math.floor(newScrollViewHeight) + scrollViewHeight.set(Math.floor(newScrollViewHeight)) }, [contentHeight, contentOffset, scrollViewHeight], ) @@ -1308,21 +1308,22 @@ function useScrollTracker({ }, }) - const onScrollViewContentSizeChange = useCallback( - (_width: number, height: number) => { - if (stickyBottom && height > contentHeight.value) { + const onScrollViewContentSizeChangeUIThread = useCallback( + (newContentHeight: number) => { + 'worklet' + const oldContentHeight = contentHeight.get() + let shouldScrollToBottom = false + if (stickyBottom && newContentHeight > oldContentHeight) { const isFairlyCloseToBottom = - contentHeight.value - contentOffset.value - 100 <= - scrollViewHeight.value + oldContentHeight - contentOffset.get() - 100 <= scrollViewHeight.get() if (isFairlyCloseToBottom) { - runOnUI(() => { - scrollTo(scrollViewRef, 0, contentHeight.value, true) - })() + shouldScrollToBottom = true } } - showHideBottomBorder({ - newContentHeight: height, - }) + showHideBottomBorder({newContentHeight}) + if (shouldScrollToBottom) { + scrollTo(scrollViewRef, 0, newContentHeight, true) + } }, [ showHideBottomBorder, @@ -1334,6 +1335,13 @@ function useScrollTracker({ ], ) + const onScrollViewContentSizeChange = useCallback( + (_width: number, height: number) => { + runOnUI(onScrollViewContentSizeChangeUIThread)(height) + }, + [onScrollViewContentSizeChangeUIThread], + ) + const onScrollViewLayout = useCallback( (evt: LayoutChangeEvent) => { showHideBottomBorder({ @@ -1347,7 +1355,7 @@ function useScrollTracker({ return { borderBottomWidth: StyleSheet.hairlineWidth, borderColor: interpolateColor( - hasScrolledToTop.value, + hasScrolledToTop.get(), [0, 1], [t.atoms.border_contrast_medium.borderColor, 'transparent'], ), @@ -1357,7 +1365,7 @@ function useScrollTracker({ return { borderTopWidth: StyleSheet.hairlineWidth, borderColor: interpolateColor( - hasScrolledToBottom.value, + hasScrolledToBottom.get(), [0, 1], [t.atoms.border_contrast_medium.borderColor, 'transparent'], ), @@ -1602,7 +1610,7 @@ function VideoUploadToolbar({state}: {state: VideoState}) { const animatedStyle = useAnimatedStyle(() => { return { - transform: [{rotateZ: `${rotate.value}deg`}], + transform: [{rotateZ: `${rotate.get()}deg`}], } }) diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx index 7049306eba..bdfc2c7ff3 100644 --- a/src/view/com/home/HomeHeaderLayout.web.tsx +++ b/src/view/com/home/HomeHeaderLayout.web.tsx @@ -93,7 +93,7 @@ function HomeHeaderLayoutDesktopAndTablet({ {tabBarAnchor} { - headerHeight.value = e.nativeEvent.layout.height + headerHeight.set(e.nativeEvent.layout.height) }} style={[ t.atoms.bg, diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index f5397d7172..98253ad748 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -43,7 +43,7 @@ export function HomeHeaderLayoutMobile({ { - headerHeight.value = e.nativeEvent.layout.height + headerHeight.set(e.nativeEvent.layout.height) }}> diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 260787d2f3..7aca8721b9 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -87,11 +87,11 @@ const ImageItem = ({ // Note: DO NOT move any logic reading animated values outside this function. useAnimatedReaction( () => { - if (pinchScale.value !== 1) { + if (pinchScale.get() !== 1) { // We're currently pinching. return true } - const [, , committedScale] = readTransform(committedTransform.value) + const [, , committedScale] = readTransform(committedTransform.get()) if (committedScale !== 1) { // We started from a pinched in state. return true @@ -147,10 +147,10 @@ const ImageItem = ({ .onStart(e => { 'worklet' const screenSize = measureSafeArea() - pinchOrigin.value = { + pinchOrigin.set({ x: e.focalX - screenSize.width / 2, y: e.focalY - screenSize.height / 2, - } + }) }) .onChange(e => { 'worklet' @@ -160,7 +160,7 @@ const ImageItem = ({ } // Don't let the picture zoom in so close that it gets blurry. // Also, like in stock Android apps, don't let the user zoom out further than 1:1. - const [, , committedScale] = readTransform(committedTransform.value) + const [, , committedScale] = readTransform(committedTransform.get()) const maxCommittedScale = Math.max( MIN_SCREEN_ZOOM, (imageDimensions.width / screenSize.width) * MAX_ORIGINAL_IMAGE_ZOOM, @@ -171,20 +171,21 @@ const ImageItem = ({ Math.max(minPinchScale, e.scale), maxPinchScale, ) - pinchScale.value = nextPinchScale + pinchScale.set(nextPinchScale) // Zooming out close to the corner could push us out of bounds, which we don't want on Android. // Calculate where we'll end up so we know how much to translate back to stay in bounds. const t = createTransform() - prependPan(t, panTranslation.value) - prependPinch(t, nextPinchScale, pinchOrigin.value, pinchTranslation.value) - prependTransform(t, committedTransform.value) + prependPan(t, panTranslation.get()) + prependPinch(t, nextPinchScale, pinchOrigin.get(), pinchTranslation.get()) + prependTransform(t, committedTransform.get()) const [dx, dy] = getExtraTranslationToStayInBounds(t, screenSize) if (dx !== 0 || dy !== 0) { - pinchTranslation.value = { - x: pinchTranslation.value.x + dx, - y: pinchTranslation.value.y + dy, - } + const pt = pinchTranslation.get() + pinchTranslation.set({ + x: pt.x + dx, + y: pt.y + dy, + }) } }) .onEnd(() => { @@ -193,18 +194,18 @@ const ImageItem = ({ let t = createTransform() prependPinch( t, - pinchScale.value, - pinchOrigin.value, - pinchTranslation.value, + pinchScale.get(), + pinchOrigin.get(), + pinchTranslation.get(), ) - prependTransform(t, committedTransform.value) + prependTransform(t, committedTransform.get()) applyRounding(t) - committedTransform.value = t + committedTransform.set(t) // Reset just the pinch. - pinchScale.value = 1 - pinchOrigin.value = {x: 0, y: 0} - pinchTranslation.value = {x: 0, y: 0} + pinchScale.set(1) + pinchOrigin.set({x: 0, y: 0}) + pinchTranslation.set({x: 0, y: 0}) }) const pan = Gesture.Pan() @@ -223,29 +224,29 @@ const ImageItem = ({ prependPan(t, nextPanTranslation) prependPinch( t, - pinchScale.value, - pinchOrigin.value, - pinchTranslation.value, + pinchScale.get(), + pinchOrigin.get(), + pinchTranslation.get(), ) - prependTransform(t, committedTransform.value) + prependTransform(t, committedTransform.get()) // Prevent panning from going out of bounds. const [dx, dy] = getExtraTranslationToStayInBounds(t, screenSize) nextPanTranslation.x += dx nextPanTranslation.y += dy - panTranslation.value = nextPanTranslation + panTranslation.set(nextPanTranslation) }) .onEnd(() => { 'worklet' // Commit just the pan. let t = createTransform() - prependPan(t, panTranslation.value) - prependTransform(t, committedTransform.value) + prependPan(t, panTranslation.get()) + prependTransform(t, committedTransform.get()) applyRounding(t) - committedTransform.value = t + committedTransform.set(t) // Reset just the pan. - panTranslation.value = {x: 0, y: 0} + panTranslation.set({x: 0, y: 0}) }) const singleTap = Gesture.Tap().onEnd(() => { @@ -261,11 +262,11 @@ const ImageItem = ({ if (!imageDimensions || !imageAspect) { return } - const [, , committedScale] = readTransform(committedTransform.value) + const [, , committedScale] = readTransform(committedTransform.get()) if (committedScale !== 1) { // Go back to 1:1 using the identity vector. let t = createTransform() - committedTransform.value = withClampedSpring(t) + committedTransform.set(withClampedSpring(t)) return } @@ -299,7 +300,7 @@ const ImageItem = ({ ) const finalTransform = createTransform() prependPinch(finalTransform, scale, origin, {x: dx, y: dy}) - committedTransform.value = withClampedSpring(finalTransform) + committedTransform.set(withClampedSpring(finalTransform)) }) const composedGesture = isScrollViewBeingDragged @@ -313,13 +314,13 @@ const ImageItem = ({ ) const containerStyle = useAnimatedStyle(() => { - const {scaleAndMoveTransform, isHidden} = transforms.value + const {scaleAndMoveTransform, isHidden} = transforms.get() // Apply the active adjustments on top of the committed transform before the gestures. // This is matrix multiplication, so operations are applied in the reverse order. let t = createTransform() - prependPan(t, panTranslation.value) - prependPinch(t, pinchScale.value, pinchOrigin.value, pinchTranslation.value) - prependTransform(t, committedTransform.value) + prependPan(t, panTranslation.get()) + prependPinch(t, pinchScale.get(), pinchOrigin.get(), pinchTranslation.get()) + prependTransform(t, committedTransform.get()) const [translateX, translateY, scale] = readTransform(t) const manipulationTransform = [ {translateX}, @@ -338,7 +339,7 @@ const ImageItem = ({ }) const imageCropStyle = useAnimatedStyle(() => { - const {cropFrameTransform} = transforms.value + const {cropFrameTransform} = transforms.get() return { flex: 1, overflow: 'hidden', @@ -347,7 +348,7 @@ const ImageItem = ({ }) const imageStyle = useAnimatedStyle(() => { - const {cropContentTransform} = transforms.value + const {cropContentTransform} = transforms.get() return { flex: 1, transform: cropContentTransform, @@ -359,7 +360,7 @@ const ImageItem = ({ const [hasLoaded, setHasLoaded] = useState(false) useAnimatedReaction( () => { - return transforms.value.isResting && !hasLoaded + return transforms.get().isResting && !hasLoaded }, (show, prevShow) => { if (show && !prevShow) { diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index f06a59ed60..c7be4f3e36 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -148,7 +148,7 @@ const ImageItem = ({ ) const containerStyle = useAnimatedStyle(() => { - const {scaleAndMoveTransform, isHidden} = transforms.value + const {scaleAndMoveTransform, isHidden} = transforms.get() return { flex: 1, transform: scaleAndMoveTransform, @@ -158,7 +158,7 @@ const ImageItem = ({ const imageCropStyle = useAnimatedStyle(() => { const screenSize = measureSafeArea() - const {cropFrameTransform} = transforms.value + const {cropFrameTransform} = transforms.get() return { overflow: 'hidden', transform: cropFrameTransform, @@ -171,7 +171,7 @@ const ImageItem = ({ }) const imageStyle = useAnimatedStyle(() => { - const {cropContentTransform} = transforms.value + const {cropContentTransform} = transforms.get() return { transform: cropContentTransform, width: '100%', @@ -184,7 +184,7 @@ const ImageItem = ({ const [hasLoaded, setHasLoaded] = useState(false) useAnimatedReaction( () => { - return transforms.value.isResting && !hasLoaded + return transforms.get().isResting && !hasLoaded }, (show, prevShow) => { if (show && !prevShow) { diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index 37a9aa1400..ee9f831bcf 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -109,18 +109,22 @@ export default function ImageViewRoot({ // https://github.com/software-mansion/react-native-reanimated/issues/6677 requestAnimationFrame(() => { - openProgress.value = canAnimate ? withClampedSpring(1, SLOW_SPRING) : 1 + openProgress.set(() => + canAnimate ? withClampedSpring(1, SLOW_SPRING) : 1, + ) }) return () => { // https://github.com/software-mansion/react-native-reanimated/issues/6677 requestAnimationFrame(() => { - openProgress.value = canAnimate ? withClampedSpring(0, SLOW_SPRING) : 0 + openProgress.set(() => + canAnimate ? withClampedSpring(0, SLOW_SPRING) : 0, + ) }) } }, [nextLightbox, openProgress]) useAnimatedReaction( - () => openProgress.value === 0, + () => openProgress.get() === 0, (isGone, wasGone) => { if (isGone && !wasGone) { runOnJS(setActiveLightbox)(null) @@ -130,7 +134,7 @@ export default function ImageViewRoot({ const onFlyAway = React.useCallback(() => { 'worklet' - openProgress.value = 0 + openProgress.set(0) runOnJS(onRequestClose)() }, [onRequestClose, openProgress]) @@ -187,7 +191,7 @@ function ImageView({ const isFlyingAway = useSharedValue(false) const containerStyle = useAnimatedStyle(() => { - if (openProgress.value < 1 || isFlyingAway.value) { + if (openProgress.get() < 1 || isFlyingAway.get()) { return {pointerEvents: 'none'} } return {pointerEvents: 'auto'} @@ -196,11 +200,12 @@ function ImageView({ const backdropStyle = useAnimatedStyle(() => { const screenSize = measure(safeAreaRef) let opacity = 1 - if (openProgress.value < 1) { - opacity = Math.sqrt(openProgress.value) + const openProgressValue = openProgress.get() + if (openProgressValue < 1) { + opacity = Math.sqrt(openProgressValue) } else if (screenSize) { const dragProgress = Math.min( - Math.abs(dismissSwipeTranslateY.value) / (screenSize.height / 2), + Math.abs(dismissSwipeTranslateY.get()) / (screenSize.height / 2), 1, ) opacity -= dragProgress @@ -212,11 +217,11 @@ function ImageView({ }) const animatedHeaderStyle = useAnimatedStyle(() => { - const show = showControls && dismissSwipeTranslateY.value === 0 + const show = showControls && dismissSwipeTranslateY.get() === 0 return { pointerEvents: show ? 'box-none' : 'none', opacity: withClampedSpring( - show && openProgress.value === 1 ? 1 : 0, + show && openProgress.get() === 1 ? 1 : 0, FAST_SPRING, ), transform: [ @@ -227,12 +232,12 @@ function ImageView({ } }) const animatedFooterStyle = useAnimatedStyle(() => { - const show = showControls && dismissSwipeTranslateY.value === 0 + const show = showControls && dismissSwipeTranslateY.get() === 0 return { flexGrow: 1, pointerEvents: show ? 'box-none' : 'none', opacity: withClampedSpring( - show && openProgress.value === 1 ? 1 : 0, + show && openProgress.get() === 1 ? 1 : 0, FAST_SPRING, ), transform: [ @@ -259,7 +264,7 @@ function ImageView({ const screenSize = measure(safeAreaRef) return ( !screenSize || - Math.abs(dismissSwipeTranslateY.value) > screenSize.height + Math.abs(dismissSwipeTranslateY.get()) > screenSize.height ) }, (isOut, wasOut) => { @@ -397,10 +402,11 @@ function LightboxImage({ const transforms = useDerivedValue(() => { 'worklet' const safeArea = measureSafeArea() + const openProgressValue = openProgress.get() const dismissTranslateY = - isActive && openProgress.value === 1 ? dismissSwipeTranslateY.value : 0 + isActive && openProgressValue === 1 ? dismissSwipeTranslateY.get() : 0 - if (openProgress.value === 0 && isFlyingAway.value) { + if (openProgressValue === 0 && isFlyingAway.get()) { return { isHidden: true, isResting: false, @@ -410,9 +416,9 @@ function LightboxImage({ } } - if (isActive && thumbRect && imageAspect && openProgress.value < 1) { + if (isActive && thumbRect && imageAspect && openProgressValue < 1) { return interpolateTransform( - openProgress.value, + openProgressValue, thumbRect, safeArea, imageAspect, @@ -434,33 +440,37 @@ function LightboxImage({ .maxPointers(1) .onUpdate(e => { 'worklet' - if (openProgress.value !== 1 || isFlyingAway.value) { + if (openProgress.get() !== 1 || isFlyingAway.get()) { return } - dismissSwipeTranslateY.value = e.translationY + dismissSwipeTranslateY.set(e.translationY) }) .onEnd(e => { 'worklet' - if (openProgress.value !== 1 || isFlyingAway.value) { + if (openProgress.get() !== 1 || isFlyingAway.get()) { return } if (Math.abs(e.velocityY) > 200) { - isFlyingAway.value = true - if (dismissSwipeTranslateY.value === 0) { + isFlyingAway.set(true) + if (dismissSwipeTranslateY.get() === 0) { // HACK: If the initial value is 0, withDecay() animation doesn't start. // This is a bug in Reanimated, but for now we'll work around it like this. - dismissSwipeTranslateY.value = 1 + dismissSwipeTranslateY.set(1) } - dismissSwipeTranslateY.value = withDecay({ - velocity: e.velocityY, - velocityFactor: Math.max(3500 / Math.abs(e.velocityY), 1), // Speed up if it's too slow. - deceleration: 1, // Danger! This relies on the reaction below stopping it. - }) + dismissSwipeTranslateY.set(() => + withDecay({ + velocity: e.velocityY, + velocityFactor: Math.max(3500 / Math.abs(e.velocityY), 1), // Speed up if it's too slow. + deceleration: 1, // Danger! This relies on the reaction below stopping it. + }), + ) } else { - dismissSwipeTranslateY.value = withSpring(0, { - stiffness: 700, - damping: 50, - }) + dismissSwipeTranslateY.set(() => + withSpring(0, { + stiffness: 700, + damping: 50, + }), + ) } }) diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx index 6d601c2899..92b98dc2e6 100644 --- a/src/view/com/pager/PagerWithHeader.tsx +++ b/src/view/com/pager/PagerWithHeader.tsx @@ -131,11 +131,11 @@ export const PagerWithHeader = React.forwardRef( const lastForcedScrollY = useSharedValue(0) const adjustScrollForOtherPages = () => { 'worklet' - const currentScrollY = scrollY.value + const currentScrollY = scrollY.get() const forcedScrollY = Math.min(currentScrollY, headerOnlyHeight) - if (lastForcedScrollY.value !== forcedScrollY) { - lastForcedScrollY.value = forcedScrollY - const refs = scrollRefs.value + if (lastForcedScrollY.get() !== forcedScrollY) { + lastForcedScrollY.set(forcedScrollY) + const refs = scrollRefs.get() for (let i = 0; i < refs.length; i++) { const scollRef = refs[i] if (i !== currentPage && scollRef != null) { @@ -167,7 +167,7 @@ export const PagerWithHeader = React.forwardRef( const isPossiblyInvalid = headerHeight > 0 && Math.round(nextScrollY * 2) / 2 === -headerHeight if (!isPossiblyInvalid) { - scrollY.value = nextScrollY + scrollY.set(nextScrollY) runOnJS(queueThrottledOnScroll)() } }, @@ -246,7 +246,7 @@ let PagerTabBar = ({ allowHeaderOverScroll?: boolean }): React.ReactNode => { const headerTransform = useAnimatedStyle(() => { - const translateY = Math.min(scrollY.value, headerOnlyHeight) * -1 + const translateY = Math.min(scrollY.get(), headerOnlyHeight) * -1 return { transform: [ { diff --git a/src/view/com/util/BottomSheetCustomBackdrop.tsx b/src/view/com/util/BottomSheetCustomBackdrop.tsx index 25e882e87a..86751861f0 100644 --- a/src/view/com/util/BottomSheetCustomBackdrop.tsx +++ b/src/view/com/util/BottomSheetCustomBackdrop.tsx @@ -18,7 +18,7 @@ export function createCustomBackdrop( // animated variables const opacity = useAnimatedStyle(() => ({ opacity: interpolate( - animatedIndex.value, // current snap index + animatedIndex.get(), // current snap index [-1, 0], // input range [0, 0.5], // output range Extrapolation.CLAMP, diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index 52314f954f..4ee4d7d0b3 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -79,8 +79,8 @@ function ListImpl( onScrollFromContext?.(e, ctx) const didScrollDown = e.contentOffset.y > SCROLLED_DOWN_LIMIT - if (isScrolledDown.value !== didScrollDown) { - isScrolledDown.value = didScrollDown + if (isScrolledDown.get() !== didScrollDown) { + isScrolledDown.set(didScrollDown) if (onScrolledDownChange != null) { runOnJS(handleScrolledDownChange)(didScrollDown) } diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index 193d07d724..0d084993b7 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -44,7 +44,7 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (v: boolean) => { 'worklet' cancelAnimation(headerMode) - headerMode.value = v ? V1.value : V0.value + headerMode.set(v ? V1.get() : V0.get()) }, [headerMode], ) @@ -52,9 +52,9 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { useEffect(() => { if (isWeb) { return listenToForcedWindowScroll(() => { - startDragOffset.value = null - startMode.value = null - didJustRestoreScroll.value = true + startDragOffset.set(null) + startMode.set(null) + didJustRestoreScroll.set(true) }) } }) @@ -63,13 +63,14 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (e: NativeScrollEvent) => { 'worklet' if (isNative) { - if (startDragOffset.value === null) { + const startDragOffsetValue = startDragOffset.get() + if (startDragOffsetValue === null) { return } - const didScrollDown = e.contentOffset.y > startDragOffset.value - startDragOffset.value = null - startMode.value = null - if (e.contentOffset.y < headerHeight.value) { + const didScrollDown = e.contentOffset.y > startDragOffsetValue + startDragOffset.set(null) + startMode.set(null) + if (e.contentOffset.y < headerHeight.get()) { // If we're close to the top, show the shell. setMode(false) } else if (didScrollDown) { @@ -77,7 +78,7 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { setMode(true) } else { // Snap to whichever state is the closest. - setMode(Math.round(headerMode.value) === 1) + setMode(Math.round(headerMode.get()) === 1) } } }, @@ -88,8 +89,8 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (e: NativeScrollEvent) => { 'worklet' if (isNative) { - startDragOffset.value = e.contentOffset.y - startMode.value = headerMode.value + startDragOffset.set(e.contentOffset.y) + startMode.set(headerMode.get()) } }, [headerMode, startDragOffset, startMode], @@ -123,10 +124,12 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (e: NativeScrollEvent) => { 'worklet' if (isNative) { - if (startDragOffset.value === null || startMode.value === null) { + const startDragOffsetValue = startDragOffset.get() + const startModeValue = startMode.get() + if (startDragOffsetValue === null || startModeValue === null) { if ( - headerMode.value !== 0 && - e.contentOffset.y < headerHeight.value + headerMode.get() !== 0 && + e.contentOffset.y < headerHeight.get() ) { // If we're close enough to the top, always show the shell. // Even if we're not dragging. @@ -137,29 +140,29 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { // The "mode" value is always between 0 and 1. // Figure out how much to move it based on the current dragged distance. - const dy = e.contentOffset.y - startDragOffset.value + const dy = e.contentOffset.y - startDragOffsetValue const dProgress = interpolate( dy, - [-headerHeight.value, headerHeight.value], + [-headerHeight.get(), headerHeight.get()], [-1, 1], ) - const newValue = clamp(startMode.value + dProgress, 0, 1) - if (newValue !== headerMode.value) { + const newValue = clamp(startModeValue + dProgress, 0, 1) + if (newValue !== headerMode.get()) { // Manually adjust the value. This won't be (and shouldn't be) animated. // Cancel any any existing animation cancelAnimation(headerMode) - headerMode.value = newValue + headerMode.set(newValue) } } else { - if (didJustRestoreScroll.value) { - didJustRestoreScroll.value = false + if (didJustRestoreScroll.get()) { + didJustRestoreScroll.set(false) // Don't hide/show navbar based on scroll restoratoin. return } // On the web, we don't try to follow the drag because we don't know when it ends. // Instead, show/hide immediately based on whether we're scrolling up or down. - const dy = e.contentOffset.y - (startDragOffset.value ?? 0) - startDragOffset.value = e.contentOffset.y + const dy = e.contentOffset.y - (startDragOffset.get() ?? 0) + startDragOffset.set(e.contentOffset.y) if (dy < 0 || e.contentOffset.y < WEB_HIDE_SHELL_THRESHOLD) { setMode(false) diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index 855ba21b23..1d1023c2b2 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -134,7 +134,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { footerMinimalShellTransform, ]} onLayout={e => { - footerHeight.value = e.nativeEvent.layout.height + footerHeight.set(e.nativeEvent.layout.height) }}> {hasSession ? ( <>