From c7b9245f49b38fae0dd03962efb84c0ca9c6bfe1 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Mon, 5 Jun 2023 19:28:38 +0530 Subject: [PATCH 001/197] Migrate NewChatPage to functional component --- src/pages/NewChatPage.js | 266 +++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 149 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 1765c700221e..d241c46415f4 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {Component} from 'react'; +import React, {useState, useEffect, useMemo} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -44,80 +44,57 @@ const defaultProps = { reports: {}, }; -class NewChatPage extends Component { - constructor(props) { - super(props); - - this.toggleOption = this.toggleOption.bind(this); - this.createChat = this.createChat.bind(this); - this.createGroup = this.createGroup.bind(this); - this.updateOptionsWithSearchTerm = this.updateOptionsWithSearchTerm.bind(this); - this.excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); - - const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( - props.reports, - props.personalDetails, - props.betas, - '', - [], - this.props.isGroupChat ? this.excludedGroupEmails : [], - ); - this.state = { - searchTerm: '', - recentReports, - personalDetails, - selectedOptions: [], - userToInvite, - }; - } - - componentDidUpdate(prevProps) { - if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { - return; - } - this.updateOptionsWithSearchTerm(this.state.searchTerm); - } - - /** - * Returns the sections needed for the OptionsSelector - * - * @param {Boolean} maxParticipantsReached - * @returns {Array} - */ - getSections(maxParticipantsReached) { - const sections = []; +const NewChatPage = (props) => { + const [searchTerm, setSearchTerm] = useState(''); + const [filteredRecentReports, setFilteredRecentReports] = useState([]); + const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); + const [filteredUserToInvite, setFilteredUserToInvite] = useState(); + const [selectedOptions, setSelectedOptions] = useState([]); + + const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); + const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; + const headerMessage = OptionsListUtils.getHeaderMessage( + filteredPersonalDetails.length + filteredRecentReports.length !== 0, + Boolean(filteredUserToInvite), + searchTerm, + maxParticipantsReached, + ); + const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(props.personalDetails); + + const sections = useMemo(() => { + const sectionsList = []; let indexOffset = 0; - if (this.props.isGroupChat) { - sections.push({ + if (props.isGroupChat) { + sectionsList.push({ title: undefined, - data: this.state.selectedOptions, - shouldShow: !_.isEmpty(this.state.selectedOptions), + data: selectedOptions, + shouldShow: !_.isEmpty(selectedOptions), indexOffset, }); - indexOffset += this.state.selectedOptions.length; + indexOffset += selectedOptions.length; if (maxParticipantsReached) { - return sections; + return sectionsList; } } // Filtering out selected users from the search results - const filterText = _.reduce(this.state.selectedOptions, (str, {login}) => `${str} ${login}`, ''); - const recentReportsWithoutSelected = _.filter(this.state.recentReports, ({login}) => !filterText.includes(login)); - const personalDetailsWithoutSelected = _.filter(this.state.personalDetails, ({login}) => !filterText.includes(login)); - const hasUnselectedUserToInvite = this.state.userToInvite && !filterText.includes(this.state.userToInvite.login); + const filterText = _.reduce(selectedOptions, (str, {login}) => `${str} ${login}`, ''); + const recentReportsWithoutSelected = _.filter(filteredRecentReports, ({login}) => !filterText.includes(login)); + const personalDetailsWithoutSelected = _.filter(filteredPersonalDetails, ({login}) => !filterText.includes(login)); + const hasUnselectedUserToInvite = filteredUserToInvite && !filterText.includes(filteredUserToInvite.login); - sections.push({ - title: this.props.translate('common.recents'), + sectionsList.push({ + title: props.translate('common.recents'), data: recentReportsWithoutSelected, shouldShow: !_.isEmpty(recentReportsWithoutSelected), indexOffset, }); indexOffset += recentReportsWithoutSelected.length; - sections.push({ - title: this.props.translate('common.contacts'), + sectionsList.push({ + title: props.translate('common.contacts'), data: personalDetailsWithoutSelected, shouldShow: !_.isEmpty(personalDetailsWithoutSelected), indexOffset, @@ -125,67 +102,61 @@ class NewChatPage extends Component { indexOffset += personalDetailsWithoutSelected.length; if (hasUnselectedUserToInvite) { - sections.push({ + sectionsList.push({ title: undefined, - data: [this.state.userToInvite], + data: [filteredUserToInvite], shouldShow: true, indexOffset, }); } - return sections; - } + return sectionsList; + // eslint-disable-next-line react-hooks/exhaustive-deps -- to avoid destructuring props and adding all 'props' as a dependency + }, [props.isGroupChat, props.translate, selectedOptions, filteredRecentReports, filteredPersonalDetails, filteredUserToInvite, maxParticipantsReached]); - updateOptionsWithSearchTerm(searchTerm = '') { + const updateOptionsWithSearchTerm = (newSearchTerm = '') => { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( - this.props.reports, - this.props.personalDetails, - this.props.betas, - searchTerm, + props.reports, + props.personalDetails, + props.betas, + newSearchTerm, [], - this.props.isGroupChat ? this.excludedGroupEmails : [], + props.isGroupChat ? excludedGroupEmails : [], ); - this.setState({ - searchTerm, - userToInvite, - recentReports, - personalDetails, - }); - } + setSearchTerm(newSearchTerm); + setFilteredRecentReports(recentReports); + setFilteredPersonalDetails(personalDetails); + setFilteredUserToInvite(userToInvite); + }; /** * Removes a selected option from list if already selected. If not already selected add this option to the list. * @param {Object} option */ - toggleOption(option) { - this.setState((prevState) => { - const isOptionInList = _.some(prevState.selectedOptions, (selectedOption) => selectedOption.login === option.login); + const toggleOption = (option) => { + const isOptionInList = _.some(selectedOptions, (selectedOption) => selectedOption.login === option.login); - let newSelectedOptions; + let newSelectedOptions; - if (isOptionInList) { - newSelectedOptions = _.reject(prevState.selectedOptions, (selectedOption) => selectedOption.login === option.login); - } else { - newSelectedOptions = [...prevState.selectedOptions, option]; - } + if (isOptionInList) { + newSelectedOptions = _.reject(selectedOptions, (selectedOption) => selectedOption.login === option.login); + } else { + newSelectedOptions = [...selectedOptions, option]; + } - const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( - this.props.reports, - this.props.personalDetails, - this.props.betas, - prevState.searchTerm, - [], - this.excludedGroupEmails, - ); + const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( + props.reports, + props.personalDetails, + props.betas, + searchTerm, + [], + excludedGroupEmails, + ); - return { - selectedOptions: newSelectedOptions, - recentReports, - personalDetails, - userToInvite, - searchTerm: prevState.searchTerm, - }; - }); + setSelectedOptions(newSelectedOptions); + setFilteredRecentReports(recentReports); + setFilteredPersonalDetails(personalDetails); + setFilteredUserToInvite(userToInvite); } /** @@ -194,7 +165,7 @@ class NewChatPage extends Component { * * @param {Object} option */ - createChat(option) { + const createChat = (option) => { Report.navigateToAndOpenReport([option.login]); } @@ -202,68 +173,65 @@ class NewChatPage extends Component { * Creates a new group chat with all the selected options and the current user, * or navigates to the existing chat if one with those participants already exists. */ - createGroup() { - if (!this.props.isGroupChat) { + const createGroup = () => { + if (!props.isGroupChat) { return; } - const userLogins = _.pluck(this.state.selectedOptions, 'login'); + const userLogins = _.pluck(selectedOptions, 'login'); if (userLogins.length < 1) { return; } Report.navigateToAndOpenReport(userLogins); } - render() { - const maxParticipantsReached = this.state.selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; - const sections = this.getSections(maxParticipantsReached); - const headerMessage = OptionsListUtils.getHeaderMessage( - this.state.personalDetails.length + this.state.recentReports.length !== 0, - Boolean(this.state.userToInvite), - this.state.searchTerm, - maxParticipantsReached, - ); - const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); - - return ( - - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( - <> - Navigation.dismissModal(true)} + useEffect(() => { + updateOptionsWithSearchTerm(searchTerm); + // all dependencies are not added below - + // 1. searchTerm - when searchTerm changes updateOptionsWithSearchTerm is called by OptionsSelector's onChangeText prop + // 2. updateOptionsWithSearchTerm - it will change its reference upon each rerender unnecessarily + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.reports, props.personalDetails]); + + return ( + + {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + <> + Navigation.dismissModal(true)} + /> + 0 ? safeAreaPaddingBottomStyle : {}]}> + (props.isGroupChat ? toggleOption(option) : createChat(option))} + onChangeText={updateOptionsWithSearchTerm} + headerMessage={headerMessage} + boldStyle + shouldFocusOnSelectRow={props.isGroupChat} + shouldShowConfirmButton={props.isGroupChat} + shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} + confirmButtonText={props.translate('newChatPage.createGroup')} + onConfirmSelection={createGroup} + textInputLabel={props.translate('optionsSelector.nameEmailOrPhoneNumber')} + safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} /> - 0 ? safeAreaPaddingBottomStyle : {}]}> - (this.props.isGroupChat ? this.toggleOption(option) : this.createChat(option))} - onChangeText={this.updateOptionsWithSearchTerm} - headerMessage={headerMessage} - boldStyle - shouldFocusOnSelectRow={this.props.isGroupChat} - shouldShowConfirmButton={this.props.isGroupChat} - shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} - confirmButtonText={this.props.translate('newChatPage.createGroup')} - onConfirmSelection={this.createGroup} - textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} - safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} - /> - - - )} - - ); - } -} + + + )} + + ); +}; NewChatPage.propTypes = propTypes; NewChatPage.defaultProps = defaultProps; +NewChatPage.displayName = 'NewChatPage'; export default compose( withLocalize, From a357a85e5f415cd9cf6b4af5982c77659fe121e9 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Mon, 5 Jun 2023 19:49:45 +0530 Subject: [PATCH 002/197] fix linting issues --- src/pages/NewChatPage.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index d241c46415f4..e43d0a81d586 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -144,20 +144,13 @@ const NewChatPage = (props) => { newSelectedOptions = [...selectedOptions, option]; } - const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( - props.reports, - props.personalDetails, - props.betas, - searchTerm, - [], - excludedGroupEmails, - ); + const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions(props.reports, props.personalDetails, props.betas, searchTerm, [], excludedGroupEmails); setSelectedOptions(newSelectedOptions); setFilteredRecentReports(recentReports); setFilteredPersonalDetails(personalDetails); setFilteredUserToInvite(userToInvite); - } + }; /** * Creates a new 1:1 chat with the option and the current user, @@ -167,7 +160,7 @@ const NewChatPage = (props) => { */ const createChat = (option) => { Report.navigateToAndOpenReport([option.login]); - } + }; /** * Creates a new group chat with all the selected options and the current user, @@ -183,7 +176,7 @@ const NewChatPage = (props) => { return; } Report.navigateToAndOpenReport(userLogins); - } + }; useEffect(() => { updateOptionsWithSearchTerm(searchTerm); From 7804df230f1d5f3deeca9d7fb113c3a3a17782ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 5 Jun 2023 18:57:49 +0200 Subject: [PATCH 003/197] initial rewrite of BaseTextInput to FC --- src/components/TextInput/BaseTextInput.js | 658 +++++++++--------- .../TextInput/baseTextInputPropTypes.js | 12 +- 2 files changed, 344 insertions(+), 326 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 91ab2162674f..12cbd9a1fcd6 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {Component} from 'react'; +import React, {useState, useRef, useEffect, useCallback} from 'react'; import {Animated, View, AppState, Keyboard, StyleSheet} from 'react-native'; import Str from 'expensify-common/lib/str'; import RNTextInput from '../RNTextInput'; @@ -21,64 +21,72 @@ import isInputAutoFilled from '../../libs/isInputAutoFilled'; import * as Pressables from '../Pressable'; const PressableWithoutFeedback = Pressables.PressableWithoutFeedback; -class BaseTextInput extends Component { - constructor(props) { - super(props); - - const value = props.value || props.defaultValue || ''; - const activeLabel = props.forceActiveLabel || value.length > 0 || Boolean(props.prefixCharacter); - - this.state = { - isFocused: false, - labelTranslateY: new Animated.Value(activeLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y), - labelScale: new Animated.Value(activeLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE), - passwordHidden: props.secureTextEntry, - textInputWidth: 0, - prefixWidth: 0, - selection: props.selection, - height: variables.componentSizeLarge, - - // Value should be kept in state for the autoGrow feature to work - https://github.com/Expensify/App/pull/8232#issuecomment-1077282006 - value, - }; - this.input = null; - this.isLabelActive = activeLabel; - this.onPress = this.onPress.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onBlur = this.onBlur.bind(this); - this.setValue = this.setValue.bind(this); - this.togglePasswordVisibility = this.togglePasswordVisibility.bind(this); - this.dismissKeyboardWhenBackgrounded = this.dismissKeyboardWhenBackgrounded.bind(this); - this.storePrefixLayoutDimensions = this.storePrefixLayoutDimensions.bind(this); +function dismissKeyboardWhenBackgrounded(nextAppState) { + if (!nextAppState.match(/inactive|background/)) { + return; } - componentDidMount() { - if (this.props.disableKeyboard) { - this.appStateSubscription = AppState.addEventListener('change', this.dismissKeyboardWhenBackgrounded); + Keyboard.dismiss(); +} + +function BaseTextInput(props) { + const initialActiveLabel = props.forceActiveLabel || props.value.length > 0 || Boolean(props.prefixCharacter); + + const [isFocused, setIsFocused] = useState(false); + const [labelTranslateY] = useState(() => new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)); + const [labelScale] = useState(() => new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)); + const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry); + const [textInputWidth, setTextInputWidth] = useState(0); + const [textInputHeight, setTextInputHeight] = useState(); + const [prefixWidth, setPrefixWidth] = useState(0); + const [height, setHeight] = useState(variables.componentSizeLarge); + const [width, setWidth] = useState(); + + const input = useRef(null); + const isLabelActive = useRef(initialActiveLabel); + + useEffect(() => { + let appStateSubscription; + if (props.disableKeyboard) { + appStateSubscription = AppState.addEventListener('change', dismissKeyboardWhenBackgrounded); } // We are manually managing focus to prevent this issue: https://github.com/Expensify/App/issues/4514 - if (!this.props.autoFocus || !this.input) { + if (!props.autoFocus || !input.current) { return; } - if (this.props.shouldDelayFocus) { - this.focusTimeout = setTimeout(() => this.input.focus(), CONST.ANIMATED_TRANSITION); + let focusTimeout; + if (props.shouldDelayFocus) { + focusTimeout = setTimeout(() => input.current.focus(), CONST.ANIMATED_TRANSITION); return; } - this.input.focus(); - } + input.current.focus(); + + return () => { + if (focusTimeout) { + clearTimeout(focusTimeout); + } + + if (!props.disableKeyboard || !appStateSubscription) { + return; + } + + appStateSubscription.remove(); + }; + }, [props.autoFocus, props.disableKeyboard, props.shouldDelayFocus]); - componentDidUpdate(prevProps) { + /* + componentDidUpdate(prevProps) { // Activate or deactivate the label when value is changed programmatically from outside - const inputValue = _.isUndefined(this.props.value) ? this.input.value : this.props.value; - if ((_.isUndefined(inputValue) || this.state.value === inputValue) && _.isEqual(prevProps.selection, this.props.selection)) { + const inputValue = _.isUndefined(props.value) ? this.input.value : props.value; + if ((_.isUndefined(inputValue) || props.value === inputValue) && _.isEqual(prevProps.selection, props.selection)) { return; } // eslint-disable-next-line react/no-did-update-set-state - this.setState({value: inputValue, selection: this.props.selection}); + this.setState({value: inputValue, selection: props.selection}); // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput. if (inputValue === '') { @@ -87,315 +95,317 @@ class BaseTextInput extends Component { if (inputValue) { this.activateLabel(); - } else if (!this.state.isFocused) { + } else if (!props.isFocused) { this.deactivateLabel(); } } - - componentWillUnmount() { - if (this.focusTimeout) { - clearTimeout(this.focusTimeout); - } - - if (!this.props.disableKeyboard || !this.appStateSubscription) { + */ + + const propIsDisabled = props.disabled; + const propOnPress = props.onPress; + const onPress = useCallback( + (event) => { + if (propIsDisabled) { + return; + } + + if (propOnPress) { + propOnPress(event); + } + + if (!event.isDefaultPrevented()) { + input.current.focus(); + } + }, + [propIsDisabled, propOnPress], + ); + + const animateLabel = useCallback( + (translateY, scale) => { + Animated.parallel([ + Animated.spring(props.labelTranslateY, { + toValue: translateY, + duration: styleConst.LABEL_ANIMATION_DURATION, + useNativeDriver: true, + }), + Animated.spring(props.labelScale, { + toValue: scale, + duration: styleConst.LABEL_ANIMATION_DURATION, + useNativeDriver: true, + }), + ]).start(); + }, + [props.labelScale, props.labelTranslateY], + ); + + const activateLabel = useCallback(() => { + if (props.value.length < 0 || isLabelActive) { return; } - this.appStateSubscription.remove(); - } + animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE); + isLabelActive.current = true; + }, [animateLabel, props.value.length]); - onPress(event) { - if (this.props.disabled) { + const deactivateLabel = useCallback(() => { + if (props.forceActiveLabel || props.value.length !== 0 || props.prefixCharacter) { return; } - if (this.props.onPress) { - this.props.onPress(event); - } - - if (!event.isDefaultPrevented()) { - this.input.focus(); - } - } - - onFocus(event) { - if (this.props.onFocus) { - this.props.onFocus(event); - } - this.setState({isFocused: true}); - this.activateLabel(); - } - - onBlur(event) { - if (this.props.onBlur) { - this.props.onBlur(event); - } - this.setState({isFocused: false}); - - // If the text has been supplied by Chrome autofill, the value state is not synced with the value - // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label. - if (!isInputAutoFilled(this.input)) { - this.deactivateLabel(); - } - } - + animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE); + isLabelActive.current = false; + }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, props.value.length]); + + const propOnFocus = props.onFocus; + const onFocus = useCallback( + (event) => { + if (propOnFocus) { + propOnFocus(event); + } + setIsFocused(true); + activateLabel(); + }, + [activateLabel, propOnFocus], + ); + + const propOnBlur = props.onBlur; + const onBlur = useCallback( + (event) => { + if (propOnBlur) { + propOnBlur(event); + } + setIsFocused(false); + + // If the text has been supplied by Chrome autofill, the value state is not synced with the value + // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label. + if (!isInputAutoFilled(input.current)) { + deactivateLabel(); + } + }, + [deactivateLabel, propOnBlur], + ); + + const onLayout = useCallback( + (event) => { + if (!props.autoGrowHeight && props.multiline) { + return; + } + + const layout = event.nativeEvent.layout; + + setWidth((prevWidth) => (props.autoGrowHeight ? layout.width : prevWidth)); + setHeight((prevHeight) => (!props.multiline ? layout.height : prevHeight)); + }, + [props.autoGrowHeight, props.multiline], + ); + + // I feel like this is the region where imperative functions are starting: + + const propOnInputChange = props.onInputChange; /** * Set Value & activateLabel * * @param {String} value * @memberof BaseTextInput */ - setValue(value) { - if (this.props.onInputChange) { - this.props.onInputChange(value); - } - this.setState({value}); - Str.result(this.props.onChangeText, value); - this.activateLabel(); - } - - activateLabel() { - if (this.state.value.length < 0 || this.isLabelActive) { - return; - } - - this.animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE); - this.isLabelActive = true; - } - - deactivateLabel() { - if (this.props.forceActiveLabel || this.state.value.length !== 0 || this.props.prefixCharacter) { - return; - } - - this.animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE); - this.isLabelActive = false; - } - - dismissKeyboardWhenBackgrounded(nextAppState) { - if (!nextAppState.match(/inactive|background/)) { - return; - } - - Keyboard.dismiss(); - } - - animateLabel(translateY, scale) { - Animated.parallel([ - Animated.spring(this.state.labelTranslateY, { - toValue: translateY, - duration: styleConst.LABEL_ANIMATION_DURATION, - useNativeDriver: true, - }), - Animated.spring(this.state.labelScale, { - toValue: scale, - duration: styleConst.LABEL_ANIMATION_DURATION, - useNativeDriver: true, - }), - ]).start(); - } - - togglePasswordVisibility() { - this.setState((prevState) => ({passwordHidden: !prevState.passwordHidden})); - } - - storePrefixLayoutDimensions(event) { - this.setState({prefixWidth: Math.abs(event.nativeEvent.layout.width)}); - } - - render() { - // eslint-disable-next-line react/forbid-foreign-prop-types - const inputProps = _.omit(this.props, _.keys(baseTextInputPropTypes.propTypes)); - const hasLabel = Boolean(this.props.label.length); - const isEditable = _.isUndefined(this.props.editable) ? !this.props.disabled : this.props.editable; - const inputHelpText = this.props.errorText || this.props.hint; - const placeholder = this.props.prefixCharacter || this.state.isFocused || !hasLabel || (hasLabel && this.props.forceActiveLabel) ? this.props.placeholder : null; - const maxHeight = StyleSheet.flatten(this.props.containerStyles).maxHeight; - const textInputContainerStyles = _.reduce( - [ - styles.textInputContainer, - ...this.props.textInputContainerStyles, - this.props.autoGrow && StyleUtils.getWidthStyle(this.state.textInputWidth), - !this.props.hideFocusedState && this.state.isFocused && styles.borderColorFocus, - (this.props.hasError || this.props.errorText) && styles.borderColorDanger, - this.props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight}, - ], - (finalStyles, s) => ({...finalStyles, ...s}), - {}, - ); - const isMultiline = this.props.multiline || this.props.autoGrowHeight; - - return ( - <> - - { + if (propOnInputChange) { + propOnInputChange(value); + } + + // TODO: what is the next line used for? + Str.result(props.onChangeText, value); + activateLabel(); + }, + [activateLabel, propOnInputChange, props.onChangeText], + ); + + const togglePasswordVisibility = useCallback(() => { + setPasswordHidden((prevState) => !prevState.passwordHidden); + }, []); + + const storePrefixLayoutDimensions = useCallback((event) => { + setPrefixWidth(Math.abs(event.nativeEvent.layout.width)); + }, []); + + // TODO: don't do that all here? + // eslint-disable-next-line react/forbid-foreign-prop-types + const inputProps = _.omit(props, _.keys(baseTextInputPropTypes.propTypes)); + const hasLabel = Boolean(props.label.length); + const isEditable = _.isUndefined(props.editable) ? !props.disabled : props.editable; + const inputHelpText = props.errorText || props.hint; + const placeholder = props.prefixCharacter || isFocused || !hasLabel || (hasLabel && props.forceActiveLabel) ? props.placeholder : null; + const maxHeight = StyleSheet.flatten(props.containerStyles).maxHeight; + const textInputContainerStyles = _.reduce( + [ + styles.textInputContainer, + ...props.textInputContainerStyles, + props.autoGrow && StyleUtils.getWidthStyle(textInputWidth), + !props.hideFocusedState && isFocused && styles.borderColorFocus, + (props.hasError || props.errorText) && styles.borderColorDanger, + props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight}, + ], + (finalStyles, s) => ({...finalStyles, ...s}), + {}, + ); + const isMultiline = props.multiline || props.autoGrowHeight; + + return ( + <> + + + - { - if (!this.props.autoGrowHeight && this.props.multiline) { - return; - } - - const layout = event.nativeEvent.layout; - - this.setState((prevState) => ({ - width: this.props.autoGrowHeight ? layout.width : prevState.width, - height: !isMultiline ? layout.height : prevState.height, - })); - }} - style={[ - textInputContainerStyles, - - // When autoGrow is on and minWidth is not supplied, add a minWidth to allow the input to be focusable. - this.props.autoGrow && !textInputContainerStyles.minWidth && styles.mnw2, - ]} - > - {hasLabel ? ( - <> - {/* Adding this background to the label only for multiline text input, - to prevent text overlapping with label when scrolling */} - {isMultiline && ( - - )} - + {/* Adding this background to the label only for multiline text input, + to prevent text overlapping with label when scrolling */} + {isMultiline && ( + - - ) : null} - - {Boolean(this.props.prefixCharacter) && ( - - - {this.props.prefixCharacter} - - )} - { - if (typeof this.props.innerRef === 'function') { - this.props.innerRef(ref); - } else if (this.props.innerRef && _.has(this.props.innerRef, 'current')) { - this.props.innerRef.current = ref; - } - this.input = ref; - }} - // eslint-disable-next-line - {...inputProps} - autoCorrect={this.props.secureTextEntry ? false : this.props.autoCorrect} - placeholder={placeholder} - placeholderTextColor={themeColors.placeholderText} - underlineColorAndroid="transparent" - style={[ - styles.flex1, - styles.w100, - this.props.inputStyle, - (!hasLabel || isMultiline) && styles.pv0, - this.props.prefixCharacter && StyleUtils.getPaddingLeft(this.state.prefixWidth + styles.pl1.paddingLeft), - this.props.secureTextEntry && styles.secureInput, - - // Explicitly remove `lineHeight` from single line inputs so that long text doesn't disappear - // once it exceeds the input space (See https://github.com/Expensify/App/issues/13802) - !isMultiline && {height: this.state.height, lineHeight: undefined}, - - // Stop scrollbar flashing when breaking lines with autoGrowHeight enabled. - this.props.autoGrowHeight && StyleUtils.getAutoGrowHeightInputStyle(this.state.textInputHeight, maxHeight), - ]} - multiline={isMultiline} - maxLength={this.props.maxLength} - onFocus={this.onFocus} - onBlur={this.onBlur} - onChangeText={this.setValue} - secureTextEntry={this.state.passwordHidden} - onPressOut={this.props.onPress} - showSoftInputOnFocus={!this.props.disableKeyboard} - keyboardType={getSecureEntryKeyboardType(this.props.keyboardType, this.props.secureTextEntry, this.state.passwordHidden)} - value={this.state.value} - selection={this.state.selection} - editable={isEditable} - // FormSubmit Enter key handler does not have access to direct props. - // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback. - dataSet={{submitOnEnter: isMultiline && this.props.submitOnEnter}} + - {Boolean(this.props.secureTextEntry) && ( - e.preventDefault()} + + ) : null} + + {Boolean(props.prefixCharacter) && ( + + - - - )} - {!this.props.secureTextEntry && Boolean(this.props.icon) && ( - - - - )} - + {props.prefixCharacter} + + + )} + { + if (typeof props.innerRef === 'function') { + props.innerRef(ref); + } else if (props.innerRef && _.has(props.innerRef, 'current')) { + // eslint-disable-next-line no-param-reassign + props.innerRef.current = ref; + } + input.current = ref; + }} + // eslint-disable-next-line + {...inputProps} + autoCorrect={props.secureTextEntry ? false : props.autoCorrect} + placeholder={placeholder} + placeholderTextColor={themeColors.placeholderText} + underlineColorAndroid="transparent" + style={[ + styles.flex1, + styles.w100, + props.inputStyle, + (!hasLabel || isMultiline) && styles.pv0, + props.prefixCharacter && StyleUtils.getPaddingLeft(prefixWidth + styles.pl1.paddingLeft), + props.secureTextEntry && styles.secureInput, + + // Explicitly remove `lineHeight` from single line inputs so that long text doesn't disappear + // once it exceeds the input space (See https://github.com/Expensify/App/issues/13802) + !isMultiline && {height, lineHeight: undefined}, + + // Stop scrollbar flashing when breaking lines with autoGrowHeight enabled. + props.autoGrowHeight && StyleUtils.getAutoGrowHeightInputStyle(textInputHeight, maxHeight), + ]} + multiline={isMultiline} + maxLength={props.maxLength} + onFocus={onFocus} + onBlur={onBlur} + onChangeText={setValue} + secureTextEntry={passwordHidden} + onPressOut={props.onPress} + showSoftInputOnFocus={!props.disableKeyboard} + keyboardType={getSecureEntryKeyboardType(props.keyboardType, props.secureTextEntry, passwordHidden)} + value={props.value} + selection={props.selection} + editable={isEditable} + // FormSubmit Enter key handler does not have access to direct props. + // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback. + dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}} + /> + {Boolean(props.secureTextEntry) && ( + e.preventDefault()} + > + + + )} + {!props.secureTextEntry && Boolean(props.icon) && ( + + + + )} - - {!_.isEmpty(inputHelpText) && ( - - )} - - {/* - Text input component doesn't support auto grow by default. - We're using a hidden text input to achieve that. - This text view is used to calculate width or height of the input value given textStyle in this component. - This Text component is intentionally positioned out of the screen. - */} - {(this.props.autoGrow || this.props.autoGrowHeight) && ( - // Add +2 to width so that the first digit of amount do not cut off on mWeb - https://github.com/Expensify/App/issues/8158. - this.setState({textInputWidth: e.nativeEvent.layout.width + 2, textInputHeight: e.nativeEvent.layout.height})} - > - {this.state.value || this.props.placeholder} - + + + {!_.isEmpty(inputHelpText) && ( + )} - - ); - } + + {/* + Text input component doesn't support auto grow by default. + We're using a hidden text input to achieve that. + This text view is used to calculate width or height of the input value given textStyle in this component. + This Text component is intentionally positioned out of the screen. + */} + {(props.autoGrow || props.autoGrowHeight) && ( + // Add +2 to width so that the first digit of amount do not cut off on mWeb - https://github.com/Expensify/App/issues/8158. + { + setTextInputWidth(e.nativeEvent.layout.width + 2); + setTextInputHeight(e.nativeEvent.layout.height); + }} + > + {props.value || props.placeholder} + + )} + + ); } +BaseTextInput.displayName = 'BaseTextInput'; BaseTextInput.propTypes = baseTextInputPropTypes.propTypes; BaseTextInput.defaultProps = baseTextInputPropTypes.defaultProps; diff --git a/src/components/TextInput/baseTextInputPropTypes.js b/src/components/TextInput/baseTextInputPropTypes.js index 45720ceb8c47..899bbe532d3c 100644 --- a/src/components/TextInput/baseTextInputPropTypes.js +++ b/src/components/TextInput/baseTextInputPropTypes.js @@ -40,10 +40,18 @@ const propTypes = { /** Disable the virtual keyboard */ disableKeyboard: PropTypes.bool, - /** Autogrow input container length based on the entered text */ + /** + * Autogrow input container length based on the entered text. + * Note: If you use this prop, the text input has to be controlled + * by a value prop. + */ autoGrow: PropTypes.bool, - /** Autogrow input container height based on the entered text */ + /** + * Autogrow input container height based on the entered text + * Note: If you use this prop, the text input has to be controlled + * by a value prop. + */ autoGrowHeight: PropTypes.bool, /** Hide the focus styles on TextInput */ From 880c4c008d5defa3a95f197f2f81fd2702f99be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 5 Jun 2023 19:12:29 +0200 Subject: [PATCH 004/197] fix focus/label not working --- src/components/TextInput/BaseTextInput.js | 47 +++++++++-------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 12cbd9a1fcd6..8431073052ac 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -77,30 +77,6 @@ function BaseTextInput(props) { }; }, [props.autoFocus, props.disableKeyboard, props.shouldDelayFocus]); - /* - componentDidUpdate(prevProps) { - // Activate or deactivate the label when value is changed programmatically from outside - const inputValue = _.isUndefined(props.value) ? this.input.value : props.value; - if ((_.isUndefined(inputValue) || props.value === inputValue) && _.isEqual(prevProps.selection, props.selection)) { - return; - } - - // eslint-disable-next-line react/no-did-update-set-state - this.setState({value: inputValue, selection: props.selection}); - - // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput. - if (inputValue === '') { - this.input.clear(); - } - - if (inputValue) { - this.activateLabel(); - } else if (!props.isFocused) { - this.deactivateLabel(); - } - } - */ - const propIsDisabled = props.disabled; const propOnPress = props.onPress; const onPress = useCallback( @@ -123,23 +99,23 @@ function BaseTextInput(props) { const animateLabel = useCallback( (translateY, scale) => { Animated.parallel([ - Animated.spring(props.labelTranslateY, { + Animated.spring(labelTranslateY, { toValue: translateY, duration: styleConst.LABEL_ANIMATION_DURATION, useNativeDriver: true, }), - Animated.spring(props.labelScale, { + Animated.spring(labelScale, { toValue: scale, duration: styleConst.LABEL_ANIMATION_DURATION, useNativeDriver: true, }), ]).start(); }, - [props.labelScale, props.labelTranslateY], + [labelScale, labelTranslateY], ); const activateLabel = useCallback(() => { - if (props.value.length < 0 || isLabelActive) { + if (props.value.length < 0 || isLabelActive.current) { return; } @@ -199,6 +175,21 @@ function BaseTextInput(props) { [props.autoGrowHeight, props.multiline], ); + useEffect(() => { + // Activate or deactivate the label when value is changed programmatically from outside + + // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput. + if (props.value === '') { + input.current.clear(); + } + + if (props.value) { + activateLabel(); + } else if (!isFocused) { + deactivateLabel(); + } + }, [activateLabel, deactivateLabel, isFocused, props.value]); + // I feel like this is the region where imperative functions are starting: const propOnInputChange = props.onInputChange; From 13d2e602f3c8383503b61bb51ea5fdc14a8e7845 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Tue, 6 Jun 2023 00:13:14 +0530 Subject: [PATCH 005/197] use function instead of const --- src/pages/NewChatPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index e43d0a81d586..6ee132c4abd6 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -44,7 +44,7 @@ const defaultProps = { reports: {}, }; -const NewChatPage = (props) => { +function NewChatPage(props) { const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); From 36eb6eb40529e2d221144bd0ee488cfd81fbc04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 6 Jun 2023 09:36:35 +0200 Subject: [PATCH 006/197] remove comment --- src/components/TextInput/BaseTextInput.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 8431073052ac..0705e607bd4f 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -190,8 +190,6 @@ function BaseTextInput(props) { } }, [activateLabel, deactivateLabel, isFocused, props.value]); - // I feel like this is the region where imperative functions are starting: - const propOnInputChange = props.onInputChange; /** * Set Value & activateLabel From bd6cef713ef2f356215bcbe45b08d467bef60b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 6 Jun 2023 11:04:58 +0200 Subject: [PATCH 007/197] fix text input focus --- src/components/TextInput/BaseTextInput.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 0705e607bd4f..bfddf9bab9c1 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -135,13 +135,13 @@ function BaseTextInput(props) { const propOnFocus = props.onFocus; const onFocus = useCallback( (event) => { + console.log('onFocus'); if (propOnFocus) { propOnFocus(event); } setIsFocused(true); - activateLabel(); }, - [activateLabel, propOnFocus], + [propOnFocus], ); const propOnBlur = props.onBlur; @@ -183,7 +183,7 @@ function BaseTextInput(props) { input.current.clear(); } - if (props.value) { + if (props.value || isFocused) { activateLabel(); } else if (!isFocused) { deactivateLabel(); From 519b1923830f2bd16a1ae10580b7d4247204cdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 6 Jun 2023 13:35:09 +0200 Subject: [PATCH 008/197] fix focus --- src/components/TextInput/BaseTextInput.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index bfddf9bab9c1..80f8ca128421 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -31,7 +31,8 @@ function dismissKeyboardWhenBackgrounded(nextAppState) { } function BaseTextInput(props) { - const initialActiveLabel = props.forceActiveLabel || props.value.length > 0 || Boolean(props.prefixCharacter); + const inputValue = props.value || ''; + const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter); const [isFocused, setIsFocused] = useState(false); const [labelTranslateY] = useState(() => new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)); @@ -115,27 +116,26 @@ function BaseTextInput(props) { ); const activateLabel = useCallback(() => { - if (props.value.length < 0 || isLabelActive.current) { + if (inputValue.length < 0 || isLabelActive.current) { return; } animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE); isLabelActive.current = true; - }, [animateLabel, props.value.length]); + }, [animateLabel, inputValue]); const deactivateLabel = useCallback(() => { - if (props.forceActiveLabel || props.value.length !== 0 || props.prefixCharacter) { + if (props.forceActiveLabel || inputValue.length !== 0 || props.prefixCharacter) { return; } animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE); isLabelActive.current = false; - }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, props.value.length]); + }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, inputValue]); const propOnFocus = props.onFocus; const onFocus = useCallback( (event) => { - console.log('onFocus'); if (propOnFocus) { propOnFocus(event); } @@ -179,16 +179,16 @@ function BaseTextInput(props) { // Activate or deactivate the label when value is changed programmatically from outside // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput. - if (props.value === '') { + if (inputValue === '') { input.current.clear(); } - if (props.value || isFocused) { + if (inputValue || isFocused) { activateLabel(); } else if (!isFocused) { deactivateLabel(); } - }, [activateLabel, deactivateLabel, isFocused, props.value]); + }, [activateLabel, deactivateLabel, inputValue, isFocused]); const propOnInputChange = props.onInputChange; /** From c26fdb9334e6acb5c5cd4f6e8a6a72393ef495ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 7 Jun 2023 10:00:28 +0200 Subject: [PATCH 009/197] fix usage of useCallback --- src/components/TextInput/BaseTextInput.js | 95 ++++++++++------------- 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 80f8ca128421..2208abb38890 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -78,25 +78,6 @@ function BaseTextInput(props) { }; }, [props.autoFocus, props.disableKeyboard, props.shouldDelayFocus]); - const propIsDisabled = props.disabled; - const propOnPress = props.onPress; - const onPress = useCallback( - (event) => { - if (propIsDisabled) { - return; - } - - if (propOnPress) { - propOnPress(event); - } - - if (!event.isDefaultPrevented()) { - input.current.focus(); - } - }, - [propIsDisabled, propOnPress], - ); - const animateLabel = useCallback( (translateY, scale) => { Animated.parallel([ @@ -133,33 +114,39 @@ function BaseTextInput(props) { isLabelActive.current = false; }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, inputValue]); - const propOnFocus = props.onFocus; - const onFocus = useCallback( - (event) => { - if (propOnFocus) { - propOnFocus(event); - } - setIsFocused(true); - }, - [propOnFocus], - ); + const onFocus = (event) => { + if (props.onFocus) { + props.onFocus(event); + } + setIsFocused(true); + }; - const propOnBlur = props.onBlur; - const onBlur = useCallback( - (event) => { - if (propOnBlur) { - propOnBlur(event); - } - setIsFocused(false); + const onBlur = (event) => { + if (props.onBlur) { + props.onBlur(event); + } + setIsFocused(false); - // If the text has been supplied by Chrome autofill, the value state is not synced with the value - // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label. - if (!isInputAutoFilled(input.current)) { - deactivateLabel(); - } - }, - [deactivateLabel, propOnBlur], - ); + // If the text has been supplied by Chrome autofill, the value state is not synced with the value + // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label. + if (!isInputAutoFilled(input.current)) { + deactivateLabel(); + } + }; + + const onPress = (event) => { + if (props.disabled) { + return; + } + + if (props.onPress) { + props.onPress(event); + } + + if (!event.isDefaultPrevented()) { + input.current.focus(); + } + }; const onLayout = useCallback( (event) => { @@ -190,25 +177,21 @@ function BaseTextInput(props) { } }, [activateLabel, deactivateLabel, inputValue, isFocused]); - const propOnInputChange = props.onInputChange; /** * Set Value & activateLabel * * @param {String} value * @memberof BaseTextInput */ - const setValue = useCallback( - (value) => { - if (propOnInputChange) { - propOnInputChange(value); - } + const setValue = (value) => { + if (props.onInputChange) { + props.onInputChange(value); + } - // TODO: what is the next line used for? - Str.result(props.onChangeText, value); - activateLabel(); - }, - [activateLabel, propOnInputChange, props.onChangeText], - ); + // TODO: what is the next line used for? + Str.result(props.onChangeText, value); + activateLabel(); + }; const togglePasswordVisibility = useCallback(() => { setPasswordHidden((prevState) => !prevState.passwordHidden); From a75802f4c84f55cf13f9a215e8b2a8fe4cf1b24a Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Wed, 7 Jun 2023 20:34:31 +0530 Subject: [PATCH 010/197] fix linting issues --- src/pages/NewChatPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index ce5b11c703ad..80e39422821b 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -216,7 +216,7 @@ function NewChatPage(props) { )} ); -}; +} NewChatPage.propTypes = propTypes; NewChatPage.defaultProps = defaultProps; From 7b00c12f563da5c5e7bc350589e8b3506f5191a3 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 5 Jun 2023 14:57:04 +0300 Subject: [PATCH 011/197] Add `REPORT_ATTACHMENT` route --- src/ROUTES.js | 2 + .../HTMLRenderers/ImageRenderer.js | 41 ++++++++----------- .../Navigation/AppNavigator/AuthScreens.js | 10 +++++ src/libs/Navigation/linkingConfig.js | 2 + 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index bd0ad304c2f5..ad8608593614 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -68,6 +68,8 @@ export default { getReportRoute: (reportID) => `r/${reportID}`, REPORT_WITH_ID_DETAILS_SHARE_CODE: 'r/:reportID/details/shareCode', getReportShareCodeRoute: (reportID) => `r/${reportID}/details/shareCode`, + REPORT_ATTACHMENT: 'r/:reportID/attachment', + getReportAttachmentRoute: (reportID, source) => `r/${reportID}/attachment?source=${encodeURI(source)}`, SELECT_YEAR: 'select-year', getYearSelectionRoute: (minYear, maxYear, currYear, backTo) => `select-year?min=${minYear}&max=${maxYear}&year=${currYear}&backTo=${backTo}`, diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js index 4342b86ec6f2..15ca2bee58f8 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js @@ -1,6 +1,6 @@ import React from 'react'; +import Navigation from '../../../libs/Navigation/Navigation'; import htmlRendererPropTypes from './htmlRendererPropTypes'; -import AttachmentModal from '../../AttachmentModal'; import styles from '../../../styles/styles'; import ThumbnailImage from '../../ThumbnailImage'; import PressableWithoutFocus from '../../PressableWithoutFocus'; @@ -8,6 +8,7 @@ import CONST from '../../../CONST'; import {ShowContextMenuContext, showContextMenuForReport} from '../../ShowContextMenuContext'; import tryResolveUrlFromApiRoot from '../../../libs/tryResolveUrlFromApiRoot'; import * as ReportUtils from '../../../libs/ReportUtils'; +import ROUTES from '../../../ROUTES'; const ImageRenderer = (props) => { const htmlAttribs = props.tnode.attributes; @@ -30,7 +31,6 @@ const ImageRenderer = (props) => { // control and thus require no authToken to verify access. // const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]); - const originalFileName = htmlAttribs['data-name']; // Files created/uploaded/hosted by App should resolve from API ROOT. Other URLs aren't modified const previewSource = tryResolveUrlFromApiRoot(htmlAttribs.src); @@ -51,29 +51,22 @@ const ImageRenderer = (props) => { ) : ( {({anchor, report, action, checkIfContextMenuActive}) => ( - { + const route = ROUTES.getReportAttachmentRoute(report.reportID, source); + Navigation.navigate(route); + }} + onLongPress={(event) => showContextMenuForReport(event, anchor, report.reportID, action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} > - {({show}) => ( - showContextMenuForReport(event, anchor, report.reportID, action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} - > - - - )} - + + )} ); diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 8539f384504c..385290943299 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -261,6 +261,16 @@ class AuthScreens extends React.Component { component={RightModalNavigator} listeners={modalScreenListeners} /> + + { + const ReportAttachments = require('../../../pages/home/report/ReportAttachments').default; + return ReportAttachments; + }} + listeners={modalScreenListeners} + /> ); } diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 666506ca1ab8..a8c1e8abdeff 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -23,6 +23,7 @@ export default { [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID, + Report_Attachments: ROUTES.REPORT_ATTACHMENT, }, }, [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { @@ -340,6 +341,7 @@ export default { }, }, }, + [SCREENS.NOT_FOUND]: '*', }, }, }; From 1aec799a831efdd4ffca27d2900d52f6c90f8095 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 5 Jun 2023 15:44:32 +0300 Subject: [PATCH 012/197] Update parent modal attachment state The Carousel already updates the parent component this way, when attachments change Updating it here allows the parent component to not need `isAuthenticated` and `originalFileName` for the initial image source provided, since they are obtained here as well --- src/components/AttachmentCarousel/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/AttachmentCarousel/index.js b/src/components/AttachmentCarousel/index.js index 778044586172..ed80bce87697 100644 --- a/src/components/AttachmentCarousel/index.js +++ b/src/components/AttachmentCarousel/index.js @@ -177,6 +177,9 @@ class AttachmentCarousel extends React.Component { throw new Error('Attachment not found'); } + // Update the parent modal's state with the source and name from the mapped attachments + this.props.onNavigate(attachments[page]); + return { page, attachments, From eb3e0924db1b8bb7d5a0a1f9959fee548d54b6e8 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 5 Jun 2023 16:12:39 +0300 Subject: [PATCH 013/197] AttachmentModal: add `defaultOpen` prop --- src/components/AttachmentModal.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index b29c910e83e4..e56fa19590e6 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -36,6 +36,9 @@ const propTypes = { /** Optional callback to fire when we want to preview an image and approve it for use. */ onConfirm: PropTypes.func, + /** Whether the modal should be open by default */ + defaultOpen: PropTypes.bool, + /** Optional callback to fire when we want to do something after modal show. */ onModalShow: PropTypes.func, @@ -68,6 +71,7 @@ const propTypes = { const defaultProps = { source: '', onConfirm: null, + defaultOpen: false, originalFileName: '', isAuthTokenRequired: false, allowDownload: false, @@ -82,7 +86,7 @@ class AttachmentModal extends PureComponent { super(props); this.state = { - isModalOpen: false, + isModalOpen: this.props.defaultOpen, shouldLoadAttachment: false, isAttachmentInvalid: false, isAuthTokenRequired: props.isAuthTokenRequired, From 2c758a4b7511f095760f6b49ffc387b6a8187b2d Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 5 Jun 2023 16:16:34 +0300 Subject: [PATCH 014/197] AttachmentModal: make passing `children` optional --- src/components/AttachmentModal.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index e56fa19590e6..71e479b71dc5 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -49,7 +49,7 @@ const propTypes = { originalFileName: PropTypes.string, /** A function as a child to pass modal launching methods to */ - children: PropTypes.func.isRequired, + children: PropTypes.func, /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, @@ -73,6 +73,7 @@ const defaultProps = { onConfirm: null, defaultOpen: false, originalFileName: '', + children: null, isAuthTokenRequired: false, allowDownload: false, headerTitle: null, @@ -341,7 +342,7 @@ class AttachmentModal extends PureComponent { shouldShowCancelButton={false} /> - {this.props.children({ + {this.props.children && this.props.children({ displayFileInModal: this.validateAndDisplayFileToUpload, show: () => { this.setState({isModalOpen: true}); From 236df33afb4d3ef60ad8a503f26bcecab133c3b8 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 5 Jun 2023 16:18:46 +0300 Subject: [PATCH 015/197] Add `src/pages/home/report/ReportAttachments.js` modal screen --- src/pages/home/report/ReportAttachments.js | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/pages/home/report/ReportAttachments.js diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js new file mode 100644 index 000000000000..217028841c83 --- /dev/null +++ b/src/pages/home/report/ReportAttachments.js @@ -0,0 +1,37 @@ +import React from 'react'; +import _ from 'underscore'; +import PropTypes from 'prop-types'; +import AttachmentModal from '../../../components/AttachmentModal'; +import Navigation from '../../../libs/Navigation/Navigation'; + +const propTypes = { + /** Navigation route context info provided by react navigation */ + route: PropTypes.shape({ + /** Route specific parameters used on this screen */ + params: PropTypes.shape({ + /** The report ID which the attachment is associated with */ + reportID: PropTypes.string.isRequired, + /** The uri encoded source of the attachment */ + source: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, +}; + +function ReportAttachments(props) { + const reportID = _.get(props, ['route', 'params', 'reportID']); + const source = decodeURI(_.get(props, ['route', 'params', 'source'])); + + return ( + + ); +} + +ReportAttachments.propTypes = propTypes; +ReportAttachments.displayName = 'ReportAttachments'; + +export default ReportAttachments; From 2b164e98521d41c83e38e40d4f124d96e63530e1 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Tue, 6 Jun 2023 22:05:47 +0300 Subject: [PATCH 016/197] Apply prettier on the changes --- src/components/AttachmentModal.js | 13 +++++++------ src/pages/home/report/ReportAttachments.js | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 71e479b71dc5..a6022f4a8fce 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -342,12 +342,13 @@ class AttachmentModal extends PureComponent { shouldShowCancelButton={false} /> - {this.props.children && this.props.children({ - displayFileInModal: this.validateAndDisplayFileToUpload, - show: () => { - this.setState({isModalOpen: true}); - }, - })} + {this.props.children && + this.props.children({ + displayFileInModal: this.validateAndDisplayFileToUpload, + show: () => { + this.setState({isModalOpen: true}); + }, + })} ); } diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index 217028841c83..b591a8740a4e 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -27,7 +27,8 @@ function ReportAttachments(props) { defaultOpen reportID={reportID} source={source} - onModalHide={Navigation.dismissModal} /> + onModalHide={Navigation.dismissModal} + /> ); } From 7a3f3f3fff1320e0159cb1d5c34e2d8ae87ab3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 11:17:27 +0200 Subject: [PATCH 017/197] remove todo --- src/components/TextInput/BaseTextInput.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 2208abb38890..12206f82d163 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -188,7 +188,6 @@ function BaseTextInput(props) { props.onInputChange(value); } - // TODO: what is the next line used for? Str.result(props.onChangeText, value); activateLabel(); }; From 3886aaf84de1d827b3aa07a70ab2e50c43d1e9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 11:19:10 +0200 Subject: [PATCH 018/197] use named import directly --- src/components/TextInput/BaseTextInput.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 12206f82d163..e55b81f78925 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -20,8 +20,6 @@ import FormHelpMessage from '../FormHelpMessage'; import isInputAutoFilled from '../../libs/isInputAutoFilled'; import * as Pressables from '../Pressable'; -const PressableWithoutFeedback = Pressables.PressableWithoutFeedback; - function dismissKeyboardWhenBackgrounded(nextAppState) { if (!nextAppState.match(/inactive|background/)) { return; @@ -225,7 +223,7 @@ function BaseTextInput(props) { return ( <> - - + {!_.isEmpty(inputHelpText) && ( Date: Mon, 12 Jun 2023 11:20:02 +0200 Subject: [PATCH 019/197] use function directly --- src/components/TextInput/BaseTextInput.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index e55b81f78925..2fc5c395981d 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -20,14 +20,6 @@ import FormHelpMessage from '../FormHelpMessage'; import isInputAutoFilled from '../../libs/isInputAutoFilled'; import * as Pressables from '../Pressable'; -function dismissKeyboardWhenBackgrounded(nextAppState) { - if (!nextAppState.match(/inactive|background/)) { - return; - } - - Keyboard.dismiss(); -} - function BaseTextInput(props) { const inputValue = props.value || ''; const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter); @@ -48,7 +40,13 @@ function BaseTextInput(props) { useEffect(() => { let appStateSubscription; if (props.disableKeyboard) { - appStateSubscription = AppState.addEventListener('change', dismissKeyboardWhenBackgrounded); + appStateSubscription = AppState.addEventListener('change', (nextAppState) => { + if (!nextAppState.match(/inactive|background/)) { + return; + } + + Keyboard.dismiss(); + }); } // We are manually managing focus to prevent this issue: https://github.com/Expensify/App/issues/4514 From 21ded8cef5e59393745387b13972c116db89c277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 11:22:09 +0200 Subject: [PATCH 020/197] only autoFocus on mount --- src/components/TextInput/BaseTextInput.js | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 2fc5c395981d..b30da00048b5 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -49,6 +49,17 @@ function BaseTextInput(props) { }); } + return () => { + if (!props.disableKeyboard || !appStateSubscription) { + return; + } + + appStateSubscription.remove(); + }; + }, [props.disableKeyboard]); + + // AutoFocus which only works on mount: + useEffect(() => { // We are manually managing focus to prevent this issue: https://github.com/Expensify/App/issues/4514 if (!props.autoFocus || !input.current) { return; @@ -62,17 +73,14 @@ function BaseTextInput(props) { input.current.focus(); return () => { - if (focusTimeout) { - clearTimeout(focusTimeout); - } - - if (!props.disableKeyboard || !appStateSubscription) { + if (!focusTimeout) { return; } - - appStateSubscription.remove(); + clearTimeout(focusTimeout); }; - }, [props.autoFocus, props.disableKeyboard, props.shouldDelayFocus]); + // We only want this to run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const animateLabel = useCallback( (translateY, scale) => { From 68ee867dd2fb9af1a8ea75f064c291b7b7437690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 11:27:55 +0200 Subject: [PATCH 021/197] use stylesheet flatten --- src/components/TextInput/BaseTextInput.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index b30da00048b5..7038a4e321f9 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -204,7 +204,6 @@ function BaseTextInput(props) { setPrefixWidth(Math.abs(event.nativeEvent.layout.width)); }, []); - // TODO: don't do that all here? // eslint-disable-next-line react/forbid-foreign-prop-types const inputProps = _.omit(props, _.keys(baseTextInputPropTypes.propTypes)); const hasLabel = Boolean(props.label.length); @@ -212,18 +211,14 @@ function BaseTextInput(props) { const inputHelpText = props.errorText || props.hint; const placeholder = props.prefixCharacter || isFocused || !hasLabel || (hasLabel && props.forceActiveLabel) ? props.placeholder : null; const maxHeight = StyleSheet.flatten(props.containerStyles).maxHeight; - const textInputContainerStyles = _.reduce( - [ - styles.textInputContainer, - ...props.textInputContainerStyles, - props.autoGrow && StyleUtils.getWidthStyle(textInputWidth), - !props.hideFocusedState && isFocused && styles.borderColorFocus, - (props.hasError || props.errorText) && styles.borderColorDanger, - props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight}, - ], - (finalStyles, s) => ({...finalStyles, ...s}), - {}, - ); + const textInputContainerStyles = StyleSheet.flatten([ + styles.textInputContainer, + ...props.textInputContainerStyles, + props.autoGrow && StyleUtils.getWidthStyle(textInputWidth), + !props.hideFocusedState && isFocused && styles.borderColorFocus, + (props.hasError || props.errorText) && styles.borderColorDanger, + props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight}, + ]); const isMultiline = props.multiline || props.autoGrowHeight; return ( From cb9cdab170de98aa742024849e1cc71228454778 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 11:35:55 +0100 Subject: [PATCH 022/197] switch from class to functional component --- src/pages/iou/IOUCurrencySelection.js | 233 +++++++++++++------------- 1 file changed, 115 insertions(+), 118 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 92a737295da2..5f8447661fe1 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -56,125 +56,122 @@ const defaultProps = { }, }; -class IOUCurrencySelection extends Component { - constructor(props) { - super(props); - - this.state = { - searchValue: '', - currencyData: this.getCurrencyOptions(this.props.currencyList), - }; - this.getCurrencyOptions = this.getCurrencyOptions.bind(this); - this.getSections = this.getSections.bind(this); - this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); - this.changeSearchValue = this.changeSearchValue.bind(this); - } - - /** - * Returns the sections needed for the OptionsSelector - * - * @returns {Array} - */ - getSections() { - if (this.state.searchValue.trim() && !this.state.currencyData.length) { - return []; - } - const sections = []; - sections.push({ - title: this.props.translate('iOUCurrencySelection.allCurrencies'), - data: this.state.currencyData, - shouldShow: true, - indexOffset: 0, - }); - - return sections; - } - - getSelectedCurrencyCode() { - return lodashGet(this.props.route, 'params.currency', this.props.iou.selectedCurrencyCode); - } - - /** - * @returns {Object} - */ - getCurrencyOptions() { - return _.map(this.props.currencyList, (currencyInfo, currencyCode) => { - const isSelectedCurrency = currencyCode === this.getSelectedCurrencyCode(); - return { - text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - currencyCode, - keyForList: currencyCode, - customIcon: isSelectedCurrency ? greenCheckmark : undefined, - boldStyle: isSelectedCurrency, - }; - }); - } - - /** - * Sets new search value - * @param {String} searchValue - * @return {void} - */ - changeSearchValue(searchValue) { - const currencyOptions = this.getCurrencyOptions(this.props.currencyList); - const searchRegex = new RegExp(Str.escapeForRegExp(searchValue), 'i'); - const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); - - this.setState({ - searchValue, - currencyData: filteredCurrencies, - }); - } - - /** - * Confirms the selection of currency - * - * @param {Object} option - * @param {String} option.currencyCode - */ - confirmCurrencySelection(option) { - const backTo = lodashGet(this.props.route, 'params.backTo', ''); - // When we refresh the web, the money request route gets cleared from the navigation stack. - // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. - // To prevent any negative experience, we have made the decision to simply close the currency selection page. - if (_.isEmpty(backTo) || this.props.navigation.getState().routes.length === 1) { - Navigation.goBack(); - } else { - Navigation.navigate(`${this.props.route.params.backTo}?currency=${option.currencyCode}`); - } - } - - render() { - const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? this.props.translate('common.noResultsFound') : ''; - return ( - - {({safeAreaPaddingBottomStyle}) => ( - <> - Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} - /> - currency.currencyCode === this.getSelectedCurrencyCode()), - 'keyForList', - )} - shouldHaveOptionSeparator - /> - - )} - - ); - } +const IOUCurrencySelection = (props) => { + this.state = { + searchValue: '', + currencyData: this.getCurrencyOptions(this.props.currencyList), + }; + this.getCurrencyOptions = this.getCurrencyOptions.bind(this); + this.getSections = this.getSections.bind(this); + this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); + this.changeSearchValue = this.changeSearchValue.bind(this); + + const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? this.props.translate('common.noResultsFound') : ''; + + return ( + + {({safeAreaPaddingBottomStyle}) => ( + <> + Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} + /> + currency.currencyCode === this.getSelectedCurrencyCode()), + 'keyForList', + )} + shouldHaveOptionSeparator + /> + + )} + + ); } + // /** + // * Returns the sections needed for the OptionsSelector + // * + // * @returns {Array} + // */ + // getSections() { + // if (this.state.searchValue.trim() && !this.state.currencyData.length) { + // return []; + // } + // const sections = []; + // sections.push({ + // title: this.props.translate('iOUCurrencySelection.allCurrencies'), + // data: this.state.currencyData, + // shouldShow: true, + // indexOffset: 0, + // }); + + // return sections; + // } + + // getSelectedCurrencyCode() { + // return lodashGet(this.props.route, 'params.currency', this.props.iou.selectedCurrencyCode); + // } + + // /** + // * @returns {Object} + // */ + // getCurrencyOptions() { + // return _.map(this.props.currencyList, (currencyInfo, currencyCode) => { + // const isSelectedCurrency = currencyCode === this.getSelectedCurrencyCode(); + // return { + // text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, + // currencyCode, + // keyForList: currencyCode, + // customIcon: isSelectedCurrency ? greenCheckmark : undefined, + // boldStyle: isSelectedCurrency, + // }; + // }); + // } + + // /** + // * Sets new search value + // * @param {String} searchValue + // * @return {void} + // */ + // changeSearchValue(searchValue) { + // const currencyOptions = this.getCurrencyOptions(this.props.currencyList); + // const searchRegex = new RegExp(Str.escapeForRegExp(searchValue), 'i'); + // const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); + + // this.setState({ + // searchValue, + // currencyData: filteredCurrencies, + // }); + // } + + // /** + // * Confirms the selection of currency + // * + // * @param {Object} option + // * @param {String} option.currencyCode + // */ + // confirmCurrencySelection(option) { + // const backTo = lodashGet(this.props.route, 'params.backTo', ''); + // // When we refresh the web, the money request route gets cleared from the navigation stack. + // // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. + // // To prevent any negative experience, we have made the decision to simply close the currency selection page. + // if (_.isEmpty(backTo) || this.props.navigation.getState().routes.length === 1) { + // Navigation.goBack(); + // } else { + // Navigation.navigate(`${this.props.route.params.backTo}?currency=${option.currencyCode}`); + // } + // } +// } + +IOUCurrencySelection.displayName = 'IOUCurrencySelection'; IOUCurrencySelection.propTypes = propTypes; IOUCurrencySelection.defaultProps = defaultProps; From 6491d6eb1ee5aa38a9d3b09dd163107a1c3acfa8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 11:45:35 +0100 Subject: [PATCH 023/197] refactor props uses --- src/pages/iou/IOUCurrencySelection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 5f8447661fe1..da8fe7264c9a 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -59,7 +59,7 @@ const defaultProps = { const IOUCurrencySelection = (props) => { this.state = { searchValue: '', - currencyData: this.getCurrencyOptions(this.props.currencyList), + currencyData: this.getCurrencyOptions(props.currencyList), }; this.getCurrencyOptions = this.getCurrencyOptions.bind(this); this.getSections = this.getSections.bind(this); @@ -73,7 +73,7 @@ const IOUCurrencySelection = (props) => { {({safeAreaPaddingBottomStyle}) => ( <> Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} /> { onSelectRow={this.confirmCurrencySelection} value={this.state.searchValue} onChangeText={this.changeSearchValue} - textInputLabel={this.props.translate('common.search')} + textInputLabel={props.translate('common.search')} headerMessage={headerMessage} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} initiallyFocusedOptionKey={_.get( From daf3b25fca63d6331c376b705a58fd67f3c273ca Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 11:46:25 +0100 Subject: [PATCH 024/197] migrate searchValue state to useState hook --- src/pages/iou/IOUCurrencySelection.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index da8fe7264c9a..e9249f86e632 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -57,16 +57,32 @@ const defaultProps = { }; const IOUCurrencySelection = (props) => { + const [searchValue, setCurrentSearchValue] = useState(''); this.state = { - searchValue: '', currencyData: this.getCurrencyOptions(props.currencyList), }; this.getCurrencyOptions = this.getCurrencyOptions.bind(this); this.getSections = this.getSections.bind(this); this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); - this.changeSearchValue = this.changeSearchValue.bind(this); - const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? this.props.translate('common.noResultsFound') : ''; + const getSections = useCallback(() => { + if (searchValue.trim() && !currencyData.length) { + return []; + } + const sections = []; + sections.push({ + title: this.props.translate('iOUCurrencySelection.allCurrencies'), + data: this.state.currencyData, + shouldShow: true, + indexOffset: 0, + }); + + return sections; + }, + [searchValue, currencyData.length], + ); + + const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? props.translate('common.noResultsFound') : ''; return ( @@ -80,7 +96,7 @@ const IOUCurrencySelection = (props) => { sections={this.getSections()} onSelectRow={this.confirmCurrencySelection} value={this.state.searchValue} - onChangeText={this.changeSearchValue} + onChangeText={setCurrentSearchValue} textInputLabel={props.translate('common.search')} headerMessage={headerMessage} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} From 14b0a3cd07e1edc7116388b1d75396d1a8950637 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 12:03:11 +0100 Subject: [PATCH 025/197] refactor IOUCurrencySelection to use hooks --- src/pages/iou/IOUCurrencySelection.js | 78 +++++++++------------------ 1 file changed, 25 insertions(+), 53 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index e9249f86e632..52358b2926db 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState, useCallback} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -58,12 +58,11 @@ const defaultProps = { const IOUCurrencySelection = (props) => { const [searchValue, setCurrentSearchValue] = useState(''); - this.state = { - currencyData: this.getCurrencyOptions(props.currencyList), - }; - this.getCurrencyOptions = this.getCurrencyOptions.bind(this); - this.getSections = this.getSections.bind(this); - this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); + const [currencyData, setCurrencyData] = useState(getCurrencyOptions); + + const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode);; + + // this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); const getSections = useCallback(() => { if (searchValue.trim() && !currencyData.length) { @@ -79,8 +78,21 @@ const IOUCurrencySelection = (props) => { return sections; }, - [searchValue, currencyData.length], - ); + [searchValue, currencyData.length]); + + const getCurrencyOptions = useCallback(() => { + return _.map(props.currencyList, (currencyCode) => { + const isSelectedCurrency = currencyCode === selectedCurrencyCode; + return { + text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, + currencyCode, + keyForList: currencyCode, + customIcon: isSelectedCurrency ? greenCheckmark : undefined, + boldStyle: isSelectedCurrency, + }; + }) + }, + [currencyCode]); const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? props.translate('common.noResultsFound') : ''; @@ -93,15 +105,15 @@ const IOUCurrencySelection = (props) => { onBackButtonPress={() => Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} /> currency.currencyCode === this.getSelectedCurrencyCode()), + _.find(currencyData, (currency) => currency.currencyCode === selectedCurrencyCode), 'keyForList', )} shouldHaveOptionSeparator @@ -112,46 +124,6 @@ const IOUCurrencySelection = (props) => { ); } - // /** - // * Returns the sections needed for the OptionsSelector - // * - // * @returns {Array} - // */ - // getSections() { - // if (this.state.searchValue.trim() && !this.state.currencyData.length) { - // return []; - // } - // const sections = []; - // sections.push({ - // title: this.props.translate('iOUCurrencySelection.allCurrencies'), - // data: this.state.currencyData, - // shouldShow: true, - // indexOffset: 0, - // }); - - // return sections; - // } - - // getSelectedCurrencyCode() { - // return lodashGet(this.props.route, 'params.currency', this.props.iou.selectedCurrencyCode); - // } - - // /** - // * @returns {Object} - // */ - // getCurrencyOptions() { - // return _.map(this.props.currencyList, (currencyInfo, currencyCode) => { - // const isSelectedCurrency = currencyCode === this.getSelectedCurrencyCode(); - // return { - // text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - // currencyCode, - // keyForList: currencyCode, - // customIcon: isSelectedCurrency ? greenCheckmark : undefined, - // boldStyle: isSelectedCurrency, - // }; - // }); - // } - // /** // * Sets new search value // * @param {String} searchValue From 341632621aa49d7655e77c40957ec255aab5e090 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 12:17:44 +0100 Subject: [PATCH 026/197] refactor IOUCurrencySelection function to hook --- src/pages/iou/IOUCurrencySelection.js | 76 +++++++++++++-------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 52358b2926db..12f03415f92b 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -57,12 +57,34 @@ const defaultProps = { }; const IOUCurrencySelection = (props) => { - const [searchValue, setCurrentSearchValue] = useState(''); - const [currencyData, setCurrencyData] = useState(getCurrencyOptions); - const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode);; - // this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); + const getCurrencyOptions = useCallback(() => _.map(props.currencyList, (currencyCode) => { + const isSelectedCurrency = currencyCode === selectedCurrencyCode; + return { + text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, + currencyCode, + keyForList: currencyCode, + customIcon: isSelectedCurrency ? greenCheckmark : undefined, + boldStyle: isSelectedCurrency, + }; + }), + [selectedCurrencyCode, props.currencyList]); + + const confirmCurrencySelection = useCallback((option) => { + const backTo = lodashGet(props.route, 'params.backTo', ''); + // When we refresh the web, the money request route gets cleared from the navigation stack. + // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. + // To prevent any negative experience, we have made the decision to simply close the currency selection page. + if (_.isEmpty(backTo) || props.navigation.getState().routes.length === 1) { + Navigation.goBack(); + } else { + Navigation.navigate(`${props.route.params.backTo}?currency=${option.currencyCode}`); + } + }, [props.route, props.navigation]); + + const [searchValue, setCurrentSearchValue] = useState(''); + const [currencyData, setCurrencyData] = useState(getCurrencyOptions); // just use a const? it doesn't seem to change... const getSections = useCallback(() => { if (searchValue.trim() && !currencyData.length) { @@ -70,8 +92,8 @@ const IOUCurrencySelection = (props) => { } const sections = []; sections.push({ - title: this.props.translate('iOUCurrencySelection.allCurrencies'), - data: this.state.currencyData, + title: props.translate('iOUCurrencySelection.allCurrencies'), + data: currencyData, shouldShow: true, indexOffset: 0, }); @@ -80,21 +102,7 @@ const IOUCurrencySelection = (props) => { }, [searchValue, currencyData.length]); - const getCurrencyOptions = useCallback(() => { - return _.map(props.currencyList, (currencyCode) => { - const isSelectedCurrency = currencyCode === selectedCurrencyCode; - return { - text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - currencyCode, - keyForList: currencyCode, - customIcon: isSelectedCurrency ? greenCheckmark : undefined, - boldStyle: isSelectedCurrency, - }; - }) - }, - [currencyCode]); - - const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? props.translate('common.noResultsFound') : ''; + const headerMessage = searchValue.trim() && !currencyData.length ? props.translate('common.noResultsFound') : ''; return ( @@ -140,24 +148,16 @@ const IOUCurrencySelection = (props) => { // }); // } - // /** - // * Confirms the selection of currency - // * - // * @param {Object} option - // * @param {String} option.currencyCode - // */ - // confirmCurrencySelection(option) { - // const backTo = lodashGet(this.props.route, 'params.backTo', ''); - // // When we refresh the web, the money request route gets cleared from the navigation stack. - // // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. - // // To prevent any negative experience, we have made the decision to simply close the currency selection page. - // if (_.isEmpty(backTo) || this.props.navigation.getState().routes.length === 1) { - // Navigation.goBack(); - // } else { - // Navigation.navigate(`${this.props.route.params.backTo}?currency=${option.currencyCode}`); - // } + // // If we're coming from the confirm step, it means we were editing something so go back to the confirm step. + // const confirmIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); + // if (previousStepIndex === confirmIndex) { + // navigateToStep(confirmIndex); + // return; // } -// } + + // setPreviousStepIndex(currentStepIndex); + // setCurrentStepIndex(currentStepIndex + 1); +// }, [currentStepIndex, previousStepIndex, navigateToStep, steps]); IOUCurrencySelection.displayName = 'IOUCurrencySelection'; IOUCurrencySelection.propTypes = propTypes; From 02d052285174afc09c79fadf0f0ad69bdfab5e1c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 14:53:05 +0100 Subject: [PATCH 027/197] finish refactoring and formatting IOUCurrency page --- src/pages/iou/IOUCurrencySelection.js | 101 ++++++++++++-------------- 1 file changed, 45 insertions(+), 56 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 12f03415f92b..0f1331541cc0 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -56,32 +56,38 @@ const defaultProps = { }, }; -const IOUCurrencySelection = (props) => { - const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode);; - - const getCurrencyOptions = useCallback(() => _.map(props.currencyList, (currencyCode) => { - const isSelectedCurrency = currencyCode === selectedCurrencyCode; - return { - text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - currencyCode, - keyForList: currencyCode, - customIcon: isSelectedCurrency ? greenCheckmark : undefined, - boldStyle: isSelectedCurrency, - }; - }), - [selectedCurrencyCode, props.currencyList]); - - const confirmCurrencySelection = useCallback((option) => { - const backTo = lodashGet(props.route, 'params.backTo', ''); - // When we refresh the web, the money request route gets cleared from the navigation stack. - // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. - // To prevent any negative experience, we have made the decision to simply close the currency selection page. - if (_.isEmpty(backTo) || props.navigation.getState().routes.length === 1) { - Navigation.goBack(); - } else { - Navigation.navigate(`${props.route.params.backTo}?currency=${option.currencyCode}`); - } - }, [props.route, props.navigation]); +function IOUCurrencySelection(props) { + const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode, CONST.CURRENCY.USD); + + const getCurrencyOptions = useCallback( + () => + _.map(props.currencyList, (currencyCode) => { + const isSelectedCurrency = currencyCode === selectedCurrencyCode; + return { + text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, + currencyCode, + keyForList: currencyCode, + customIcon: isSelectedCurrency ? greenCheckmark : undefined, + boldStyle: isSelectedCurrency, + }; + }), + [selectedCurrencyCode, props.currencyList], + ); + + const confirmCurrencySelection = useCallback( + (option) => { + const backTo = lodashGet(props.route, 'params.backTo', ''); + // When we refresh the web, the money request route gets cleared from the navigation stack. + // Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection. + // To prevent any negative experience, we have made the decision to simply close the currency selection page. + if (_.isEmpty(backTo) || props.navigation.getState().routes.length === 1) { + Navigation.goBack(); + } else { + Navigation.navigate(`${props.route.params.backTo}?currency=${option.currencyCode}`); + } + }, + [props.route, props.navigation], + ); const [searchValue, setCurrentSearchValue] = useState(''); const [currencyData, setCurrencyData] = useState(getCurrencyOptions); // just use a const? it doesn't seem to change... @@ -99,8 +105,18 @@ const IOUCurrencySelection = (props) => { }); return sections; - }, - [searchValue, currencyData.length]); + }, [searchValue, currencyData, props]); + + const changeSearchValue = useCallback( + (searchQuery) => { + const searchRegex = new RegExp(Str.escapeForRegExp(searchQuery), 'i'); + const filteredCurrencies = _.filter(currencyData, (currencyOption) => searchRegex.test(currencyOption.text)); + + setCurrentSearchValue(searchQuery); + setCurrencyData(filteredCurrencies); + }, + [currencyData], + ); const headerMessage = searchValue.trim() && !currencyData.length ? props.translate('common.noResultsFound') : ''; @@ -116,7 +132,7 @@ const IOUCurrencySelection = (props) => { sections={getSections} onSelectRow={confirmCurrencySelection} value={searchValue} - onChangeText={setCurrentSearchValue} + onChangeText={changeSearchValue} textInputLabel={props.translate('common.search')} headerMessage={headerMessage} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} @@ -132,33 +148,6 @@ const IOUCurrencySelection = (props) => { ); } - // /** - // * Sets new search value - // * @param {String} searchValue - // * @return {void} - // */ - // changeSearchValue(searchValue) { - // const currencyOptions = this.getCurrencyOptions(this.props.currencyList); - // const searchRegex = new RegExp(Str.escapeForRegExp(searchValue), 'i'); - // const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); - - // this.setState({ - // searchValue, - // currencyData: filteredCurrencies, - // }); - // } - - // // If we're coming from the confirm step, it means we were editing something so go back to the confirm step. - // const confirmIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); - // if (previousStepIndex === confirmIndex) { - // navigateToStep(confirmIndex); - // return; - // } - - // setPreviousStepIndex(currentStepIndex); - // setCurrentStepIndex(currentStepIndex + 1); -// }, [currentStepIndex, previousStepIndex, navigateToStep, steps]); - IOUCurrencySelection.displayName = 'IOUCurrencySelection'; IOUCurrencySelection.propTypes = propTypes; IOUCurrencySelection.defaultProps = defaultProps; From 025bf9bc33fa60984f3f0ad6bebb1dfd306e979f Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Jun 2023 16:21:17 +0100 Subject: [PATCH 028/197] make getCurrencyOptions use useMemo and other improvements --- src/pages/iou/IOUCurrencySelection.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 0f1331541cc0..d617f0bfa11a 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback} from 'react'; +import React, {useState, useMemo, useCallback} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -58,10 +58,9 @@ const defaultProps = { function IOUCurrencySelection(props) { const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode, CONST.CURRENCY.USD); - - const getCurrencyOptions = useCallback( + const currencyOptions = useMemo( () => - _.map(props.currencyList, (currencyCode) => { + _.map(props.currencyList, (currencyInfo, currencyCode) => { const isSelectedCurrency = currencyCode === selectedCurrencyCode; return { text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, @@ -90,9 +89,9 @@ function IOUCurrencySelection(props) { ); const [searchValue, setCurrentSearchValue] = useState(''); - const [currencyData, setCurrencyData] = useState(getCurrencyOptions); // just use a const? it doesn't seem to change... + const [currencyData, setCurrencyData] = useState(currencyOptions); - const getSections = useCallback(() => { + const getSections = useMemo(() => { if (searchValue.trim() && !currencyData.length) { return []; } @@ -105,7 +104,7 @@ function IOUCurrencySelection(props) { }); return sections; - }, [searchValue, currencyData, props]); + }, [searchValue, currencyData, props.translate]); const changeSearchValue = useCallback( (searchQuery) => { From 86dea8ce4d5d13ede77bf10c2ba4dfd38c192b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 17:59:11 +0200 Subject: [PATCH 029/197] Update src/components/TextInput/BaseTextInput.js Co-authored-by: Rajat Parashar --- src/components/TextInput/BaseTextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 7038a4e321f9..5a59db07879f 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -18,7 +18,7 @@ import getSecureEntryKeyboardType from '../../libs/getSecureEntryKeyboardType'; import CONST from '../../CONST'; import FormHelpMessage from '../FormHelpMessage'; import isInputAutoFilled from '../../libs/isInputAutoFilled'; -import * as Pressables from '../Pressable'; +import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; function BaseTextInput(props) { const inputValue = props.value || ''; From 71fb82c0dad473ac551beeb742e4cf3ca0772fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 17:59:28 +0200 Subject: [PATCH 030/197] Update src/components/TextInput/BaseTextInput.js Co-authored-by: Rajat Parashar --- src/components/TextInput/BaseTextInput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 5a59db07879f..9875dead95d1 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -39,7 +39,9 @@ function BaseTextInput(props) { useEffect(() => { let appStateSubscription; - if (props.disableKeyboard) { + if (!props.disableKeyboard) { + return; + } appStateSubscription = AppState.addEventListener('change', (nextAppState) => { if (!nextAppState.match(/inactive|background/)) { return; From 528d63eb33e74b724c75dbf09fce247f42f2b8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 18:00:18 +0200 Subject: [PATCH 031/197] Update src/components/TextInput/BaseTextInput.js Co-authored-by: Rajat Parashar --- src/components/TextInput/BaseTextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 9875dead95d1..dded7a117d70 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -226,7 +226,7 @@ function BaseTextInput(props) { return ( <> - Date: Mon, 12 Jun 2023 18:04:34 +0200 Subject: [PATCH 032/197] fix useEffect --- src/components/TextInput/BaseTextInput.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index dded7a117d70..dca8d4b83f26 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -38,21 +38,20 @@ function BaseTextInput(props) { const isLabelActive = useRef(initialActiveLabel); useEffect(() => { - let appStateSubscription; - if (!props.disableKeyboard) { - return; + if (!props.disableKeyboard) { + return; } - appStateSubscription = AppState.addEventListener('change', (nextAppState) => { - if (!nextAppState.match(/inactive|background/)) { - return; - } - Keyboard.dismiss(); - }); - } + const appStateSubscription = AppState.addEventListener('change', (nextAppState) => { + if (!nextAppState.match(/inactive|background/)) { + return; + } + + Keyboard.dismiss(); + }); return () => { - if (!props.disableKeyboard || !appStateSubscription) { + if (!props.disableKeyboard) { return; } @@ -347,7 +346,7 @@ function BaseTextInput(props) { )} - + {!_.isEmpty(inputHelpText) && ( Date: Mon, 12 Jun 2023 18:06:06 +0200 Subject: [PATCH 033/197] useRef for stable values --- src/components/TextInput/BaseTextInput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index dca8d4b83f26..f797df2063c6 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -25,8 +25,8 @@ function BaseTextInput(props) { const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter); const [isFocused, setIsFocused] = useState(false); - const [labelTranslateY] = useState(() => new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)); - const [labelScale] = useState(() => new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)); + const labelTranslateY = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)).current; + const labelScale = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)).current; const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry); const [textInputWidth, setTextInputWidth] = useState(0); const [textInputHeight, setTextInputHeight] = useState(); From 0a756fc5534f35c6c2f321f0d316c1c5415107dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 12 Jun 2023 18:19:56 +0200 Subject: [PATCH 034/197] use defaultValue as well --- src/components/TextInput/BaseTextInput.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index f797df2063c6..ff03590844e3 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -21,7 +21,7 @@ import isInputAutoFilled from '../../libs/isInputAutoFilled'; import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; function BaseTextInput(props) { - const inputValue = props.value || ''; + const inputValue = props.value || props.defaultValue || ''; const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter); const [isFocused, setIsFocused] = useState(false); @@ -102,22 +102,26 @@ function BaseTextInput(props) { ); const activateLabel = useCallback(() => { - if (inputValue.length < 0 || isLabelActive.current) { + const value = props.value || ''; + + if (value.length < 0 || isLabelActive.current) { return; } animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE); isLabelActive.current = true; - }, [animateLabel, inputValue]); + }, [animateLabel, props.value]); const deactivateLabel = useCallback(() => { - if (props.forceActiveLabel || inputValue.length !== 0 || props.prefixCharacter) { + const value = props.value || ''; + + if (props.forceActiveLabel || value.length !== 0 || props.prefixCharacter) { return; } animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE); isLabelActive.current = false; - }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, inputValue]); + }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, props.value]); const onFocus = (event) => { if (props.onFocus) { From 7d1cb482a6c8627268743adcaf12edad5a6b8cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 13 Jun 2023 10:20:47 +0200 Subject: [PATCH 035/197] removed unnecessary condition --- src/components/TextInput/BaseTextInput.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index ff03590844e3..60f2ed739740 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -51,10 +51,6 @@ function BaseTextInput(props) { }); return () => { - if (!props.disableKeyboard) { - return; - } - appStateSubscription.remove(); }; }, [props.disableKeyboard]); From 553111e9845d363ae0c7a58ba32aa186c3fe2916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 13 Jun 2023 10:21:33 +0200 Subject: [PATCH 036/197] beautify code order --- src/components/TextInput/BaseTextInput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 60f2ed739740..724e39c0cdce 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -25,14 +25,14 @@ function BaseTextInput(props) { const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter); const [isFocused, setIsFocused] = useState(false); - const labelTranslateY = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)).current; - const labelScale = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)).current; const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry); const [textInputWidth, setTextInputWidth] = useState(0); const [textInputHeight, setTextInputHeight] = useState(); const [prefixWidth, setPrefixWidth] = useState(0); const [height, setHeight] = useState(variables.componentSizeLarge); const [width, setWidth] = useState(); + const labelScale = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)).current; + const labelTranslateY = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)).current; const input = useRef(null); const isLabelActive = useRef(initialActiveLabel); From 5d9143ce4238250ad1da276bbeefc1f3cfdbd241 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 13:47:47 +0100 Subject: [PATCH 037/197] use prop destructuring --- src/pages/iou/IOUCurrencySelection.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index d617f0bfa11a..cfc7fc326faf 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -90,6 +90,7 @@ function IOUCurrencySelection(props) { const [searchValue, setCurrentSearchValue] = useState(''); const [currencyData, setCurrencyData] = useState(currencyOptions); + const {translate} = props; const getSections = useMemo(() => { if (searchValue.trim() && !currencyData.length) { @@ -97,14 +98,14 @@ function IOUCurrencySelection(props) { } const sections = []; sections.push({ - title: props.translate('iOUCurrencySelection.allCurrencies'), + title: translate('iOUCurrencySelection.allCurrencies'), data: currencyData, shouldShow: true, indexOffset: 0, }); return sections; - }, [searchValue, currencyData, props.translate]); + }, [searchValue, currencyData, translate]); const changeSearchValue = useCallback( (searchQuery) => { From 5e2ea3b2510beff47a595df6467881191cb5847e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 13:48:01 +0100 Subject: [PATCH 038/197] reorder hook statements --- src/pages/iou/IOUCurrencySelection.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index cfc7fc326faf..afad15d06dbb 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -57,6 +57,7 @@ const defaultProps = { }; function IOUCurrencySelection(props) { + const [searchValue, setCurrentSearchValue] = useState(''); const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode, CONST.CURRENCY.USD); const currencyOptions = useMemo( () => @@ -73,6 +74,8 @@ function IOUCurrencySelection(props) { [selectedCurrencyCode, props.currencyList], ); + const [currencyData, setCurrencyData] = useState(currencyOptions); + const confirmCurrencySelection = useCallback( (option) => { const backTo = lodashGet(props.route, 'params.backTo', ''); @@ -88,8 +91,6 @@ function IOUCurrencySelection(props) { [props.route, props.navigation], ); - const [searchValue, setCurrentSearchValue] = useState(''); - const [currencyData, setCurrencyData] = useState(currencyOptions); const {translate} = props; const getSections = useMemo(() => { From 0f0ea5be219d9601ea51d35cf8a6a8c4ad6d1fc8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 13:50:45 +0100 Subject: [PATCH 039/197] apply review suggestion and format code with prettier --- src/pages/iou/IOUCurrencySelection.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index afad15d06dbb..eaba6de07cd4 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -91,19 +91,18 @@ function IOUCurrencySelection(props) { [props.route, props.navigation], ); - const {translate} = props; + const { translate } = props; const getSections = useMemo(() => { if (searchValue.trim() && !currencyData.length) { return []; } - const sections = []; - sections.push({ + return [{ title: translate('iOUCurrencySelection.allCurrencies'), data: currencyData, shouldShow: true, indexOffset: 0, - }); + }]; return sections; }, [searchValue, currencyData, translate]); From 7248544b06b788ba74c4fca975a7c988d8d455d6 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 13:51:55 +0100 Subject: [PATCH 040/197] remove useless return statement --- src/pages/iou/IOUCurrencySelection.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index eaba6de07cd4..3a160c7eb4e4 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -103,8 +103,6 @@ function IOUCurrencySelection(props) { shouldShow: true, indexOffset: 0, }]; - - return sections; }, [searchValue, currencyData, translate]); const changeSearchValue = useCallback( From 0f41718d0d6b2aa94fbde4daf18f6a079f820e01 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 14:33:27 +0100 Subject: [PATCH 041/197] fix issue where filtered currencies were permanent --- src/pages/iou/IOUCurrencySelection.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 3a160c7eb4e4..b57c488daadc 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -91,29 +91,31 @@ function IOUCurrencySelection(props) { [props.route, props.navigation], ); - const { translate } = props; + const {translate} = props; const getSections = useMemo(() => { if (searchValue.trim() && !currencyData.length) { return []; } - return [{ - title: translate('iOUCurrencySelection.allCurrencies'), - data: currencyData, - shouldShow: true, - indexOffset: 0, - }]; + return [ + { + title: translate('iOUCurrencySelection.allCurrencies'), + data: currencyData, + shouldShow: true, + indexOffset: 0, + }, + ]; }, [searchValue, currencyData, translate]); const changeSearchValue = useCallback( (searchQuery) => { const searchRegex = new RegExp(Str.escapeForRegExp(searchQuery), 'i'); - const filteredCurrencies = _.filter(currencyData, (currencyOption) => searchRegex.test(currencyOption.text)); + const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); setCurrentSearchValue(searchQuery); setCurrencyData(filteredCurrencies); }, - [currencyData], + [currencyOptions], ); const headerMessage = searchValue.trim() && !currencyData.length ? props.translate('common.noResultsFound') : ''; From 9012baeb172844411ac1020c29ec062d43d0009b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 13 Jun 2023 16:10:55 +0100 Subject: [PATCH 042/197] remove unnecessary uses of props.translate in IOUCurrencySelection --- src/pages/iou/IOUCurrencySelection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index b57c488daadc..26a95033300d 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -118,14 +118,14 @@ function IOUCurrencySelection(props) { [currencyOptions], ); - const headerMessage = searchValue.trim() && !currencyData.length ? props.translate('common.noResultsFound') : ''; + const headerMessage = searchValue.trim() && !currencyData.length ? translate('common.noResultsFound') : ''; return ( {({safeAreaPaddingBottomStyle}) => ( <> Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} /> Date: Tue, 13 Jun 2023 12:30:58 -0700 Subject: [PATCH 043/197] Create usePermissions hook --- src/components/OnyxProvider.js | 4 ++-- src/hooks/usePermissions.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/hooks/usePermissions.js diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js index 6cee7e5b7a62..76cda71da2b2 100644 --- a/src/components/OnyxProvider.js +++ b/src/components/OnyxProvider.js @@ -11,7 +11,7 @@ const [withPersonalDetails, PersonalDetailsProvider] = createOnyxContext(ONYXKEY const [withCurrentDate, CurrentDateProvider] = createOnyxContext(ONYXKEYS.CURRENT_DATE); const [withReportActionsDrafts, ReportActionsDraftsProvider] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); const [withBlockedFromConcierge, BlockedFromConciergeProvider] = createOnyxContext(ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE); -const [withBetas, BetasProvider] = createOnyxContext(ONYXKEYS.BETAS); +const [withBetas, BetasProvider, BetasContext] = createOnyxContext(ONYXKEYS.BETAS); const propTypes = { /** Rendered child component */ @@ -29,4 +29,4 @@ OnyxProvider.propTypes = propTypes; export default OnyxProvider; -export {withNetwork, withPersonalDetails, withReportActionsDrafts, withCurrentDate, withBlockedFromConcierge, withBetas, NetworkContext}; +export {withNetwork, withPersonalDetails, withReportActionsDrafts, withCurrentDate, withBlockedFromConcierge, withBetas, NetworkContext, BetasContext}; diff --git a/src/hooks/usePermissions.js b/src/hooks/usePermissions.js new file mode 100644 index 000000000000..1c31ffc8bb64 --- /dev/null +++ b/src/hooks/usePermissions.js @@ -0,0 +1,15 @@ +import _ from 'underscore'; +import {useContext, useMemo} from 'react'; +import Permissions from '../libs/Permissions'; +import {BetasContext} from '../components/OnyxProvider'; + +export default function usePermissions() { + const betas = useContext(BetasContext); + return useMemo(() => { + const permissions = {}; + _.each(Permissions, (checkerFunction, beta) => { + permissions[beta] = checkerFunction(betas); + }); + return permissions; + }, [betas]); +} From cc028f6f4a1c0556b62fcdd9c45273a126efd59b Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 13 Jun 2023 12:32:21 -0700 Subject: [PATCH 044/197] Create useLocalize hook --- src/components/withLocalize.js | 2 +- src/hooks/useLocalize.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/hooks/useLocalize.js diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 4cbdda876231..def7110c1b40 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -179,4 +179,4 @@ export default function withLocalize(WrappedComponent) { return WithLocalize; } -export {withLocalizePropTypes, Provider as LocaleContextProvider}; +export {withLocalizePropTypes, Provider as LocaleContextProvider, LocaleContext}; diff --git a/src/hooks/useLocalize.js b/src/hooks/useLocalize.js new file mode 100644 index 000000000000..9ad5048729bd --- /dev/null +++ b/src/hooks/useLocalize.js @@ -0,0 +1,6 @@ +import {useContext} from 'react'; +import {LocaleContext} from '../components/withLocalize'; + +export default function useLocalize() { + return useContext(LocaleContext); +} From 64811dc2c71089abfd1387197b410c47f6018ccb Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 13 Jun 2023 12:41:11 -0700 Subject: [PATCH 045/197] Migrate SignInPage to functional component --- src/pages/signin/SignInPage.js | 257 ++++++++++++++++----------------- 1 file changed, 125 insertions(+), 132 deletions(-) diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index a875c25359b0..8c3d513f5303 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -1,30 +1,27 @@ -import React, {Component} from 'react'; +import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; -import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; -import {withSafeAreaInsets} from 'react-native-safe-area-context'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; import ONYXKEYS from '../../ONYXKEYS'; import styles from '../../styles/styles'; -import compose from '../../libs/compose'; import SignInPageLayout from './SignInPageLayout'; import LoginForm from './LoginForm'; import PasswordForm from './PasswordForm'; import ValidateCodeForm from './ValidateCodeForm'; import ResendValidationForm from './ResendValidationForm'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import Performance from '../../libs/Performance'; import * as App from '../../libs/actions/App'; -import Permissions from '../../libs/Permissions'; import UnlinkLoginForm from './UnlinkLoginForm'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import * as Localize from '../../libs/Localize'; import * as StyleUtils from '../../styles/StyleUtils'; +import useLocalize from '../../hooks/useLocalize'; +import usePermissions from '../../hooks/usePermissions'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; +import Log from '../../libs/Log'; const propTypes = { - /* Onyx Props */ - /** The details about the account that the user is signing in with */ account: PropTypes.shape({ /** Error to display when there is an account error returned */ @@ -35,153 +32,149 @@ const propTypes = { /** The primaryLogin associated with the account */ primaryLogin: PropTypes.string, - }), - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), + /** Has the user pressed the forgot password button? */ + forgotPassword: PropTypes.bool, + + /** Does this account require 2FA? */ + requiresTwoFactorAuth: PropTypes.bool, + }), /** The credentials of the person signing in */ credentials: PropTypes.shape({ login: PropTypes.string, password: PropTypes.string, twoFactorAuthCode: PropTypes.string, + validateCode: PropTypes.string, }), - - ...withLocalizePropTypes, - - ...windowDimensionsPropTypes, }; const defaultProps = { account: {}, - betas: [], credentials: {}, }; -class SignInPage extends Component { - componentDidMount() { - Performance.measureTTI(); +/** + * @param {Boolean} hasLogin + * @param {Boolean} hasPassword + * @param {Boolean} hasValidateCode + * @param {Boolean} isPrimaryLogin + * @param {Boolean} isAccountValidated + * @param {Boolean} didForgetPassword + * @param {Boolean} canUsePasswordlessLogins + * @returns {Object} + */ +function getRenderOptions({hasLogin, hasPassword, hasValidateCode, isPrimaryLogin, isAccountValidated, didForgetPassword, canUsePasswordlessLogins}) { + const shouldShowLoginForm = !hasLogin && !hasValidateCode; + const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !isAccountValidated; + const shouldShowPasswordForm = hasLogin && isAccountValidated && !hasPassword && !didForgetPassword && !isUnvalidatedSecondaryLogin && !canUsePasswordlessLogins; + const shouldShowValidateCodeForm = (hasLogin || hasValidateCode) && !isUnvalidatedSecondaryLogin && canUsePasswordlessLogins; + const shouldShowResendValidationForm = hasLogin && (!isAccountValidated || didForgetPassword) && !isUnvalidatedSecondaryLogin && !canUsePasswordlessLogins; + const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowPasswordForm || shouldShowValidateCodeForm || isUnvalidatedSecondaryLogin; + const shouldShowWelcomeText = shouldShowLoginForm || shouldShowPasswordForm || shouldShowValidateCodeForm; + return { + shouldShowLoginForm, + shouldShowUnlinkLoginForm: isUnvalidatedSecondaryLogin, + shouldShowPasswordForm, + shouldShowValidateCodeForm, + shouldShowResendValidationForm, + shouldShowWelcomeHeader, + shouldShowWelcomeText, + }; +} - App.setLocale(Localize.getDevicePreferredLocale()); - } +function SignInPage({credentials, account}) { + const {translate, formatPhoneNumber} = useLocalize(); + const {canUsePasswordlessLogins} = usePermissions(); + const {isSmallScreenWidth} = useWindowDimensions(); + const safeAreaInsets = useSafeAreaInsets(); - render() { - // Show the login form if - // - A login has not been entered yet - // - AND a validateCode has not been cached with sign in link - const showLoginForm = !this.props.credentials.login && !this.props.credentials.validateCode; - - // Show the unlink form if - // - A login has been entered - // - AND the login is not the primary login - // - AND the login is not validated - const showUnlinkLoginForm = - Boolean(this.props.credentials.login && this.props.account.primaryLogin) && this.props.account.primaryLogin !== this.props.credentials.login && !this.props.account.validated; - - // Show the old password form if - // - A login has been entered - // - AND an account exists and is validated for this login - // - AND a password hasn't been entered yet - // - AND haven't forgotten password - // - AND the login isn't an unvalidated secondary login - // - AND the user is NOT on the passwordless beta - const showPasswordForm = - Boolean(this.props.credentials.login) && - this.props.account.validated && - !this.props.credentials.password && - !this.props.account.forgotPassword && - !showUnlinkLoginForm && - !Permissions.canUsePasswordlessLogins(this.props.betas); - - // Show the new magic code / validate code form if - // - A login has been entered or a validateCode has been cached from sign in link - // - AND the login isn't an unvalidated secondary login - // - AND the user is on the 'passwordless' beta - const showValidateCodeForm = - Boolean(this.props.credentials.login || this.props.credentials.validateCode) && !showUnlinkLoginForm && Permissions.canUsePasswordlessLogins(this.props.betas); - - // Show the resend validation link form if - // - A login has been entered - // - AND is not validated or password is forgotten - // - AND the login isn't an unvalidated secondary login - // - AND user is not on 'passwordless' beta - const showResendValidationForm = - Boolean(this.props.credentials.login) && - (!this.props.account.validated || this.props.account.forgotPassword) && - !showUnlinkLoginForm && - !Permissions.canUsePasswordlessLogins(this.props.betas); - - let welcomeHeader = ''; - let welcomeText = ''; - if (showValidateCodeForm) { - if (this.props.account.requiresTwoFactorAuth) { - // We will only know this after a user signs in successfully, without their 2FA code - welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack'); - welcomeText = this.props.translate('validateCodeForm.enterAuthenticatorCode'); + useEffect(() => Performance.measureTTI(), []); + useEffect(() => { + App.setLocale(Localize.getDevicePreferredLocale()); + }, []); + + const { + shouldShowLoginForm, + shouldShowUnlinkLoginForm, + shouldShowPasswordForm, + shouldShowValidateCodeForm, + shouldShowResendValidationForm, + shouldShowWelcomeHeader, + shouldShowWelcomeText, + } = getRenderOptions({ + hasLogin: Boolean(credentials.login), + hasPassword: Boolean(credentials.password), + hasValidateCode: Boolean(credentials.validateCode), + isPrimaryLogin: !account.primaryLogin || account.primaryLogin === credentials.login, + isAccountValidated: Boolean(account.validated), + didForgetPassword: Boolean(account.forgotPassword), + canUsePasswordlessLogins, + }); + + let welcomeHeader; + let welcomeText; + if (shouldShowValidateCodeForm) { + if (account.requiresTwoFactorAuth) { + // We will only know this after a user signs in successfully, without their 2FA code + welcomeHeader = isSmallScreenWidth ? '' : translate('welcomeText.welcomeBack'); + welcomeText = translate('validateCodeForm.enterAuthenticatorCode'); + } else { + const userLogin = Str.removeSMSDomain(credentials.login || ''); + + // replacing spaces with "hard spaces" to prevent breaking the number + const userLoginToDisplay = Str.isSMSLogin(userLogin) ? formatPhoneNumber(userLogin).replace(/ /g, '\u00A0') : userLogin; + if (account.validated) { + welcomeHeader = isSmallScreenWidth ? '' : translate('welcomeText.welcomeBack'); + welcomeText = isSmallScreenWidth + ? `${translate('welcomeText.welcomeBack')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}` + : translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay}); } else { - const userLogin = Str.removeSMSDomain(lodashGet(this.props, 'credentials.login', '')); - - // replacing spaces with "hard spaces" to prevent breaking the number - const userLoginToDisplay = Str.isSMSLogin(userLogin) ? this.props.formatPhoneNumber(userLogin).replace(/ /g, '\u00A0') : userLogin; - if (this.props.account.validated) { - welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack'); - welcomeText = this.props.isSmallScreenWidth - ? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}` - : this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay}); - } else { - welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcome'); - welcomeText = this.props.isSmallScreenWidth - ? `${this.props.translate('welcomeText.welcome')} ${this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay})}` - : this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay}); - } + welcomeHeader = isSmallScreenWidth ? '' : translate('welcomeText.welcome'); + welcomeText = isSmallScreenWidth + ? `${translate('welcomeText.welcome')} ${translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay})}` + : translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay}); } - } else if (showPasswordForm) { - welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack'); - welcomeText = this.props.isSmallScreenWidth - ? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.enterPassword')}` - : this.props.translate('welcomeText.enterPassword'); - } else if (showUnlinkLoginForm) { - welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.welcomeBack'); - } else if (!showResendValidationForm) { - welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.getStarted'); - welcomeText = this.props.isSmallScreenWidth ? this.props.translate('welcomeText.getStarted') : ''; } + } else if (shouldShowPasswordForm) { + welcomeHeader = isSmallScreenWidth ? '' : translate('welcomeText.welcomeBack'); + welcomeText = isSmallScreenWidth ? `${translate('welcomeText.welcomeBack')} ${translate('welcomeText.enterPassword')}` : translate('welcomeText.enterPassword'); + } else if (shouldShowUnlinkLoginForm) { + welcomeHeader = isSmallScreenWidth ? translate('login.hero.header') : translate('welcomeText.welcomeBack'); + } else if (!shouldShowResendValidationForm) { + welcomeHeader = isSmallScreenWidth ? translate('login.hero.header') : translate('welcomeText.getStarted'); + welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; + } else { + Log.warn('SignInPage in unexpected state!'); + } - return ( - // There is an issue SafeAreaView on Android where wrong insets flicker on app start. - // Can be removed once https://github.com/th3rdwave/react-native-safe-area-context/issues/364 is resolved. - - - {/* LoginForm and PasswordForm must use the isVisible prop. This keeps them mounted, but visually hidden + return ( + + + {/* LoginForm and PasswordForm must use the isVisible prop. This keeps them mounted, but visually hidden so that password managers can access the values. Conditionally rendering these components will break this feature. */} - - {showValidateCodeForm ? : } - {showResendValidationForm && } - {showUnlinkLoginForm && } - - - ); - } + + {shouldShowValidateCodeForm ? : } + {shouldShowResendValidationForm && } + {shouldShowUnlinkLoginForm && } + + + ); } SignInPage.propTypes = propTypes; SignInPage.defaultProps = defaultProps; +SignInPage.displayName = 'SignInPage'; -export default compose( - withSafeAreaInsets, - withLocalize, - withWindowDimensions, - withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, - betas: {key: ONYXKEYS.BETAS}, - credentials: {key: ONYXKEYS.CREDENTIALS}, - }), -)(SignInPage); +export default withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, + credentials: {key: ONYXKEYS.CREDENTIALS}, +})(SignInPage); From ca27f0d073b6815b540f8917df942f326ac49f41 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Wed, 14 Jun 2023 13:55:27 +0300 Subject: [PATCH 046/197] Refactor after rebasing - move `Report_Attachments` to `CentralPaneNavigator` --- src/libs/Navigation/AppNavigator/AuthScreens.js | 10 ---------- .../AppNavigator/Navigators/CentralPaneNavigator.js | 10 ++++++++++ src/libs/Navigation/linkingConfig.js | 1 - 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 385290943299..8539f384504c 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -261,16 +261,6 @@ class AuthScreens extends React.Component { component={RightModalNavigator} listeners={modalScreenListeners} /> - - { - const ReportAttachments = require('../../../pages/home/report/ReportAttachments').default; - return ReportAttachments; - }} - listeners={modalScreenListeners} - /> ); } diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js index 471be5c7209c..188f152bc089 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js @@ -28,6 +28,16 @@ function CentralPaneNavigator() { }} component={ReportScreenWrapper} /> + { + const ReportAttachments = require('../../../../pages/home/report/ReportAttachments').default; + return ReportAttachments; + }} + /> ); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index a8c1e8abdeff..3f63deedde7c 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -341,7 +341,6 @@ export default { }, }, }, - [SCREENS.NOT_FOUND]: '*', }, }, }; From d5c6e4ccabcaaa1497c7e1330458d509620af2eb Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Wed, 14 Jun 2023 15:40:48 +0300 Subject: [PATCH 047/197] Refactor after rebasing - move `Report_Attachments` to root navigation Otherwise, when closing the modal there's a blank screen displayed briefly It seems the previous screen does not stay underneath the modal unless it's on the root --- .../Navigation/AppNavigator/AuthScreens.js | 12 ++++++++ .../Navigators/CentralPaneNavigator.js | 10 ------- src/libs/Navigation/Navigation.js | 29 +++++++++++-------- src/libs/Navigation/linkingConfig.js | 2 +- src/pages/home/report/ReportAttachments.js | 2 +- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 8539f384504c..735f531bcaef 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -250,6 +250,18 @@ class AuthScreens extends React.Component { return ConciergePage; }} /> + { + const ReportAttachments = require('../../../pages/home/report/ReportAttachments').default; + return ReportAttachments; + }} + listeners={modalScreenListeners} + /> - { - const ReportAttachments = require('../../../../pages/home/report/ReportAttachments').default; - return ReportAttachments; - }} - /> ); diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index d61eb7143de0..cf9b7b979daf 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -132,19 +132,24 @@ function dismissModal(targetReportID) { } const rootState = navigationRef.getRootState(); const lastRoute = _.last(rootState.routes); - if (lastRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || lastRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { - // if we are not in the target report, we need to navigate to it after dismissing the modal - if (targetReportID && targetReportID !== getTopmostReportId(rootState)) { - const state = getStateFromPath(ROUTES.getReportRoute(targetReportID)); - - const action = getActionFromState(state, linkingConfig.config); - action.type = 'REPLACE'; - navigationRef.current.dispatch(action); - } else { - navigationRef.current.dispatch(StackActions.pop()); + switch (lastRoute.name) { + case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: + case NAVIGATORS.FULL_SCREEN_NAVIGATOR: + case 'Report_Attachments': + // if we are not in the target report, we need to navigate to it after dismissing the modal + if (targetReportID && targetReportID !== getTopmostReportId(rootState)) { + const state = getStateFromPath(ROUTES.getReportRoute(targetReportID)); + + const action = getActionFromState(state, linkingConfig.config); + action.type = 'REPLACE'; + navigationRef.current.dispatch(action); + } else { + navigationRef.current.dispatch(StackActions.pop()); + } + break; + default: { + Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); } - } else { - Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); } } diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 3f63deedde7c..a4a142658236 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -14,6 +14,7 @@ export default { UnlinkLogin: ROUTES.UNLINK_LOGIN, [SCREENS.TRANSITION_FROM_OLD_DOT]: ROUTES.TRANSITION_FROM_OLD_DOT, Concierge: ROUTES.CONCIERGE, + Report_Attachments: ROUTES.REPORT_ATTACHMENT, // Sidebar [SCREENS.HOME]: { @@ -23,7 +24,6 @@ export default { [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID, - Report_Attachments: ROUTES.REPORT_ATTACHMENT, }, }, [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index b591a8740a4e..203ba3d8c93e 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -27,7 +27,7 @@ function ReportAttachments(props) { defaultOpen reportID={reportID} source={source} - onModalHide={Navigation.dismissModal} + onModalHide={() => Navigation.dismissModal(reportID)} /> ); } From 3d5bc852356f98109d91b64bcb4b42c0548859de Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Wed, 14 Jun 2023 15:53:47 +0300 Subject: [PATCH 048/197] Refactor: extract "Report_Attachments" string to SCREENS --- src/ROUTES.js | 2 +- src/SCREENS.js | 1 + src/libs/Navigation/AppNavigator/AuthScreens.js | 2 +- src/libs/Navigation/Navigation.js | 3 ++- src/libs/Navigation/linkingConfig.js | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index ad8608593614..4c3f24b08bd4 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -68,7 +68,7 @@ export default { getReportRoute: (reportID) => `r/${reportID}`, REPORT_WITH_ID_DETAILS_SHARE_CODE: 'r/:reportID/details/shareCode', getReportShareCodeRoute: (reportID) => `r/${reportID}/details/shareCode`, - REPORT_ATTACHMENT: 'r/:reportID/attachment', + REPORT_ATTACHMENTS: 'r/:reportID/attachment', getReportAttachmentRoute: (reportID, source) => `r/${reportID}/attachment?source=${encodeURI(source)}`, SELECT_YEAR: 'select-year', getYearSelectionRoute: (minYear, maxYear, currYear, backTo) => `select-year?min=${minYear}&max=${maxYear}&year=${currYear}&backTo=${backTo}`, diff --git a/src/SCREENS.js b/src/SCREENS.js index 24ea27fe9689..aec13a490376 100644 --- a/src/SCREENS.js +++ b/src/SCREENS.js @@ -6,6 +6,7 @@ export default { HOME: 'Home', LOADING: 'Loading', REPORT: 'Report', + REPORT_ATTACHMENTS: 'ReportAttachments', NOT_FOUND: 'not-found', TRANSITION_FROM_OLD_DOT: 'TransitionFromOldDot', }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 735f531bcaef..edac1d1638bf 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -251,7 +251,7 @@ class AuthScreens extends React.Component { }} /> { @@ -135,7 +136,7 @@ function dismissModal(targetReportID) { switch (lastRoute.name) { case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case NAVIGATORS.FULL_SCREEN_NAVIGATOR: - case 'Report_Attachments': + case SCREENS.REPORT_ATTACHMENTS: // if we are not in the target report, we need to navigate to it after dismissing the modal if (targetReportID && targetReportID !== getTopmostReportId(rootState)) { const state = getStateFromPath(ROUTES.getReportRoute(targetReportID)); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index a4a142658236..33a35fc25e69 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -14,7 +14,7 @@ export default { UnlinkLogin: ROUTES.UNLINK_LOGIN, [SCREENS.TRANSITION_FROM_OLD_DOT]: ROUTES.TRANSITION_FROM_OLD_DOT, Concierge: ROUTES.CONCIERGE, - Report_Attachments: ROUTES.REPORT_ATTACHMENT, + [SCREENS.REPORT_ATTACHMENTS]: ROUTES.REPORT_ATTACHMENTS, // Sidebar [SCREENS.HOME]: { From 81158575786f1375fe94f695f7fbda3dd53a8daf Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Thu, 15 Jun 2023 15:53:03 +0530 Subject: [PATCH 049/197] Change const to function --- src/pages/NewChatPage.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 80e39422821b..0eb28f5a8c75 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useState, useEffect, useMemo} from 'react'; +import React, {useState, useEffect} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -60,7 +60,7 @@ function NewChatPage(props) { ); const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(props.personalDetails); - const sections = useMemo(() => { + function getSections() { const sectionsList = []; let indexOffset = 0; @@ -110,8 +110,7 @@ function NewChatPage(props) { } return sectionsList; - // eslint-disable-next-line react-hooks/exhaustive-deps -- to avoid destructuring props and adding all 'props' as a dependency - }, [props.isGroupChat, props.translate, selectedOptions, filteredRecentReports, filteredPersonalDetails, filteredUserToInvite, maxParticipantsReached]); + } const updateOptionsWithSearchTerm = (newSearchTerm = '') => { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( @@ -132,7 +131,7 @@ function NewChatPage(props) { * Removes a selected option from list if already selected. If not already selected add this option to the list. * @param {Object} option */ - const toggleOption = (option) => { + function toggleOption(option) { const isOptionInList = _.some(selectedOptions, (selectedOption) => selectedOption.login === option.login); let newSelectedOptions; @@ -149,7 +148,7 @@ function NewChatPage(props) { setFilteredRecentReports(recentReports); setFilteredPersonalDetails(personalDetails); setFilteredUserToInvite(userToInvite); - }; + } /** * Creates a new 1:1 chat with the option and the current user, @@ -157,9 +156,9 @@ function NewChatPage(props) { * * @param {Object} option */ - const createChat = (option) => { + function createChat(option) { Report.navigateToAndOpenReport([option.login]); - }; + } /** * Creates a new group chat with all the selected options and the current user, @@ -185,6 +184,8 @@ function NewChatPage(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.reports, props.personalDetails]); + const sections = getSections(); + return ( Date: Thu, 15 Jun 2023 14:02:57 +0100 Subject: [PATCH 050/197] merge individual state changes to a single useMemo --- src/pages/iou/IOUCurrencySelection.js | 88 ++++++++++++--------------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 26a95033300d..8e654dc19d06 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -57,24 +57,8 @@ const defaultProps = { }; function IOUCurrencySelection(props) { - const [searchValue, setCurrentSearchValue] = useState(''); + const [searchValue, setSearchValue] = useState(''); const selectedCurrencyCode = lodashGet(props.route, 'params.currency', props.iou.selectedCurrencyCode, CONST.CURRENCY.USD); - const currencyOptions = useMemo( - () => - _.map(props.currencyList, (currencyInfo, currencyCode) => { - const isSelectedCurrency = currencyCode === selectedCurrencyCode; - return { - text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - currencyCode, - keyForList: currencyCode, - customIcon: isSelectedCurrency ? greenCheckmark : undefined, - boldStyle: isSelectedCurrency, - }; - }), - [selectedCurrencyCode, props.currencyList], - ); - - const [currencyData, setCurrencyData] = useState(currencyOptions); const confirmCurrencySelection = useCallback( (option) => { @@ -92,33 +76,40 @@ function IOUCurrencySelection(props) { ); const {translate} = props; - - const getSections = useMemo(() => { - if (searchValue.trim() && !currencyData.length) { - return []; - } - return [ - { - title: translate('iOUCurrencySelection.allCurrencies'), - data: currencyData, - shouldShow: true, - indexOffset: 0, - }, - ]; - }, [searchValue, currencyData, translate]); - - const changeSearchValue = useCallback( - (searchQuery) => { - const searchRegex = new RegExp(Str.escapeForRegExp(searchQuery), 'i'); - const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); - - setCurrentSearchValue(searchQuery); - setCurrencyData(filteredCurrencies); - }, - [currencyOptions], - ); - - const headerMessage = searchValue.trim() && !currencyData.length ? translate('common.noResultsFound') : ''; + const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => { + const currencyOptions = _.map(props.currencyList, (currencyInfo, currencyCode) => { + const isSelectedCurrency = currencyCode === selectedCurrencyCode; + return { + text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, + currencyCode, + keyForList: currencyCode, + customIcon: isSelectedCurrency ? greenCheckmark : undefined, + boldStyle: isSelectedCurrency, + }; + }); + + const searchRegex = new RegExp(Str.escapeForRegExp(searchValue), 'i'); + const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text)); + const isEmpty = searchValue.trim() && !filteredCurrencies.length; + + return { + initiallyFocusedOptionKey: _.get( + _.find(filteredCurrencies, (currency) => currency.currencyCode === selectedCurrencyCode), + 'keyForList', + ), + sections: isEmpty + ? [] + : [ + { + title: translate('iOUCurrencySelection.allCurrencies'), + data: filteredCurrencies, + shouldShow: true, + indexOffset: 0, + }, + ], + headerMessage: isEmpty ? translate('common.noResultsFound') : '', + }; + }, [props.currencyList, searchValue, selectedCurrencyCode, translate]); return ( @@ -129,17 +120,14 @@ function IOUCurrencySelection(props) { onBackButtonPress={() => Navigation.goBack(ROUTES.getIouRequestRoute(Navigation.getTopmostReportId()))} /> currency.currencyCode === selectedCurrencyCode), - 'keyForList', - )} + initiallyFocusedOptionKey={initiallyFocusedOptionKey} shouldHaveOptionSeparator /> From ba01429ecd9c99c161dc687a26d2283f3f04c30f Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Jun 2023 14:47:50 +0100 Subject: [PATCH 051/197] pull currencyList from props in IOUCurrencySelection --- src/pages/iou/IOUCurrencySelection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 8e654dc19d06..752273a6377b 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -75,9 +75,9 @@ function IOUCurrencySelection(props) { [props.route, props.navigation], ); - const {translate} = props; + const {translate, currencyList} = props; const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => { - const currencyOptions = _.map(props.currencyList, (currencyInfo, currencyCode) => { + const currencyOptions = _.map(currencyList, (currencyInfo, currencyCode) => { const isSelectedCurrency = currencyCode === selectedCurrencyCode; return { text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, @@ -109,7 +109,7 @@ function IOUCurrencySelection(props) { ], headerMessage: isEmpty ? translate('common.noResultsFound') : '', }; - }, [props.currencyList, searchValue, selectedCurrencyCode, translate]); + }, [currencyList, searchValue, selectedCurrencyCode, translate]); return ( From 8e176eece66b154c89f8914296d27be101dc98b5 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 16 Jun 2023 11:52:47 -0700 Subject: [PATCH 052/197] refactor isThread to isChatThread --- src/libs/OptionsListUtils.js | 4 ++-- src/libs/ReportUtils.js | 22 +++++++++---------- src/libs/SidebarUtils.js | 2 +- src/pages/ReportDetailsPage.js | 2 +- src/pages/ReportParticipantsPage.js | 2 +- src/pages/home/HeaderView.js | 2 +- src/pages/home/report/ReportActionsList.js | 2 +- .../settings/Report/ReportSettingsPage.js | 4 ++-- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 8669eea4703a..40a3a3dc53fb 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -452,7 +452,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.isArchivedRoom = ReportUtils.isArchivedRoom(report); result.isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - result.isThread = ReportUtils.isThread(report); + result.isThread = ReportUtils.isChatThread(report); result.isTaskReport = ReportUtils.isTaskReport(report); result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); result.allReportErrors = getAllReportErrors(report, reportActions); @@ -630,7 +630,7 @@ function getOptions( return; } - const isThread = ReportUtils.isThread(report); + const isThread = ReportUtils.isChatThread(report); const isChatRoom = ReportUtils.isChatRoom(report); const isTaskReport = ReportUtils.isTaskReport(report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 02c3308898f8..9a4dd5a5c738 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -536,7 +536,7 @@ function isPolicyExpenseChatAdmin(report, policies) { * @param {Object} report * @returns {Boolean} */ -function isThread(report) { +function isChatThread(report) { return Boolean(report && report.parentReportID && report.parentReportActionID && report.type === CONST.REPORT.TYPE.CHAT); } @@ -733,7 +733,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false) result.source = Expensicons.DeletedRoomAvatar; return [result]; } - if (isThread(report)) { + if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const actorEmail = lodashGet(parentReportAction, 'actorEmail', ''); @@ -1006,7 +1006,7 @@ function getTransactionReportName(reportAction) { */ function getReportName(report) { let formattedName; - if (isThread(report)) { + if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (ReportActionsUtils.isTransactionThread(parentReportAction)) { return getTransactionReportName(parentReportAction); @@ -1053,7 +1053,7 @@ function getReportName(report) { * @returns {String|*} */ function getDMRootReportName(report) { - if (isThread(report) && !getChatType(report)) { + if (isChatThread(report) && !getChatType(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); return getDMRootReportName(parentReport); } @@ -1067,7 +1067,7 @@ function getDMRootReportName(report) { * @returns {String} */ function getChatRoomSubtitle(report) { - if (isThread(report)) { + if (isChatThread(report)) { if (!getChatType(report)) { return `${Localize.translateLocal('threads.from')} ${getDMRootReportName(report)}`; } @@ -1119,7 +1119,7 @@ function getReport(reportID) { function navigateToDetailsPage(report) { const participantAccountIDs = lodashGet(report, 'participantAccountIDs', []); - if (isChatRoom(report) || isPolicyExpenseChat(report) || isThread(report)) { + if (isChatRoom(report) || isPolicyExpenseChat(report) || isChatThread(report)) { Navigation.navigate(ROUTES.getReportDetailsRoute(report.reportID)); return; } @@ -1918,7 +1918,7 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, iouR if ( !report || !report.reportID || - (_.isEmpty(report.participantAccountIDs) && !isThread(report) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report) && !isTaskReport(report)) + (_.isEmpty(report.participantAccountIDs) && !isChatThread(report) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report) && !isTaskReport(report)) ) { return false; } @@ -1973,7 +1973,7 @@ function getChatByParticipants(newParticipantList) { newParticipantList.sort(); return _.find(allReports, (report) => { // If the report has been deleted, or there are no participants (like an empty #admins room) then skip it - if (!report || !report.participantAccountIDs || isThread(report)) { + if (!report || !report.participantAccountIDs || isChatThread(report)) { return false; } @@ -2113,7 +2113,7 @@ function canRequestMoney(report) { */ function getMoneyRequestOptions(report, reportParticipants, betas) { // In any thread, we do not allow any new money requests yet - if (isThread(report)) { + if (isChatThread(report)) { return []; } @@ -2219,7 +2219,7 @@ function shouldReportShowSubscript(report) { return false; } - if (isPolicyExpenseChat(report) && !isThread(report) && !isTaskReport(report) && !report.isOwnPolicyExpenseChat) { + if (isPolicyExpenseChat(report) && !isChatThread(report) && !isTaskReport(report) && !report.isOwnPolicyExpenseChat) { return true; } @@ -2329,7 +2329,7 @@ export { canRequestMoney, getWhisperDisplayNames, getWorkspaceAvatar, - isThread, + isChatThread, isThreadParent, isThreadFirstChat, shouldReportShowSubscript, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index a41249ebfa3d..7bd30a13af16 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -263,7 +263,7 @@ function getOptionData(reportID) { const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(report.participantAccountIDs, personalDetails)); const personalDetail = participantPersonalDetailList[0] || {}; - result.isThread = ReportUtils.isThread(report); + result.isThread = ReportUtils.isChatThread(report); result.isChatRoom = ReportUtils.isChatRoom(report); result.isTaskReport = ReportUtils.isTaskReport(report); if (result.isTaskReport) { diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 19f78e08c604..c0e3a07bc214 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -63,7 +63,7 @@ function ReportDetailsPage(props) { const isPolicyAdmin = useMemo(() => PolicyUtils.isPolicyAdmin(policy), [policy]); const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(props.report), [props.report]); const isChatRoom = useMemo(() => ReportUtils.isChatRoom(props.report), [props.report]); - const isThread = useMemo(() => ReportUtils.isThread(props.report), [props.report]); + const isThread = useMemo(() => ReportUtils.isChatThread(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index da065dadcb77..176a0d8f07a4 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -92,7 +92,7 @@ function ReportParticipantsPage(props) { 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); - const isThread = ReportUtils.isThread(props.report); + const isThread = ReportUtils.isChatThread(props.report); const isChatRoom = ReportUtils.isChatRoom(props.report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); const isTaskReport = ReportUtils.isTaskReport(props.report); diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index c3288c3eeb6a..1a8b74600e28 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -115,7 +115,7 @@ function ReportActionsList(props) { ({item: reportAction, index}) => { // When the new indicator should not be displayed we explicitly set it to null const shouldDisplayNewMarker = reportAction.reportActionID === newMarkerReportActionID; - const shouldDisplayParentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && ReportUtils.isThread(report); + const shouldDisplayParentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && ReportUtils.isChatThread(report); const shouldHideThreadDividerLine = shouldDisplayParentAction && sortedReportActions.length > 1 && sortedReportActions[sortedReportActions.length - 2].reportActionID === newMarkerReportActionID; return shouldDisplayParentAction ? ( diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index ad57de80aaaa..d07da128acd6 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -94,9 +94,9 @@ class ReportSettingsPage extends Component { } render() { - const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report) && !ReportUtils.isThread(this.props.report); + const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report) && !ReportUtils.isChatThread(this.props.report); const linkedWorkspace = _.find(this.props.policies, (policy) => policy && policy.id === this.props.report.policyID); - const shouldDisableRename = this.shouldDisableRename(linkedWorkspace) || ReportUtils.isThread(this.props.report); + const shouldDisableRename = this.shouldDisableRename(linkedWorkspace) || ReportUtils.isChatThread(this.props.report); const notificationPreference = this.props.translate(`notificationPreferencesPage.notificationPreferences.${this.props.report.notificationPreference}`); const shouldDisableWelcomeMessage = this.shouldDisableWelcomeMessage(linkedWorkspace); const writeCapability = this.props.report.writeCapability || CONST.REPORT.WRITE_CAPABILITIES.ALL; From f054742c6a02441ca0e9d301a7f4e4ff0d68e1d1 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 16 Jun 2023 11:55:47 -0700 Subject: [PATCH 053/197] isthread and getChatRoomSubtitleLink --- src/libs/ReportUtils.js | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 9a4dd5a5c738..2a5476e1ef64 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -531,7 +531,17 @@ function isPolicyExpenseChatAdmin(report, policies) { } /** - * Returns true if report has a parent and is therefore a Thread. + * Returns true if report has a parent + * + * @param {Object} report + * @returns {Boolean} + */ +function isThread(report) { + return Boolean(report && report.parentReportID && report.parentReportActionID); +} + +/** + * Returns true if report is of type chat and has a parent and is therefore a Thread. * * @param {Object} report * @returns {Boolean} @@ -1101,6 +1111,33 @@ function getChatRoomSubtitle(report) { return getPolicyName(report); } +/** + * Get either the policyName or domainName the chat is tied to + * @param {Object} report + * @returns {String} + */ +function getChatRoomSubtitleLink(report) { + if (isThread(report)) { + if (!getChatType(report)) { + return `${Localize.translateLocal('threads.from')} ${getDMRootReportName(report)}`; + } + + let roomName = ''; + if (isChatRoom(report)) { + const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); + if (parentReport) { + roomName = lodashGet(parentReport, 'displayName', ''); + } else { + roomName = lodashGet(report, 'displayName', ''); + } + } + + const workspaceName = getPolicyName(report); + return `${Localize.translateLocal('threads.from')} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; + } + return ''; +} + /** * Get the report for a reportID * @@ -2263,6 +2300,7 @@ export { isUserCreatedPolicyRoom, isChatRoom, getChatRoomSubtitle, + getChatRoomSubtitleLink, getPolicyName, getPolicyType, isArchivedRoom, @@ -2329,6 +2367,7 @@ export { canRequestMoney, getWhisperDisplayNames, getWorkspaceAvatar, + isThread, isChatThread, isThreadParent, isThreadFirstChat, From d78fe9420702d98093560c22eeed14bb0ba8d329 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 16 Jun 2023 11:58:14 -0700 Subject: [PATCH 054/197] getRootReportName refactor --- src/libs/ReportUtils.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2a5476e1ef64..0ca112f01703 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1058,14 +1058,14 @@ function getReportName(report) { } /** - * Recursively navigates through parent to get the root reports name only for DM reports. + * Recursively navigates through thread parents to get the root reports name. * @param {Object} report * @returns {String|*} */ -function getDMRootReportName(report) { - if (isChatThread(report) && !getChatType(report)) { +function getRootReportName(report) { + if (isThread(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - return getDMRootReportName(parentReport); + return getRootReportName(parentReport); } return getReportName(report); @@ -1079,7 +1079,7 @@ function getDMRootReportName(report) { function getChatRoomSubtitle(report) { if (isChatThread(report)) { if (!getChatType(report)) { - return `${Localize.translateLocal('threads.from')} ${getDMRootReportName(report)}`; + return `${Localize.translateLocal('threads.from')} ${getRootReportName(report)}`; } let roomName = ''; @@ -1118,10 +1118,6 @@ function getChatRoomSubtitle(report) { */ function getChatRoomSubtitleLink(report) { if (isThread(report)) { - if (!getChatType(report)) { - return `${Localize.translateLocal('threads.from')} ${getDMRootReportName(report)}`; - } - let roomName = ''; if (isChatRoom(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); @@ -1130,10 +1126,12 @@ function getChatRoomSubtitleLink(report) { } else { roomName = lodashGet(report, 'displayName', ''); } + + const workspaceName = getPolicyName(report); + return `${Localize.translateLocal('threads.from')} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; } - const workspaceName = getPolicyName(report); - return `${Localize.translateLocal('threads.from')} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; + return `${Localize.translateLocal('threads.from')} ${getRootReportName(report)}`; } return ''; } From 563fe3220c4736e13d0af6769fb4a9c1bb0c7b95 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 16 Jun 2023 12:45:14 -0700 Subject: [PATCH 055/197] updating getChatRoomSubtitleLink --- src/libs/ReportUtils.js | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 0ca112f01703..bb8bd8b7c288 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1078,22 +1078,7 @@ function getRootReportName(report) { */ function getChatRoomSubtitle(report) { if (isChatThread(report)) { - if (!getChatType(report)) { - return `${Localize.translateLocal('threads.from')} ${getRootReportName(report)}`; - } - - let roomName = ''; - if (isChatRoom(report)) { - const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - if (parentReport) { - roomName = lodashGet(parentReport, 'displayName', ''); - } else { - roomName = lodashGet(report, 'displayName', ''); - } - } - - const workspaceName = getPolicyName(report); - return `${Localize.translateLocal('threads.from')} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; + return ''; } if (!isDefaultRoom(report) && !isUserCreatedPolicyRoom(report) && !isPolicyExpenseChat(report)) { return ''; @@ -1117,10 +1102,15 @@ function getChatRoomSubtitle(report) { * @returns {String} */ function getChatRoomSubtitleLink(report) { + if (report.reportID == '3346402972554959') { + debugger; + } + if (isThread(report)) { - let roomName = ''; + const from = Localize.translateLocal('threads.from'); if (isChatRoom(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); + let roomName = ''; if (parentReport) { roomName = lodashGet(parentReport, 'displayName', ''); } else { @@ -1128,10 +1118,15 @@ function getChatRoomSubtitleLink(report) { } const workspaceName = getPolicyName(report); - return `${Localize.translateLocal('threads.from')} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; + return `${from} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; } - return `${Localize.translateLocal('threads.from')} ${getRootReportName(report)}`; + if (isExpenseReport(report)) { + const payeeEmail = getDisplayNameForParticipant(report.managerID); + const workspaceName = getPolicyName(report); + return `${from} ${workspaceName ? [payeeEmail, workspaceName].join(' in ') : payeeEmail}`; + } + return `${from} ${getRootReportName(report)}`; } return ''; } From ff3b93d06c816cc5fdaab5431b3c1b04ec8dc9bc Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 16 Jun 2023 12:45:25 -0700 Subject: [PATCH 056/197] using subtitleLink --- src/pages/home/HeaderView.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 7334bf174e60..86eaa10c2311 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -82,6 +82,7 @@ function HeaderView(props) { const reportHeaderData = !isTaskReport && !isThread && props.report.parentReportID ? props.parentReport : props.report; const title = ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); + const subtitleLink = ReportUtils.getChatRoomSubtitleLink(reportHeaderData); const isConcierge = participants.length === 1 && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); const isAutomatedExpensifyAccount = participants.length === 1 && ReportUtils.hasAutomatedExpensifyAccountIDs(participants); const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); @@ -177,24 +178,25 @@ function HeaderView(props) { textStyles={[styles.headerText, styles.pre]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isThread || isTaskReport} /> - {(isChatRoom || isPolicyExpenseChat || isThread) && !_.isEmpty(subtitle) && ( + {(isChatRoom || isPolicyExpenseChat || isThread) && ( <> - {isThread ? ( + {!_.isEmpty(subtitleLink) && ( { Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); }} - accessibilityLabel={subtitle} + accessibilityLabel={subtitleLink} accessibilityRole="link" > - {subtitle} + {subtitleLink} - ) : ( + )} + {!_.isEmpty(subtitle) && ( Date: Fri, 16 Jun 2023 12:45:46 -0700 Subject: [PATCH 057/197] using subtitleLink in AvatarWithDisplayName --- src/components/AvatarWithDisplayName.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 7889dcb0b703..c5a7fcbacc1c 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -17,6 +17,10 @@ import compose from '../libs/compose'; import * as OptionsListUtils from '../libs/OptionsListUtils'; import Text from './Text'; import * as StyleUtils from '../styles/StyleUtils'; +import {getChatRoomSubtitleLink} from '../libs/ReportUtils'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; const propTypes = { /** The report currently being looked at */ @@ -57,6 +61,7 @@ function AvatarWithDisplayName(props) { const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const avatarContainerStyle = StyleUtils.getEmptyAvatarStyle(props.size) || styles.emptyAvatar; + const subtitleLink = ReportUtils.getChatRoomSubtitleLink(props.report); return ( {Boolean(props.report && title) && ( @@ -88,6 +93,22 @@ function AvatarWithDisplayName(props) { textStyles={[props.isAnonymous ? styles.headerAnonymousFooter : styles.headerText, styles.pre]} shouldUseFullTitle={isExpenseReport || props.isAnonymous} /> + {!_.isEmpty(subtitleLink) && ( + { + Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); + }} + accessibilityLabel={subtitle} + accessibilityRole="link" + > + + {subtitleLink} + + + )} {!_.isEmpty(subtitle) && ( Date: Fri, 16 Jun 2023 16:57:33 -0700 Subject: [PATCH 058/197] getRootOrExpenseReportName refactor --- src/libs/ReportUtils.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index bb8bd8b7c288..d3d15f812372 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1062,10 +1062,13 @@ function getReportName(report) { * @param {Object} report * @returns {String|*} */ -function getRootReportName(report) { +function getRootOrExpenseReportName(report) { if (isThread(report)) { + if (isExpenseReport(report)) { + return lodashGet(report, 'displayName', ''); + } const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - return getRootReportName(parentReport); + return getRootOrExpenseReportName(parentReport); } return getReportName(report); @@ -1102,31 +1105,24 @@ function getChatRoomSubtitle(report) { * @returns {String} */ function getChatRoomSubtitleLink(report) { - if (report.reportID == '3346402972554959') { - debugger; - } - if (isThread(report)) { const from = Localize.translateLocal('threads.from'); + const workspaceName = getPolicyName(report); if (isChatRoom(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - let roomName = ''; - if (parentReport) { - roomName = lodashGet(parentReport, 'displayName', ''); - } else { - roomName = lodashGet(report, 'displayName', ''); - } - - const workspaceName = getPolicyName(report); + const roomName = parentReport + ? lodashGet(parentReport, 'displayName', '') + : lodashGet(report, 'displayName', ''); + return `${from} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; } if (isExpenseReport(report)) { const payeeEmail = getDisplayNameForParticipant(report.managerID); - const workspaceName = getPolicyName(report); return `${from} ${workspaceName ? [payeeEmail, workspaceName].join(' in ') : payeeEmail}`; } - return `${from} ${getRootReportName(report)}`; + + return `${from} ${getRootOrExpenseReportName(report)}`; } return ''; } From 511c5b6e8bbad414952e5cd9205863ae97bf97ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Sat, 17 Jun 2023 16:57:53 +0200 Subject: [PATCH 059/197] fix default value --- src/components/TextInput/BaseTextInput.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 724e39c0cdce..6fe66c76f5aa 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -320,6 +320,7 @@ function BaseTextInput(props) { value={props.value} selection={props.selection} editable={isEditable} + defaultValue={props.defaultValue} // FormSubmit Enter key handler does not have access to direct props. // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback. dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}} From 682ad7e548607b3fc15102676a8694f955ca1acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Sat, 17 Jun 2023 16:58:17 +0200 Subject: [PATCH 060/197] fix stories --- src/stories/TextInput.stories.js | 65 +++++++++++++++++++------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/stories/TextInput.stories.js b/src/stories/TextInput.stories.js index 64329ffed715..2a9e862d6876 100644 --- a/src/stories/TextInput.stories.js +++ b/src/stories/TextInput.stories.js @@ -60,32 +60,6 @@ PlaceholderInput.args = { placeholder: 'My placeholder text', }; -const AutoGrowInput = Template.bind({}); -AutoGrowInput.args = { - label: 'Autogrow input', - name: 'AutoGrow', - placeholder: 'My placeholder text', - autoGrow: true, - textInputContainerStyles: [ - { - minWidth: 150, - }, - ], -}; - -const AutoGrowHeightInput = Template.bind({}); -AutoGrowHeightInput.args = { - label: 'Autogrowheight input', - name: 'AutoGrowHeight', - placeholder: 'My placeholder text', - autoGrowHeight: true, - textInputContainerStyles: [ - { - maxHeight: 115, - }, - ], -}; - const PrefixedInput = Template.bind({}); PrefixedInput.args = { label: 'Prefixed input', @@ -126,5 +100,44 @@ HintAndErrorInput.args = { hint: 'Type "Oops!" to see the error', }; +// To use autoGrow we need to control the TextInput's value +function AutoGrowSupportInput(args) { + const [value, setValue] = useState(''); + return ( + + ); +} + +const AutoGrowInput = AutoGrowSupportInput.bind({}); +AutoGrowInput.args = { + label: 'Autogrow input', + name: 'AutoGrow', + placeholder: 'My placeholder text', + autoGrow: true, + textInputContainerStyles: [ + { + minWidth: 150, + }, + ], +}; + +const AutoGrowHeightInput = AutoGrowSupportInput.bind({}); +AutoGrowHeightInput.args = { + label: 'Autogrowheight input', + name: 'AutoGrowHeight', + placeholder: 'My placeholder text', + autoGrowHeight: true, + textInputContainerStyles: [ + { + maxHeight: 115, + }, + ], +}; + export default story; export {AutoFocus, DefaultInput, DefaultValueInput, ErrorInput, ForceActiveLabel, PlaceholderInput, AutoGrowInput, AutoGrowHeightInput, PrefixedInput, MaxLengthInput, HintAndErrorInput}; From 305fcc4bdb68212130998eaf4bdfe63be7b8261f Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 19 Jun 2023 17:43:45 +0300 Subject: [PATCH 061/197] Refactor: ReportAttachments pass `report` prop to `AttachmentModal` --- src/pages/home/report/ReportAttachments.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index 203ba3d8c93e..38e7781aedf5 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -3,6 +3,7 @@ import _ from 'underscore'; import PropTypes from 'prop-types'; import AttachmentModal from '../../../components/AttachmentModal'; import Navigation from '../../../libs/Navigation/Navigation'; +import * as ReportUtils from '../../../libs/ReportUtils'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -19,13 +20,14 @@ const propTypes = { function ReportAttachments(props) { const reportID = _.get(props, ['route', 'params', 'reportID']); + const report = ReportUtils.getReport(reportID); const source = decodeURI(_.get(props, ['route', 'params', 'source'])); return ( Navigation.dismissModal(reportID)} /> From 31da9a50bdf120026763c100c35016d5a74ff4a3 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 13:48:53 -0700 Subject: [PATCH 062/197] centralizing logic --- src/components/AvatarWithDisplayName.js | 4 ++ src/components/MoneyRequestHeader.js | 1 - src/pages/home/HeaderView.js | 50 ++++++++++++------------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index c5a7fcbacc1c..359734b5ba5e 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -54,6 +54,10 @@ const defaultProps = { }; function AvatarWithDisplayName(props) { + if (props.report.reportID == '5154639516299574') { + debugger; + } + const title2 = ReportUtils.getReportName(props.report); const title = props.isAnonymous ? props.report.displayName : ReportUtils.getDisplayNameForParticipant(props.report.ownerAccountID, true); const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const isExpenseReport = ReportUtils.isExpenseReport(props.report); diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index af79fd2b8132..714a41035b75 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -105,7 +105,6 @@ function MoneyRequestHeader(props) { ]} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(props.windowWidth)} report={props.report} - parentReport={moneyRequestReport} policies={props.policies} personalDetails={props.personalDetails} shouldShowBackButton={props.isSmallScreenWidth} diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 86eaa10c2311..eae64822aa34 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -178,33 +178,29 @@ function HeaderView(props) { textStyles={[styles.headerText, styles.pre]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isThread || isTaskReport} /> - {(isChatRoom || isPolicyExpenseChat || isThread) && ( - <> - {!_.isEmpty(subtitleLink) && ( - { - Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); - }} - accessibilityLabel={subtitleLink} - accessibilityRole="link" - > - - {subtitleLink} - - - )} - {!_.isEmpty(subtitle) && ( - - {subtitle} - - )} - + {!_.isEmpty(subtitleLink) && ( + { + Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); + }} + accessibilityLabel={subtitleLink} + accessibilityRole="link" + > + + {subtitleLink} + + + )} + {!_.isEmpty(subtitle) && ( + + {subtitle} + )} {brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && ( From 4285fc2a82341bc22fd0e6f3c6010f77087ee7f0 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 15:52:03 -0700 Subject: [PATCH 063/197] removing debug code --- src/components/AvatarWithDisplayName.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 359734b5ba5e..c5a7fcbacc1c 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -54,10 +54,6 @@ const defaultProps = { }; function AvatarWithDisplayName(props) { - if (props.report.reportID == '5154639516299574') { - debugger; - } - const title2 = ReportUtils.getReportName(props.report); const title = props.isAnonymous ? props.report.displayName : ReportUtils.getDisplayNameForParticipant(props.report.ownerAccountID, true); const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const isExpenseReport = ReportUtils.isExpenseReport(props.report); From 5bccf00bd969b243f8d8603d34d0216478b8f12e Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 15:52:18 -0700 Subject: [PATCH 064/197] correcting props --- src/components/MoneyRequestHeader.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 714a41035b75..80b2b54bd9a1 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -90,6 +90,11 @@ function MoneyRequestHeader(props) { const shouldShowSettlementButton = !isSettled && !props.isSingleTransactionView && isPayer; const bankAccountRoute = ReportUtils.getBankAccountRoute(props.chatReport); const shouldShowPaypal = Boolean(lodashGet(props.personalDetails, [moneyRequestReport.managerID, 'payPalMeAddress'])); + const report = props.report; + if (props.isSingleTransactionView) { + report.ownerAccountID = props.parentReport.ownerAccountID; + report.ownerEmail = props.parentReport.ownerEmail; + } return ( Date: Mon, 19 Jun 2023 15:52:42 -0700 Subject: [PATCH 065/197] updating logic for IOU and expense report children --- src/libs/ReportUtils.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index d3d15f812372..589c653a9662 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -20,6 +20,7 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; +import {isMoneyRequestAction} from './ReportActionsUtils'; let sessionEmail; let sessionAccountID; @@ -1064,7 +1065,7 @@ function getReportName(report) { */ function getRootOrExpenseReportName(report) { if (isThread(report)) { - if (isExpenseReport(report)) { + if (isMoneyRequestReport(report)) { return lodashGet(report, 'displayName', ''); } const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); @@ -1105,6 +1106,9 @@ function getChatRoomSubtitle(report) { * @returns {String} */ function getChatRoomSubtitleLink(report) { + if (report.reportID == '3519318573359925') { + debugger; + } if (isThread(report)) { const from = Localize.translateLocal('threads.from'); const workspaceName = getPolicyName(report); @@ -1117,9 +1121,9 @@ function getChatRoomSubtitleLink(report) { return `${from} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; } - if (isExpenseReport(report)) { + if (isMoneyRequestReport(report)) { const payeeEmail = getDisplayNameForParticipant(report.managerID); - return `${from} ${workspaceName ? [payeeEmail, workspaceName].join(' in ') : payeeEmail}`; + return isIOUReport(report) ? `${from} ${payeeEmail}` : `${from} ${payeeEmail} in ${workspaceName}`; } return `${from} ${getRootOrExpenseReportName(report)}`; From 599d489d8aa685c080a249cdb3a7ea8a30bf278d Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 15:52:57 -0700 Subject: [PATCH 066/197] removing debug --- src/libs/ReportUtils.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 589c653a9662..5e4ebd190440 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1106,9 +1106,6 @@ function getChatRoomSubtitle(report) { * @returns {String} */ function getChatRoomSubtitleLink(report) { - if (report.reportID == '3519318573359925') { - debugger; - } if (isThread(report)) { const from = Localize.translateLocal('threads.from'); const workspaceName = getPolicyName(report); From 687fff96fb398ced0ea359eaba023fe24902febc Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 17:22:50 -0700 Subject: [PATCH 067/197] code improvements --- src/libs/ReportUtils.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 1e3ccdce0360..68fea3726ec6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1058,13 +1058,18 @@ function getReportName(report) { * @param {Object} report * @returns {String|*} */ -function getRootOrExpenseReportName(report) { +function getRootOrExpenseReportNameWithWorkspace(report) { if (isThread(report)) { - if (isMoneyRequestReport(report)) { + if (isIOUReport(report)) { return lodashGet(report, 'displayName', ''); } + if (isExpenseReport(report)) { + const workspaceName = getPolicyName(report); + return `${lodashGet(report, 'displayName', '')} in ${workspaceName}`; + } + const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - return getRootOrExpenseReportName(parentReport); + return getRootOrExpenseReportNameWithWorkspace(parentReport); } return getReportName(report); @@ -1118,7 +1123,7 @@ function getChatRoomSubtitleLink(report) { return isIOUReport(report) ? `${from} ${payeeEmail}` : `${from} ${payeeEmail} in ${workspaceName}`; } - return `${from} ${getRootOrExpenseReportName(report)}`; + return `${from} ${getRootOrExpenseReportNameWithWorkspace(report)}`; } return ''; } From a0765633253b843a703bd13097f5282ef270240e Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 19 Jun 2023 17:36:13 -0700 Subject: [PATCH 068/197] task logic improvements --- src/libs/ReportUtils.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 68fea3726ec6..2660c6751c92 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1068,6 +1068,12 @@ function getRootOrExpenseReportNameWithWorkspace(report) { return `${lodashGet(report, 'displayName', '')} in ${workspaceName}`; } + if (isTaskReport(report)) { + const workspaceName = report.policyID === CONST.POLICY.OWNER_EMAIL_FAKE ? '' : getPolicyName(report); + const displayName = getDisplayNameForParticipant(report.ownerAccountID); + return `${workspaceName ? [displayName, workspaceName].join(' in ') : displayName}`; + } + const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); return getRootOrExpenseReportNameWithWorkspace(parentReport); } From dc5a7f005392f9e6b2c5b39f1bb37bb51f7df711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 20 Jun 2023 09:27:42 +0200 Subject: [PATCH 069/197] fix update label --- src/components/TextInput/BaseTextInput.js | 22 ++++++++++++++++++---- src/stories/TextInput.stories.js | 7 ++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 6fe66c76f5aa..2c4f5a983e6e 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -167,17 +167,26 @@ function BaseTextInput(props) { [props.autoGrowHeight, props.multiline], ); + const hasValueRef = useRef(inputValue.length > 0); useEffect(() => { - // Activate or deactivate the label when value is changed programmatically from outside + // Handle side effects when the value gets changed programatically from the outside // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput. if (inputValue === '') { input.current.clear(); } - if (inputValue || isFocused) { + if (inputValue) { + activateLabel() + } + }, [activateLabel, inputValue]) + + useEffect(() => { + // Activate or deactivate the label when the focus changes + + if (hasValueRef.current || isFocused) { activateLabel(); - } else if (!isFocused) { + } else if (!hasValueRef.current && !isFocused) { deactivateLabel(); } }, [activateLabel, deactivateLabel, inputValue, isFocused]); @@ -194,7 +203,12 @@ function BaseTextInput(props) { } Str.result(props.onChangeText, value); - activateLabel(); + if (value && value.length > 0) { + hasValueRef.current = true + activateLabel(); + } else { + hasValueRef.current = false + } }; const togglePasswordVisibility = useCallback(() => { diff --git a/src/stories/TextInput.stories.js b/src/stories/TextInput.stories.js index 2a9e862d6876..4c24e5b7904e 100644 --- a/src/stories/TextInput.stories.js +++ b/src/stories/TextInput.stories.js @@ -102,7 +102,11 @@ HintAndErrorInput.args = { // To use autoGrow we need to control the TextInput's value function AutoGrowSupportInput(args) { - const [value, setValue] = useState(''); + const [value, setValue] = useState(args.value || ''); + React.useEffect(() => { + setValue(args.value || '') + }, [args.value]) + return ( Date: Tue, 20 Jun 2023 09:28:33 +0200 Subject: [PATCH 070/197] add max width --- src/stories/TextInput.stories.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stories/TextInput.stories.js b/src/stories/TextInput.stories.js index 4c24e5b7904e..72467f0535bf 100644 --- a/src/stories/TextInput.stories.js +++ b/src/stories/TextInput.stories.js @@ -123,12 +123,13 @@ AutoGrowInput.args = { name: 'AutoGrow', placeholder: 'My placeholder text', autoGrow: true, - value: '', textInputContainerStyles: [ { minWidth: 150, + maxWidth: 500, }, ], + value: '', }; const AutoGrowHeightInput = AutoGrowSupportInput.bind({}); From 7c52921b1e1e0ec6f2ade41dbf88ea949115efa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Tue, 20 Jun 2023 09:51:57 +0200 Subject: [PATCH 071/197] prettier --- src/components/TextInput/BaseTextInput.js | 8 ++++---- src/stories/TextInput.stories.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 2c4f5a983e6e..b3fe23f99511 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -177,9 +177,9 @@ function BaseTextInput(props) { } if (inputValue) { - activateLabel() + activateLabel(); } - }, [activateLabel, inputValue]) + }, [activateLabel, inputValue]); useEffect(() => { // Activate or deactivate the label when the focus changes @@ -204,10 +204,10 @@ function BaseTextInput(props) { Str.result(props.onChangeText, value); if (value && value.length > 0) { - hasValueRef.current = true + hasValueRef.current = true; activateLabel(); } else { - hasValueRef.current = false + hasValueRef.current = false; } }; diff --git a/src/stories/TextInput.stories.js b/src/stories/TextInput.stories.js index 72467f0535bf..098828c65198 100644 --- a/src/stories/TextInput.stories.js +++ b/src/stories/TextInput.stories.js @@ -104,8 +104,8 @@ HintAndErrorInput.args = { function AutoGrowSupportInput(args) { const [value, setValue] = useState(args.value || ''); React.useEffect(() => { - setValue(args.value || '') - }, [args.value]) + setValue(args.value || ''); + }, [args.value]); return ( Date: Tue, 20 Jun 2023 19:55:14 +0530 Subject: [PATCH 072/197] Fix wrongly merged code --- src/pages/NewChatPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 159500a22367..ed8ddf69b18f 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -14,6 +14,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../components/wit import HeaderWithBackButton from '../components/HeaderWithBackButton'; import ScreenWrapper from '../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import * as Browser from '../libs/Browser'; import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; @@ -203,7 +204,7 @@ function NewChatPage(props) { onChangeText={updateOptionsWithSearchTerm} headerMessage={headerMessage} boldStyle - shouldFocusOnSelectRow={props.isGroupChat} + shouldFocusOnSelectRow={props.isGroupChat && !Browser.isMobile()} shouldShowConfirmButton={props.isGroupChat} shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} confirmButtonText={props.translate('newChatPage.createGroup')} From 80a9a9b0c44e9ddf79611f36c1c4a4b6fba23905 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Tue, 20 Jun 2023 20:01:38 +0530 Subject: [PATCH 073/197] fix lint --- src/pages/NewChatPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index ed8ddf69b18f..7585c979b2b4 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -174,7 +174,7 @@ function NewChatPage(props) { return; } Report.navigateToAndOpenReport(logins); - } + }; useEffect(() => { updateOptionsWithSearchTerm(searchTerm); From dadbc9cb9955dfd5357baec381ef7eab69f134b4 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 14:52:44 -0700 Subject: [PATCH 074/197] logic corrections --- src/libs/ReportUtils.js | 63 +++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2660c6751c92..2b8283e81c7a 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1056,29 +1056,30 @@ function getReportName(report) { /** * Recursively navigates through thread parents to get the root reports name. * @param {Object} report - * @returns {String|*} + * @returns {Object} */ -function getRootOrExpenseReportNameWithWorkspace(report) { - if (isThread(report)) { - if (isIOUReport(report)) { - return lodashGet(report, 'displayName', ''); - } - if (isExpenseReport(report)) { - const workspaceName = getPolicyName(report); - return `${lodashGet(report, 'displayName', '')} in ${workspaceName}`; - } - - if (isTaskReport(report)) { - const workspaceName = report.policyID === CONST.POLICY.OWNER_EMAIL_FAKE ? '' : getPolicyName(report); - const displayName = getDisplayNameForParticipant(report.ownerAccountID); - return `${workspaceName ? [displayName, workspaceName].join(' in ') : displayName}`; - } - +function getRootReportAndWorkspaceName(report) { + if (isThread(report) && !isMoneyRequestReport(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - return getRootOrExpenseReportNameWithWorkspace(parentReport); + return getRootReportAndWorkspaceName(parentReport); } - return getReportName(report); + if (isIOUReport(report)) { + return { + rootReportName: lodashGet(report, 'displayName', ''), + }; + } + if (isMoneyRequestReport(report)) { + return { + rootReportName: lodashGet(report, 'displayName', ''), + workspaceName: isIOUReport(report) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(report), + }; + } + + return { + rootReportName: getReportName(report), + workspaceName: getPolicyName(report), + }; } /** @@ -1113,23 +1114,17 @@ function getChatRoomSubtitle(report) { */ function getChatRoomSubtitleLink(report) { if (isThread(report)) { - const from = Localize.translateLocal('threads.from'); - const workspaceName = getPolicyName(report); - if (isChatRoom(report)) { - const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); - const roomName = parentReport - ? lodashGet(parentReport, 'displayName', '') - : lodashGet(report, 'displayName', ''); - - return `${from} ${roomName ? [roomName, workspaceName].join(' in ') : workspaceName}`; + if (report.reportID == '280084830934340') { + debugger; } - - if (isMoneyRequestReport(report)) { - const payeeEmail = getDisplayNameForParticipant(report.managerID); - return isIOUReport(report) ? `${from} ${payeeEmail}` : `${from} ${payeeEmail} in ${workspaceName}`; + const from = Localize.translateLocal('threads.from'); + const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); + const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); + let subtitleLink = `${from} ${rootReportName}`; + if (workspaceName && workspaceName !== rootReportName) { + subtitleLink += ` in ${workspaceName}`; } - - return `${from} ${getRootOrExpenseReportNameWithWorkspace(report)}`; + return subtitleLink; } return ''; } From a3954a7ddd37e3b1b90ca95de1a5589a5a42a56c Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 15:00:28 -0700 Subject: [PATCH 075/197] ignore unavailable workspace --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2b8283e81c7a..d676f8ea3364 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1121,7 +1121,7 @@ function getChatRoomSubtitleLink(report) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); let subtitleLink = `${from} ${rootReportName}`; - if (workspaceName && workspaceName !== rootReportName) { + if (workspaceName && workspaceName !== rootReportName && workspaceName !== Localize.translateLocal('workspace.common.unavailable')) { subtitleLink += ` in ${workspaceName}`; } return subtitleLink; From e7cc616e5924154329b3588323d499b0315be868 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 15:08:29 -0700 Subject: [PATCH 076/197] translation for in --- src/languages/en.js | 3 ++- src/languages/es.js | 3 ++- src/libs/ReportUtils.js | 10 +++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index fac5a6c82db1..1293041f9d1e 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -146,6 +146,8 @@ export default { km: 'kilometer', copied: 'Copied!', someone: 'Someone', + from: 'From', + conjunctionIn: 'in', }, anonymousReportFooter: { logoTagline: 'Join in on the discussion.', @@ -1412,7 +1414,6 @@ export default { lastReply: 'Last reply', replies: 'Replies', reply: 'Reply', - from: 'From', }, qrCodes: { copyUrlToClipboard: 'Copy URL to clipboard', diff --git a/src/languages/es.js b/src/languages/es.js index 28b98f18e25b..a0a3995ea9e6 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -145,6 +145,8 @@ export default { km: 'kilómetro', copied: '¡Copiado!', someone: 'Alguien', + from: 'De', + conjunctionIn: 'en', }, anonymousReportFooter: { logoTagline: 'Únete a la discussion.', @@ -1880,7 +1882,6 @@ export default { lastReply: 'Última respuesta', replies: 'Respuestas', reply: 'Respuesta', - from: 'De', }, qrCodes: { copyUrlToClipboard: 'Copiar URL al portapapeles', diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index d676f8ea3364..eba7aa9843dc 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1108,21 +1108,17 @@ function getChatRoomSubtitle(report) { } /** - * Get either the policyName or domainName the chat is tied to + * Get the subtitleLink for the report * @param {Object} report * @returns {String} */ function getChatRoomSubtitleLink(report) { if (isThread(report)) { - if (report.reportID == '280084830934340') { - debugger; - } - const from = Localize.translateLocal('threads.from'); const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); - let subtitleLink = `${from} ${rootReportName}`; + let subtitleLink = `${Localize.translateLocal('common.from')} ${rootReportName}`; if (workspaceName && workspaceName !== rootReportName && workspaceName !== Localize.translateLocal('workspace.common.unavailable')) { - subtitleLink += ` in ${workspaceName}`; + subtitleLink += ` ${Localize.translateLocal('common.conjunctionIn')} ${workspaceName}`; } return subtitleLink; } From 1ceff1ead32c6da093b89fd9851934ec41a0b59e Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 15:10:45 -0700 Subject: [PATCH 077/197] comment update --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index eba7aa9843dc..e3ec3767a4c7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1054,7 +1054,7 @@ function getReportName(report) { } /** - * Recursively navigates through thread parents to get the root reports name. + * Recursively navigates through thread parents to get the root report and workspace name. * @param {Object} report * @returns {Object} */ From f10878fd12b5b47861a91d8684e9d76eebe0fee7 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 15:13:39 -0700 Subject: [PATCH 078/197] lint fixes --- src/components/AvatarWithDisplayName.js | 1 - src/libs/ReportUtils.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index c5a7fcbacc1c..b5de99514b42 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -17,7 +17,6 @@ import compose from '../libs/compose'; import * as OptionsListUtils from '../libs/OptionsListUtils'; import Text from './Text'; import * as StyleUtils from '../styles/StyleUtils'; -import {getChatRoomSubtitleLink} from '../libs/ReportUtils'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e3ec3767a4c7..14adf3be5461 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -20,7 +20,6 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; -import {isMoneyRequestAction} from './ReportActionsUtils'; let sessionEmail; let sessionAccountID; From a96154590e4cefa722da4e174f85ffbf6ff4bc99 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 16:04:05 -0700 Subject: [PATCH 079/197] prettier fixes --- src/libs/ReportUtils.js | 4 ++++ src/pages/ReportParticipantsPage.js | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 14adf3be5461..9526247cd1c6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -470,6 +470,10 @@ function isArchivedRoom(report) { * @returns {String} */ function getPolicyName(report) { + if (report === undefined) { + return ''; + } + // Public rooms send back the policy name with the reportSummary, // since they can also be accessed by people who aren't in the workspace if (report.policyName) { diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index b0c2f2e38954..6d9bdbffba54 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -93,7 +93,9 @@ function ReportParticipantsPage(props) { Date: Tue, 20 Jun 2023 17:17:00 -0700 Subject: [PATCH 080/197] code correction to not show anything if data is missing --- src/libs/ReportUtils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f64b6feece21..ae415ba61f12 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1108,6 +1108,10 @@ function getChatRoomSubtitleLink(report) { if (isThread(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); + if (_.isEmpty(rootReportName)) { + return ''; + } + let subtitleLink = `${Localize.translateLocal('common.from')} ${rootReportName}`; if (workspaceName && workspaceName !== rootReportName && workspaceName !== Localize.translateLocal('workspace.common.unavailable')) { subtitleLink += ` ${Localize.translateLocal('common.conjunctionIn')} ${workspaceName}`; From f2f010c8431be20358dda5f342616a823e890de9 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 17:19:35 -0700 Subject: [PATCH 081/197] removing parentReport --- src/components/HeaderWithBackButton.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/HeaderWithBackButton.js b/src/components/HeaderWithBackButton.js index c612b82851f9..bb707b8f8873 100755 --- a/src/components/HeaderWithBackButton.js +++ b/src/components/HeaderWithBackButton.js @@ -96,9 +96,6 @@ const propTypes = { /** Whether we should show an avatar */ shouldShowAvatarWithDisplay: PropTypes.bool, - /** Parent report, if provided it will override props.report for AvatarWithDisplay */ - parentReport: iouReportPropTypes, - /** Report, if we're showing the details for one and using AvatarWithDisplay */ report: iouReportPropTypes, @@ -133,7 +130,6 @@ const defaultProps = { shouldShowBackButton: true, shouldShowAvatarWithDisplay: false, report: null, - parentReport: null, policies: {}, personalDetails: {}, guidesCallTaskID: '', @@ -192,7 +188,7 @@ class HeaderWithBackButton extends Component { )} {this.props.shouldShowAvatarWithDisplay && ( From 325935c9126e472a3d41649bdbbc452b124af5ea Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Tue, 20 Jun 2023 17:24:53 -0700 Subject: [PATCH 082/197] cleanup --- src/libs/ReportUtils.js | 3 ++- src/pages/home/HeaderView.js | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index ae415ba61f12..8274ef1eb12c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1047,6 +1047,7 @@ function getReportName(report) { /** * Recursively navigates through thread parents to get the root report and workspace name. + * The recursion stops when we find a non thread or money request report, whichever comes first. * @param {Object} report * @returns {Object} */ @@ -1100,7 +1101,7 @@ function getChatRoomSubtitle(report) { } /** - * Get the subtitleLink for the report + * Get the subtitle link for the report * @param {Object} report * @returns {String} */ diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 30c46a7389af..685bd21c2ad6 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -83,11 +83,11 @@ function HeaderView(props) { const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); const isMultipleParticipant = participants.length > 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); - const isThread = ReportUtils.isChatThread(props.report); + const isChatThread = ReportUtils.isChatThread(props.report); const isChatRoom = ReportUtils.isChatRoom(props.report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); const isTaskReport = ReportUtils.isTaskReport(props.report); - const reportHeaderData = !isTaskReport && !isThread && props.report.parentReportID ? props.parentReport : props.report; + const reportHeaderData = !isTaskReport && !isChatThread && props.report.parentReportID ? props.parentReport : props.report; const title = ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const subtitleLink = ReportUtils.getChatRoomSubtitleLink(reportHeaderData); @@ -175,7 +175,7 @@ function HeaderView(props) { ) : ( )} @@ -185,7 +185,7 @@ function HeaderView(props) { tooltipEnabled numberOfLines={1} textStyles={[styles.headerText, styles.pre]} - shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isThread || isTaskReport} + shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport} /> {!_.isEmpty(subtitleLink) && ( Date: Tue, 20 Jun 2023 17:29:40 -0700 Subject: [PATCH 083/197] using lodash --- src/components/MoneyRequestHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 87f7c6e1cf87..ad199210fbf2 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -92,8 +92,8 @@ function MoneyRequestHeader(props) { const shouldShowPaypal = Boolean(lodashGet(props.personalDetails, [moneyRequestReport.managerID, 'payPalMeAddress'])); const report = props.report; if (props.isSingleTransactionView) { - report.ownerAccountID = props.parentReport.ownerAccountID; - report.ownerEmail = props.parentReport.ownerEmail; + report.ownerAccountID = lodashGet(props, ['parentReport', 'ownerAccountID'], null); + report.ownerEmail = lodashGet(props, ['parentReport', 'ownerEmail'], ''); } return ( From 4392dc1cc21dc8239b8b051be598c3b30c8fafed Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 21 Jun 2023 10:39:02 +0700 Subject: [PATCH 084/197] Fix navigate wrong route when submiting in new task title page --- src/pages/tasks/NewTaskTitlePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/tasks/NewTaskTitlePage.js b/src/pages/tasks/NewTaskTitlePage.js index b1e7b689f85d..d0eed9c25156 100644 --- a/src/pages/tasks/NewTaskTitlePage.js +++ b/src/pages/tasks/NewTaskTitlePage.js @@ -58,7 +58,7 @@ function NewTaskTitlePage(props) { // the response function onSubmit(values) { TaskUtils.setTitleValue(values.taskTitle); - Navigation.navigate(ROUTES.getNewTaskRoute()); + Navigation.navigate(ROUTES.NEW_TASK); } if (!Permissions.canUseTasks(props.betas)) { From 25ca03bbb74afff3f8a8b0643f75c33688215d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 21 Jun 2023 12:06:09 +0200 Subject: [PATCH 085/197] make ref usage more explanatory --- src/components/TextInput/BaseTextInput.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index b3fe23f99511..63fa036d57f9 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -167,7 +167,6 @@ function BaseTextInput(props) { [props.autoGrowHeight, props.multiline], ); - const hasValueRef = useRef(inputValue.length > 0); useEffect(() => { // Handle side effects when the value gets changed programatically from the outside @@ -181,9 +180,15 @@ function BaseTextInput(props) { } }, [activateLabel, inputValue]); - useEffect(() => { - // Activate or deactivate the label when the focus changes + // We capture whether the input has a value or not in a ref. + // It gets updated when the text gets changed. + const hasValueRef = useRef(inputValue.length > 0); + // Activate or deactivate the label when the focus changes: + useEffect(() => { + // We can't use inputValue here directly, as it might contain + // the defaultValue, which doesn't get updated when the text changes. + // We can't use props.value either, as it might be undefined. if (hasValueRef.current || isFocused) { activateLabel(); } else if (!hasValueRef.current && !isFocused) { From 56b54915b82b37fef47834763d990e8915f3f1cf Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Wed, 21 Jun 2023 23:23:13 +0530 Subject: [PATCH 086/197] Use useMemo hook --- src/pages/NewChatPage.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 7585c979b2b4..e6f0472241dc 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useState, useEffect} from 'react'; +import React, {useState, useEffect, useMemo} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -61,7 +61,7 @@ function NewChatPage(props) { ); const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(props.personalDetails); - function getSections() { + const sections = useMemo(() => { const sectionsList = []; let indexOffset = 0; @@ -111,7 +111,8 @@ function NewChatPage(props) { } return sectionsList; - } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, maxParticipantsReached, props.isGroupChat, selectedOptions]); const updateOptionsWithSearchTerm = (newSearchTerm = '') => { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( @@ -184,8 +185,6 @@ function NewChatPage(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.reports, props.personalDetails]); - const sections = getSections(); - return ( Date: Wed, 21 Jun 2023 16:01:44 -0700 Subject: [PATCH 087/197] using isThread --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 8274ef1eb12c..67a495c7248f 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -540,7 +540,7 @@ function isThread(report) { * @returns {Boolean} */ function isChatThread(report) { - return Boolean(report && report.parentReportID && report.parentReportActionID && report.type === CONST.REPORT.TYPE.CHAT); + return isThread && report.type === CONST.REPORT.TYPE.CHAT; } /** From edb7a486f15fb4ad14269829e29ed2d29bc78827 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Wed, 21 Jun 2023 16:19:06 -0700 Subject: [PATCH 088/197] rename to parentNavigationSubtitle --- src/components/AvatarWithDisplayName.js | 6 +++--- src/libs/ReportUtils.js | 6 +++--- src/pages/home/HeaderView.js | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index b5de99514b42..152eaadcf709 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -55,12 +55,12 @@ const defaultProps = { function AvatarWithDisplayName(props) { const title = props.isAnonymous ? props.report.displayName : ReportUtils.getDisplayNameForParticipant(props.report.ownerAccountID, true); const subtitle = ReportUtils.getChatRoomSubtitle(props.report); + const parentNavigationSubtitle = ReportUtils.getParentNavigationSubtitle(props.report); const isExpenseReport = ReportUtils.isExpenseReport(props.report); const icons = ReportUtils.getIcons(props.report, props.personalDetails, props.policies); const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const avatarContainerStyle = StyleUtils.getEmptyAvatarStyle(props.size) || styles.emptyAvatar; - const subtitleLink = ReportUtils.getChatRoomSubtitleLink(props.report); return ( {Boolean(props.report && title) && ( @@ -92,7 +92,7 @@ function AvatarWithDisplayName(props) { textStyles={[props.isAnonymous ? styles.headerAnonymousFooter : styles.headerText, styles.pre]} shouldUseFullTitle={isExpenseReport || props.isAnonymous} /> - {!_.isEmpty(subtitleLink) && ( + {!_.isEmpty(parentNavigationSubtitle) && ( { Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); @@ -104,7 +104,7 @@ function AvatarWithDisplayName(props) { style={[styles.optionAlternateText, styles.textLabelSupporting, styles.link]} numberOfLines={1} > - {subtitleLink} + {parentNavigationSubtitle} )} diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 67a495c7248f..4a816731d4aa 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1101,11 +1101,11 @@ function getChatRoomSubtitle(report) { } /** - * Get the subtitle link for the report + * Gets the parent navigation subtitle for the report * @param {Object} report * @returns {String} */ -function getChatRoomSubtitleLink(report) { +function getParentNavigationSubtitle(report) { if (isThread(report)) { const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]); const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); @@ -2283,7 +2283,7 @@ export { isUserCreatedPolicyRoom, isChatRoom, getChatRoomSubtitle, - getChatRoomSubtitleLink, + getParentNavigationSubtitle, getPolicyName, getPolicyType, isArchivedRoom, diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 685bd21c2ad6..030f4eef5e18 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -90,7 +90,7 @@ function HeaderView(props) { const reportHeaderData = !isTaskReport && !isChatThread && props.report.parentReportID ? props.parentReport : props.report; const title = ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); - const subtitleLink = ReportUtils.getChatRoomSubtitleLink(reportHeaderData); + const parentNavigationSubtitle = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = participants.length === 1 && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); const isAutomatedExpensifyAccount = participants.length === 1 && ReportUtils.hasAutomatedExpensifyAccountIDs(participants); const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); @@ -187,19 +187,19 @@ function HeaderView(props) { textStyles={[styles.headerText, styles.pre]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport} /> - {!_.isEmpty(subtitleLink) && ( + {!_.isEmpty(parentNavigationSubtitle) && ( { Navigation.navigate(ROUTES.getReportRoute(props.report.parentReportID)); }} - accessibilityLabel={subtitleLink} + accessibilityLabel={parentNavigationSubtitle} accessibilityRole="link" > - {subtitleLink} + {parentNavigationSubtitle} )} From f09d106fa5fc85d022e226d03c3bb993f65515ea Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Wed, 21 Jun 2023 16:39:24 -0700 Subject: [PATCH 089/197] logic correction --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e93804e8eb18..97559e520940 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -540,7 +540,7 @@ function isThread(report) { * @returns {Boolean} */ function isChatThread(report) { - return isThread && report.type === CONST.REPORT.TYPE.CHAT; + return isThread(report) && report.type === CONST.REPORT.TYPE.CHAT; } /** From b5a67763bc70ae89efcb20c6ee44b84be0b88c71 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Wed, 21 Jun 2023 16:56:43 -0700 Subject: [PATCH 090/197] setting parentActionID for task optimistically --- src/libs/actions/Task.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 5e27d7db764d..a03b0e32b856 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -57,6 +57,7 @@ function createTaskAndNavigate(currentUserEmail, currentUserAccountID, parentRep // Create the CreatedReportAction on the task const optimisticTaskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(optimisticTaskReport.reportID); const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assignee, assigneeAccountID, `Created a task: ${title}`, parentReportID); + optimisticTaskReport.parentReportActionID = optimisticAddCommentReport.reportAction.reportActionID; const currentTime = DateUtils.getDBTime(); From c023725c3b7e3572a6ddaae974ae3c56f159b901 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Thu, 22 Jun 2023 14:08:07 -0700 Subject: [PATCH 091/197] parentNavigationSummary translation --- src/languages/en.js | 1 + src/languages/es.js | 1 + src/libs/ReportUtils.js | 15 ++++++--------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index bdaa0786b627..274bded36110 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -1415,6 +1415,7 @@ export default { lastReply: 'Last reply', replies: 'Replies', reply: 'Reply', + parentNavigationSummary: ({rootReportName, workspaceName}) => `From ${rootReportName} ${workspaceName ? `in ${workspaceName}` : ''}`, }, qrCodes: { copyUrlToClipboard: 'Copy URL to clipboard', diff --git a/src/languages/es.js b/src/languages/es.js index 2bd602694f68..4de7e60a9db4 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -1883,6 +1883,7 @@ export default { lastReply: 'Última respuesta', replies: 'Respuestas', reply: 'Respuesta', + parentNavigationSummary: ({rootReportName, workspaceName}) => `De ${rootReportName} ${workspaceName ? `en ${workspaceName}` : ''}`, }, qrCodes: { copyUrlToClipboard: 'Copiar URL al portapapeles', diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 97559e520940..f847d4332f1d 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -467,9 +467,10 @@ function isArchivedRoom(report) { * @param {String} report.policyID * @param {String} report.oldPolicyName * @param {String} report.policyName + * @param {Boolean} [returnEmptyIfNotFound] * @returns {String} */ -function getPolicyName(report) { +function getPolicyName(report, returnEmptyIfNotFound = false) { if ((!allPolicies || _.size(allPolicies) === 0) && !report.policyName) { return Localize.translateLocal('workspace.common.unavailable'); } @@ -478,7 +479,7 @@ function getPolicyName(report) { // // Public rooms send back the policy name with the reportSummary, // // since they can also be accessed by people who aren't in the workspace - return lodashGet(policy, 'name') || report.policyName || report.oldPolicyName || Localize.translateLocal('workspace.common.unavailable'); + return lodashGet(policy, 'name') || report.policyName || report.oldPolicyName || (returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable')); } /** @@ -1065,13 +1066,13 @@ function getRootReportAndWorkspaceName(report) { if (isMoneyRequestReport(report)) { return { rootReportName: lodashGet(report, 'displayName', ''), - workspaceName: isIOUReport(report) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(report), + workspaceName: isIOUReport(report) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(report, true), }; } return { rootReportName: getReportName(report), - workspaceName: getPolicyName(report), + workspaceName: getPolicyName(report, true), }; } @@ -1113,11 +1114,7 @@ function getParentNavigationSubtitle(report) { return ''; } - let subtitleLink = `${Localize.translateLocal('common.from')} ${rootReportName}`; - if (workspaceName && workspaceName !== rootReportName && workspaceName !== Localize.translateLocal('workspace.common.unavailable')) { - subtitleLink += ` ${Localize.translateLocal('common.conjunctionIn')} ${workspaceName}`; - } - return subtitleLink; + return Localize.translateLocal('threads.parentNavigationSummary', {rootReportName, workspaceName}); } return ''; } From 2a5d48d4d176432bb94f89c40b6d5f9a63893efc Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Thu, 22 Jun 2023 14:08:48 -0700 Subject: [PATCH 092/197] removing unused translation --- src/languages/en.js | 2 -- src/languages/es.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 274bded36110..8ef4c08ace1f 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -146,8 +146,6 @@ export default { km: 'kilometer', copied: 'Copied!', someone: 'Someone', - from: 'From', - conjunctionIn: 'in', }, anonymousReportFooter: { logoTagline: 'Join in on the discussion.', diff --git a/src/languages/es.js b/src/languages/es.js index 4de7e60a9db4..4af03ce4de03 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -145,8 +145,6 @@ export default { km: 'kilómetro', copied: '¡Copiado!', someone: 'Alguien', - from: 'De', - conjunctionIn: 'en', }, anonymousReportFooter: { logoTagline: 'Únete a la discussion.', From d423244e13d1afef51263b2dfaf10b100b27c1b6 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Thu, 22 Jun 2023 14:17:33 -0700 Subject: [PATCH 093/197] moving space into the condition --- src/languages/en.js | 2 +- src/languages/es.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 8ef4c08ace1f..a58223458350 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -1413,7 +1413,7 @@ export default { lastReply: 'Last reply', replies: 'Replies', reply: 'Reply', - parentNavigationSummary: ({rootReportName, workspaceName}) => `From ${rootReportName} ${workspaceName ? `in ${workspaceName}` : ''}`, + parentNavigationSummary: ({rootReportName, workspaceName}) => `From ${rootReportName}${workspaceName ? ` in ${workspaceName}` : ''}`, }, qrCodes: { copyUrlToClipboard: 'Copy URL to clipboard', diff --git a/src/languages/es.js b/src/languages/es.js index 4af03ce4de03..4729e7a93896 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -1881,7 +1881,7 @@ export default { lastReply: 'Última respuesta', replies: 'Respuestas', reply: 'Respuesta', - parentNavigationSummary: ({rootReportName, workspaceName}) => `De ${rootReportName} ${workspaceName ? `en ${workspaceName}` : ''}`, + parentNavigationSummary: ({rootReportName, workspaceName}) => `De ${rootReportName}${workspaceName ? ` en ${workspaceName}` : ''}`, }, qrCodes: { copyUrlToClipboard: 'Copiar URL al portapapeles', From 05a9634b3542955de53512ac2d46961194c50eef Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 23 Jun 2023 12:07:08 +0800 Subject: [PATCH 094/197] don't strikethorugh the text --- src/pages/ReimbursementAccount/ContinueBankAccountSetup.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js index 4eca196d328a..592ae2b9a24a 100644 --- a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js +++ b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js @@ -60,7 +60,6 @@ function ContinueBankAccountSetup(props) { icon={Illustrations.BankArrow} > Date: Fri, 23 Jun 2023 12:07:37 +0800 Subject: [PATCH 095/197] reduce the opacity when disabled --- src/components/MenuItem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index eae6069783f3..c96bfaea3f31 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -79,7 +79,7 @@ function MenuItem(props) { props.icon && !_.isArray(props.icon) ? styles.ml3 : undefined, props.shouldShowBasicTitle ? undefined : styles.textStrong, props.shouldShowHeaderTitle ? styles.textHeadlineH1 : undefined, - props.interactive && props.disabled ? {...styles.disabledText, ...styles.userSelectNone} : undefined, + props.interactive && props.disabled ? {...styles.userSelectNone} : undefined, styles.pre, styles.ltr, isDeleted ? styles.offlineFeedback.deleted : undefined, @@ -119,6 +119,7 @@ function MenuItem(props) { StyleUtils.getButtonBackgroundColorStyle(getButtonState(props.focused || hovered, pressed, props.success, props.disabled, props.interactive), true), (hovered || pressed) && props.hoverAndPressStyle, ...(_.isArray(props.wrapperStyle) ? props.wrapperStyle : [props.wrapperStyle]), + props.disabled && styles.buttonOpacityDisabled, ]} disabled={props.disabled} ref={props.forwardedRef} From 7416f4d128812b846dc2028a2a152d97db9ac2c5 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 23 Jun 2023 17:12:53 +0500 Subject: [PATCH 096/197] fix: add outline to checkbox when focused via button --- web/index.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/index.html b/web/index.html index 90011c87e7a9..ec1250b23b08 100644 --- a/web/index.html +++ b/web/index.html @@ -60,6 +60,10 @@ outline: 0; box-shadow: inset 0px 0px 0px 1px #5AB0FF; } + div[role="checkbox"]:focus { + outline: 0; + box-shadow: inset 0px 0px 0px 1px #5AB0FF; + } input:focus-visible, input:focus[data-focusvisible-polyfill], select:focus-visible, select:focus[data-focusvisible-polyfill] { box-shadow: none; From b13d207bdaa60cdbddc6a625784bc4bd90385569 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Fri, 23 Jun 2023 12:09:58 -0500 Subject: [PATCH 097/197] fix: correct dependency of effect hook --- src/pages/home/report/ReportActionItem.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 24be64dc6468..73235deeabca 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -149,25 +149,18 @@ function ReportActionItem(props) { // Hide the message if it is being moderated for a higher offense, or is hidden by a moderator // Removed messages should not be shown anyway and should not need this flow + const latestDecision = _.get(props, ['action', 'message', 0, 'moderationDecisions', 0, 'decision'], ''); useEffect(() => { - if (!props.action.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT || _.isEmpty(props.action.message[0].moderationDecisions)) { + if (!props.action.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT || _.isEmpty(latestDecision)) { return; } - // Right now we are only sending the latest moderationDecision to the frontend even though it is an array - let decisions = props.action.message[0].moderationDecisions; - if (decisions.length > 1) { - decisions = decisions.slice(-1); - } - const latestDecision = decisions[0]; - if (latestDecision.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || latestDecision.decision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN) { + if (latestDecision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || latestDecision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN) { setIsHidden(true); } - setModerationDecision(latestDecision.decision); + setModerationDecision(latestDecision); - // props.action.message doesn't need to be a dependency, we only need to check the change of props.action.message[0].moderationDecisions - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.action.message[0].moderationDecisions, props.action.actionName]); + }, [latestDecision, props.action.actionName]); const toggleContextMenuFromActiveReportAction = useCallback(() => { setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); From 950f7d1bf352e8d495d38a23b4e40989757657b5 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 23 Jun 2023 13:26:58 -0700 Subject: [PATCH 098/197] improved safety check --- src/libs/ReportUtils.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e056a4ec6ce4..73be4623d929 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -472,6 +472,11 @@ function isArchivedRoom(report) { * @returns {String} */ function getPolicyName(report, returnEmptyIfNotFound = false) { + const noPolicyFound = returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable'); + if (report === undefined) { + return noPolicyFound; + } + if ((!allPolicies || _.size(allPolicies) === 0) && !report.policyName) { return Localize.translateLocal('workspace.common.unavailable'); } @@ -480,7 +485,7 @@ function getPolicyName(report, returnEmptyIfNotFound = false) { // // Public rooms send back the policy name with the reportSummary, // // since they can also be accessed by people who aren't in the workspace - return lodashGet(policy, 'name') || report.policyName || report.oldPolicyName || (returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable')); + return lodashGet(policy, 'name') || report.policyName || report.oldPolicyName || noPolicyFound; } /** From fce7f374db0e900fa6da512bb60084f7df065d3d Mon Sep 17 00:00:00 2001 From: Danut Gavrus Date: Sat, 24 Jun 2023 20:36:31 +0300 Subject: [PATCH 099/197] [FIX 20759] Remove the *New* notifiers just as the deletion starts. --- src/pages/home/report/ReportActionsView.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 8c11571d0da1..7c1d7496b38c 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import lodashGet from 'lodash/get'; +import lodashCloneDeep from 'lodash/cloneDeep'; import * as Report from '../../../libs/actions/Report'; import reportActionPropTypes from './reportActionPropTypes'; import Visibility from '../../../libs/Visibility'; @@ -218,6 +219,15 @@ class ReportActionsView extends React.Component { }); } + // If the last unread message was deleted, remove the *New* green marker and the *New Messages* notification at scroll just as the deletion starts. + if (this.props.reportActions.length > 0 && this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !this.props.network.isOffline) { + const reportActionsWithoutPendingOne = lodashCloneDeep(this.props.reportActions); + reportActionsWithoutPendingOne.shift(); + this.setState({ + newMarkerReportActionID: ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne), + }); + } + // Checks to see if a report comment has been manually "marked as unread". All other times when the lastReadTime // changes it will be because we marked the entire report as read. const didManuallyMarkReportAsUnread = prevProps.report.lastReadTime !== this.props.report.lastReadTime && ReportUtils.isUnread(this.props.report); From 3be92b02dbd2e5eb74c9b7dc39dc2e6ebeb681e5 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Sun, 25 Jun 2023 15:39:20 +0000 Subject: [PATCH 100/197] fix: package.json & package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-ELECTRON-5710626 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6f038ddcc69..721451483a4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -144,7 +144,7 @@ "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", - "electron": "22.3.7", + "electron": "^22.3.14", "electron-builder": "24.5.0", "eslint": "^7.6.0", "eslint-config-expensify": "^2.0.38", @@ -22063,9 +22063,9 @@ } }, "node_modules/electron": { - "version": "22.3.7", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.7.tgz", - "integrity": "sha512-QUuRCl0QJk0w2yPAQXl6sk4YV1b9353w4e1eO/fF2OUmrGQV9Fy2pEpEDV1PIq/JJ/oeVVlI3H07LHpEcNb0TA==", + "version": "22.3.14", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.14.tgz", + "integrity": "sha512-WxVcLnC4DrkBLN1/BwpxNkGvVq8iq1hM7lae5nvjnSYg/bwVbuo1Cwc80Keft4MIWKlYCXNiKKqs3qCXV4Aiaw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -58529,9 +58529,9 @@ } }, "electron": { - "version": "22.3.7", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.7.tgz", - "integrity": "sha512-QUuRCl0QJk0w2yPAQXl6sk4YV1b9353w4e1eO/fF2OUmrGQV9Fy2pEpEDV1PIq/JJ/oeVVlI3H07LHpEcNb0TA==", + "version": "22.3.14", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.14.tgz", + "integrity": "sha512-WxVcLnC4DrkBLN1/BwpxNkGvVq8iq1hM7lae5nvjnSYg/bwVbuo1Cwc80Keft4MIWKlYCXNiKKqs3qCXV4Aiaw==", "dev": true, "requires": { "@electron/get": "^2.0.0", diff --git a/package.json b/package.json index cb5c05287a8b..84a49786904c 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", - "electron": "22.3.7", + "electron": "22.3.14", "electron-builder": "24.5.0", "eslint": "^7.6.0", "eslint-config-expensify": "^2.0.38", From de7ccb5562f4ba6c6a70f83797b267a0d833e6e9 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 26 Jun 2023 11:32:48 +0700 Subject: [PATCH 101/197] Fix shield icon hover effect --- src/pages/ReimbursementAccount/Enable2FAPrompt.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js index e27cba2b51a0..7681685b42ef 100644 --- a/src/pages/ReimbursementAccount/Enable2FAPrompt.js +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -29,7 +29,6 @@ function Enable2FAPrompt(props) { icon: Expensicons.Shield, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - iconFill: themeColors.success, wrapperStyle: [styles.cardMenuItem], link: () => Link.buildOldDotURL(secureYourAccountUrl), }, From 7f92ba647016e26e5ce1f25bebcf46d364811662 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 26 Jun 2023 11:48:14 +0700 Subject: [PATCH 102/197] fix lint --- src/pages/ReimbursementAccount/Enable2FAPrompt.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js index 7681685b42ef..829ac9b63848 100644 --- a/src/pages/ReimbursementAccount/Enable2FAPrompt.js +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -8,7 +8,6 @@ import * as Illustrations from '../../components/Icon/Illustrations'; import Section from '../../components/Section'; import * as Link from '../../libs/actions/Link'; import ROUTES from '../../ROUTES'; -import themeColors from '../../styles/themes/default'; const propTypes = { ...withLocalizePropTypes, From 21bf80dedd544577d6c022176ef7ca7876ad0eff Mon Sep 17 00:00:00 2001 From: Davi Rodrigues Date: Mon, 26 Jun 2023 20:58:38 -0300 Subject: [PATCH 103/197] fix: solve missing avatar workspace logo issue --- src/components/Avatar.js | 16 +++++++++------- src/components/SubscriptAvatar.js | 1 - 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 66a1b60c3cef..fa7d17d22535 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -73,9 +73,9 @@ function Avatar(props) { const isWorkspace = props.type === CONST.ICON_TYPE_WORKSPACE; const iconSize = StyleUtils.getAvatarSize(props.size); - const imageStyle = props.imageStyles ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : undefined; + const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), StyleUtils.getAvatarBorderRadius(props.size, props.type)]; - const iconStyle = props.imageStyles ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; + const iconStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon; @@ -101,11 +101,13 @@ function Avatar(props) { /> ) : ( - setImageError(true)} - /> + + setImageError(true)} + /> + )} ); diff --git a/src/components/SubscriptAvatar.js b/src/components/SubscriptAvatar.js index b94bf4dffd25..454d5204d3a4 100644 --- a/src/components/SubscriptAvatar.js +++ b/src/components/SubscriptAvatar.js @@ -68,7 +68,6 @@ function SubscriptAvatar(props) { Date: Tue, 27 Jun 2023 01:08:13 +0100 Subject: [PATCH 104/197] Remove allBetas access for dev environment --- src/libs/Permissions.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js index d3e407260e20..cce0fc984dab 100644 --- a/src/libs/Permissions.js +++ b/src/libs/Permissions.js @@ -1,5 +1,4 @@ import _ from 'underscore'; -import * as Environment from './Environment/Environment'; import CONST from '../CONST'; /** @@ -8,7 +7,7 @@ import CONST from '../CONST'; * @returns {Boolean} */ function canUseAllBetas(betas) { - return Environment.isDevelopment() || _.contains(betas, CONST.BETAS.ALL); + return _.contains(betas, CONST.BETAS.ALL); } /** @@ -75,7 +74,7 @@ function canUseCommentLinking(betas) { * @returns {Boolean} */ function canUsePolicyRooms(betas) { - return _.contains(betas, CONST.BETAS.POLICY_ROOMS) || _.contains(betas, CONST.BETAS.ALL); + return _.contains(betas, CONST.BETAS.POLICY_ROOMS) || canUseAllBetas(betas); } /** @@ -91,7 +90,7 @@ function canUsePolicyExpenseChat(betas) { * @returns {Boolean} */ function canUsePasswordlessLogins(betas) { - return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL); + return _.contains(betas, CONST.BETAS.PASSWORDLESS) || canUseAllBetas(betas); } /** @@ -99,7 +98,7 @@ function canUsePasswordlessLogins(betas) { * @returns {Boolean} */ function canUseTasks(betas) { - return _.contains(betas, CONST.BETAS.TASKS) || _.contains(betas, CONST.BETAS.ALL); + return _.contains(betas, CONST.BETAS.TASKS) || canUseAllBetas(betas); } export default { From 6a5fffc0c1fb3b1d12d19dfa9e684d4b568f857f Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 27 Jun 2023 02:22:19 +0100 Subject: [PATCH 105/197] Fix Test , add All betas access --- tests/unit/OptionsListUtilsTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 75c7927888eb..f12c367b0038 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -296,7 +296,7 @@ describe('OptionsListUtils', () => { it('getSearchOptions()', () => { // When we filter in the Search view without providing a searchValue - let results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS, ''); + let results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS, '', [CONST.BETAS.ALL]); // Then the 2 personalDetails that don't have reports should be returned expect(results.personalDetails.length).toBe(2); From d986308459119b0020c74e0bf1a2e8b511eb2a87 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Tue, 27 Jun 2023 08:46:02 +0200 Subject: [PATCH 106/197] fix OfflineWithFeedback errorRowStyles is misaligned --- src/pages/settings/Report/ReportSettingsPage.js | 1 + src/pages/workspace/WorkspaceInitialPage.js | 2 +- src/pages/workspace/WorkspacesListPage.js | 2 +- src/styles/styles.js | 4 ---- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 53f0b99910bb..4c8f82c625cd 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -119,6 +119,7 @@ class ReportSettingsPage extends Component { Report.clearPolicyRoomNameErrors(this.props.report.reportID)} > {shouldDisableRename ? ( diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 73f659574e71..5728525a12f1 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -194,7 +194,7 @@ function WorkspaceInitialPage(props) { pendingAction={policy.pendingAction} onClose={() => dismissError(policy.id)} errors={policy.errors} - errorRowStyles={[styles.ph6, styles.pv2]} + errorRowStyles={[styles.ph5, styles.pv2]} > diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index f88ced6fc9fb..6f11c70a6707 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -156,7 +156,7 @@ class WorkspacesListPage extends Component { diff --git a/src/styles/styles.js b/src/styles/styles.js index f37013eb23b0..0ffe6e579472 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2837,10 +2837,6 @@ const styles = { errorDot: { marginRight: 12, }, - menuItemErrorPadding: { - paddingLeft: 44, - paddingRight: 20, - }, }, dotIndicatorMessage: { From a95b3e27222729d202b90ba69bc7cb33b8d21602 Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 17:18:27 +0300 Subject: [PATCH 107/197] make sure inputs get blurred when validating magic code using link --- src/languages/en.js | 1 + src/languages/es.js | 1 + .../signin/ValidateCodeForm/BaseValidateCodeForm.js | 9 ++++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/languages/en.js b/src/languages/en.js index 17653692828e..a22b5380733f 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -701,6 +701,7 @@ export default { }, validateCodeForm: { magicCodeNotReceived: "Didn't receive a magic code?", + requestNewCodeAfterErrorOccured: "Request a new code", enterAuthenticatorCode: 'Please enter your authenticator code', requiredWhen2FAEnabled: 'Required when 2FA is enabled', requestNewCode: 'Request a new code in ', diff --git a/src/languages/es.js b/src/languages/es.js index 2a8af5628045..9e72d605b73a 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -703,6 +703,7 @@ export default { }, validateCodeForm: { magicCodeNotReceived: '¿No recibiste un código mágico?', + requestNewCodeAfterErrorOccured: "Solicitar un nuevo código", enterAuthenticatorCode: 'Por favor, introduce el código de autenticador', requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado', requestNewCode: 'Pedir un código nuevo en ', diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index abee3ce0f25b..695200c0d3a1 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -79,6 +79,12 @@ function BaseValidateCodeForm(props) { const input2FARef = useRef(); const timerRef = useRef(); + useEffect(() => { + if(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading)) { + inputValidateCodeRef.current.blur(); + } + }, [props.account.isLoading, props.session.autoAuthState]) + useEffect(() => { if (!inputValidateCodeRef.current || prevIsVisible || !props.isVisible || !canFocusInputOnScreenFocus()) { return; @@ -273,7 +279,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {props.translate('validateCodeForm.magicCodeNotReceived')} + {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccured') : props.translate('validateCodeForm.magicCodeNotReceived')} )} @@ -309,6 +315,7 @@ export default compose( account: {key: ONYXKEYS.ACCOUNT}, credentials: {key: ONYXKEYS.CREDENTIALS}, preferredLocale: {key: ONYXKEYS.NVP_PREFERRED_LOCALE}, + session: {key: ONYXKEYS.SESSION}, }), withToggleVisibilityView, withNetwork(), From c1edca5234b2a817cb5a89e69e14e78b0815407c Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 17:24:27 +0300 Subject: [PATCH 108/197] Added PropTypes --- .../signin/ValidateCodeForm/BaseValidateCodeForm.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 695200c0d3a1..f3528f0ee3c1 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -46,6 +46,12 @@ const propTypes = { login: PropTypes.string, }), + /** Session of currently logged in user */ + session: PropTypes.shape({ + /** Currently logged in user authToken */ + authToken: PropTypes.string, + }), + /** Indicates which locale the user currently has selected */ preferredLocale: PropTypes.string, @@ -62,6 +68,9 @@ const propTypes = { const defaultProps = { account: {}, credentials: {}, + session: { + authToken: null, + }, preferredLocale: CONST.LOCALES.DEFAULT, }; From ec8565728ebf91285232e0a092c086396b78637c Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 17:46:48 +0300 Subject: [PATCH 109/197] consistency issue fixed --- src/languages/es.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.js b/src/languages/es.js index 9e72d605b73a..3a02d096e5ec 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -703,7 +703,7 @@ export default { }, validateCodeForm: { magicCodeNotReceived: '¿No recibiste un código mágico?', - requestNewCodeAfterErrorOccured: "Solicitar un nuevo código", + requestNewCodeAfterErrorOccured: 'Solicitar un nuevo código', enterAuthenticatorCode: 'Por favor, introduce el código de autenticador', requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado', requestNewCode: 'Pedir un código nuevo en ', From d4a9daec6b358cc9708d6b8d0de545f259fad197 Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 17:48:01 +0300 Subject: [PATCH 110/197] fix --- src/languages/en.js | 2 +- src/languages/es.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index a22b5380733f..117e45c1961d 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -701,10 +701,10 @@ export default { }, validateCodeForm: { magicCodeNotReceived: "Didn't receive a magic code?", - requestNewCodeAfterErrorOccured: "Request a new code", enterAuthenticatorCode: 'Please enter your authenticator code', requiredWhen2FAEnabled: 'Required when 2FA is enabled', requestNewCode: 'Request a new code in ', + requestNewCodeAfterErrorOccured: "Request a new code", error: { pleaseFillMagicCode: 'Please enter your magic code', incorrectMagicCode: 'Incorrect magic code.', diff --git a/src/languages/es.js b/src/languages/es.js index 3a02d096e5ec..8b68cf794594 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -703,10 +703,10 @@ export default { }, validateCodeForm: { magicCodeNotReceived: '¿No recibiste un código mágico?', - requestNewCodeAfterErrorOccured: 'Solicitar un nuevo código', enterAuthenticatorCode: 'Por favor, introduce el código de autenticador', requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado', requestNewCode: 'Pedir un código nuevo en ', + requestNewCodeAfterErrorOccured: 'Solicitar un nuevo código', error: { pleaseFillMagicCode: 'Por favor, introduce el código mágico', incorrectMagicCode: 'Código mágico incorrecto.', From 1dd4349180bdaf8bd889f9566f839284cf27e904 Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 18:09:56 +0300 Subject: [PATCH 111/197] added Boolean --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index f3528f0ee3c1..38c3b8347a1b 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -288,7 +288,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccured') : props.translate('validateCodeForm.magicCodeNotReceived')} + {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccured') : props.translate('validateCodeForm.magicCodeNotReceived')} )} From 87514ea874cacd77e980c57bb7cca5c65a157239 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Tue, 27 Jun 2023 10:12:50 -0500 Subject: [PATCH 112/197] fix: update selectedOptions when personalDetails or policyMembers changes --- src/pages/workspace/WorkspaceInvitePage.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 990320037b6d..104463f85be5 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -93,10 +93,7 @@ class WorkspaceInvitePage extends React.Component { } componentDidUpdate(prevProps) { - if (!_.isEqual(prevProps.personalDetails, this.props.personalDetails)) { - this.updateOptionsWithSearchTerm(this.props.searchTerm); - } - if (!_.isEqual(prevProps.policyMembers, this.props.policyMembers)) { + if (!_.isEqual(prevProps.personalDetails, this.props.personalDetails) || !_.isEqual(prevProps.policyMembers, this.props.policyMembers)) { this.updateOptionsWithSearchTerm(this.state.searchTerm); } @@ -176,10 +173,23 @@ class WorkspaceInvitePage extends React.Component { updateOptionsWithSearchTerm(searchTerm = '') { const {personalDetails, userToInvite} = OptionsListUtils.getMemberInviteOptions(this.props.personalDetails, this.props.betas, searchTerm, this.getExcludedUsers()); + + // update selectedOptions as well + const detailsMap = {}; + _.forEach(personalDetails, (detail) => (detailsMap[detail.login] = detail)); + const selectedOptions = []; + _.forEach(this.state.selectedOptions, (option) => { + if (!_.has(detailsMap, option.login)) { + return; + } + selectedOptions.push(detailsMap[option.login]); + }); + this.setState({ searchTerm, userToInvite, personalDetails, + selectedOptions, }); } From 8e3b94f066a572e61cd6b576d75870861414a3fc Mon Sep 17 00:00:00 2001 From: Nam Le Date: Tue, 27 Jun 2023 22:59:34 +0700 Subject: [PATCH 113/197] fix style copy button --- src/pages/settings/Security/TwoFactorAuth/VerifyPage.js | 2 +- src/styles/styles.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js index 35dd3564c63d..a34cc3744d0a 100644 --- a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js @@ -132,7 +132,7 @@ function VerifyPage(props) { icon={Expensicons.Copy} inline={false} onPress={() => Clipboard.setString(props.account.twoFactorAuthSecretKey)} - styles={[styles.button, styles.buttonMedium]} + styles={[styles.button, styles.buttonMedium, styles.verifyCodesButton]} textStyles={[styles.buttonMediumText]} /> diff --git a/src/styles/styles.js b/src/styles/styles.js index 74066af5e20a..5ea9c954ddc3 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2215,7 +2215,11 @@ const styles = { }, twoFactorAuthCodesButton: { - minWidth: 100, + minWidth: 140, + }, + + verifyCodesButton: { + minWidth: 110, }, anonymousRoomFooter: { From c2c229c758e12c9c3127a94644f75757cb7b5f2a Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 21:22:37 +0300 Subject: [PATCH 114/197] typo fixed --- src/languages/en.js | 2 +- src/languages/es.js | 2 +- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 117e45c1961d..7fc56a90ec3e 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -704,7 +704,7 @@ export default { enterAuthenticatorCode: 'Please enter your authenticator code', requiredWhen2FAEnabled: 'Required when 2FA is enabled', requestNewCode: 'Request a new code in ', - requestNewCodeAfterErrorOccured: "Request a new code", + requestNewCodeAfterErrorOccurred: "Request a new code", error: { pleaseFillMagicCode: 'Please enter your magic code', incorrectMagicCode: 'Incorrect magic code.', diff --git a/src/languages/es.js b/src/languages/es.js index 8b68cf794594..b578c6479567 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -706,7 +706,7 @@ export default { enterAuthenticatorCode: 'Por favor, introduce el código de autenticador', requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado', requestNewCode: 'Pedir un código nuevo en ', - requestNewCodeAfterErrorOccured: 'Solicitar un nuevo código', + requestNewCodeAfterErrorOccurred: 'Solicitar un nuevo código', error: { pleaseFillMagicCode: 'Por favor, introduce el código mágico', incorrectMagicCode: 'Código mágico incorrecto.', diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 38c3b8347a1b..01751cc6968b 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -288,7 +288,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccured') : props.translate('validateCodeForm.magicCodeNotReceived')} + {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} )} From bb33473fe3516358b85dd5afe259cee75e8e44ab Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 21:43:13 +0300 Subject: [PATCH 115/197] single quote --- src/languages/en.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.js b/src/languages/en.js index 7fc56a90ec3e..950bd7e134e3 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -704,7 +704,7 @@ export default { enterAuthenticatorCode: 'Please enter your authenticator code', requiredWhen2FAEnabled: 'Required when 2FA is enabled', requestNewCode: 'Request a new code in ', - requestNewCodeAfterErrorOccurred: "Request a new code", + requestNewCodeAfterErrorOccurred: 'Request a new code', error: { pleaseFillMagicCode: 'Please enter your magic code', incorrectMagicCode: 'Incorrect magic code.', From 0fa37e12df881546f9847819087ed20b58038ecc Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 21:46:09 +0300 Subject: [PATCH 116/197] fix lint error --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 01751cc6968b..2286a5fb4ba6 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -88,6 +88,8 @@ function BaseValidateCodeForm(props) { const input2FARef = useRef(); const timerRef = useRef(); + const hasError = Boolean(props.account) && !_.isEmpty(props.account.errors); + useEffect(() => { if(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading)) { inputValidateCodeRef.current.blur(); @@ -234,8 +236,6 @@ function BaseValidateCodeForm(props) { } }, [props.account.requiresTwoFactorAuth, props.credentials, props.preferredLocale, twoFactorAuthCode, validateCode]); - const hasError = Boolean(props.account) && !_.isEmpty(props.account.errors); - return ( <> {/* At this point, if we know the account requires 2FA we already successfully authenticated */} From a2a780f5a5004f84c4465cccdd71a3c49f1609a0 Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 21:51:19 +0300 Subject: [PATCH 117/197] fix lint error --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 2286a5fb4ba6..4203a541f2b3 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -94,7 +94,7 @@ function BaseValidateCodeForm(props) { if(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading)) { inputValidateCodeRef.current.blur(); } - }, [props.account.isLoading, props.session.autoAuthState]) + }, [props.account.isLoading, props.session.autoAuthState, hasError]) useEffect(() => { if (!inputValidateCodeRef.current || prevIsVisible || !props.isVisible || !canFocusInputOnScreenFocus()) { @@ -288,7 +288,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} + {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} )} From 8426a5eb780b2cfe3fdfea5fe0a0694d12f8934b Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 21:57:53 +0300 Subject: [PATCH 118/197] fix lint error --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 4203a541f2b3..46b8db6b9869 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -91,9 +91,10 @@ function BaseValidateCodeForm(props) { const hasError = Boolean(props.account) && !_.isEmpty(props.account.errors); useEffect(() => { - if(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading)) { - inputValidateCodeRef.current.blur(); + if(!(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading))) { + return } + inputValidateCodeRef.current.blur(); }, [props.account.isLoading, props.session.autoAuthState, hasError]) useEffect(() => { @@ -288,7 +289,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} + {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} )} From 2f1d2fe0ee879916d9fd8896b378667d120eeeb8 Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 22:13:43 +0300 Subject: [PATCH 119/197] removed boolean --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 46b8db6b9869..7721585d7bd4 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -289,7 +289,7 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {Boolean(hasError) ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} + {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} )} From a1898ea4055986e83633bb4ccc23b31782aa3629 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Wed, 28 Jun 2023 02:17:34 +0700 Subject: [PATCH 120/197] update name style 2fa copy button --- src/pages/settings/Security/TwoFactorAuth/VerifyPage.js | 2 +- src/styles/styles.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js index a34cc3744d0a..90765b45caa2 100644 --- a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js @@ -132,7 +132,7 @@ function VerifyPage(props) { icon={Expensicons.Copy} inline={false} onPress={() => Clipboard.setString(props.account.twoFactorAuthSecretKey)} - styles={[styles.button, styles.buttonMedium, styles.verifyCodesButton]} + styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCopyCodeButton]} textStyles={[styles.buttonMediumText]} /> diff --git a/src/styles/styles.js b/src/styles/styles.js index 5ea9c954ddc3..343e13b0ce46 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2218,7 +2218,7 @@ const styles = { minWidth: 140, }, - verifyCodesButton: { + twoFactorAuthCopyCodeButton: { minWidth: 110, }, From 06c7156b0ce43270147aa2a9b13cc08a3ce954cd Mon Sep 17 00:00:00 2001 From: Hezekiel Tamire Date: Tue, 27 Jun 2023 22:20:32 +0300 Subject: [PATCH 121/197] fixed prettier issue --- .../signin/ValidateCodeForm/BaseValidateCodeForm.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 7721585d7bd4..83a4d0afbaa5 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -91,11 +91,11 @@ function BaseValidateCodeForm(props) { const hasError = Boolean(props.account) && !_.isEmpty(props.account.errors); useEffect(() => { - if(!(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading))) { - return + if (!(inputValidateCodeRef.current && ((hasError && props.session.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED) || props.account.isLoading))) { + return; } inputValidateCodeRef.current.blur(); - }, [props.account.isLoading, props.session.autoAuthState, hasError]) + }, [props.account.isLoading, props.session.autoAuthState, hasError]); useEffect(() => { if (!inputValidateCodeRef.current || prevIsVisible || !props.isVisible || !canFocusInputOnScreenFocus()) { @@ -289,7 +289,9 @@ function BaseValidateCodeForm(props) { accessibilityRole="button" accessibilityLabel={props.translate('validateCodeForm.magicCodeNotReceived')} > - {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} + + {hasError ? props.translate('validateCodeForm.requestNewCodeAfterErrorOccurred') : props.translate('validateCodeForm.magicCodeNotReceived')} + )} From cecef7e382f1d9051a9403ddf8cab0004efc12b3 Mon Sep 17 00:00:00 2001 From: Danut Gavrus Date: Wed, 28 Jun 2023 11:09:03 +0300 Subject: [PATCH 122/197] [FIX] Prevent unnecesarry re-renders. --- src/pages/home/report/ReportActionsView.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 2757bb0cc00d..2a45a62b951d 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -223,9 +223,12 @@ class ReportActionsView extends React.Component { if (this.props.reportActions.length > 0 && this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !this.props.network.isOffline) { const reportActionsWithoutPendingOne = lodashCloneDeep(this.props.reportActions); reportActionsWithoutPendingOne.shift(); - this.setState({ - newMarkerReportActionID: ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne), - }); + const newMarkerReportActionID = ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne); + if (newMarkerReportActionID !== this.state.newMarkerReportActionID) { + this.setState({ + newMarkerReportActionID: newMarkerReportActionID, + }); + } } // Checks to see if a report comment has been manually "marked as unread". All other times when the lastReadTime From bac45a2d8e826971fa8cbe1d3018881610f52665 Mon Sep 17 00:00:00 2001 From: Steven KKC Date: Wed, 28 Jun 2023 04:11:56 -0400 Subject: [PATCH 123/197] fix two same emails display for user without display name --- src/components/MentionSuggestions.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/MentionSuggestions.js b/src/components/MentionSuggestions.js index e8fc3003bf56..9caede63ae32 100644 --- a/src/components/MentionSuggestions.js +++ b/src/components/MentionSuggestions.js @@ -65,7 +65,7 @@ function MentionSuggestions(props) { const renderSuggestionMenuItem = (item) => { const isIcon = item.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT; const styledDisplayName = getStyledTextArray(item.text, props.prefix); - const styledHandle = getStyledTextArray(item.alternateText, props.prefix); + const styledHandle = item.text === item.alternateText ? '' : getStyledTextArray(item.alternateText, props.prefix); return ( @@ -96,12 +96,14 @@ function MentionSuggestions(props) { numberOfLines={1} > {_.map(styledHandle, ({text, isColored}, i) => ( - - {text} - + text !== '' && ( + + {text} + + ) ))} From 222ee2eb9b11f4ec5fc5f49503a5b0de27d33785 Mon Sep 17 00:00:00 2001 From: Steven KKC Date: Wed, 28 Jun 2023 04:56:49 -0400 Subject: [PATCH 124/197] fix lint error --- src/components/MentionSuggestions.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/MentionSuggestions.js b/src/components/MentionSuggestions.js index 9caede63ae32..1480d98d7899 100644 --- a/src/components/MentionSuggestions.js +++ b/src/components/MentionSuggestions.js @@ -95,16 +95,18 @@ function MentionSuggestions(props) { style={[styles.mentionSuggestionsText, styles.flex1]} numberOfLines={1} > - {_.map(styledHandle, ({text, isColored}, i) => ( - text !== '' && ( - - {text} - - ) - ))} + {_.map( + styledHandle, + ({text, isColored}, i) => + text !== '' && ( + + {text} + + ), + )} ); From a6e9ca6d6bf6a7260d6431c29743aa79b186a7cf Mon Sep 17 00:00:00 2001 From: Danut Gavrus Date: Wed, 28 Jun 2023 12:04:27 +0300 Subject: [PATCH 125/197] Prevent condition executing when deleting last message, but report has no unread ones. --- src/pages/home/report/ReportActionsView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 2a45a62b951d..007f2354ac8b 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -220,7 +220,7 @@ class ReportActionsView extends React.Component { } // If the last unread message was deleted, remove the *New* green marker and the *New Messages* notification at scroll just as the deletion starts. - if (this.props.reportActions.length > 0 && this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !this.props.network.isOffline) { + if (ReportUtils.isUnread(this.props.report) && this.props.reportActions.length > 0 && this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !this.props.network.isOffline) { const reportActionsWithoutPendingOne = lodashCloneDeep(this.props.reportActions); reportActionsWithoutPendingOne.shift(); const newMarkerReportActionID = ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne); @@ -230,7 +230,7 @@ class ReportActionsView extends React.Component { }); } } - + // Checks to see if a report comment has been manually "marked as unread". All other times when the lastReadTime // changes it will be because we marked the entire report as read. const didManuallyMarkReportAsUnread = prevProps.report.lastReadTime !== this.props.report.lastReadTime && ReportUtils.isUnread(this.props.report); From faf1a01195f254d1162f3dc9906bd8da1654d633 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Wed, 28 Jun 2023 19:52:44 +0530 Subject: [PATCH 126/197] Remove updateOptionsWithSearchTerm and use useEffect --- src/pages/NewChatPage.js | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index e6f0472241dc..8927088c3f67 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -44,6 +44,8 @@ const defaultProps = { reports: {}, }; +const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); + function NewChatPage(props) { const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); @@ -51,7 +53,6 @@ function NewChatPage(props) { const [filteredUserToInvite, setFilteredUserToInvite] = useState(); const [selectedOptions, setSelectedOptions] = useState([]); - const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const headerMessage = OptionsListUtils.getHeaderMessage( filteredPersonalDetails.length + filteredRecentReports.length !== 0, @@ -114,21 +115,6 @@ function NewChatPage(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, maxParticipantsReached, props.isGroupChat, selectedOptions]); - const updateOptionsWithSearchTerm = (newSearchTerm = '') => { - const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( - props.reports, - props.personalDetails, - props.betas, - newSearchTerm, - [], - props.isGroupChat ? excludedGroupEmails : [], - ); - setSearchTerm(newSearchTerm); - setFilteredRecentReports(recentReports); - setFilteredPersonalDetails(personalDetails); - setFilteredUserToInvite(userToInvite); - }; - /** * Removes a selected option from list if already selected. If not already selected add this option to the list. * @param {Object} option @@ -178,12 +164,20 @@ function NewChatPage(props) { }; useEffect(() => { - updateOptionsWithSearchTerm(searchTerm); - // all dependencies are not added below - - // 1. searchTerm - when searchTerm changes updateOptionsWithSearchTerm is called by OptionsSelector's onChangeText prop - // 2. updateOptionsWithSearchTerm - it will change its reference upon each rerender unnecessarily + const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( + props.reports, + props.personalDetails, + props.betas, + searchTerm, + [], + props.isGroupChat ? excludedGroupEmails : [], + ); + setFilteredRecentReports(recentReports); + setFilteredPersonalDetails(personalDetails); + setFilteredUserToInvite(userToInvite); + // props.betas and props.isGroupChat are not added as dependencies since they don't change during the component lifecycle // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.reports, props.personalDetails]); + }, [props.reports, props.personalDetails, searchTerm]); return ( (props.isGroupChat ? toggleOption(option) : createChat(option))} - onChangeText={updateOptionsWithSearchTerm} + onChangeText={setSearchTerm} headerMessage={headerMessage} boldStyle shouldFocusOnSelectRow={props.isGroupChat && !Browser.isMobile()} From 79a6c9b2b20b403c1d121c95c18dce7f7c0672df Mon Sep 17 00:00:00 2001 From: Danut Gavrus Date: Wed, 28 Jun 2023 20:44:41 +0300 Subject: [PATCH 127/197] Run prettier. --- src/pages/home/report/ReportActionsView.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 007f2354ac8b..9e507a86290f 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -220,7 +220,12 @@ class ReportActionsView extends React.Component { } // If the last unread message was deleted, remove the *New* green marker and the *New Messages* notification at scroll just as the deletion starts. - if (ReportUtils.isUnread(this.props.report) && this.props.reportActions.length > 0 && this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !this.props.network.isOffline) { + if ( + ReportUtils.isUnread(this.props.report) && + this.props.reportActions.length > 0 && + this.props.reportActions[0].pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && + !this.props.network.isOffline + ) { const reportActionsWithoutPendingOne = lodashCloneDeep(this.props.reportActions); reportActionsWithoutPendingOne.shift(); const newMarkerReportActionID = ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne); @@ -230,7 +235,7 @@ class ReportActionsView extends React.Component { }); } } - + // Checks to see if a report comment has been manually "marked as unread". All other times when the lastReadTime // changes it will be because we marked the entire report as read. const didManuallyMarkReportAsUnread = prevProps.report.lastReadTime !== this.props.report.lastReadTime && ReportUtils.isUnread(this.props.report); From d4d97c5cf76faf0d743ff2e55dc0b0214d0336b8 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 28 Jun 2023 11:05:55 -0700 Subject: [PATCH 128/197] Make sure we call OpenProfile action even if we're accessing the page directly --- src/libs/actions/App.js | 18 ++---------------- src/libs/actions/IOU.js | 6 ++++++ src/pages/settings/InitialSettingsPage.js | 3 +-- src/pages/settings/Profile/ProfilePage.js | 7 ++++++- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 38638e22a464..d4012829b90c 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -31,18 +31,6 @@ Onyx.connect({ initWithStoredValues: false, }); -let myPersonalDetails; -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - if (!val || !currentUserAccountID) { - return; - } - - myPersonalDetails = val[currentUserAccountID]; - }, -}); - let allPolicies = []; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, @@ -267,8 +255,8 @@ function setUpPoliciesAndNavigate(session) { } } -function openProfile() { - const oldTimezoneData = myPersonalDetails.timezone || {}; +function openProfile(personalDetails) { + const oldTimezoneData = personalDetails.timezone || {}; let newTimezoneData = oldTimezoneData; if (lodashGet(oldTimezoneData, 'automatic', true)) { @@ -308,8 +296,6 @@ function openProfile() { ], }, ); - - Navigation.navigate(ROUTES.SETTINGS_PROFILE); } export {setLocale, setLocaleAndNavigate, setSidebarLoaded, setUpPoliciesAndNavigate, openProfile, openApp, reconnectApp, confirmReadyToOpenApp}; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 1e3a297f08da..dac3b07515a0 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1070,6 +1070,11 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType optimisticData.push(optimisticPersonalDetailListData); } + let reportPreviewAction = ReportActionsUtils.getReportPreviewAction(chatReport.reportID, optimisticIOUReport.reportID); + if (!reportPreviewAction) { + reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport.reportID, optimisticIOUReport.reportID); + } + return { params: { iouReportID: optimisticIOUReport.reportID, @@ -1079,6 +1084,7 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType transactionID: optimisticTransaction.transactionID, newIOUReportDetails, createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : 0, + reportPreviewReportActionID: reportPreviewAction.reportActionID, }, optimisticData, successData, diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index b7364ce623fa..97d013f31084 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -21,7 +21,6 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize import compose from '../../libs/compose'; import CONST from '../../CONST'; import Permissions from '../../libs/Permissions'; -import * as App from '../../libs/actions/App'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../components/withCurrentUserPersonalDetails'; import * as PaymentMethods from '../../libs/actions/PaymentMethods'; import bankAccountPropTypes from '../../components/bankAccountPropTypes'; @@ -200,7 +199,7 @@ class InitialSettingsPage extends React.Component { translationKey: 'common.profile', icon: Expensicons.Profile, action: () => { - App.openProfile(); + Navigation.navigate(ROUTES.SETTINGS_PROFILE); }, brickRoadIndicator: profileBrickRoadIndicator, }, diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 1fd1e585648d..07db3d0cdffb 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import React from 'react'; +import React, {useEffect} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -23,6 +23,7 @@ import * as Expensicons from '../../../components/Icon/Expensicons'; import ONYXKEYS from '../../../ONYXKEYS'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import userPropTypes from '../userPropTypes'; +import * as App from '../../../libs/actions/App'; const propTypes = { /* Onyx Props */ @@ -84,6 +85,10 @@ function ProfilePage(props) { }, ]; + useEffect(() => { + App.openProfile(props.currentUserPersonalDetails); + }, [props.currentUserPersonalDetails]); + return ( Date: Wed, 28 Jun 2023 14:28:18 -0500 Subject: [PATCH 129/197] fix: comment --- src/pages/workspace/WorkspaceInvitePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 104463f85be5..1092579f0bc5 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -174,7 +174,7 @@ class WorkspaceInvitePage extends React.Component { updateOptionsWithSearchTerm(searchTerm = '') { const {personalDetails, userToInvite} = OptionsListUtils.getMemberInviteOptions(this.props.personalDetails, this.props.betas, searchTerm, this.getExcludedUsers()); - // update selectedOptions as well + // Update selectedOptions with the latest personalDetails and policyMembers information const detailsMap = {}; _.forEach(personalDetails, (detail) => (detailsMap[detail.login] = detail)); const selectedOptions = []; From 9b0d1d91830342df944153d463c8db3b988133a1 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Wed, 28 Jun 2023 15:12:01 -0500 Subject: [PATCH 130/197] fix: prettier --- src/pages/home/report/ReportActionItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 73235deeabca..79c2f3ed1258 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -159,7 +159,6 @@ function ReportActionItem(props) { setIsHidden(true); } setModerationDecision(latestDecision); - }, [latestDecision, props.action.actionName]); const toggleContextMenuFromActiveReportAction = useCallback(() => { From e1242bce5dde10baa46224ab3ec3c45fb29a2605 Mon Sep 17 00:00:00 2001 From: Jayesh Mangwani Date: Thu, 29 Jun 2023 02:08:33 +0530 Subject: [PATCH 131/197] fix: set validateCodeSent null on success of validateSecondaryLogin --- src/libs/actions/User.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 524444e08820..421b8781d84c 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -432,6 +432,9 @@ function validateSecondaryLogin(contactMethod, validateCode) { pendingFields: { validateLogin: null, }, + errorFields: { + validateCodeSent: null, + }, }, }, }, From b417a915ed2e13fd5df36fa74959378c56164144 Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Wed, 28 Jun 2023 17:12:50 -0400 Subject: [PATCH 132/197] Remove personalDetails as part of migration --- src/libs/migrations/PersonalDetailsByAccountID.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/migrations/PersonalDetailsByAccountID.js b/src/libs/migrations/PersonalDetailsByAccountID.js index 2872e0ed1afe..85fe15318d2d 100644 --- a/src/libs/migrations/PersonalDetailsByAccountID.js +++ b/src/libs/migrations/PersonalDetailsByAccountID.js @@ -225,6 +225,12 @@ export default function () { } }); + // The personalDetails object has been replaced by personalDetailsList + // So if we find an instance of personalDetails we will clear it out + if (oldPersonalDetails) { + onyxData[DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS] = null; + } + return Onyx.multiSet(onyxData); }, ); From c549da29b7334a3cdd7e4a5edf51b56de2ccbeaa Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Wed, 28 Jun 2023 17:19:22 -0400 Subject: [PATCH 133/197] Add test for removing personalDetails --- .../migrations/PersonalDetailsByAccountID.js | 1 + tests/unit/MigrationTest.js | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/libs/migrations/PersonalDetailsByAccountID.js b/src/libs/migrations/PersonalDetailsByAccountID.js index 85fe15318d2d..34b8ebabc150 100644 --- a/src/libs/migrations/PersonalDetailsByAccountID.js +++ b/src/libs/migrations/PersonalDetailsByAccountID.js @@ -228,6 +228,7 @@ export default function () { // The personalDetails object has been replaced by personalDetailsList // So if we find an instance of personalDetails we will clear it out if (oldPersonalDetails) { + Log.info('[Migrate Onyx] PersonalDetailsByAccountID migration: removing personalDetails'); onyxData[DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS] = null; } diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index 6162bded793b..6ca3b5b2d516 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -845,5 +845,30 @@ describe('Migrations', () => { }, }); })); + + it('Should succeed in removing the personalDetails object if found in Onyx', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing personalDetails'); + const connectionID = Onyx.connect({ + key: DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS, + callback: (allPersonalDetails) => { + Onyx.disconnect(connectionID); + expect(allPersonalDetails).toBeNull(); + }, + }); + })); }); }); From 1ed936d9cd8cf90a324d6fca07f9049a1ffadf06 Mon Sep 17 00:00:00 2001 From: Rahul kushwaha Date: Thu, 29 Jun 2023 05:44:35 +0530 Subject: [PATCH 134/197] added textInputHeight in initial state --- src/components/TextInput/BaseTextInput.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index f952e78d3459..ac06c52fe526 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -34,6 +34,7 @@ class BaseTextInput extends Component { labelScale: new Animated.Value(activeLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE), passwordHidden: props.secureTextEntry, textInputWidth: 0, + textInputHeight: 0, prefixWidth: 0, selection: props.selection, height: variables.componentSizeLarge, From 8eae5b088f1eb98d19deb135d5aeb0f720cc4be6 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Thu, 29 Jun 2023 07:15:56 +0700 Subject: [PATCH 135/197] fix: style and change text to copy --- .../settings/Security/TwoFactorAuth/CodesPage.js | 5 ++--- src/styles/styles.js | 13 +++++++------ src/styles/utilities/spacing.js | 4 ++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 91ccee630879..39d4141a2872 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -25,7 +25,6 @@ import Clipboard from '../../../../libs/Clipboard'; import themeColors from '../../../../styles/themes/default'; import localFileDownload from '../../../../libs/localFileDownload'; import * as TwoFactorAuthActions from '../../../../libs/actions/TwoFactorAuthActions'; -import * as StyleUtils from '../../../../styles/StyleUtils'; const propTypes = { ...withLocalizePropTypes, @@ -96,9 +95,9 @@ function CodesPage(props) { ))} - + { - let paddingHorizontal = styles.ph15; + let paddingHorizontal = styles.ph9; if (isSmallScreenWidth) { - paddingHorizontal = styles.ph10; + paddingHorizontal = styles.ph4; } if (isExtraSmallScreenWidth) { - paddingHorizontal = styles.ph4; + paddingHorizontal = styles.ph2; } return { @@ -2198,12 +2198,12 @@ const styles = { flexDirection: 'row', flexWrap: 'wrap', gap: 12, - height: 148, + minHeight: 148, }, twoFactorAuthCode: { fontFamily: fontFamily.MONOSPACE, - width: 100, + width: 112, textAlign: 'center', }, @@ -2212,10 +2212,11 @@ const styles = { justifyContent: 'center', gap: 12, marginTop: 20, + flexWrap: 'wrap', }, twoFactorAuthCodesButton: { - minWidth: 140, + minWidth: 112, }, twoFactorAuthCopyCodeButton: { diff --git a/src/styles/utilities/spacing.js b/src/styles/utilities/spacing.js index e6823e43e921..0e4bb8a69907 100644 --- a/src/styles/utilities/spacing.js +++ b/src/styles/utilities/spacing.js @@ -330,6 +330,10 @@ export default { paddingHorizontal: 32, }, + ph9: { + paddingHorizontal: 36, + }, + ph10: { paddingHorizontal: 40, }, From fb4e23a208d9dece3e21d30102daa0b89f64548b Mon Sep 17 00:00:00 2001 From: Nam Le Date: Thu, 29 Jun 2023 08:06:31 +0700 Subject: [PATCH 136/197] remove translation not use --- src/languages/en.js | 1 - src/languages/es.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 17653692828e..28c03c144a17 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -557,7 +557,6 @@ export default { stepSuccess: 'Finished', enabled: 'Two-factor authentication is now enabled!', congrats: 'Congrats, now you’ve got that extra security.', - copyCodes: 'Copy codes', copy: 'Copy', disable: 'Disable', }, diff --git a/src/languages/es.js b/src/languages/es.js index 2a8af5628045..6ae536a9389b 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -558,7 +558,6 @@ export default { stepSuccess: 'Finalizado', enabled: '¡La autenticación de dos factores ahora está habilitada!', congrats: 'Felicidades, ahora tienes esa seguridad adicional.', - copyCodes: 'Copiar códigos', copy: 'Copiar', disable: 'Deshabilitar', }, From 24316ab8230342fdd7cdf07e4bcdfc263da623d1 Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:12:28 +0545 Subject: [PATCH 137/197] Use fallback for country --- src/components/AddressSearch/index.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 9699eb9aab94..4f49f64f2b6c 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import {LogBox, ScrollView, View} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import lodashGet from 'lodash/get'; +import findKey from 'lodash/findKey'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; @@ -142,7 +143,15 @@ function AddressSearch(props) { // Make sure that the order of keys remains such that the country is always set above the state. // Refer to https://github.com/Expensify/App/issues/15633 for more information. - const {state: stateAutoCompleteFallback = '', city: cityAutocompleteFallback = ''} = GooglePlacesUtils.getPlaceAutocompleteTerms(autocompleteData.terms); + const { + country: countryAutoCompleteFallback = '', + state: stateAutoCompleteFallback = '', + city: cityAutocompleteFallback = '', + } = GooglePlacesUtils.getPlaceAutocompleteTerms(autocompleteData.terms); + + const autoCompleteFallbackCountryCode = findKey(CONST.ALL_COUNTRIES, (country) => country === countryAutoCompleteFallback); + + const countryWithFallback = country || autoCompleteFallbackCountryCode; const values = { street: `${streetNumber} ${streetName}`.trim(), @@ -162,13 +171,13 @@ function AddressSearch(props) { // If the address is not in the US, use the full length state name since we're displaying the address's // state / province in a TextInput instead of in a picker. - if (country !== CONST.COUNTRY.US) { + if (countryWithFallback !== CONST.COUNTRY.US) { values.state = longStateName; } // UK addresses return countries (e.g. England) in the state field (administrative_area_level_1) // So we use a secondary field (administrative_area_level_2) as a fallback - if (country === CONST.COUNTRY.GB) { + if (countryWithFallback === CONST.COUNTRY.GB) { values.state = stateFallback; } @@ -178,9 +187,10 @@ function AddressSearch(props) { values.street += `, ${subpremise}`; } - const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, country); + const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, countryWithFallback); + if (isValidCountryCode) { - values.country = country; + values.country = countryWithFallback; } if (props.inputID) { From 98a1f21d24c972efc887c374204df7e57ecd332d Mon Sep 17 00:00:00 2001 From: Danut Gavrus Date: Thu, 29 Jun 2023 13:43:11 +0300 Subject: [PATCH 138/197] Fixed lint error. --- src/pages/home/report/ReportActionsView.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 9e507a86290f..f3c9e67d79db 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -230,9 +230,7 @@ class ReportActionsView extends React.Component { reportActionsWithoutPendingOne.shift(); const newMarkerReportActionID = ReportUtils.getNewMarkerReportActionID(this.props.report, reportActionsWithoutPendingOne); if (newMarkerReportActionID !== this.state.newMarkerReportActionID) { - this.setState({ - newMarkerReportActionID: newMarkerReportActionID, - }); + this.setState({newMarkerReportActionID}); } } From 89d2893d3f33463ac35127fad8c0ae456ca9d53b Mon Sep 17 00:00:00 2001 From: Anjana Mendiratta Date: Thu, 29 Jun 2023 17:32:06 +0530 Subject: [PATCH 139/197] fix: keyboard shortcuts resize sizing Signed-off-by: Anjana Mendiratta --- src/styles/styles.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 74066af5e20a..56e959df2d65 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2997,8 +2997,7 @@ const styles = { keyboardShortcutModalContainer: { maxHeight: '100%', - flexShrink: 0, - flexGrow: 0, + flex: 0, flexBasis: 'auto', }, From b5f0e0122b03ac6643cef5698893c582af939883 Mon Sep 17 00:00:00 2001 From: Pujan Date: Thu, 29 Jun 2023 18:19:12 +0530 Subject: [PATCH 140/197] close currency update modal sync --- src/pages/workspace/WorkspaceInitialPage.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 73f659574e71..4271a81ce956 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; -import React, {useCallback, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {View, ScrollView} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; @@ -82,6 +82,13 @@ function WorkspaceInitialPage(props) { Navigation.navigate(ROUTES.SETTINGS_WORKSPACES); }, [props.reports, policy]); + useEffect(() => { + if (!isCurrencyModalOpen || policy.outputCurrency !== CONST.CURRENCY.USD) { + return; + } + setIsCurrencyModalOpen(false); + }, [policy.outputCurrency, isCurrencyModalOpen]); + /** * Call update workspace currency and hide the modal */ From d1d23ab4259dbf3803d19c0996f4aa23763fc580 Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Wed, 28 Jun 2023 12:21:12 -0400 Subject: [PATCH 141/197] Fix proptypes in ArchivedReportFooter for accountIDs instead of logins --- src/components/ArchivedReportFooter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index a742b97fb0a9..1af8cadc80ca 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -22,11 +22,11 @@ const propTypes = { /** The reason the report was closed */ reason: PropTypes.string.isRequired, - /** (For accountMerged reason only), the email of the previous owner of this report. */ - oldLogin: PropTypes.string, + /** (For accountMerged reason only), the accountID of the previous owner of this report. */ + oldAccountID: PropTypes.number, - /** (For accountMerged reason only), the email of the account the previous owner was merged into */ - newLogin: PropTypes.string, + /** (For accountMerged reason only), the accountID of the account the previous owner was merged into */ + newLogin: PropTypes.number, }).isRequired, }), From 043b9e64339e4c437449183d1a56a7f4f58c900c Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Thu, 29 Jun 2023 10:02:22 -0400 Subject: [PATCH 142/197] Update src/components/ArchivedReportFooter.js Co-authored-by: Alex Beaman --- src/components/ArchivedReportFooter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index 1af8cadc80ca..d7a138d9a473 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -26,7 +26,7 @@ const propTypes = { oldAccountID: PropTypes.number, /** (For accountMerged reason only), the accountID of the account the previous owner was merged into */ - newLogin: PropTypes.number, + newAccountID: PropTypes.number, }).isRequired, }), From cc3ec7fd9014556160cafe20d1766012e49fbd51 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Thu, 29 Jun 2023 20:46:42 +0530 Subject: [PATCH 143/197] fix regex for debit card name --- src/libs/ValidationUtils.js | 14 -------------- src/pages/settings/Payments/AddDebitCardPage.js | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index 0fdc65bc7a82..6f05da08c703 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -140,19 +140,6 @@ function isValidDebitCard(string) { return validateCardNumber(string); } -/** - * - * @param {String} nameOnCard - * @returns {Boolean} - */ -function isValidCardName(nameOnCard) { - if (!CONST.REGEX.ALPHABETIC_CHARS.test(nameOnCard)) { - return false; - } - - return !_.isEmpty(nameOnCard.trim()); -} - /** * @param {String} code * @returns {Boolean} @@ -464,7 +451,6 @@ export { getAgeRequirementError, isValidAddress, isValidDate, - isValidCardName, isValidPastDate, isValidSecurityCode, isValidExpirationDate, diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 77b7089fd2ef..f08dee0b0ccf 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -73,7 +73,7 @@ class DebitCardPage extends Component { validate(values) { const errors = {}; - if (!values.nameOnCard || !ValidationUtils.isValidCardName(values.nameOnCard)) { + if (!values.nameOnCard || !ValidationUtils.isValidLegalName(values.nameOnCard)) { errors.nameOnCard = 'addDebitCardPage.error.invalidName'; } From e5d8fab915850e72334c46b1fd52683e15c2ffcb Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Thu, 29 Jun 2023 21:10:39 +0530 Subject: [PATCH 144/197] Remove unnecessary regex --- src/CONST.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index 4c04ee5228e2..062e32271478 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1118,7 +1118,6 @@ const CONST = { REGEX: { SPECIAL_CHARS_WITHOUT_NEWLINE: /((?!\n)[()-\s\t])/g, DIGITS_AND_PLUS: /^\+?[0-9]*$/, - ALPHABETIC_CHARS: /[a-zA-Z]+/, ALPHABETIC_CHARS_WITH_NUMBER: /^[a-zA-ZÀ-ÿ0-9 ]*$/, POSITIVE_INTEGER: /^\d+$/, PO_BOX: /\b[P|p]?(OST|ost)?\.?\s*[O|o|0]?(ffice|FFICE)?\.?\s*[B|b][O|o|0]?[X|x]?\.?\s+[#]?(\d+)\b/, From c20fd4ade0fbbd008ad3ce4852b71f057d7e8ac4 Mon Sep 17 00:00:00 2001 From: Nikhil Vats Date: Thu, 29 Jun 2023 22:11:47 +0530 Subject: [PATCH 145/197] Fix inconsistent translations --- src/languages/en.js | 2 +- src/languages/es.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 0dca451b0701..2dfa0428266e 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -591,7 +591,7 @@ export default { growlMessageOnSave: 'Your debit card was successfully added', expensifyPassword: 'Expensify password', error: { - invalidName: 'Please enter a valid name', + invalidName: 'Name can only include latin letters and numbers.', addressZipCode: 'Please enter a valid zip code', debitCardNumber: 'Please enter a valid debit card number', expirationDate: 'Please select a valid expiration date', diff --git a/src/languages/es.js b/src/languages/es.js index 10e31f7225e1..cd55c8db72db 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -592,7 +592,7 @@ export default { growlMessageOnSave: 'Su tarteja de débito se agregó correctamente', expensifyPassword: 'Contraseña de Expensify', error: { - invalidName: 'Por favor, introduce un nombre válido', + invalidName: 'El nombre solo puede contener números y caracteres latinos.', addressZipCode: 'Por favor, introduce un código postal válido', debitCardNumber: 'Por favor, introduce un número de tarjeta de débito válido', expirationDate: 'Por favor, selecciona una fecha de vencimiento válida', From b895d5efe9318b9dbc61a32bf958c207b0cd2040 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Thu, 29 Jun 2023 10:21:10 -0700 Subject: [PATCH 146/197] Remove mixed up code --- src/libs/actions/IOU.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index dac3b07515a0..1e3a297f08da 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1070,11 +1070,6 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType optimisticData.push(optimisticPersonalDetailListData); } - let reportPreviewAction = ReportActionsUtils.getReportPreviewAction(chatReport.reportID, optimisticIOUReport.reportID); - if (!reportPreviewAction) { - reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport.reportID, optimisticIOUReport.reportID); - } - return { params: { iouReportID: optimisticIOUReport.reportID, @@ -1084,7 +1079,6 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType transactionID: optimisticTransaction.transactionID, newIOUReportDetails, createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : 0, - reportPreviewReportActionID: reportPreviewAction.reportActionID, }, optimisticData, successData, From 3bb529c66c53c5b032023c868084b26a292e2ffa Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Thu, 29 Jun 2023 12:37:51 -0500 Subject: [PATCH 147/197] fix: use the latest moderation decision --- src/pages/home/report/ReportActionItem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 79c2f3ed1258..93a770abea5e 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -149,7 +149,8 @@ function ReportActionItem(props) { // Hide the message if it is being moderated for a higher offense, or is hidden by a moderator // Removed messages should not be shown anyway and should not need this flow - const latestDecision = _.get(props, ['action', 'message', 0, 'moderationDecisions', 0, 'decision'], ''); + const decisions = lodashGet(props, ['action', 'message', 0, 'moderationDecisions'], []); + const latestDecision = lodashGet(_.last(decisions), 'decision', ''); useEffect(() => { if (!props.action.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT || _.isEmpty(latestDecision)) { return; From bba5e3dad90a426ff8dc6381e0982931641ea494 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Thu, 29 Jun 2023 13:00:38 -0500 Subject: [PATCH 148/197] fix: remove empty line --- src/pages/home/report/ReportActionItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 93a770abea5e..a02c94753d80 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -148,7 +148,6 @@ function ReportActionItem(props) { // Hide the message if it is being moderated for a higher offense, or is hidden by a moderator // Removed messages should not be shown anyway and should not need this flow - const decisions = lodashGet(props, ['action', 'message', 0, 'moderationDecisions'], []); const latestDecision = lodashGet(_.last(decisions), 'decision', ''); useEffect(() => { From 1f34ef8d7eb9bb1531eca99a5b64224e014662f7 Mon Sep 17 00:00:00 2001 From: Manjesh yadav Date: Thu, 29 Jun 2023 23:51:10 +0530 Subject: [PATCH 149/197] Fix phone number show twice in tooltip --- src/components/UserDetailsTooltip/index.js | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/UserDetailsTooltip/index.js b/src/components/UserDetailsTooltip/index.js index 786531801837..7641ff126dd1 100644 --- a/src/components/UserDetailsTooltip/index.js +++ b/src/components/UserDetailsTooltip/index.js @@ -10,9 +10,28 @@ import {propTypes, defaultProps} from './userDetailsTooltipPropTypes'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import * as UserUtils from '../../libs/UserUtils'; +import * as LocalePhoneNumber from '../../libs/LocalePhoneNumber'; function UserDetailsTooltip(props) { const userDetails = lodashGet(props.personalDetailsList, props.accountID, props.fallbackUserDetails); + + /** + * Converts user login to formatted phone number if login is SMS login + * @param {String} login + * @returns {String} + */ + const formatLoginIdAsDisplayName = useCallback((login) => { + if (!login) { + return null; + } + + if (!Str.isSMSLogin(login)) { + return login; + } + + return LocalePhoneNumber.formatPhoneNumber(Str.removeSMSDomain(login)); + }, []); + const renderTooltipContent = useCallback( () => ( @@ -28,11 +47,13 @@ function UserDetailsTooltip(props) { - {String(userDetails.login || '').trim() && !_.isEqual(userDetails.login, userDetails.displayName) ? Str.removeSMSDomain(userDetails.login) : ''} + {String(userDetails.login || '').trim() && !_.isEqual(formatLoginIdAsDisplayName(String(userDetails.login || '')), userDetails.displayName) + ? Str.removeSMSDomain(userDetails.login) + : ''} ), - [userDetails.avatar, userDetails.displayName, userDetails.login, userDetails.accountID], + [userDetails.avatar, userDetails.displayName, userDetails.login, userDetails.accountID, formatLoginIdAsDisplayName], ); if (!userDetails.displayName && !userDetails.login) { From 7e50eb6a280a4c14f7a6c8f8e4c4d57851ea1768 Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Fri, 30 Jun 2023 05:40:54 +0545 Subject: [PATCH 150/197] Fix lint error --- src/components/AddressSearch/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 4f49f64f2b6c..1275b26d5836 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -121,7 +121,7 @@ function AddressSearch(props) { postal_code: zipCode, administrative_area_level_1: state, administrative_area_level_2: stateFallback, - country, + country: countryPrimary, } = GooglePlacesUtils.getAddressComponents(addressComponents, { street_number: 'long_name', route: 'long_name', @@ -144,14 +144,14 @@ function AddressSearch(props) { // Make sure that the order of keys remains such that the country is always set above the state. // Refer to https://github.com/Expensify/App/issues/15633 for more information. const { - country: countryAutoCompleteFallback = '', + country: countryFallbackLongName = '', state: stateAutoCompleteFallback = '', city: cityAutocompleteFallback = '', } = GooglePlacesUtils.getPlaceAutocompleteTerms(autocompleteData.terms); - const autoCompleteFallbackCountryCode = findKey(CONST.ALL_COUNTRIES, (country) => country === countryAutoCompleteFallback); + const countryFallback = findKey(CONST.ALL_COUNTRIES, (country) => country === countryFallbackLongName); - const countryWithFallback = country || autoCompleteFallbackCountryCode; + const country = countryPrimary || countryFallback; const values = { street: `${streetNumber} ${streetName}`.trim(), @@ -171,13 +171,13 @@ function AddressSearch(props) { // If the address is not in the US, use the full length state name since we're displaying the address's // state / province in a TextInput instead of in a picker. - if (countryWithFallback !== CONST.COUNTRY.US) { + if (country !== CONST.COUNTRY.US) { values.state = longStateName; } // UK addresses return countries (e.g. England) in the state field (administrative_area_level_1) // So we use a secondary field (administrative_area_level_2) as a fallback - if (countryWithFallback === CONST.COUNTRY.GB) { + if (country === CONST.COUNTRY.GB) { values.state = stateFallback; } @@ -187,10 +187,10 @@ function AddressSearch(props) { values.street += `, ${subpremise}`; } - const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, countryWithFallback); + const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, countryFallback); if (isValidCountryCode) { - values.country = countryWithFallback; + values.country = country; } if (props.inputID) { From 180edef82d47382a021f336282f4c10c6c3df465 Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Fri, 30 Jun 2023 05:43:22 +0545 Subject: [PATCH 151/197] Check if country is valid --- src/components/AddressSearch/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 1275b26d5836..9f7ab913380b 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -187,7 +187,7 @@ function AddressSearch(props) { values.street += `, ${subpremise}`; } - const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, countryFallback); + const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, country); if (isValidCountryCode) { values.country = country; From 85c81b64fa67e8d74405cb526444df5d4f1fcb9c Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Fri, 30 Jun 2023 05:44:23 +0545 Subject: [PATCH 152/197] Cleanup --- src/components/AddressSearch/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 9f7ab913380b..34f96bd8745d 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -188,7 +188,6 @@ function AddressSearch(props) { } const isValidCountryCode = lodashGet(CONST.ALL_COUNTRIES, country); - if (isValidCountryCode) { values.country = country; } From 4047c4b6079aff5f3a1c669b34a122aa296c8acd Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 30 Jun 2023 05:30:39 +0530 Subject: [PATCH 153/197] fix: emoji showing original message for few moments --- src/libs/ReportActionsUtils.js | 15 +++++++++++++++ src/libs/actions/Report.js | 7 ++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index eb871bcf62c1..f79b6b53fc37 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -425,6 +425,20 @@ function getLinkedTransactionID(reportID, reportActionID) { return reportAction.originalMessage.IOUTransactionID; } +/** + * Returns the parentReportAction if the given report is a thread/task. + * + * @param {String} reportID + * @param {String} reportActionID + * @returns {Object} + */ +function getReportAction(reportID, reportActionID) { + if (!reportID || !reportActionID) { + return {}; + } + return lodashGet(allReportActions, [reportID, reportActionID], {}); +} + /** * @param {*} chatReportID * @param {*} iouReportID @@ -492,4 +506,5 @@ export { isMessageDeleted, isWhisperAction, isPendingRemove, + getReportAction, }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2f6f812bdbe3..a05ca26ef0df 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1639,15 +1639,16 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { * @returns {Promise} */ function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { - const message = reportAction.message[0]; + const latestReportAction = ReportActionsUtils.getReportAction(reportID, reportAction.reportActionID); + const message = latestReportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction(reportID, reportAction, emoji, skinTone); + return removeEmojiReaction(reportID, latestReportAction, emoji, skinTone); } } - return addEmojiReaction(reportID, reportAction, emoji, skinTone); + return addEmojiReaction(reportID, latestReportAction, emoji, skinTone); } /** From ba65be5b2ddab640f694d76612e35ad793299b76 Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:58:23 +0545 Subject: [PATCH 154/197] Add test for getPlaceAutocompleteTerms --- tests/unit/GooglePlacesUtilsTest.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/GooglePlacesUtilsTest.js b/tests/unit/GooglePlacesUtilsTest.js index ef7a4491fec0..1bb27bdd9f2f 100644 --- a/tests/unit/GooglePlacesUtilsTest.js +++ b/tests/unit/GooglePlacesUtilsTest.js @@ -129,6 +129,12 @@ const addressComponents = [ types: ['postal_code'], }, ]; + +const autoCompleteTerms = [ + {offset: 0, value: 'Bangladesh Border Road'}, + {offset: 24, value: 'Bangladesh'}, +]; + describe('GooglePlacesUtilsTest', () => { describe('getAddressComponents', () => { it('should find address components by type', () => { @@ -189,4 +195,14 @@ describe('GooglePlacesUtilsTest', () => { expect(executionTime).toBeLessThan(5.0); }); }); + describe('getPlaceAutocompleteTerms', () => { + it('should find auto complete terms', () => { + expect(GooglePlacesUtils.getPlaceAutocompleteTerms(autoCompleteTerms)).toStrictEqual({ + country: 'Bangladesh', + state: 'Bangladesh Border Road', + city: '', + street: '', + }); + }); + }); }); From d787ee9f7a22aad1d0ad505381e8d33409c84566 Mon Sep 17 00:00:00 2001 From: Manjesh yadav Date: Fri, 30 Jun 2023 09:21:33 +0530 Subject: [PATCH 155/197] remove unnecessary code --- src/components/UserDetailsTooltip/index.js | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/components/UserDetailsTooltip/index.js b/src/components/UserDetailsTooltip/index.js index 7641ff126dd1..42a3495c0e0f 100644 --- a/src/components/UserDetailsTooltip/index.js +++ b/src/components/UserDetailsTooltip/index.js @@ -15,23 +15,6 @@ import * as LocalePhoneNumber from '../../libs/LocalePhoneNumber'; function UserDetailsTooltip(props) { const userDetails = lodashGet(props.personalDetailsList, props.accountID, props.fallbackUserDetails); - /** - * Converts user login to formatted phone number if login is SMS login - * @param {String} login - * @returns {String} - */ - const formatLoginIdAsDisplayName = useCallback((login) => { - if (!login) { - return null; - } - - if (!Str.isSMSLogin(login)) { - return login; - } - - return LocalePhoneNumber.formatPhoneNumber(Str.removeSMSDomain(login)); - }, []); - const renderTooltipContent = useCallback( () => ( @@ -47,13 +30,13 @@ function UserDetailsTooltip(props) { - {String(userDetails.login || '').trim() && !_.isEqual(formatLoginIdAsDisplayName(String(userDetails.login || '')), userDetails.displayName) + {String(userDetails.login || '').trim() && !_.isEqual(LocalePhoneNumber.formatPhoneNumber(userDetails.login || ''), userDetails.displayName) ? Str.removeSMSDomain(userDetails.login) : ''} ), - [userDetails.avatar, userDetails.displayName, userDetails.login, userDetails.accountID, formatLoginIdAsDisplayName], + [userDetails.avatar, userDetails.displayName, userDetails.login, userDetails.accountID], ); if (!userDetails.displayName && !userDetails.login) { From 3e0c8d66fd60881f32e44fba08ef890525686491 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 Jun 2023 13:03:47 +0800 Subject: [PATCH 156/197] fix can't open link inside promise in safari --- src/libs/actions/Link.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Link.js b/src/libs/actions/Link.js index 09c040ab47bc..350528bb6086 100644 --- a/src/libs/actions/Link.js +++ b/src/libs/actions/Link.js @@ -67,17 +67,12 @@ function openOldDotLink(url) { // If shortLivedAuthToken is not accessible, fallback to opening the link without the token. // eslint-disable-next-line rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects('OpenOldDotLink', {}, {}) - .then((response) => { - buildOldDotURL(url, response.shortLivedAuthToken).then((oldDotUrl) => { - Linking.openURL(oldDotUrl); - }); - }) - .catch(() => { - buildOldDotURL(url).then((oldDotUrl) => { - Linking.openURL(oldDotUrl); - }); - }); + asyncOpenURL( + API.makeRequestWithSideEffects('OpenOldDotLink', {}, {}) + .then((response) => buildOldDotURL(url, response.shortLivedAuthToken)) + .catch(() => buildOldDotURL(url)), + (url) => url, + ); } /** From 23c891b34071768ad6d0729e77658ec97b31996e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 Jun 2023 13:12:16 +0800 Subject: [PATCH 157/197] don't submit when pressing enter --- src/pages/workspace/WorkspaceInvitePage.js | 86 ++++++++++------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 990320037b6d..3f4f944db711 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -13,7 +13,6 @@ import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import * as Policy from '../../libs/actions/Policy'; import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton'; -import FormSubmit from '../../components/FormSubmit'; import OptionsSelector from '../../components/OptionsSelector'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; import CONST from '../../CONST'; @@ -267,52 +266,47 @@ class WorkspaceInvitePage extends React.Component { shouldShow={_.isEmpty(this.props.policy)} onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > - - { - this.clearErrors(); - Navigation.goBack(ROUTES.getWorkspaceMembersRoute(this.props.route.params.policyID)); - }} + { + this.clearErrors(); + Navigation.goBack(ROUTES.getWorkspaceMembersRoute(this.props.route.params.policyID)); + }} + /> + + - - - - - - - + + + + ); From 96c4b35f8dc407e3f8486bd808b88e392031fd0f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 Jun 2023 13:23:59 +0800 Subject: [PATCH 158/197] fix lint --- src/libs/actions/Link.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Link.js b/src/libs/actions/Link.js index 350528bb6086..fc29e70de5e0 100644 --- a/src/libs/actions/Link.js +++ b/src/libs/actions/Link.js @@ -66,12 +66,12 @@ function openOldDotLink(url) { } // If shortLivedAuthToken is not accessible, fallback to opening the link without the token. - // eslint-disable-next-line rulesdir/no-api-side-effects-method asyncOpenURL( + // eslint-disable-next-line rulesdir/no-api-side-effects-method API.makeRequestWithSideEffects('OpenOldDotLink', {}, {}) .then((response) => buildOldDotURL(url, response.shortLivedAuthToken)) .catch(() => buildOldDotURL(url)), - (url) => url, + (urlRes) => urlRes, ); } From 47fffd37d1f973b0cd0f2e3bfd8f25740dd02e46 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 Jun 2023 13:53:31 +0800 Subject: [PATCH 159/197] renaming --- src/libs/actions/Link.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Link.js b/src/libs/actions/Link.js index fc29e70de5e0..b920cb1c7ee6 100644 --- a/src/libs/actions/Link.js +++ b/src/libs/actions/Link.js @@ -71,7 +71,7 @@ function openOldDotLink(url) { API.makeRequestWithSideEffects('OpenOldDotLink', {}, {}) .then((response) => buildOldDotURL(url, response.shortLivedAuthToken)) .catch(() => buildOldDotURL(url)), - (urlRes) => urlRes, + (oldDotURL) => oldDotURL, ); } From 0142e73e0110e102036095cf14651462c1159fda Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 30 Jun 2023 15:25:19 +0530 Subject: [PATCH 160/197] fix: remove extra check from getReportAction --- src/libs/ReportActionsUtils.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index f79b6b53fc37..7e2a9164746b 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -433,9 +433,6 @@ function getLinkedTransactionID(reportID, reportActionID) { * @returns {Object} */ function getReportAction(reportID, reportActionID) { - if (!reportID || !reportActionID) { - return {}; - } return lodashGet(allReportActions, [reportID, reportActionID], {}); } From 5b030d23c16e2c4ad96cfb6c8b09292314ce6abc Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 30 Jun 2023 15:26:10 +0530 Subject: [PATCH 161/197] fix: add empty check for report --- src/libs/actions/Report.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a05ca26ef0df..f7f020c960d6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1640,6 +1640,11 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { */ function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { const latestReportAction = ReportActionsUtils.getReportAction(reportID, reportAction.reportActionID); + + if(_.isEmpty(latestReportAction)) { + return; + } + const message = latestReportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it From 1702c675130046af43d46d679bb460cc8dd250fb Mon Sep 17 00:00:00 2001 From: Jayesh Mangwani Date: Fri, 30 Jun 2023 15:57:50 +0530 Subject: [PATCH 162/197] fix: replaces ErrorBoundary with try-catch --- tests/utils/LHNTestUtils.js | 42 ++++++++++++------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 25df3fb01885..c00c69c3532a 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -168,37 +168,21 @@ function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorksp * @param {String} [currentReportID] */ function getDefaultRenderedSidebarLinks(currentReportID = '') { - // An ErrorBoundary needs to be added to the rendering so that any errors that happen while the component - // renders are logged to the console. Without an error boundary, Jest only reports the error like "The above error - // occurred in your component", except, there is no "above error". It's just swallowed up by Jest somewhere. - // With the ErrorBoundary, those errors are caught and logged to the console so you can find exactly which error - // might be causing a rendering issue when developing tests. - class ErrorBoundary extends React.Component { - // Error boundaries have to implement this method. It's for providing a fallback UI, but - // we don't need that for unit testing, so this is basically a no-op. - static getDerivedStateFromError(error) { - return {error}; - } + // A try-catch block needs to be added to the rendering so that any errors that happen while the component + // renders are caught and logged to the console. Without the try-catch block, Jest might only report the error + // as "The above error occurred in your component", without providing specific details. By using a try-catch block, + // any errors are caught and logged, allowing you to identify the exact error that might be causing a rendering issue + // when developing tests. - componentDidCatch(error, errorInfo) { - console.error(error, errorInfo); - } - - render() { - // eslint-disable-next-line react/prop-types - return this.props.children; - } + try { + // Wrap the SideBarLinks inside of LocaleContextProvider so that all the locale props + // are passed to the component. If this is not done, then all the locale props are missing + // and there are a lot of render warnings. It needs to be done like this because normally in + // our app (App.js) is when the react application is wrapped in the context providers + render(); + } catch (error) { + console.error(error); } - - // Wrap the SideBarLinks inside of LocaleContextProvider so that all the locale props - // are passed to the component. If this is not done, then all the locale props are missing - // and there are a lot of render warnings. It needs to be done like this because normally in - // our app (App.js) is when the react application is wrapped in the context providers - render( - - - , - ); } /** From fa98bfd9fd9dbf3e386b99e6d114d7e2fbc34789 Mon Sep 17 00:00:00 2001 From: Pujan Date: Fri, 30 Jun 2023 17:39:03 +0530 Subject: [PATCH 163/197] react-native-google-places-autocomplete version update --- 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 26d228989f42..6a5e7d56ebe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "react-native-fast-image": "^8.6.3", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.9.0", - "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#6f436a06a3018cb49750bb110b89df75f6a865d5", + "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0", "react-native-haptic-feedback": "^1.13.0", "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.1.0", @@ -36665,8 +36665,8 @@ }, "node_modules/react-native-google-places-autocomplete": { "version": "2.5.1", - "resolved": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#6f436a06a3018cb49750bb110b89df75f6a865d5", - "integrity": "sha512-7NiBK83VggJ2HQaHGfJoaPyxtiLu1chwP1VqH9te+PZtf0L9p50IuBQciW+4s173cBamt4U2+mvnCt7zfMFeDg==", + "resolved": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0", + "integrity": "sha512-OJWCz4Epj1p8tyNImWNykAqpd/X1MkNCFPY0dSbgiTJGbW4J5T4bC0PIUQ+ExjxWpWjcFaielTLdoSz0HfeIpw==", "license": "MIT", "dependencies": { "lodash.debounce": "^4.0.8", @@ -68460,9 +68460,9 @@ } }, "react-native-google-places-autocomplete": { - "version": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#6f436a06a3018cb49750bb110b89df75f6a865d5", - "integrity": "sha512-7NiBK83VggJ2HQaHGfJoaPyxtiLu1chwP1VqH9te+PZtf0L9p50IuBQciW+4s173cBamt4U2+mvnCt7zfMFeDg==", - "from": "react-native-google-places-autocomplete@git+https://github.com/Expensify/react-native-google-places-autocomplete.git#6f436a06a3018cb49750bb110b89df75f6a865d5", + "version": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0", + "integrity": "sha512-OJWCz4Epj1p8tyNImWNykAqpd/X1MkNCFPY0dSbgiTJGbW4J5T4bC0PIUQ+ExjxWpWjcFaielTLdoSz0HfeIpw==", + "from": "react-native-google-places-autocomplete@git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0", "requires": { "lodash.debounce": "^4.0.8", "prop-types": "^15.7.2", diff --git a/package.json b/package.json index 423bbd8bce78..d69efbf2cf75 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "react-native-fast-image": "^8.6.3", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.9.0", - "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#6f436a06a3018cb49750bb110b89df75f6a865d5", + "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0", "react-native-haptic-feedback": "^1.13.0", "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^5.1.0", From 3004da3c78b40dca621b7f8a9ce99808216e0902 Mon Sep 17 00:00:00 2001 From: Nishan Chhetri <52850000+nishancx@users.noreply.github.com> Date: Fri, 30 Jun 2023 23:07:13 +0545 Subject: [PATCH 164/197] Use underscore --- src/components/AddressSearch/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 34f96bd8745d..795e45c6f892 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import {LogBox, ScrollView, View} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import lodashGet from 'lodash/get'; -import findKey from 'lodash/findKey'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; @@ -149,7 +148,7 @@ function AddressSearch(props) { city: cityAutocompleteFallback = '', } = GooglePlacesUtils.getPlaceAutocompleteTerms(autocompleteData.terms); - const countryFallback = findKey(CONST.ALL_COUNTRIES, (country) => country === countryFallbackLongName); + const countryFallback = _.findKey(CONST.ALL_COUNTRIES, (country) => country === countryFallbackLongName); const country = countryPrimary || countryFallback; From 09e99d73209ed265d873b71f7dacf9c35dea9995 Mon Sep 17 00:00:00 2001 From: Pujan Date: Fri, 30 Jun 2023 23:22:51 +0530 Subject: [PATCH 165/197] corrected the add pymnt popover vertical position --- src/components/KYCWall/BaseKYCWall.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 956e1fd425c9..1d5f0f4fe06e 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -69,7 +69,7 @@ class KYCWall extends React.Component { } return { - anchorPositionVertical: domRect.top - 150, + anchorPositionVertical: domRect.top - 8, anchorPositionHorizontal: domRect.left, }; } From 754723d4d861e8505a3c7bc4fa8b11b4e73341b5 Mon Sep 17 00:00:00 2001 From: dhairyasenjaliya Date: Sat, 1 Jul 2023 00:01:24 +0530 Subject: [PATCH 166/197] Remove extra spacing from content & header on task create flow --- src/pages/tasks/NewTaskDescriptionPage.js | 2 +- src/pages/tasks/NewTaskDetailsPage.js | 2 +- src/pages/tasks/NewTaskPage.js | 2 +- src/pages/tasks/NewTaskTitlePage.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index b283549d93f0..287ec0396d92 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -63,7 +63,7 @@ function NewTaskDescriptionPage(props) {
onSubmit(values)} enabledWhenOffline > diff --git a/src/pages/tasks/NewTaskDetailsPage.js b/src/pages/tasks/NewTaskDetailsPage.js index 80744721ad00..ef2cc8cfb3f7 100644 --- a/src/pages/tasks/NewTaskDetailsPage.js +++ b/src/pages/tasks/NewTaskDetailsPage.js @@ -84,7 +84,7 @@ function NewTaskPage(props) { validate(values)} onSubmit={(values) => onSubmit(values)} enabledWhenOffline diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index 127592601aec..f1c04a82bb0a 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -144,7 +144,7 @@ function NewTaskPage(props) { shouldShowBackButton onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK_DETAILS)} /> - + validate(values)} onSubmit={(values) => onSubmit(values)} enabledWhenOffline From c0924e6a6d74289499011ba10b5b298e49796bdd Mon Sep 17 00:00:00 2001 From: Nam Le Date: Sat, 1 Jul 2023 01:33:29 +0700 Subject: [PATCH 167/197] fix minor --- src/styles/styles.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 90169f401d1d..dff82606a435 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2198,7 +2198,6 @@ const styles = { flexDirection: 'row', flexWrap: 'wrap', gap: 12, - minHeight: 148, }, twoFactorAuthCode: { From 2cc216ac354902db52400351497fa47f8f1f6047 Mon Sep 17 00:00:00 2001 From: 0xmiroslav Date: Fri, 30 Jun 2023 22:29:29 +0200 Subject: [PATCH 168/197] fix electron version diff --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ead1e5ff9e1f..666ee8c3fb92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -144,7 +144,7 @@ "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", - "electron": "^22.3.14", + "electron": "22.3.14", "electron-builder": "24.5.0", "eslint": "^7.6.0", "eslint-config-expensify": "^2.0.38", From c606b2e12b84fedf0d72ed869b588bc1293a9cfe Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 30 Jun 2023 15:29:16 -0700 Subject: [PATCH 169/197] moving its position so its easier to review --- src/libs/ReportUtils.js | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 864a27275b86..870aae1e4390 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -464,6 +464,27 @@ function isArchivedRoom(report) { return lodashGet(report, ['statusNum']) === CONST.REPORT.STATUS.CLOSED && lodashGet(report, ['stateNum']) === CONST.REPORT.STATE_NUM.SUBMITTED; } +/** + * Returns true if report has a parent + * + * @param {Object} report + * @returns {Boolean} + */ +function isThread(report) { + return Boolean(report && report.parentReportID && report.parentReportActionID); +} + +/** + * Returns true if report is of type chat and has a parent and is therefore a Thread. + * + * @param {Object} report + * @returns {Boolean} + */ +function isChatThread(report) { + return isThread(report) && report.type === CONST.REPORT.TYPE.CHAT; +} + + /** * Get the policy name from a given report * @param {Object} report @@ -532,26 +553,6 @@ function isPolicyExpenseChatAdmin(report, policies) { return policyRole === CONST.POLICY.ROLE.ADMIN; } -/** - * Returns true if report has a parent - * - * @param {Object} report - * @returns {Boolean} - */ -function isThread(report) { - return Boolean(report && report.parentReportID && report.parentReportActionID); -} - -/** - * Returns true if report is of type chat and has a parent and is therefore a Thread. - * - * @param {Object} report - * @returns {Boolean} - */ -function isChatThread(report) { - return isThread(report) && report.type === CONST.REPORT.TYPE.CHAT; -} - /** * Returns true if reportAction has a child. * From 268ad5726533a0bcb9b7b2ed87ba9e86af7c5bb9 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Fri, 30 Jun 2023 15:31:30 -0700 Subject: [PATCH 170/197] logic fix --- src/libs/ReportUtils.js | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 870aae1e4390..5de8abb15ed6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -377,6 +377,26 @@ function getBankAccountRoute(report) { return isPolicyExpenseChat(report) ? ROUTES.getBankAccountRoute('', report.policyID) : ROUTES.SETTINGS_ADD_BANK_ACCOUNT; } +/** + * Returns true if report has a parent + * + * @param {Object} report + * @returns {Boolean} + */ +function isThread(report) { + return Boolean(report && report.parentReportID && report.parentReportActionID); +} + +/** + * Returns true if report is of type chat and has a parent and is therefore a Thread. + * + * @param {Object} report + * @returns {Boolean} + */ +function isChatThread(report) { + return isThread(report) && report.type === CONST.REPORT.TYPE.CHAT; +} + /** * Only returns true if this is our main 1:1 DM report with Concierge * @@ -384,7 +404,7 @@ function getBankAccountRoute(report) { * @returns {Boolean} */ function isConciergeChatReport(report) { - return lodashGet(report, 'participantAccountIDs', []).length === 1 && Number(report.participantAccountIDs[0]) === CONST.ACCOUNT_ID.CONCIERGE && !isThread(report); + return lodashGet(report, 'participantAccountIDs', []).length === 1 && Number(report.participantAccountIDs[0]) === CONST.ACCOUNT_ID.CONCIERGE && !isChatThread(report); } /** @@ -464,27 +484,6 @@ function isArchivedRoom(report) { return lodashGet(report, ['statusNum']) === CONST.REPORT.STATUS.CLOSED && lodashGet(report, ['stateNum']) === CONST.REPORT.STATE_NUM.SUBMITTED; } -/** - * Returns true if report has a parent - * - * @param {Object} report - * @returns {Boolean} - */ -function isThread(report) { - return Boolean(report && report.parentReportID && report.parentReportActionID); -} - -/** - * Returns true if report is of type chat and has a parent and is therefore a Thread. - * - * @param {Object} report - * @returns {Boolean} - */ -function isChatThread(report) { - return isThread(report) && report.type === CONST.REPORT.TYPE.CHAT; -} - - /** * Get the policy name from a given report * @param {Object} report From 23d16ea1f1d77d18adb136450a6081b3721e50ff Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Fri, 30 Jun 2023 16:27:23 -0700 Subject: [PATCH 171/197] Do not group actions if their delegate accountID does not match --- src/libs/ReportActionsUtils.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index e3ddff17c69a..21969e4d67e5 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -248,6 +248,11 @@ function isConsecutiveActionMadeByPreviousActor(reportActions, actionIndex) { return false; } + // Do not group if the delegate account ID is different + if (previousAction.delegateAccountID !== currentAction.delegateAccountID) { + return false; + } + return currentAction.actorEmail === previousAction.actorEmail; } From c0ece67e0edcb725d8f72cf57cd40d98854c552c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Sat, 1 Jul 2023 00:05:20 +0000 Subject: [PATCH 172/197] Update version to 1.3.35-5 --- 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 dbb3fcb14959..52e983a1ce82 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001033504 - versionName "1.3.35-4" + versionCode 1001033505 + versionName "1.3.35-5" } splits { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index acb4af5102bb..7ae3b26627ee 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 1.3.35.4 + 1.3.35.5 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index c5be730448c5..035f8ebde8d6 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.3.35.4 + 1.3.35.5 diff --git a/package-lock.json b/package-lock.json index ead1e5ff9e1f..67d2cd51360d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.35-4", + "version": "1.3.35-5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.35-4", + "version": "1.3.35-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 800be0daf7cf..5188fbc1e20b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.35-4", + "version": "1.3.35-5", "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 c3089520a60dc7492b50ce3e42d46dc3c52fb396 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 1 Jul 2023 11:59:29 +0800 Subject: [PATCH 173/197] dismiss current screen --- src/libs/actions/Report.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2f6f812bdbe3..bd62897de68e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1316,6 +1316,8 @@ function deleteReport(reportID) { * @param {String} reportID The reportID of the policy report (workspace room) */ function navigateToConciergeChatAndDeleteReport(reportID) { + // Dismiss current screen + Navigation.goBack(); navigateToConciergeChat(); deleteReport(reportID); } From 40e752263aa058f558014820d3beeb6c55fc39cd Mon Sep 17 00:00:00 2001 From: Alex D Date: Sat, 1 Jul 2023 14:59:15 +0300 Subject: [PATCH 174/197] Component refactor: WorkspaceNewRoomPage --- src/pages/workspace/WorkspaceNewRoomPage.js | 166 +++++++++----------- 1 file changed, 78 insertions(+), 88 deletions(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 361610266b42..6b0ccc6b90ee 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState,useCallback} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; @@ -65,43 +65,34 @@ const defaultProps = { allPolicyMembers: {}, }; -class WorkspaceNewRoomPage extends React.Component { - constructor(props) { - super(props); - - this.state = { - visibilityDescription: this.props.translate('newRoomPage.restrictedDescription'), - }; - - this.validate = this.validate.bind(this); - this.submit = this.submit.bind(this); - this.updateVisibilityDescription = this.updateVisibilityDescription.bind(this); - } +function WorkspaceNewRoomPage(props) { + const [visibilityDescription, setVisibilityDescription] = useState(props.translate('newRoomPage.restrictedDescription')); /** * @param {Object} values - form input values passed by the Form component */ - submit(values) { - const policyMembers = _.map(_.keys(this.props.allPolicyMembers[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${values.policyID}`]), (accountID) => Number(accountID)); + const submit = (values) => { + const policyMembers = _.map(_.keys(props.allPolicyMembers[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${values.policyID}`]), (accountID) => Number(accountID)); Report.addPolicyReport(values.policyID, values.roomName, values.visibility, policyMembers); - } + }; /** * @param {String} visibility - form input value passed by the Form component */ - updateVisibilityDescription(visibility) { - const visibilityDescription = this.props.translate(`newRoomPage.${visibility}Description`); - if (visibilityDescription === this.state.visibilityDescription) { + const updateVisibilityDescription = useCallback((visibility) => { + const newVisibilityDescription = props.translate(`newRoomPage.${visibility}Description`); + if (newVisibilityDescription === visibilityDescription) { return; } - this.setState({visibilityDescription}); - } + setVisibilityDescription({newVisibilityDescription}); + // eslint-disable-next-line react-hooks/exhaustive-deps + },[visibilityDescription]); /** * @param {Object} values - form input values passed by the Form component * @returns {Boolean} */ - validate(values) { + const validate = useCallback((values) => { const errors = {}; if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { @@ -113,7 +104,7 @@ class WorkspaceNewRoomPage extends React.Component { } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); - } else if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, values.policyID)) { + } else if (ValidationUtils.isExistingRoomName(values.roomName, props.reports, values.policyID)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); } @@ -123,78 +114,77 @@ class WorkspaceNewRoomPage extends React.Component { } return errors; - } + },[props.reports]); - render() { - if (!Permissions.canUsePolicyRooms(this.props.betas)) { - Log.info('Not showing create Policy Room page since user is not on policy rooms beta'); - Navigation.dismissModal(); - return null; - } + if (!Permissions.canUsePolicyRooms(props.betas)) { + Log.info('Not showing create Policy Room page since user is not on policy rooms beta'); + Navigation.dismissModal(); + return null; + } - // Workspaces are policies with type === 'free' - const workspaceOptions = _.map( - _.filter(this.props.policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE), - (policy) => ({label: policy.name, key: policy.id, value: policy.id}), - ); - - const visibilityOptions = _.map( - _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), - (visibilityOption) => ({ - label: this.props.translate(`newRoomPage.visibilityOptions.${visibilityOption}`), - value: visibilityOption, - description: this.props.translate(`newRoomPage.${visibilityOption}Description`), - }), - ); - - return ( - policy && policy.type === CONST.POLICY.TYPE.FREE), + (policy) => ({label: policy.name, key: policy.id, value: policy.id}), + ); + + const visibilityOptions = _.map( + _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), + (visibilityOption) => ({ + label: props.translate(`newRoomPage.visibilityOptions.${visibilityOption}`), + value: visibilityOption, + description: props.translate(`newRoomPage.${visibilityOption}Description`), + }), + ); + + return ( + + + - - - - - - - - - - - - {this.state.visibilityDescription} - - - ); - } + + + + + + + + + + {visibilityDescription} + + + ); } WorkspaceNewRoomPage.propTypes = propTypes; WorkspaceNewRoomPage.defaultProps = defaultProps; +WorkspaceNewRoomPage.displayName = 'WorkspaceNewRoomPage'; export default compose( withOnyx({ From 874ad668e9307f56126608b77811bbbc5bd2d599 Mon Sep 17 00:00:00 2001 From: Alex D Date: Sat, 1 Jul 2023 16:04:09 +0300 Subject: [PATCH 175/197] Fix: changes after prettier run --- src/pages/workspace/WorkspaceNewRoomPage.js | 70 +++++++++++---------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 6b0ccc6b90ee..b69ef0e2b09d 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -1,4 +1,4 @@ -import React, {useState,useCallback} from 'react'; +import React, {useState, useCallback} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; @@ -79,42 +79,48 @@ function WorkspaceNewRoomPage(props) { /** * @param {String} visibility - form input value passed by the Form component */ - const updateVisibilityDescription = useCallback((visibility) => { - const newVisibilityDescription = props.translate(`newRoomPage.${visibility}Description`); - if (newVisibilityDescription === visibilityDescription) { - return; - } - setVisibilityDescription({newVisibilityDescription}); - // eslint-disable-next-line react-hooks/exhaustive-deps - },[visibilityDescription]); + const updateVisibilityDescription = useCallback( + (visibility) => { + const newVisibilityDescription = props.translate(`newRoomPage.${visibility}Description`); + if (newVisibilityDescription === visibilityDescription) { + return; + } + setVisibilityDescription({newVisibilityDescription}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + [visibilityDescription], + ); /** * @param {Object} values - form input values passed by the Form component * @returns {Boolean} */ - const validate = useCallback((values) => { - const errors = {}; - - if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { - // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.pleaseEnterRoomName'); - } else if (values.roomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.roomName)) { - // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomNameInvalidError'); - } else if (ValidationUtils.isReservedRoomName(values.roomName)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); - } else if (ValidationUtils.isExistingRoomName(values.roomName, props.reports, values.policyID)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); - } - - if (!values.policyID) { - errors.policyID = 'newRoomPage.pleaseSelectWorkspace'; - } - - return errors; - },[props.reports]); + const validate = useCallback( + (values) => { + const errors = {}; + + if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { + // We error if the user doesn't enter a room name or left blank + ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.pleaseEnterRoomName'); + } else if (values.roomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.roomName)) { + // We error if the room name has invalid characters + ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomNameInvalidError'); + } else if (ValidationUtils.isReservedRoomName(values.roomName)) { + // Certain names are reserved for default rooms and should not be used for policy rooms. + ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); + } else if (ValidationUtils.isExistingRoomName(values.roomName, props.reports, values.policyID)) { + // Certain names are reserved for default rooms and should not be used for policy rooms. + ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); + } + + if (!values.policyID) { + errors.policyID = 'newRoomPage.pleaseSelectWorkspace'; + } + + return errors; + }, + [props.reports], + ); if (!Permissions.canUsePolicyRooms(props.betas)) { Log.info('Not showing create Policy Room page since user is not on policy rooms beta'); From 8f5236317d9c2db612ce206ce1131a76e70563b2 Mon Sep 17 00:00:00 2001 From: Alex D Date: Sat, 1 Jul 2023 16:32:50 +0300 Subject: [PATCH 176/197] Fix lint issue after prettier run --- src/pages/workspace/WorkspaceNewRoomPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index b69ef0e2b09d..688bbac02495 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -86,8 +86,8 @@ function WorkspaceNewRoomPage(props) { return; } setVisibilityDescription({newVisibilityDescription}); - // eslint-disable-next-line react-hooks/exhaustive-deps }, + // eslint-disable-next-line react-hooks/exhaustive-deps [visibilityDescription], ); From b6b43c62e374a4e529fdde927889ebccfcca2c25 Mon Sep 17 00:00:00 2001 From: Davi Rodrigues Date: Sat, 1 Jul 2023 20:57:35 -0300 Subject: [PATCH 177/197] fix: change subscript avatar border styles --- src/components/Avatar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index fa7d17d22535..48d24744438f 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -73,7 +73,7 @@ function Avatar(props) { const isWorkspace = props.type === CONST.ICON_TYPE_WORKSPACE; const iconSize = StyleUtils.getAvatarSize(props.size); - const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), StyleUtils.getAvatarBorderRadius(props.size, props.type)]; + const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), { borderRadius: 0 }]; const iconStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; @@ -101,7 +101,7 @@ function Avatar(props) { /> ) : ( - + Date: Sun, 2 Jul 2023 17:45:12 +0800 Subject: [PATCH 178/197] update comment --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index bd62897de68e..60e83f6ebec5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1316,7 +1316,7 @@ function deleteReport(reportID) { * @param {String} reportID The reportID of the policy report (workspace room) */ function navigateToConciergeChatAndDeleteReport(reportID) { - // Dismiss current screen + // Dismiss the current report screen and replace it with Concierge Chat Navigation.goBack(); navigateToConciergeChat(); deleteReport(reportID); From 8db7706a8f1bb04eda424478c08e975d4e9545cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Sun, 2 Jul 2023 13:54:31 +0200 Subject: [PATCH 179/197] Repeat: https://github.com/Expensify/App/commit/f71a1f7c8bda15eab014f88d7575048819971a77 --- src/components/TextInput/BaseTextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index a2893347be52..85d2b1b992c1 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -28,7 +28,7 @@ function BaseTextInput(props) { const [isFocused, setIsFocused] = useState(false); const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry); const [textInputWidth, setTextInputWidth] = useState(0); - const [textInputHeight, setTextInputHeight] = useState(); + const [textInputHeight, setTextInputHeight] = useState(0); const [prefixWidth, setPrefixWidth] = useState(0); const [height, setHeight] = useState(variables.componentSizeLarge); const [width, setWidth] = useState(); From 74dc4d006f6499af8c217b9e591cfc2907acd950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Sun, 2 Jul 2023 13:55:27 +0200 Subject: [PATCH 180/197] Repeat: https://github.com/Expensify/App/commit/a63eee4b177d4f57754baeab47fb58836592fb76#diff-511655d817607a5a2b12918a659da08129dbf4e74dc693f9a93f8ac9df7eb5ef --- src/components/TextInput/BaseTextInput.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 85d2b1b992c1..2522798bcc8f 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -350,6 +350,7 @@ function BaseTextInput(props) { style={styles.textInputIconContainer} onPress={togglePasswordVisibility} onMouseDown={(e) => e.preventDefault()} + accessibilityLabel={props.translate('common.visible')} > Date: Sun, 2 Jul 2023 11:29:37 -0300 Subject: [PATCH 181/197] fix: remove inline styles --- src/components/Avatar.js | 2 +- src/styles/styles.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 48d24744438f..3bec55586444 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -73,7 +73,7 @@ function Avatar(props) { const isWorkspace = props.type === CONST.ICON_TYPE_WORKSPACE; const iconSize = StyleUtils.getAvatarSize(props.size); - const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), { borderRadius: 0 }]; + const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), styles.br0]; const iconStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; diff --git a/src/styles/styles.js b/src/styles/styles.js index 74066af5e20a..2c9928bd1a6f 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -742,6 +742,10 @@ const styles = { borderColor: themeColors.border, }, + br0: { + borderRadius: 0, + }, + borderColorFocus: { borderColor: themeColors.borderFocus, }, From 2d281be897b05e8fe64a91b8294f31919c8b6a3a Mon Sep 17 00:00:00 2001 From: Aleksei Dvoretskii Date: Sun, 2 Jul 2023 13:51:07 -0700 Subject: [PATCH 182/197] Update src/pages/workspace/WorkspaceNewRoomPage.js Co-authored-by: Fedi Rajhi --- src/pages/workspace/WorkspaceNewRoomPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 688bbac02495..3e303e8cb376 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -153,7 +153,7 @@ function WorkspaceNewRoomPage(props) { formID={ONYXKEYS.FORMS.NEW_ROOM_FORM} submitButtonText={props.translate('newRoomPage.createRoom')} scrollContextEnabled - style={[styles.mh5, styles.mt5, styles.flexGrow1]} + style={[styles.mh5, styles.flexGrow1]} validate={validate} onSubmit={submit} enabledWhenOffline From 1aa2452b1c9e47ef12a9947d2a2b9c3f2abb1a38 Mon Sep 17 00:00:00 2001 From: Alex D Date: Mon, 3 Jul 2023 01:08:45 +0300 Subject: [PATCH 183/197] Add useLocalize, useMemo, minor fixes --- src/pages/workspace/WorkspaceNewRoomPage.js | 107 +++++++++----------- 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 3e303e8cb376..c05430300b76 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -1,11 +1,10 @@ -import React, {useState, useCallback} from 'react'; +import React, {useState, useCallback, useMemo} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import * as Report from '../../libs/actions/Report'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; -import compose from '../../libs/compose'; +import useLocalize from '../../hooks/useLocalize'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import Navigation from '../../libs/Navigation/Navigation'; import ScreenWrapper from '../../components/ScreenWrapper'; @@ -55,8 +54,6 @@ const propTypes = { /** A collection of objects for all policies which key policy member objects by accountIDs */ allPolicyMembers: PropTypes.objectOf(PropTypes.objectOf(policyMemberPropType)), - - ...withLocalizePropTypes, }; const defaultProps = { betas: [], @@ -66,7 +63,9 @@ const defaultProps = { }; function WorkspaceNewRoomPage(props) { - const [visibilityDescription, setVisibilityDescription] = useState(props.translate('newRoomPage.restrictedDescription')); + const {translate} = useLocalize(); + const [visibility, setVisibility] = useState(CONST.REPORT.VISIBILITY.RESTRICTED); + const visibilityDescription = useMemo(() => translate(`newRoomPage.${visibility}Description`), [translate, visibility]); /** * @param {Object} values - form input values passed by the Form component @@ -76,21 +75,6 @@ function WorkspaceNewRoomPage(props) { Report.addPolicyReport(values.policyID, values.roomName, values.visibility, policyMembers); }; - /** - * @param {String} visibility - form input value passed by the Form component - */ - const updateVisibilityDescription = useCallback( - (visibility) => { - const newVisibilityDescription = props.translate(`newRoomPage.${visibility}Description`); - if (newVisibilityDescription === visibilityDescription) { - return; - } - setVisibilityDescription({newVisibilityDescription}); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [visibilityDescription], - ); - /** * @param {Object} values - form input values passed by the Form component * @returns {Boolean} @@ -122,36 +106,44 @@ function WorkspaceNewRoomPage(props) { [props.reports], ); + // Workspaces are policies with type === 'free' + const workspaceOptions = useMemo( + () => + _.map( + _.filter(props.policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE), + (policy) => ({label: policy.name, key: policy.id, value: policy.id}), + ), + [props.policies], + ); + + const visibilityOptions = useMemo( + () => + _.map( + _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), + (visibilityOption) => ({ + label: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), + value: visibilityOption, + description: translate(`newRoomPage.${visibilityOption}Description`), + }), + ), + [translate], + ); + if (!Permissions.canUsePolicyRooms(props.betas)) { Log.info('Not showing create Policy Room page since user is not on policy rooms beta'); Navigation.dismissModal(); return null; } - // Workspaces are policies with type === 'free' - const workspaceOptions = _.map( - _.filter(props.policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE), - (policy) => ({label: policy.name, key: policy.id, value: policy.id}), - ); - - const visibilityOptions = _.map( - _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), - (visibilityOption) => ({ - label: props.translate(`newRoomPage.visibilityOptions.${visibilityOption}`), - value: visibilityOption, - description: props.translate(`newRoomPage.${visibilityOption}Description`), - }), - ); - return ( - +
@@ -192,20 +184,17 @@ WorkspaceNewRoomPage.propTypes = propTypes; WorkspaceNewRoomPage.defaultProps = defaultProps; WorkspaceNewRoomPage.displayName = 'WorkspaceNewRoomPage'; -export default compose( - withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - allPolicyMembers: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, - }, - }), - withLocalize, -)(WorkspaceNewRoomPage); +export default withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + allPolicyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, + }, +})(WorkspaceNewRoomPage); From 208cce7029c5979c63293c9664300d981ac7cf63 Mon Sep 17 00:00:00 2001 From: Aleksei Dvoretskii Date: Sun, 2 Jul 2023 17:56:53 -0700 Subject: [PATCH 184/197] Update src/pages/workspace/WorkspaceNewRoomPage.js Co-authored-by: Fedi Rajhi --- src/pages/workspace/WorkspaceNewRoomPage.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index c05430300b76..255ffb5b9e3a 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -129,9 +129,15 @@ function WorkspaceNewRoomPage(props) { [translate], ); - if (!Permissions.canUsePolicyRooms(props.betas)) { + useEffect(() => { + if (Permissions.canUsePolicyRooms(props.betas)) { + return; + } Log.info('Not showing create Policy Room page since user is not on policy rooms beta'); Navigation.dismissModal(); + }, [props.betas]); + + if (!Permissions.canUsePolicyRooms(props.betas)) { return null; } From a1848cbaf395db9a4b8d32c33a2af6a2c2e9edaa Mon Sep 17 00:00:00 2001 From: Alex D Date: Mon, 3 Jul 2023 04:01:13 +0300 Subject: [PATCH 185/197] Fix missing import --- src/pages/workspace/WorkspaceNewRoomPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 255ffb5b9e3a..c59b6687a809 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback, useMemo} from 'react'; +import React, {useState, useCallback, useMemo, useEffect} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; From 08f3d3a9192592cab17d5a02eae830f7d8c1dea1 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 3 Jul 2023 14:15:11 +0000 Subject: [PATCH 186/197] Update version to 1.3.36-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 52e983a1ce82..98f2f103b119 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001033505 - versionName "1.3.35-5" + versionCode 1001033600 + versionName "1.3.36-0" } splits { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7ae3b26627ee..8258d8c03b61 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.35 + 1.3.36 CFBundleSignature ???? CFBundleURLTypes @@ -32,7 +32,7 @@ CFBundleVersion - 1.3.35.5 + 1.3.36.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 035f8ebde8d6..ca3b9da2e100 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.35 + 1.3.36 CFBundleSignature ???? CFBundleVersion - 1.3.35.5 + 1.3.36.0 diff --git a/package-lock.json b/package-lock.json index 67d2cd51360d..4f0ec5f929b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.35-5", + "version": "1.3.36-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.35-5", + "version": "1.3.36-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 5188fbc1e20b..89b19f59fab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.35-5", + "version": "1.3.36-0", "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 8b7a65dcd36fa2b47ffb39c69f2cb294b94a7171 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 3 Jul 2023 14:54:30 +0000 Subject: [PATCH 187/197] Update version to 1.3.36-1 --- 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 98f2f103b119..cdba607eb552 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001033600 - versionName "1.3.36-0" + versionCode 1001033601 + versionName "1.3.36-1" } splits { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 8258d8c03b61..b1b1af68a12d 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 1.3.36.0 + 1.3.36.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index ca3b9da2e100..6a08952a8c6b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.3.36.0 + 1.3.36.1 diff --git a/package-lock.json b/package-lock.json index 4f0ec5f929b4..00792ab49fe4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.36-0", + "version": "1.3.36-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.36-0", + "version": "1.3.36-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 89b19f59fab2..4fd81037181a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.36-0", + "version": "1.3.36-1", "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 96abeac37511f83fbb4aa90895e6f69c20667583 Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Mon, 3 Jul 2023 18:41:32 +0200 Subject: [PATCH 188/197] Revert "fix: add outline to checkbox when focused via button" --- web/index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/index.html b/web/index.html index ec1250b23b08..90011c87e7a9 100644 --- a/web/index.html +++ b/web/index.html @@ -60,10 +60,6 @@ outline: 0; box-shadow: inset 0px 0px 0px 1px #5AB0FF; } - div[role="checkbox"]:focus { - outline: 0; - box-shadow: inset 0px 0px 0px 1px #5AB0FF; - } input:focus-visible, input:focus[data-focusvisible-polyfill], select:focus-visible, select:focus[data-focusvisible-polyfill] { box-shadow: none; From f8306e3e058d3da8846de059e02e682aec99cc5a Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 3 Jul 2023 17:18:59 +0000 Subject: [PATCH 189/197] Update version to 1.3.36-2 --- 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 cdba607eb552..12f144c4daa8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001033601 - versionName "1.3.36-1" + versionCode 1001033602 + versionName "1.3.36-2" } splits { diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b1b1af68a12d..867e9778ddba 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 1.3.36.1 + 1.3.36.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6a08952a8c6b..e9f95bc6a1dc 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.3.36.1 + 1.3.36.2 diff --git a/package-lock.json b/package-lock.json index 00792ab49fe4..317ea0548706 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.36-1", + "version": "1.3.36-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.36-1", + "version": "1.3.36-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 4fd81037181a..0006964c3a80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.36-1", + "version": "1.3.36-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 3b6900cf77d66c8722cd16e6e68e8133ed321249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 3 Jul 2023 19:29:12 +0200 Subject: [PATCH 190/197] Update src/components/TextInput/BaseTextInput.js Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/components/TextInput/BaseTextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 2522798bcc8f..c9c61207f81e 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -252,7 +252,7 @@ function BaseTextInput(props) { style={[props.autoGrowHeight && styles.autoGrowHeightInputContainer(textInputHeight, maxHeight), !isMultiline && styles.componentHeightLarge, ...props.containerStyles]} > Date: Mon, 3 Jul 2023 23:00:19 +0530 Subject: [PATCH 191/197] fix: requested changes --- src/libs/ReportActionsUtils.js | 7 ------- src/libs/actions/Report.js | 14 +++++++------- .../home/report/ContextMenu/ContextMenuActions.js | 2 +- src/pages/home/report/ReportActionItem.js | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 7e2a9164746b..fc6ac92dadb5 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -425,13 +425,6 @@ function getLinkedTransactionID(reportID, reportActionID) { return reportAction.originalMessage.IOUTransactionID; } -/** - * Returns the parentReportAction if the given report is a thread/task. - * - * @param {String} reportID - * @param {String} reportActionID - * @returns {Object} - */ function getReportAction(reportID, reportActionID) { return lodashGet(allReportActions, [reportID, reportActionID], {}); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index f7f020c960d6..aabec697e1aa 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1633,27 +1633,27 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { /** * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * @param {String} reportID - * @param {Object} reportAction + * @param {String} reportActionID * @param {Object} emoji * @param {number} paramSkinTone * @returns {Promise} */ -function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { - const latestReportAction = ReportActionsUtils.getReportAction(reportID, reportAction.reportActionID); +function toggleEmojiReaction(reportID, reportActionID, emoji, paramSkinTone = preferredSkinTone) { + const reportAction = ReportActionsUtils.getReportAction(reportID, reportActionID); - if(_.isEmpty(latestReportAction)) { + if(_.isEmpty(reportAction)) { return; } - const message = latestReportAction.message[0]; + const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction(reportID, latestReportAction, emoji, skinTone); + return removeEmojiReaction(reportID, reportAction, emoji, skinTone); } } - return addEmojiReaction(reportID, latestReportAction, emoji, skinTone); + return addEmojiReaction(reportID, reportAction, emoji, skinTone); } /** diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 0a0388e414c7..b05c29804d19 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -58,7 +58,7 @@ export default [ }; const onEmojiSelected = (emoji) => { - Report.toggleEmojiReaction(reportID, reportAction, emoji); + Report.toggleEmojiReaction(reportID, reportAction.reportActionID, emoji); closeContextMenu(); }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 926774dfc755..c8cf023a6138 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -207,7 +207,7 @@ function ReportActionItem(props) { const toggleReaction = useCallback( (emoji) => { - Report.toggleEmojiReaction(props.report.reportID, props.action, emoji); + Report.toggleEmojiReaction(props.report.reportID, props.action.reportActionID, emoji); }, [props.report, props.action], ); From e9040559e7f35f12ca26d4cacd8cb40ca237e42e Mon Sep 17 00:00:00 2001 From: Davi Rodrigues Date: Mon, 3 Jul 2023 14:39:43 -0300 Subject: [PATCH 192/197] fix(lint): add prettier changes --- src/components/Avatar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 3bec55586444..740e21bf5cce 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -73,7 +73,10 @@ function Avatar(props) { const isWorkspace = props.type === CONST.ICON_TYPE_WORKSPACE; const iconSize = StyleUtils.getAvatarSize(props.size); - const imageStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), styles.br0]; + const imageStyle = + props.imageStyles && props.imageStyles.length > 0 + ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] + : [StyleUtils.getAvatarStyle(props.size), styles.br0]; const iconStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; From e835c839affb69ac36c614c61b2663339c6ac07c Mon Sep 17 00:00:00 2001 From: Davi Rodrigues Date: Mon, 3 Jul 2023 14:41:34 -0300 Subject: [PATCH 193/197] fix: remove greater than zero comparison --- src/components/Avatar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 740e21bf5cce..941b6365f206 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -74,11 +74,11 @@ function Avatar(props) { const iconSize = StyleUtils.getAvatarSize(props.size); const imageStyle = - props.imageStyles && props.imageStyles.length > 0 + props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] : [StyleUtils.getAvatarStyle(props.size), styles.br0]; - const iconStyle = props.imageStyles && props.imageStyles.length > 0 ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; + const iconStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon; From 035d7f3ed90d86d1be69574a1d9ba7ba7f36b26a Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 3 Jul 2023 23:12:47 +0530 Subject: [PATCH 194/197] fix: requested changes in tests --- tests/actions/ReportTest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index e98a5249187f..5ebffff507b8 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -659,7 +659,7 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -668,7 +668,7 @@ describe('actions/Report', () => { // Now we toggle the reaction while the skin tone has changed. // As the emoji doesn't support skin tones, the emoji // should get removed instead of added again. - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, 2); + Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI, 2); return waitForPromisesToResolve(); }) .then(() => { From 0f268552cb558915fd50f595efdb0017e56788e2 Mon Sep 17 00:00:00 2001 From: Davi Rodrigues Date: Mon, 3 Jul 2023 14:43:00 -0300 Subject: [PATCH 195/197] fix: update no border radius style --- src/components/Avatar.js | 2 +- src/styles/styles.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 941b6365f206..b59a8902eb13 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -76,7 +76,7 @@ function Avatar(props) { const imageStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), ...props.imageStyles, StyleUtils.getAvatarBorderRadius(props.size, props.type)] - : [StyleUtils.getAvatarStyle(props.size), styles.br0]; + : [StyleUtils.getAvatarStyle(props.size), styles.noBorderRadius]; const iconStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; diff --git a/src/styles/styles.js b/src/styles/styles.js index 2c9928bd1a6f..f26f39adefba 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -551,6 +551,10 @@ const styles = { marginVertical: 1, }, + noBorderRadius: { + borderRadius: 0, + }, + noRightBorderRadius: { borderTopRightRadius: 0, borderBottomRightRadius: 0, @@ -742,10 +746,6 @@ const styles = { borderColor: themeColors.border, }, - br0: { - borderRadius: 0, - }, - borderColorFocus: { borderColor: themeColors.borderFocus, }, From 3449e3400d01ec32211f758ee126594a7daf9057 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 3 Jul 2023 23:14:38 +0530 Subject: [PATCH 196/197] fix: added description --- src/libs/ReportActionsUtils.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index fc6ac92dadb5..78dea645e18e 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -425,6 +425,12 @@ function getLinkedTransactionID(reportID, reportActionID) { return reportAction.originalMessage.IOUTransactionID; } +/** + * + * @param {String} reportID + * @param {String} reportActionID + * @returns {Object} + */ function getReportAction(reportID, reportActionID) { return lodashGet(allReportActions, [reportID, reportActionID], {}); } From 035c101836d0203117682199c02a162b752304cd Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 3 Jul 2023 23:18:59 +0530 Subject: [PATCH 197/197] fix: lint issues fixed --- src/libs/ReportActionsUtils.js | 2 +- src/libs/actions/Report.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 78dea645e18e..8ce7240f7bfb 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -426,7 +426,7 @@ function getLinkedTransactionID(reportID, reportActionID) { } /** - * + * * @param {String} reportID * @param {String} reportActionID * @returns {Object} diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index aabec697e1aa..55eaee691ad9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1640,11 +1640,11 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { */ function toggleEmojiReaction(reportID, reportActionID, emoji, paramSkinTone = preferredSkinTone) { const reportAction = ReportActionsUtils.getReportAction(reportID, reportActionID); - - if(_.isEmpty(reportAction)) { + + if (_.isEmpty(reportAction)) { return; } - + const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it