From ccd3017ae43ac5fd00c431b4b043707379e75e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 20 Jul 2023 14:04:32 +0200 Subject: [PATCH 01/67] wip: adding status page --- src/ROUTES.js | 10 +++++ src/languages/en.js | 3 ++ src/languages/es.js | 3 ++ .../AppNavigator/ModalStackNavigators.js | 7 ++++ src/libs/Navigation/linkingConfig.js | 4 ++ .../Profile/CustomStatus/StatusPage.js | 42 +++++++++++++++++++ src/pages/settings/Profile/ProfilePage.js | 5 +++ 7 files changed, 74 insertions(+) create mode 100644 src/pages/settings/Profile/CustomStatus/StatusPage.js diff --git a/src/ROUTES.js b/src/ROUTES.js index d32deaa63ab0..47dc9d6d7790 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -12,6 +12,11 @@ const IOU_SEND = 'send/new'; const NEW_TASK = 'new/task'; const SETTINGS_PERSONAL_DETAILS = 'settings/profile/personal-details'; const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods'; +const SETTINGS_STATUS = 'settings/profile/status'; +const SETTINGS_STATUS_SET = 'settings/profile/status/set'; +const SETTINGS_STATUS_CLEAR_AFTER = 'settings/profile/status/clear-after'; +const SETTINGS_STATUS_SET_TIME = 'settings/profile/status/set-time'; +const SETTINGS_STATUS_SET_DATE = 'settings/profile/status/set-date'; export default { BANK_ACCOUNT: 'bank-account', @@ -59,6 +64,11 @@ export default { SETTINGS_2FA_CODES: 'settings/security/two-factor-auth/codes', SETTINGS_2FA_VERIFY: 'settings/security/two-factor-auth/verify', SETTINGS_2FA_SUCCESS: 'settings/security/two-factor-auth/success', + SETTINGS_STATUS, + SETTINGS_STATUS_SET, + SETTINGS_STATUS_CLEAR_AFTER, + SETTINGS_STATUS_SET_TIME, + SETTINGS_STATUS_SET_DATE, NEW_GROUP: 'new/group', NEW_CHAT: 'new/chat', NEW_TASK, diff --git a/src/languages/en.js b/src/languages/en.js index 4cf79bbcd097..b30d244274e0 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -845,6 +845,9 @@ export default { setPasswordLinkInvalid: 'This set password link is invalid or has expired. A new one is waiting for you in your email inbox!', validateAccount: 'Verify account', }, + statusPage: { + status: 'Status', + }, stepCounter: ({step, total, text}) => { let result = `Step ${step}`; diff --git a/src/languages/es.js b/src/languages/es.js index 27fde8e602ad..e20be36d1e66 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -849,6 +849,9 @@ export default { setPasswordLinkInvalid: 'El enlace para configurar tu contraseña ha expirado. Te hemos enviado un nuevo enlace a tu correo.', validateAccount: 'Verificar cuenta', }, + StatusPage: { + status: 'Estado', + }, stepCounter: ({step, total, text}) => { let result = `Paso ${step}`; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 732ec938e8ae..66f35cd10233 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -522,6 +522,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, name: 'Settings_Add_Bank_Account', }, + { + getComponent: () => { + const SettingsStatus = require('../../../pages/settings/Profile/CustomStatus/StatusPage').default; + return SettingsStatus; + }, + name: 'Settings_Status', + }, { getComponent: () => { const WorkspaceInitialPage = require('../../../pages/workspace/WorkspaceInitialPage').default; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 2657be156a01..d4b705d946fe 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -181,6 +181,10 @@ export default { path: ROUTES.SETTINGS_SHARE_CODE, exact: true, }, + Settings_Status: { + path: ROUTES.SETTINGS_STATUS, + exact: true, + }, Workspace_Initial: { path: ROUTES.WORKSPACE_INITIAL, }, diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js new file mode 100644 index 000000000000..d039cd358115 --- /dev/null +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -0,0 +1,42 @@ +import React from 'react'; +import {View} from 'react-native'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; +import ROUTES from '../../../../ROUTES'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import styles from '../../../../styles/styles'; +import Text from '../../../../components/Text'; +import MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription'; + +function StatusPage() { + return ( + + Navigation.goBack(ROUTES.SETTINGS_PROFILE)} + /> + + Set your status + Add an emoji to give your colleagues and friends an easy way to know what's going on. You can optionally add a message too! + + + Navigation.navigate(ROUTES.SETTINGS_STATUS_SET)} + /> + Navigation.navigate(ROUTES.SETTINGS_STATUS_CLEAR_AFTER)} + /> + + + ); +} + +StatusPage.displayName = 'StatusPage'; + +export default StatusPage; diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 07db3d0cdffb..e66a06938ba9 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -73,6 +73,11 @@ function ProfilePage(props) { pageRoute: ROUTES.SETTINGS_CONTACT_METHODS, brickRoadIndicator: contactMethodBrickRoadIndicator, }, + { + description: props.translate('statusPage.status'), + title: '', // TODO: Get the status from onyx + pageRoute: ROUTES.SETTINGS_STATUS, + }, { description: props.translate('pronounsPage.pronouns'), title: getPronouns(), From cf6fa1cd3af47f56c7bd352f44b55f9ab7cc12fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 20 Jul 2023 14:50:20 +0200 Subject: [PATCH 02/67] wip --- src/languages/en.js | 2 ++ src/pages/settings/Profile/CustomStatus/StatusPage.js | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index b30d244274e0..cf9710a2b516 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -847,6 +847,8 @@ export default { }, statusPage: { status: 'Status', + setStatusTitle: 'Set your status', + statusExplanation: "Add an emoji to give your colleagues and friends an easy way to know what's going on. You can optionally add a message too!", }, stepCounter: ({step, total, text}) => { let result = `Step ${step}`; diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index d039cd358115..bb696a16a0e2 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -7,8 +7,11 @@ import Navigation from '../../../../libs/Navigation/Navigation'; import styles from '../../../../styles/styles'; import Text from '../../../../components/Text'; import MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription'; +import useLocalize from '../../../../hooks/useLocalize'; function StatusPage() { + const localize = useLocalize(); + return ( Navigation.goBack(ROUTES.SETTINGS_PROFILE)} /> - Set your status - Add an emoji to give your colleagues and friends an easy way to know what's going on. You can optionally add a message too! + {localize.translate('statusPage.setStatusTitle')} + {localize.translate('statusPage.statusExplanation')} - + Date: Thu, 20 Jul 2023 15:38:12 +0200 Subject: [PATCH 03/67] WIP --- src/components/Tooltip/index.js | 224 +++++++++++++++----------------- 1 file changed, 105 insertions(+), 119 deletions(-) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 0454327de284..db67335d719a 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {PureComponent} from 'react'; +import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import {BoundsObserver} from '@react-ng/bounds-observer'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; @@ -11,111 +11,99 @@ import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; import compose from '../../libs/compose'; import withLocalize from '../withLocalize'; +const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); + /** * A component used to wrap an element intended for displaying a tooltip. The term "tooltip's target" refers to the * wrapped element, which, upon hover, triggers the tooltip to be shown. + * @param {propTypes} props + * @returns {ReactNodeLike} */ -class Tooltip extends PureComponent { - constructor(props) { - super(props); - - this.state = { - // Is tooltip already rendered on the page's body? This happens once. - isRendered: false, - - // Is the tooltip currently visible? - isVisible: false, - - // The distance between the left side of the wrapper view and the left side of the window - xOffset: 0, - - // The distance between the top of the wrapper view and the top of the window - yOffset: 0, - - // The width and height of the wrapper view - wrapperWidth: 0, - wrapperHeight: 0, - }; - - // Whether the tooltip is first tooltip to activate the TooltipSense - this.isTooltipSenseInitiator = false; - this.animation = new Animated.Value(0); - this.hasHoverSupport = DeviceCapabilities.hasHoverSupport(); - - this.showTooltip = this.showTooltip.bind(this); - this.hideTooltip = this.hideTooltip.bind(this); - this.updateBounds = this.updateBounds.bind(this); - this.isAnimationCanceled = React.createRef(false); - } - - // eslint-disable-next-line rulesdir/prefer-early-return - componentDidUpdate(prevProps) { - // if the tooltip text changed before the initial animation was finished, then the tooltip won't be shown - // we need to show the tooltip again - if (this.state.isVisible && this.isAnimationCanceled.current && this.props.text && prevProps.text !== this.props.text) { - this.isAnimationCanceled.current = false; - this.showTooltip(); - } - } - - /** - * Update the tooltip bounding rectangle - * - * @param {Object} bounds - updated bounds - */ - updateBounds(bounds) { - if (bounds.width === 0) { - this.setState({isRendered: false}); - } - this.setState({ - wrapperWidth: bounds.width, - wrapperHeight: bounds.height, - xOffset: bounds.x, - yOffset: bounds.y, - }); - } +function Tooltip(props) { + // Is tooltip already rendered on the page's body? happens once. + const [isRendered, setIsRendered] = useState(false); + // Is the tooltip currently visible? + const [isVisible, setIsVisible] = useState(false); + // The distance between the left side of the wrapper view and the left side of the window + const [xOffset, setXOffset] = useState(0); + // The distance between the top of the wrapper view and the top of the window + const [yOffset, setYOffset] = useState(0); + // The width and height of the wrapper view + const [wrapperWidth, setWrapperWidth] = useState(0); + const [wrapperHeight, setWrapperHeight] = useState(0); + + const isTooltipSenseInitiator = useRef(false); + const animation = useRef(new Animated.Value(0)); + const isAnimationCanceled = useRef(false); + const prevText = useRef(props.text); /** * Display the tooltip in an animation. */ - showTooltip() { - if (!this.state.isRendered) { - this.setState({isRendered: true}); + const showTooltip = useCallback(() => { + if (!isRendered) { + setIsRendered(true); } - this.setState({isVisible: true}); + setIsVisible(true); - this.animation.stopAnimation(); + animation.current.stopAnimation(); // When TooltipSense is active, immediately show the tooltip if (TooltipSense.isActive()) { - this.animation.setValue(1); + animation.setValue(1); } else { - this.isTooltipSenseInitiator = true; - Animated.timing(this.animation, { + isTooltipSenseInitiator.current = true; + Animated.timing(animation.current, { toValue: 1, duration: 140, delay: 500, useNativeDriver: false, }).start(({finished}) => { - this.isAnimationCanceled.current = !finished; + isAnimationCanceled.current = !finished; }); } TooltipSense.activate(); - } + }, [isRendered]); + + useEffect(() => { + // if the tooltip text changed before the initial animation was finished, then the tooltip won't be shown + // we need to show the tooltip again + if (isVisible && isAnimationCanceled.current && props.text && prevText !== props.text) { + isAnimationCanceled.current = false; + showTooltip(); + } + + prevText.current = props.text; + }, [isVisible, props.text, showTooltip]); + + /** + * Update the tooltip bounding rectangle + * + * @param {Object} bounds - updated bounds + */ + const updateBounds = (bounds) => { + if (bounds.width === 0) { + setIsRendered(false); + } + setWrapperWidth(bounds.width); + setWrapperHeight(bounds.height); + setXOffset(bounds.x); + setYOffset(bounds.y); + }; /** * Hide the tooltip in an animation. */ - hideTooltip() { - this.animation.stopAnimation(); + const hideTooltip = () => { + animation.current.stopAnimation(); - if (TooltipSense.isActive() && !this.isTooltipSenseInitiator) { - this.animation.setValue(0); + if (TooltipSense.isActive() && !isTooltipSenseInitiator.current) { + animation.current.setValue(0); } else { // Hide the first tooltip which initiated the TooltipSense with animation - this.isTooltipSenseInitiator = false; - Animated.timing(this.animation, { + isTooltipSenseInitiator.current = false; + Animated.timing(animation.current, { toValue: 0, duration: 140, useNativeDriver: false, @@ -124,53 +112,51 @@ class Tooltip extends PureComponent { TooltipSense.deactivate(); - this.setState({isVisible: false}); - } + setIsVisible(false); + }; - render() { - // Skip the tooltip and return the children if the text is empty, - // we don't have a render function or the device does not support hovering - if ((_.isEmpty(this.props.text) && this.props.renderTooltipContent == null) || !this.hasHoverSupport) { - return this.props.children; - } + // Skip the tooltip and return the children if the text is empty, + // we don't have a render function or the device does not support hovering + if ((_.isEmpty(props.text) && props.renderTooltipContent == null) || !hasHoverSupport) { + return props.children; + } - return ( - <> - {this.state.isRendered && ( - - )} - + {isRendered && ( + + )} + + - - {this.props.children} - - - - ); - } + {props.children} + + + + ); } Tooltip.propTypes = tooltipPropTypes.propTypes; Tooltip.defaultProps = tooltipPropTypes.defaultProps; -export default compose(withWindowDimensions, withLocalize)(Tooltip); +export default compose(withWindowDimensions, withLocalize)(memo(Tooltip)); From 3b8a08639c1539dcce61b2a330e7189d71773197 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 20 Jul 2023 16:04:05 +0200 Subject: [PATCH 04/67] fix .current issues --- src/components/Tooltip/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index db67335d719a..32eb54a9592b 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -51,7 +51,7 @@ function Tooltip(props) { // When TooltipSense is active, immediately show the tooltip if (TooltipSense.isActive()) { - animation.setValue(1); + animation.current.setValue(1); } else { isTooltipSenseInitiator.current = true; Animated.timing(animation.current, { @@ -125,7 +125,7 @@ function Tooltip(props) { <> {isRendered && ( Date: Thu, 20 Jul 2023 16:28:58 +0200 Subject: [PATCH 05/67] cleanup --- src/components/Tooltip/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 32eb54a9592b..ebb5afb420a8 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -10,13 +10,14 @@ import TooltipSense from './TooltipSense'; import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; import compose from '../../libs/compose'; import withLocalize from '../withLocalize'; +import usePrevious from '../../hooks/usePrevious'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); /** * A component used to wrap an element intended for displaying a tooltip. The term "tooltip's target" refers to the * wrapped element, which, upon hover, triggers the tooltip to be shown. - * @param {propTypes} props + * @param {propTypes} props * @returns {ReactNodeLike} */ function Tooltip(props) { @@ -35,7 +36,7 @@ function Tooltip(props) { const isTooltipSenseInitiator = useRef(false); const animation = useRef(new Animated.Value(0)); const isAnimationCanceled = useRef(false); - const prevText = useRef(props.text); + const prevText = usePrevious(props.text); /** * Display the tooltip in an animation. @@ -69,13 +70,14 @@ function Tooltip(props) { useEffect(() => { // if the tooltip text changed before the initial animation was finished, then the tooltip won't be shown // we need to show the tooltip again + if (isVisible && isAnimationCanceled.current && props.text && prevText !== props.text) { isAnimationCanceled.current = false; showTooltip(); } prevText.current = props.text; - }, [isVisible, props.text, showTooltip]); + }, [isVisible, props.text, prevText, showTooltip]); /** * Update the tooltip bounding rectangle @@ -137,8 +139,8 @@ function Tooltip(props) { maxWidth={props.maxWidth} numberOfLines={props.numberOfLines} renderTooltipContent={props.renderTooltipContent} - // We pass a key, so whenever the content changes component will completely remount with a fresh - // prevents flickering/moving while remaining performant. + // We pass a key, so whenever the content changes this component will completely remount with a fresh state. + // This prevents flickering/moving while remaining performant. key={[props.text, ...props.renderTooltipContentKey, props.preferredLocale]} /> )} From 237c036a24fc65b938e8ec3f43a874b229d47af7 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 20 Jul 2023 16:39:49 +0200 Subject: [PATCH 06/67] clean up --- src/components/Tooltip/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index ebb5afb420a8..d2af74cad2b5 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -67,16 +67,14 @@ function Tooltip(props) { TooltipSense.activate(); }, [isRendered]); + // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { // if the tooltip text changed before the initial animation was finished, then the tooltip won't be shown // we need to show the tooltip again - if (isVisible && isAnimationCanceled.current && props.text && prevText !== props.text) { isAnimationCanceled.current = false; showTooltip(); } - - prevText.current = props.text; }, [isVisible, props.text, prevText, showTooltip]); /** From c3edf1a7e14f75311c31e598a3ad763c73e35ec7 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Tue, 18 Jul 2023 19:57:05 +0200 Subject: [PATCH 07/67] check Keyboard status before dismissing --- src/components/ScreenWrapper/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index f5f0f2fadd41..9803914f39d4 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -29,6 +29,22 @@ class ScreenWrapper extends React.Component { onPanResponderRelease: toggleTestToolsModal, }); + this.keyboardDissmissPanResponder = PanResponder.create({ + onMoveShouldSetPanResponderCapture: (e, gestureState) => { + const isHorizontalSwipe = Math.abs(gestureState.dx) > Math.abs(gestureState.dy); + if (isHorizontalSwipe && this.props.shouldDismissKeyboardBeforeClose) { + return true; + } + return false; + }, + onPanResponderGrant: () => { + if(!Keyboard.isVisible()) { + return; + } + Keyboard.dismiss(); + }, + }); + this.state = { didScreenTransitionEnd: false, }; From ed2e461a0a8911f149ec8ee19ed30d278dc03094 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Wed, 19 Jul 2023 20:30:26 +0200 Subject: [PATCH 08/67] check if browser is mobile before keboard dismissing --- src/components/ScreenWrapper/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index 9803914f39d4..535669576c22 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -19,6 +19,7 @@ import withWindowDimensions from '../withWindowDimensions'; import withEnvironment from '../withEnvironment'; import toggleTestToolsModal from '../../libs/actions/TestTool'; import CustomDevMenu from '../CustomDevMenu'; +import * as Browser from '../../libs/Browser'; class ScreenWrapper extends React.Component { constructor(props) { @@ -38,7 +39,7 @@ class ScreenWrapper extends React.Component { return false; }, onPanResponderGrant: () => { - if(!Keyboard.isVisible()) { + if(!Keyboard.isVisible() || !Browser.isMobile()) { return; } Keyboard.dismiss(); From 7e10cdff3fee6345599c32d91fcf137d1c42a397 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 20 Jul 2023 16:55:20 +0200 Subject: [PATCH 09/67] pretty prettier --- src/components/ScreenWrapper/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index 535669576c22..f29f562cf622 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -39,7 +39,7 @@ class ScreenWrapper extends React.Component { return false; }, onPanResponderGrant: () => { - if(!Keyboard.isVisible() || !Browser.isMobile()) { + if (!Keyboard.isVisible() || !Browser.isMobile()) { return; } Keyboard.dismiss(); From efce4e4f39e5a1c75994b8ea6889cca39b71555a Mon Sep 17 00:00:00 2001 From: jczekalski Date: Thu, 20 Jul 2023 19:36:57 +0200 Subject: [PATCH 10/67] add missing comment --- src/components/Tooltip/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index d2af74cad2b5..004d27ef969c 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -33,6 +33,7 @@ function Tooltip(props) { const [wrapperWidth, setWrapperWidth] = useState(0); const [wrapperHeight, setWrapperHeight] = useState(0); + // Whether the tooltip is first tooltip to activate the TooltipSense const isTooltipSenseInitiator = useRef(false); const animation = useRef(new Animated.Value(0)); const isAnimationCanceled = useRef(false); From 58e6de5e9ffad782eb1464e82a237f7342338a02 Mon Sep 17 00:00:00 2001 From: jczekalski Date: Mon, 24 Jul 2023 10:42:15 +0200 Subject: [PATCH 11/67] replace HOCs with hooks, props destructuring --- src/components/Tooltip/index.js | 36 ++++++++++++---------- src/components/Tooltip/tooltipPropTypes.js | 4 --- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 004d27ef969c..398df07649cf 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -4,13 +4,12 @@ import {Animated} from 'react-native'; import {BoundsObserver} from '@react-ng/bounds-observer'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import Hoverable from '../Hoverable'; -import withWindowDimensions from '../withWindowDimensions'; import * as tooltipPropTypes from './tooltipPropTypes'; import TooltipSense from './TooltipSense'; import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; -import compose from '../../libs/compose'; -import withLocalize from '../withLocalize'; import usePrevious from '../../hooks/usePrevious'; +import useLocalize from '../../hooks/useLocalize'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); @@ -21,6 +20,11 @@ const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); * @returns {ReactNodeLike} */ function Tooltip(props) { + const {children, numberOfLines, maxWidth, text, renderTooltipContent, renderTooltipContentKey} = props; + + const {preferredLocale} = useLocalize(); + const {windowWidth} = useWindowDimensions(); + // Is tooltip already rendered on the page's body? happens once. const [isRendered, setIsRendered] = useState(false); // Is the tooltip currently visible? @@ -37,7 +41,7 @@ function Tooltip(props) { const isTooltipSenseInitiator = useRef(false); const animation = useRef(new Animated.Value(0)); const isAnimationCanceled = useRef(false); - const prevText = usePrevious(props.text); + const prevText = usePrevious(text); /** * Display the tooltip in an animation. @@ -72,11 +76,11 @@ function Tooltip(props) { useEffect(() => { // if the tooltip text changed before the initial animation was finished, then the tooltip won't be shown // we need to show the tooltip again - if (isVisible && isAnimationCanceled.current && props.text && prevText !== props.text) { + if (isVisible && isAnimationCanceled.current && text && prevText !== text) { isAnimationCanceled.current = false; showTooltip(); } - }, [isVisible, props.text, prevText, showTooltip]); + }, [isVisible, text, prevText, showTooltip]); /** * Update the tooltip bounding rectangle @@ -118,8 +122,8 @@ function Tooltip(props) { // Skip the tooltip and return the children if the text is empty, // we don't have a render function or the device does not support hovering - if ((_.isEmpty(props.text) && props.renderTooltipContent == null) || !hasHoverSupport) { - return props.children; + if ((_.isEmpty(text) && renderTooltipContent == null) || !hasHoverSupport) { + return children; } return ( @@ -127,20 +131,20 @@ function Tooltip(props) { {isRendered && ( )} - {props.children} + {children} @@ -160,4 +164,4 @@ function Tooltip(props) { Tooltip.propTypes = tooltipPropTypes.propTypes; Tooltip.defaultProps = tooltipPropTypes.defaultProps; -export default compose(withWindowDimensions, withLocalize)(memo(Tooltip)); +export default memo(Tooltip); diff --git a/src/components/Tooltip/tooltipPropTypes.js b/src/components/Tooltip/tooltipPropTypes.js index f9a1847df242..af18c4cfa412 100644 --- a/src/components/Tooltip/tooltipPropTypes.js +++ b/src/components/Tooltip/tooltipPropTypes.js @@ -1,5 +1,4 @@ import PropTypes from 'prop-types'; -import {windowDimensionsPropTypes} from '../withWindowDimensions'; import variables from '../../styles/variables'; import CONST from '../../CONST'; @@ -13,9 +12,6 @@ const propTypes = { /** Children to wrap with Tooltip. */ children: PropTypes.node.isRequired, - /** Props inherited from withWindowDimensions */ - ...windowDimensionsPropTypes, - /** Any additional amount to manually adjust the horizontal position of the tooltip. A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ shiftHorizontal: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), From 1b8169e333961898968c957efa083327f23b99dd Mon Sep 17 00:00:00 2001 From: Aswin S Date: Wed, 26 Jul 2023 13:40:48 +0530 Subject: [PATCH 12/67] fix: handle path prefixes for split bill, request money and new chat --- .well-known/apple-app-site-association | 12 ++++++++++++ android/app/src/main/AndroidManifest.xml | 10 ++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association index 8318b394d778..cf6bdf1dedef 100644 --- a/.well-known/apple-app-site-association +++ b/.well-known/apple-app-site-association @@ -47,6 +47,18 @@ { "/": "/concierge/*", "comment": "Concierge" + }, + { + "/": "/split/*", + "comment": "Split Bill" + }, + { + "/": "/request/*", + "comment": "Request Money" + }, + { + "/": "/new/*", + "comment": "New Chat" } ] } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b95d68c9c935..3597d6be924d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -54,7 +54,7 @@ - + @@ -63,10 +63,13 @@ + + + - + @@ -75,6 +78,9 @@ + + + From 9271651f9ed4d35a608b4035e5e8137c6bad2c0f Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Wed, 26 Jul 2023 21:05:14 +0200 Subject: [PATCH 13/67] update keybordDismissResponder as per review requests --- src/components/ScreenWrapper/index.js | 72 +++++++++++++-------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index ffc49eee1983..98ef7c8c7dc5 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -33,17 +33,10 @@ class ScreenWrapper extends React.Component { this.keyboardDissmissPanResponder = PanResponder.create({ onMoveShouldSetPanResponderCapture: (e, gestureState) => { const isHorizontalSwipe = Math.abs(gestureState.dx) > Math.abs(gestureState.dy); - if (isHorizontalSwipe && this.props.shouldDismissKeyboardBeforeClose) { - return true; - } - return false; - }, - onPanResponderGrant: () => { - if (!Keyboard.isVisible() || !Browser.isMobile()) { - return; - } - Keyboard.dismiss(); + const shouldDismissKeyboard = this.props.shouldDismissKeyboardBeforeClose && this.props.isKeyboardShown && Browser.isMobile(); + return isHorizontalSwipe && shouldDismissKeyboard; }, + onPanResponderGrant: Keyboard.dismiss, }); this.state = { @@ -112,36 +105,39 @@ class ScreenWrapper extends React.Component { } return ( - - + - - - {this.props.environment === CONST.ENVIRONMENT.DEV && } - {this.props.environment === CONST.ENVIRONMENT.DEV && } - { - // If props.children is a function, call it to provide the insets to the children. - _.isFunction(this.props.children) - ? this.props.children({ - insets, - safeAreaPaddingBottomStyle, - didScreenTransitionEnd: this.state.didScreenTransitionEnd, - }) - : this.props.children - } - {this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && } - - + + + {this.props.environment === CONST.ENVIRONMENT.DEV && } + {this.props.environment === CONST.ENVIRONMENT.DEV && } + { + // If props.children is a function, call it to provide the insets to the children. + _.isFunction(this.props.children) + ? this.props.children({ + insets, + safeAreaPaddingBottomStyle, + didScreenTransitionEnd: this.state.didScreenTransitionEnd, + }) + : this.props.children + } + {this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && } + + + ); }} From 15f1120f52d5c891ec6c28a87869ba828376018a Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 28 Jul 2023 14:09:16 +0200 Subject: [PATCH 14/67] move style to wrapper view --- src/components/ScreenWrapper/index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js index 98ef7c8c7dc5..618b2b97d9f0 100644 --- a/src/components/ScreenWrapper/index.js +++ b/src/components/ScreenWrapper/index.js @@ -105,10 +105,13 @@ class ScreenWrapper extends React.Component { } return ( - // eslint-disable-next-line react/jsx-props-no-spreading - + From 725f8eba51c59e5e98d5c85deb5da271067b1aee Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 31 Jul 2023 22:29:03 +0200 Subject: [PATCH 15/67] add routes --- src/ROUTES.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ROUTES.js b/src/ROUTES.js index 90af74efd22e..e4fa03729919 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -15,6 +15,7 @@ const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods'; const SETTINGS_STATUS = 'settings/profile/status'; const SETTINGS_STATUS_SET = 'settings/profile/status/set'; const SETTINGS_STATUS_CLEAR_AFTER = 'settings/profile/status/clear-after'; +const SETTINGS_STATUS_CUSTOM_CLEAR_AFTER = 'settings/profile/status/clear-after/custom'; const SETTINGS_STATUS_SET_TIME = 'settings/profile/status/set-time'; const SETTINGS_STATUS_SET_DATE = 'settings/profile/status/set-date'; @@ -66,6 +67,7 @@ export default { SETTINGS_STATUS, SETTINGS_STATUS_SET, SETTINGS_STATUS_CLEAR_AFTER, + SETTINGS_STATUS_CUSTOM_CLEAR_AFTER, SETTINGS_STATUS_SET_TIME, SETTINGS_STATUS_SET_DATE, NEW_GROUP: 'new/group', From a5cd5b41e652110a72a0e8ab9bd71a12f62db8d8 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 1 Aug 2023 14:42:12 +0300 Subject: [PATCH 16/67] Update expensify-common --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48c4befc96be..9236fbaa3045 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#98d8fea356f114f8b5b0cea889a41b355e5daf58", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#56db2a0fc9df6b4270a99e4d3a9a7b0730ad2aa4", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "jest-when": "^3.5.2", @@ -24387,8 +24387,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#98d8fea356f114f8b5b0cea889a41b355e5daf58", - "integrity": "sha512-gs3xWgNCjLJsgB9j/oIZaGxQXNM61MapC0eGqtFHgKEwHy6rt08zyZq5CZCow4PVTtF8xgWgkpVifti2dr43cQ==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#56db2a0fc9df6b4270a99e4d3a9a7b0730ad2aa4", + "integrity": "sha512-krP/XqFQRkgt83FEyuEIaVsOAfqTxlqhqDHTMm0/XZWA29bR7WB56AYsDNDHcRS7RVmudBtG9lUbEROeA8dApg==", "license": "MIT", "dependencies": { "classnames": "2.3.1", @@ -59954,9 +59954,9 @@ } }, "expensify-common": { - "version": "git+ssh://git@github.com/Expensify/expensify-common.git#98d8fea356f114f8b5b0cea889a41b355e5daf58", - "integrity": "sha512-gs3xWgNCjLJsgB9j/oIZaGxQXNM61MapC0eGqtFHgKEwHy6rt08zyZq5CZCow4PVTtF8xgWgkpVifti2dr43cQ==", - "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#98d8fea356f114f8b5b0cea889a41b355e5daf58", + "version": "git+ssh://git@github.com/Expensify/expensify-common.git#56db2a0fc9df6b4270a99e4d3a9a7b0730ad2aa4", + "integrity": "sha512-krP/XqFQRkgt83FEyuEIaVsOAfqTxlqhqDHTMm0/XZWA29bR7WB56AYsDNDHcRS7RVmudBtG9lUbEROeA8dApg==", + "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#56db2a0fc9df6b4270a99e4d3a9a7b0730ad2aa4", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", diff --git a/package.json b/package.json index 772b441667dc..8a457ebdb99e 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#98d8fea356f114f8b5b0cea889a41b355e5daf58", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#56db2a0fc9df6b4270a99e4d3a9a7b0730ad2aa4", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "jest-when": "^3.5.2", From 10b17791a665d431a47305229d926593805d6c8b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 1 Aug 2023 21:05:36 +0200 Subject: [PATCH 17/67] add const --- src/CONST.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/CONST.js b/src/CONST.js index 39d190788b41..7e6c090478d3 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -2545,6 +2545,14 @@ const CONST = { TRANSLATION_KEYS: { ATTACHMENT: 'common.attachment', }, + CUSTOM_STATUS_TYPES: { + NEVER: 'never', + THIRTY_MINUTES: 'thirtyMinutes', + ONE_HOUR: 'oneHour', + AFTER_TODAY: 'afterToday', + AFTER_WEEK: 'afterWeek', + CUSTOM: 'custom', + }, }; export default CONST; From f054f6f7eaa79da8f75af2f6e1cd4044261a3e98 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 1 Aug 2023 21:06:51 +0200 Subject: [PATCH 18/67] update onyx keys --- src/ONYXKEYS.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index dd73bde936f9..b1000d6e67bc 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -42,6 +42,9 @@ export default { // Has information about the network status (offline/online) NETWORK: 'network', + // Contains all the personalDetails of the user + PERSONAL_DETAILS: 'personalDetails', + // Contains all the personalDetails the user has access to, keyed by accountID PERSONAL_DETAILS_LIST: 'personalDetailsList', @@ -212,6 +215,9 @@ export default { MONEY_REQUEST_DESCRIPTION_FORM: 'moneyRequestDescriptionForm', NEW_CONTACT_METHOD_FORM: 'newContactMethodForm', PAYPAL_FORM: 'payPalForm', + SETTINGS_STATUS_SET_FORM: 'settingsStatusSetForm', + SETTINGS_STATUS_CLEAR_AFTER_FORM: 'settingsStatusClearAfterForm', + SETTINGS_STATUS_SET_CLEAR_AFTER_FORM: 'settingsStatusSetClearAfterForm', }, // Whether we should show the compose input or not From f5bdc40ed5d5622390bc2e3ad024209e9b5629fc Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 3 Aug 2023 20:14:32 +0200 Subject: [PATCH 19/67] add svg --- assets/images/money-stack.svg | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 assets/images/money-stack.svg diff --git a/assets/images/money-stack.svg b/assets/images/money-stack.svg new file mode 100644 index 000000000000..b9a93c76198c --- /dev/null +++ b/assets/images/money-stack.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 87fcf743ff720c12a44ce496bf0b2e349e6bcc54 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 3 Aug 2023 20:16:47 +0200 Subject: [PATCH 20/67] add translations --- src/languages/en.js | 4 ++++ src/languages/es.js | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 4d218a6e53e2..14987d02a422 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -842,6 +842,10 @@ export default { status: 'Status', setStatusTitle: 'Set your status', statusExplanation: "Add an emoji to give your colleagues and friends an easy way to know what's going on. You can optionally add a message too!", + today: 'Today', + clearStatus: 'Clear status', + save: 'Save', + message: 'Message', }, stepCounter: ({step, total, text}) => { let result = `Step ${step}`; diff --git a/src/languages/es.js b/src/languages/es.js index bd89ede675d5..76eecb08628e 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -842,9 +842,15 @@ export default { setPasswordLinkInvalid: 'El enlace para configurar tu contraseña ha expirado. Te hemos enviado un nuevo enlace a tu correo.', validateAccount: 'Verificar cuenta', }, - StatusPage: { - status: 'Estado', - }, + statusPage: { + status: 'Estado', + setStatusTitle: 'Establece tu estado', + statusExplanation: "Agrega un emoji para que tus colegas y amigos puedan saber fácilmente qué está pasando. ¡También puedes agregar un mensaje opcionalmente!", + today: 'Hoy', + clearStatus: 'Borrar estado', + save: 'Guardar', + message: 'Mensaje', + }, stepCounter: ({step, total, text}) => { let result = `Paso ${step}`; From bd5ff5d7774c221c265e63c0d318a25f0448abb8 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 3 Aug 2023 20:18:50 +0200 Subject: [PATCH 21/67] update StatusPage --- .../Profile/CustomStatus/StatusPage.js | 128 ++++++++++++++---- src/styles/styles.js | 15 ++ 2 files changed, 113 insertions(+), 30 deletions(-) diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index bb696a16a0e2..c6c0ec978770 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -1,45 +1,113 @@ -import React from 'react'; +import React, {useMemo, useCallback} from 'react'; import {View} from 'react-native'; -import ScreenWrapper from '../../../../components/ScreenWrapper'; -import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; -import ROUTES from '../../../../ROUTES'; -import Navigation from '../../../../libs/Navigation/Navigation'; -import styles from '../../../../styles/styles'; -import Text from '../../../../components/Text'; +import {withOnyx} from 'react-native-onyx'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '../../../../components/withCurrentUserPersonalDetails'; import MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription'; +import StaticHeaderPageLayout from '../../../../components/StaticHeaderPageLayout'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import withLocalize from '../../../../components/withLocalize'; +import MenuItem from '../../../../components/MenuItem'; +import Button from '../../../../components/Button'; +import Text from '../../../../components/Text'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import * as User from '../../../../libs/actions/User'; +import MobileBackgroundImage from '../../../../../assets/images/money-stack.svg'; +import themeColors from '../../../../styles/themes/default'; import useLocalize from '../../../../hooks/useLocalize'; +import styles from '../../../../styles/styles'; +import compose from '../../../../libs/compose'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import ROUTES from '../../../../ROUTES'; -function StatusPage() { +const propTypes = { + ...withCurrentUserPersonalDetailsPropTypes, +}; + +function StatusPage(props) { const localize = useLocalize(); - return ( - - Navigation.goBack(ROUTES.SETTINGS_PROFILE)} - /> + const defaultEmoji = props.draftStatus?.emojiCode || props.currentUserPersonalDetails?.status?.emojiCode || ''; + const defaultText = props.draftStatus?.text || props.currentUserPersonalDetails?.status?.text || ''; + const hasDraftStatus = !!defaultEmoji || !!defaultText; - {localize.translate('statusPage.setStatusTitle')} - {localize.translate('statusPage.statusExplanation')} + const updateStatus = useCallback(() => { + // TODO: part of next PR + const endOfDay = new Date(); + endOfDay.setHours(23, 59, 59, 999); + User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji, clearAfter: endOfDay}); + + User.clearDraftCustomStatus(); + Navigation.goBack(ROUTES.SETTINGS); + }, [defaultText, defaultEmoji]); + + const clearStatus = () => { + User.clearCustomStatus(); + User.clearDraftCustomStatus(); + }; - - Navigation.navigate(ROUTES.SETTINGS_STATUS_SET)} - /> - Navigation.navigate(ROUTES.SETTINGS_STATUS_CLEAR_AFTER)} + const footerComponent = useMemo( + () => + hasDraftStatus ? ( +