From 56be291164b0d91b54a282385919c322571cce43 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 20 Dec 2023 02:41:32 +0000 Subject: [PATCH 1/3] Update version to 1.4.14-2 (cherry picked from commit 52ecad401123dbeced531d63dc722aa92e84f537) --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 597d85de1c09..b7ed29f176dc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001041401 - versionName "1.4.14-1" + versionCode 1001041402 + versionName "1.4.14-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 897a300143b7..67c47dbb30cf 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.14.1 + 1.4.14.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 01316af0c986..d13112319dd6 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.14.1 + 1.4.14.2 diff --git a/package-lock.json b/package-lock.json index 1f6496c08566..5b4c8c737681 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.14-1", + "version": "1.4.14-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.14-1", + "version": "1.4.14-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d11a8c69f8f9..25e818c3cfcb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.14-1", + "version": "1.4.14-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 0d0da5f077e89749a61f24a185c5e16f62fb5bc1 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Wed, 20 Dec 2023 10:27:00 +0800 Subject: [PATCH 2/3] Merge pull request #33325 from rayane-djouah/Revert-"Migrate-Icon/index.js-and-FloatingActionButton-to-function-component" Revert: Migrate Icon/index.js and FloatingActionButton to function component (cherry picked from commit 9a936b194a301cb35941e0e4f7e69dedd4d61dcb) --- ios/Podfile.lock | 31 +++- package-lock.json | 14 +- package.json | 2 +- src/components/FloatingActionButton.js | 132 ++++++++++++++++++ .../FloatingActionButton/FabPlusIcon.js | 49 ------- src/components/FloatingActionButton/index.js | 85 ----------- src/components/Icon/index.tsx | 114 ++++++++------- src/styles/utils/index.ts | 8 ++ 8 files changed, 238 insertions(+), 197 deletions(-) create mode 100644 src/components/FloatingActionButton.js delete mode 100644 src/components/FloatingActionButton/FabPlusIcon.js delete mode 100644 src/components/FloatingActionButton/index.js diff --git a/ios/Podfile.lock b/ios/Podfile.lock index eebd6ad532d4..0fac30a26430 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -777,10 +777,35 @@ PODS: - React-Core - RNReactNativeHapticFeedback (1.14.0): - React-Core - - RNReanimated (3.6.1): - - RCT-Folly (= 2021.07.22.00) + - RNReanimated (3.5.4): + - DoubleConversion + - FBLazyVector + - glog + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-callinvoker - React-Core + - React-Core/DevSupport + - React-Core/RCTWebSocket + - React-CoreModules + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-RCTActionSheet + - React-RCTAnimation + - React-RCTAppDelegate + - React-RCTBlob + - React-RCTImage + - React-RCTLinking + - React-RCTNetwork + - React-RCTSettings + - React-RCTText - ReactCommon/turbomodule/core + - Yoga - RNScreens (3.21.0): - React-Core - React-RCTImage @@ -1255,7 +1280,7 @@ SPEC CHECKSUMS: rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64 RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c - RNReanimated: fdbaa9c964bbab7fac50c90862b6cc5f041679b9 + RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87 RNScreens: d037903436160a4b039d32606668350d2a808806 RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d diff --git a/package-lock.json b/package-lock.json index 5b4c8c737681..1c2ae325575d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,7 +100,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.6.1", + "react-native-reanimated": "3.5.4", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.4.1", "react-native-screens": "3.21.0", @@ -44555,9 +44555,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz", - "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz", + "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", @@ -84872,9 +84872,9 @@ "requires": {} }, "react-native-reanimated": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz", - "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz", + "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==", "requires": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", diff --git a/package.json b/package.json index 25e818c3cfcb..7281ab12cfa7 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.6.1", + "react-native-reanimated": "3.5.4", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.4.1", "react-native-screens": "3.21.0", diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js new file mode 100644 index 000000000000..791eb150f8c9 --- /dev/null +++ b/src/components/FloatingActionButton.js @@ -0,0 +1,132 @@ +import PropTypes from 'prop-types'; +import React, {PureComponent} from 'react'; +import {Animated, Easing, View} from 'react-native'; +import compose from '@libs/compose'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; +import PressableWithFeedback from './Pressable/PressableWithFeedback'; +import Tooltip from './Tooltip/PopoverAnchorTooltip'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import withStyleUtils, {withStyleUtilsPropTypes} from './withStyleUtils'; +import withTheme, {withThemePropTypes} from './withTheme'; +import withThemeStyles, {withThemeStylesPropTypes} from './withThemeStyles'; + +const AnimatedIcon = Animated.createAnimatedComponent(Icon); +AnimatedIcon.displayName = 'AnimatedIcon'; + +const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback); +AnimatedPressable.displayName = 'AnimatedPressable'; + +const propTypes = { + // Callback to fire on request to toggle the FloatingActionButton + onPress: PropTypes.func.isRequired, + + // Current state (active or not active) of the component + isActive: PropTypes.bool.isRequired, + + // Ref for the button + buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + + ...withLocalizePropTypes, + ...withThemePropTypes, + ...withThemeStylesPropTypes, + ...withStyleUtilsPropTypes, +}; + +const defaultProps = { + buttonRef: () => {}, +}; + +class FloatingActionButton extends PureComponent { + constructor(props) { + super(props); + this.animatedValue = new Animated.Value(props.isActive ? 1 : 0); + } + + componentDidUpdate(prevProps) { + if (prevProps.isActive === this.props.isActive) { + return; + } + + this.animateFloatingActionButton(); + } + + /** + * Animates the floating action button + * Method is called when the isActive prop changes + */ + animateFloatingActionButton() { + const animationFinalValue = this.props.isActive ? 1 : 0; + + Animated.timing(this.animatedValue, { + toValue: animationFinalValue, + duration: 340, + easing: Easing.inOut(Easing.ease), + useNativeDriver: false, + }).start(); + } + + render() { + const rotate = this.animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '135deg'], + }); + + const backgroundColor = this.animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [this.props.theme.success, this.props.theme.buttonDefaultBG], + }); + + const fill = this.animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [this.props.theme.textLight, this.props.theme.textDark], + }); + + return ( + + + { + this.fabPressable = el; + if (this.props.buttonRef) { + this.props.buttonRef.current = el; + } + }} + accessibilityLabel={this.props.accessibilityLabel} + role={this.props.role} + pressDimmingValue={1} + onPress={(e) => { + // Drop focus to avoid blue focus ring. + this.fabPressable.blur(); + this.props.onPress(e); + }} + onLongPress={() => {}} + style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]} + > + + + + + ); + } +} + +FloatingActionButton.propTypes = propTypes; +FloatingActionButton.defaultProps = defaultProps; + +const FloatingActionButtonWithLocalize = withLocalize(FloatingActionButton); + +const FloatingActionButtonWithLocalizeWithRef = React.forwardRef((props, ref) => ( + +)); + +FloatingActionButtonWithLocalizeWithRef.displayName = 'FloatingActionButtonWithLocalizeWithRef'; + +export default compose(withThemeStyles, withTheme, withStyleUtils)(FloatingActionButtonWithLocalizeWithRef); diff --git a/src/components/FloatingActionButton/FabPlusIcon.js b/src/components/FloatingActionButton/FabPlusIcon.js deleted file mode 100644 index 09afa00f119d..000000000000 --- a/src/components/FloatingActionButton/FabPlusIcon.js +++ /dev/null @@ -1,49 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {useEffect} from 'react'; -import Animated, {Easing, interpolateColor, useAnimatedProps, useSharedValue, withTiming} from 'react-native-reanimated'; -import Svg, {Path} from 'react-native-svg'; -import useTheme from '@hooks/useTheme'; - -const AnimatedPath = Animated.createAnimatedComponent(Path); - -const propTypes = { - /* Current state (active or not active) of the component */ - isActive: PropTypes.bool.isRequired, -}; - -function FabPlusIcon({isActive}) { - const theme = useTheme(); - const animatedValue = useSharedValue(isActive ? 1 : 0); - - useEffect(() => { - animatedValue.value = withTiming(isActive ? 1 : 0, { - duration: 340, - easing: Easing.inOut(Easing.ease), - }); - }, [isActive, animatedValue]); - - const animatedProps = useAnimatedProps(() => { - const fill = interpolateColor(animatedValue.value, [0, 1], [theme.textLight, theme.textDark]); - - return { - fill, - }; - }); - - return ( - - - - ); -} - -FabPlusIcon.propTypes = propTypes; -FabPlusIcon.displayName = 'FabPlusIcon'; - -export default FabPlusIcon; diff --git a/src/components/FloatingActionButton/index.js b/src/components/FloatingActionButton/index.js deleted file mode 100644 index d341396c44b7..000000000000 --- a/src/components/FloatingActionButton/index.js +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {useEffect, useRef} from 'react'; -import {View} from 'react-native'; -import Animated, {Easing, interpolateColor, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; -import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; -import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; -import FabPlusIcon from './FabPlusIcon'; - -const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback); -AnimatedPressable.displayName = 'AnimatedPressable'; - -const propTypes = { - /* Callback to fire on request to toggle the FloatingActionButton */ - onPress: PropTypes.func.isRequired, - - /* Current state (active or not active) of the component */ - isActive: PropTypes.bool.isRequired, - - /* An accessibility label for the button */ - accessibilityLabel: PropTypes.string.isRequired, - - /* An accessibility role for the button */ - role: PropTypes.string.isRequired, -}; - -const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibilityLabel, role}, ref) => { - const theme = useTheme(); - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const fabPressable = useRef(null); - const animatedValue = useSharedValue(isActive ? 1 : 0); - const buttonRef = ref; - - useEffect(() => { - animatedValue.value = withTiming(isActive ? 1 : 0, { - duration: 340, - easing: Easing.inOut(Easing.ease), - }); - }, [isActive, animatedValue]); - - const animatedStyle = useAnimatedStyle(() => { - const backgroundColor = interpolateColor(animatedValue.value, [0, 1], [theme.success, theme.buttonDefaultBG]); - - return { - transform: [{rotate: `${animatedValue.value * 135}deg`}], - backgroundColor, - borderRadius: styles.floatingActionButton.borderRadius, - }; - }); - - return ( - - - { - fabPressable.current = el; - if (buttonRef) { - buttonRef.current = el; - } - }} - accessibilityLabel={accessibilityLabel} - role={role} - pressDimmingValue={1} - onPress={(e) => { - // Drop focus to avoid blue focus ring. - fabPressable.current.blur(); - onPress(e); - }} - onLongPress={() => {}} - style={[styles.floatingActionButton, animatedStyle]} - > - - - - - ); -}); - -FloatingActionButton.propTypes = propTypes; -FloatingActionButton.displayName = 'FloatingActionButton'; - -export default FloatingActionButton; diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 5f82421c0e8e..1d2d54042d4c 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -1,8 +1,8 @@ -import React from 'react'; +import React, {PureComponent} from 'react'; import {StyleProp, View, ViewStyle} from 'react-native'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; +import withStyleUtils, {WithStyleUtilsProps} from '@components/withStyleUtils'; +import withTheme, {WithThemeProps} from '@components/withTheme'; +import withThemeStyles, {type WithThemeStylesProps} from '@components/withThemeStyles'; import variables from '@styles/variables'; import IconWrapperStyles from './IconWrapperStyles'; @@ -41,63 +41,73 @@ type IconProps = { /** Additional styles to add to the Icon */ additionalStyles?: StyleProp; -}; +} & WithThemeStylesProps & + WithThemeProps & + WithStyleUtilsProps; + +// We must use a class component to create an animatable component with the Animated API +// eslint-disable-next-line react/prefer-stateless-function +class Icon extends PureComponent { + // eslint-disable-next-line react/static-property-placement + public static defaultProps = { + width: variables.iconSizeNormal, + height: variables.iconSizeNormal, + fill: undefined, + small: false, + inline: false, + additionalStyles: [], + hovered: false, + pressed: false, + }; + + render() { + const width = this.props.small ? variables.iconSizeSmall : this.props.width; + const height = this.props.small ? variables.iconSizeSmall : this.props.height; + const iconStyles = [this.props.StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, this.props.themeStyles.pAbsolute, this.props.additionalStyles]; + const fill = this.props.fill ?? this.props.theme.icon; + + if (this.props.inline) { + return ( + + + + + + ); + } -function Icon({ - src, - width = variables.iconSizeNormal, - height = variables.iconSizeNormal, - fill = undefined, - small = false, - inline = false, - hovered = false, - pressed = false, - additionalStyles = [], -}: IconProps) { - const theme = useTheme(); - const StyleUtils = useStyleUtils(); - const styles = useThemeStyles(); - const iconWidth = small ? variables.iconSizeSmall : width; - const iconHeight = small ? variables.iconSizeSmall : height; - const iconStyles = [StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, styles.pAbsolute, additionalStyles]; - const iconFill = fill ?? theme.icon; - const IconComponent = src; - - if (inline) { return ( - - - + ); } - - return ( - - - - ); } +<<<<<<< HEAD Icon.displayName = 'Icon'; export default Icon; +======= +export type {SrcProps}; + +export default withTheme(withThemeStyles(withStyleUtils(Icon))); +>>>>>>> 9a936b1 (Merge pull request #33325 from rayane-djouah/Revert-"Migrate-Icon/index.js-and-FloatingActionButton-to-function-component") diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 228643e5ba2e..e932d221cc5a 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -446,6 +446,13 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu return {}; } +function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.Value): Animated.WithAnimatedValue { + return { + transform: [{rotate}], + backgroundColor, + }; +} + function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { return { width, @@ -1008,6 +1015,7 @@ const staticStyleUtils = { combineStyles, displayIfTrue, getAmountFontSizeAndLineHeight, + getAnimatedFABStyle, getAutoCompleteSuggestionContainerStyle, getAvatarBorderRadius, getAvatarBorderStyle, From c47870cd835142e8925dca5a4e9c8a97c43297e9 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Wed, 20 Dec 2023 11:43:34 +0800 Subject: [PATCH 3/3] resolve merge conflicts --- src/components/Icon/index.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 1d2d54042d4c..59b1639dce33 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -102,12 +102,6 @@ class Icon extends PureComponent { } } -<<<<<<< HEAD -Icon.displayName = 'Icon'; - -export default Icon; -======= export type {SrcProps}; export default withTheme(withThemeStyles(withStyleUtils(Icon))); ->>>>>>> 9a936b1 (Merge pull request #33325 from rayane-djouah/Revert-"Migrate-Icon/index.js-and-FloatingActionButton-to-function-component")