From e9023f02bee22394fcc06afbafb4e36d5135c6a7 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 26 Jan 2024 12:10:48 +0100 Subject: [PATCH 1/6] limit gyroscope animation and add reduce motion --- src/CONST.ts | 2 ++ src/libs/Accessibility/index.ts | 10 ++++++++++ src/libs/NumberUtils.ts | 14 +------------- .../home/report/AnimatedEmptyStateBackground.tsx | 13 +++++++------ 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ff3934c31943..f4a66b8fe1ba 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.65, ARROW_HIDE_DELAY: 3000, API_ATTACHMENT_VALIDATIONS: { diff --git a/src/libs/Accessibility/index.ts b/src/libs/Accessibility/index.ts index 167634acce56..e98b15948023 100644 --- a/src/libs/Accessibility/index.ts +++ b/src/libs/Accessibility/index.ts @@ -18,6 +18,15 @@ const useScreenReaderStatus = (): boolean => { return isScreenReaderEnabled; }; +const useReduceMotionStatus = (): boolean => { + const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); + useEffect(() => { + AccessibilityInfo.isReduceMotionEnabled().then(enabled => setIsScreenReaderEnabled(enabled)) + }, []); + + return isScreenReaderEnabled; +}; + const getHitSlopForSize = ({x, y}: HitSlop) => { /* according to https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/ the minimum tappable area is 44x44 points */ @@ -49,5 +58,6 @@ const useAutoHitSlop = () => { export default { moveAccessibilityFocus, useScreenReaderStatus, + useReduceMotionStatus, useAutoHitSlop, }; 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..47edab5457d3 100644 --- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx +++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import Animated, {SensorType, useAnimatedSensor, useAnimatedStyle, useSharedValue, withSpring} from 'react-native-reanimated'; +import Animated, {clamp, SensorType, useAnimatedSensor, useAnimatedStyle, 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 Accessibility from '@libs/Accessibility'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -22,10 +22,11 @@ function AnimatedEmptyStateBackground() { const animatedSensor = useAnimatedSensor(SensorType.GYROSCOPE); const xOffset = useSharedValue(0); const yOffset = useSharedValue(0); + const isReducedMotionEnabled = Accessibility.useReduceMotionStatus(); // Apply data to create style object const animatedStyles = useAnimatedStyle(() => { - if (!isSmallScreenWidth) { + if (!isSmallScreenWidth || isReducedMotionEnabled) { return {}; } /* @@ -34,12 +35,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 ( Date: Fri, 26 Jan 2024 12:23:45 +0100 Subject: [PATCH 2/6] fix prettier --- src/libs/Accessibility/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Accessibility/index.ts b/src/libs/Accessibility/index.ts index e98b15948023..7c7d2a71ad92 100644 --- a/src/libs/Accessibility/index.ts +++ b/src/libs/Accessibility/index.ts @@ -21,7 +21,7 @@ const useScreenReaderStatus = (): boolean => { const useReduceMotionStatus = (): boolean => { const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); useEffect(() => { - AccessibilityInfo.isReduceMotionEnabled().then(enabled => setIsScreenReaderEnabled(enabled)) + AccessibilityInfo.isReduceMotionEnabled().then((enabled) => setIsScreenReaderEnabled(enabled)); }, []); return isScreenReaderEnabled; From f0f114420272e0db2875b1dc546b826bc2f6e439 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 26 Jan 2024 15:06:29 +0100 Subject: [PATCH 3/6] use useRedusedMotion from reanimated --- src/libs/Accessibility/index.ts | 10 ---------- src/pages/home/report/AnimatedEmptyStateBackground.tsx | 5 ++--- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/libs/Accessibility/index.ts b/src/libs/Accessibility/index.ts index 7c7d2a71ad92..167634acce56 100644 --- a/src/libs/Accessibility/index.ts +++ b/src/libs/Accessibility/index.ts @@ -18,15 +18,6 @@ const useScreenReaderStatus = (): boolean => { return isScreenReaderEnabled; }; -const useReduceMotionStatus = (): boolean => { - const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); - useEffect(() => { - AccessibilityInfo.isReduceMotionEnabled().then((enabled) => setIsScreenReaderEnabled(enabled)); - }, []); - - return isScreenReaderEnabled; -}; - const getHitSlopForSize = ({x, y}: HitSlop) => { /* according to https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/ the minimum tappable area is 44x44 points */ @@ -58,6 +49,5 @@ const useAutoHitSlop = () => { export default { moveAccessibilityFocus, useScreenReaderStatus, - useReduceMotionStatus, useAutoHitSlop, }; diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx index 47edab5457d3..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, {clamp, 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 Accessibility from '@libs/Accessibility'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -22,7 +21,7 @@ function AnimatedEmptyStateBackground() { const animatedSensor = useAnimatedSensor(SensorType.GYROSCOPE); const xOffset = useSharedValue(0); const yOffset = useSharedValue(0); - const isReducedMotionEnabled = Accessibility.useReduceMotionStatus(); + const isReducedMotionEnabled = useReducedMotion(); // Apply data to create style object const animatedStyles = useAnimatedStyle(() => { From 7fe93d8869167fe7a4dba8e0db101a5451f26990 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Mon, 29 Jan 2024 09:39:21 +0100 Subject: [PATCH 4/6] add adjustments --- src/CONST.ts | 2 +- src/pages/home/report/ReportActionItem.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index f4a66b8fe1ba..cbb99833fcce 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -47,7 +47,7 @@ const CONST = { OUT: 'out', }, // Multiplier for gyroscope animation in order to make it a bit more subtle - ANIMATION_GYROSCOPE_VALUE: 0.65, + ANIMATION_GYROSCOPE_VALUE: 0.4, ARROW_HIDE_DELAY: 3000, API_ATTACHMENT_VALIDATIONS: { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 80fb341a2cf8..3a333576965d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -619,7 +619,7 @@ function ReportActionItem(props) { if (ReportUtils.isTaskReport(props.report)) { if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) { return ( - <> + - + ); } return ( - <> + - + ); } if (ReportUtils.isExpenseReport(props.report) || ReportUtils.isIOUReport(props.report)) { From cfc6291bb64e83a13ac93f4b191a92b6bfe52ea2 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 30 Jan 2024 11:12:03 +0100 Subject: [PATCH 5/6] fix test --- tests/ui/UnreadIndicatorsTest.js | 1 + 1 file changed, 1 insertion(+) 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, })); /** From 7e6042dbc70a468a9fdf19bd467500f5a40bdb13 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 31 Jan 2024 12:37:01 +0100 Subject: [PATCH 6/6] rename const money_report --- src/CONST.ts | 2 +- src/styles/utils/index.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 86be7997b25c..0bb24c68928e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -147,7 +147,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/styles/utils/index.ts b/src/styles/utils/index.ts index a0f8f52927b9..79e3809108b4 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,