diff --git a/src/CONST.ts b/src/CONST.ts index b27923465a1f..accc351f3151 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -46,6 +46,8 @@ const CONST = { IN: 'in', OUT: 'out', }, + // Multiplier for gyroscope animation in order to make it a bit more subtle + ANIMATION_GYROSCOPE_VALUE: 0.4, BACKGROUND_IMAGE_TRANSITION_DURATION: 1000, ARROW_HIDE_DELAY: 3000, @@ -146,7 +148,7 @@ const CONST = { CONTAINER_MINHEIGHT: 500, VIEW_HEIGHT: 275, }, - MONEY_REPORT: { + MONEY_OR_TASK_REPORT: { SMALL_SCREEN: { IMAGE_HEIGHT: 300, CONTAINER_MINHEIGHT: 280, diff --git a/src/libs/NumberUtils.ts b/src/libs/NumberUtils.ts index ddbd42243758..d7eb87a2ed1e 100644 --- a/src/libs/NumberUtils.ts +++ b/src/libs/NumberUtils.ts @@ -47,18 +47,6 @@ function generateHexadecimalValue(num: number): string { return result.join('').toUpperCase(); } -/** - * Clamp a number in a range. - * This is a worklet so it should be used only from UI thread. - - * @returns clamped value between min and max - */ -function clampWorklet(num: number, min: number, max: number): number { - 'worklet'; - - return Math.min(Math.max(num, min), max); -} - /** * Generates a random integer between a and b * It's and equivalent of _.random(a, b) @@ -81,4 +69,4 @@ function parseFloatAnyLocale(value: string): number { return parseFloat(value ? value.replace(',', '.') : value); } -export {rand64, generateHexadecimalValue, generateRandomInt, clampWorklet, parseFloatAnyLocale}; +export {rand64, generateHexadecimalValue, generateRandomInt, parseFloatAnyLocale}; diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index 7e259b7473cf..c2ae6ddeb54c 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import Animated, {SensorType, useAnimatedSensor, useAnimatedStyle, useSharedValue, withSpring} from 'react-native-reanimated'; +import Animated, {clamp, SensorType, useAnimatedSensor, useAnimatedStyle, useReducedMotion, useSharedValue, withSpring} from 'react-native-reanimated'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import * as NumberUtils from '@libs/NumberUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -22,10 +21,11 @@ function AnimatedEmptyStateBackground() { const animatedSensor = useAnimatedSensor(SensorType.GYROSCOPE); const xOffset = useSharedValue(0); const yOffset = useSharedValue(0); + const isReducedMotionEnabled = useReducedMotion(); // Apply data to create style object const animatedStyles = useAnimatedStyle(() => { - if (!isSmallScreenWidth) { + if (!isSmallScreenWidth || isReducedMotionEnabled) { return {}; } /* @@ -34,12 +34,12 @@ function AnimatedEmptyStateBackground() { */ const {x, y} = animatedSensor.sensor.value; // The x vs y here seems wrong but is the way to make it feel right to the user - xOffset.value = NumberUtils.clampWorklet(xOffset.value + y, -IMAGE_OFFSET_X, IMAGE_OFFSET_X); - yOffset.value = NumberUtils.clampWorklet(yOffset.value - x, -IMAGE_OFFSET_Y, IMAGE_OFFSET_Y); + 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); return { transform: [{translateX: withSpring(-IMAGE_OFFSET_X - xOffset.value)}, {translateY: withSpring(yOffset.value)}], }; - }, []); + }, [isReducedMotionEnabled]); return ( + - + ); } return ( - <> + - + ); } if (ReportUtils.isExpenseReport(props.report) || ReportUtils.isIOUReport(props.report)) { diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 010714f4208f..a45b7cdbcb34 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -699,8 +699,8 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ImageStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ImageStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { @@ -720,8 +720,8 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMon /** * Gets the correct top margin size for the chat welcome message based on screen dimensions */ -function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ViewStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ViewStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { marginTop: emptyStateBackground.SMALL_SCREEN.VIEW_HEIGHT, @@ -754,8 +754,8 @@ function getLineHeightStyle(lineHeight: number): TextStyle { /** * Gets the correct size for the empty state container based on screen dimensions */ -function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ViewStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ViewStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { minHeight: emptyStateBackground.SMALL_SCREEN.CONTAINER_MINHEIGHT, diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index e4d4d877f66b..6da7005bfb77 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -43,6 +43,7 @@ jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ jest.mock('react-native-reanimated', () => ({ ...jest.requireActual('react-native-reanimated/mock'), createAnimatedPropAdapter: jest.fn, + useReducedMotion: jest.fn, })); /**