From 59e0697189c843c8bbdf9c33b345b4697a79bde5 Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Tue, 21 Nov 2023 16:15:48 -0600 Subject: [PATCH 1/5] Remove managerEmail and ownerEmail from Onyx --- src/libs/migrations/PersonalDetailsByAccountID.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/migrations/PersonalDetailsByAccountID.js b/src/libs/migrations/PersonalDetailsByAccountID.js index 38c992a6a375..c08ec6fb2c43 100644 --- a/src/libs/migrations/PersonalDetailsByAccountID.js +++ b/src/libs/migrations/PersonalDetailsByAccountID.js @@ -257,6 +257,18 @@ export default function () { delete newReport.participants; } + if (lodashHas(newReport, ['ownerEmail'])) { + reportWasModified = true; + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing ownerEmail from report ${newReport.reportID}`); + delete newReport.ownerEmail; + } + + if (lodashHas(newReport, ['managerEmail'])) { + reportWasModified = true; + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing managerEmail from report ${newReport.reportID}`); + delete newReport.managerEmail; + } + if (reportWasModified) { onyxData[onyxKey] = newReport; } From dbb4cba45d2fc405a9d6aec39e3326c44666230a Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Tue, 21 Nov 2023 16:21:17 -0600 Subject: [PATCH 2/5] update migration tests for new data being removed --- tests/unit/MigrationTest.js | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index f212dd75447d..ebffc71e4e0e 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -452,6 +452,56 @@ describe('Migrations', () => { }, }); })); + + it('Should remove any instances of ownerEmail found in a report', () => + Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.REPORT}1`]: { + reportID: 1, + ownerEmail: 'fake@test.com', + ownerAccountID: 5, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing ownerEmail from report 1'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + const expectedReport = { + reportID: 1, + ownerAccountID: 5, + }; + expect(allReports[`${ONYXKEYS.COLLECTION.REPORT}1`]).toMatchObject(expectedReport); + }, + }); + })); + + it('Should remove any instances of managerEmail found in a report', () => + Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.REPORT}1`]: { + reportID: 1, + managerEmail: 'fake@test.com', + managerID: 5, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing managerEmail from report 1'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + const expectedReport = { + reportID: 1, + managerID: 5, + }; + expect(allReports[`${ONYXKEYS.COLLECTION.REPORT}1`]).toMatchObject(expectedReport); + }, + }); + })); }); describe('CheckForPreviousReportActionID', () => { From 0c6fd1266cdf2df511023acd2dc70d78748c63bd Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 22 Nov 2023 11:42:37 +0700 Subject: [PATCH 3/5] refactor HeaderView component --- src/pages/home/HeaderView.js | 93 +++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 7b315ff6c819..5b57419c8530 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -19,14 +19,12 @@ import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; import Text from '@components/Text'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import Tooltip from '@components/Tooltip'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import compose from '@libs/compose'; +import useLocalize from '@hooks/useLocalize'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import {getGroupChatName} from '@libs/GroupChatUtils'; import * as HeaderUtils from '@libs/HeaderUtils'; import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import reportPropTypes from '@pages/reportPropTypes'; @@ -60,8 +58,14 @@ const propTypes = { accountID: PropTypes.number, }), - ...windowDimensionsPropTypes, - ...withLocalizePropTypes, + /** The current policy of the report */ + policy: PropTypes.shape({ + /** The policy name */ + name: PropTypes.string, + + /** The URL for the policy avatar */ + avatar: PropTypes.string, + }), }; const defaultProps = { @@ -72,9 +76,12 @@ const defaultProps = { session: { accountID: 0, }, + policy: {}, }; function HeaderView(props) { + const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); + const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const participants = lodashGet(props.report, 'participantAccountIDs', []); @@ -97,10 +104,9 @@ function HeaderView(props) { const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(props.report.reportID); const isEmptyChat = !props.report.lastMessageText && !props.report.lastMessageTranslationKey && !lastVisibleMessage.lastMessageText && !lastVisibleMessage.lastMessageTranslationKey; const isUserCreatedPolicyRoom = ReportUtils.isUserCreatedPolicyRoom(props.report); - const policy = useMemo(() => props.policies[`${ONYXKEYS.COLLECTION.POLICY}${props.report.policyID}`], [props.policies, props.report.policyID]); - const canLeaveRoom = ReportUtils.canLeaveRoom(props.report, !_.isEmpty(policy)); + const isPolicyMember = useMemo(() => !_.isEmpty(props.policy), [props.policy]); + const canLeaveRoom = ReportUtils.canLeaveRoom(props.report, isPolicyMember); const isArchivedRoom = ReportUtils.isArchivedRoom(props.report); - const isPolicyMember = useMemo(() => PolicyUtils.isPolicyMember(props.report.policyID, props.policies), [props.report.policyID, props.policies]); // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact // these users via alternative means. It is possible to request a call with Concierge so we leave the option for them. @@ -112,7 +118,7 @@ function HeaderView(props) { if (ReportUtils.isCompletedTaskReport(props.report) && canModifyTask) { threeDotMenuItems.push({ icon: Expensicons.Checkmark, - text: props.translate('task.markAsIncomplete'), + text: translate('task.markAsIncomplete'), onSelected: Session.checkIfActionIsAllowed(() => Task.reopenTask(props.report)), }); } @@ -121,7 +127,7 @@ function HeaderView(props) { if (props.report.stateNum !== CONST.REPORT.STATE_NUM.SUBMITTED && props.report.statusNum !== CONST.REPORT.STATUS.CLOSED && canModifyTask) { threeDotMenuItems.push({ icon: Expensicons.Trashcan, - text: props.translate('common.cancel'), + text: translate('common.cancel'), onSelected: Session.checkIfActionIsAllowed(() => Task.cancelTask(props.report.reportID, props.report.reportName, props.report.stateNum, props.report.statusNum)), }); } @@ -131,7 +137,7 @@ function HeaderView(props) { if (props.report.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, - text: props.translate('common.join'), + text: translate('common.join'), onSelected: Session.checkIfActionIsAllowed(() => Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false), ), @@ -140,7 +146,7 @@ function HeaderView(props) { const isWorkspaceMemberLeavingWorkspaceRoom = lodashGet(props.report, 'visibility', '') === CONST.REPORT.VISIBILITY.RESTRICTED && isPolicyMember; threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, - text: props.translate('common.leave'), + text: translate('common.leave'), onSelected: Session.checkIfActionIsAllowed(() => Report.leaveRoom(props.report.reportID, isWorkspaceMemberLeavingWorkspaceRoom)), }); } @@ -151,7 +157,7 @@ function HeaderView(props) { if (isConcierge && props.guideCalendarLink) { threeDotMenuItems.push({ icon: Expensicons.Phone, - text: props.translate('videoChatButtonAndMenu.tooltip'), + text: translate('videoChatButtonAndMenu.tooltip'), onSelected: Session.checkIfActionIsAllowed(() => { Link.openExternalLink(props.guideCalendarLink); }), @@ -159,14 +165,14 @@ function HeaderView(props) { } else if (!isAutomatedExpensifyAccount && !isTaskReport && !isArchivedRoom) { threeDotMenuItems.push({ icon: ZoomIcon, - text: props.translate('videoChatButtonAndMenu.zoom'), + text: translate('videoChatButtonAndMenu.zoom'), onSelected: Session.checkIfActionIsAllowed(() => { Link.openExternalLink(CONST.NEW_ZOOM_MEETING_URL); }), }); threeDotMenuItems.push({ icon: GoogleMeetIcon, - text: props.translate('videoChatButtonAndMenu.googleMeet'), + text: translate('videoChatButtonAndMenu.googleMeet'), onSelected: Session.checkIfActionIsAllowed(() => { Link.openExternalLink(CONST.NEW_GOOGLE_MEET_MEETING_URL); }), @@ -179,7 +185,7 @@ function HeaderView(props) { const defaultSubscriptSize = ReportUtils.isExpenseRequest(props.report) ? CONST.AVATAR_SIZE.SMALL_NORMAL : CONST.AVATAR_SIZE.DEFAULT; const icons = ReportUtils.getIcons(reportHeaderData, props.personalDetails); const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; - const shouldShowBorderBottom = !isTaskReport || !props.isSmallScreenWidth; + const shouldShowBorderBottom = !isTaskReport || !isSmallScreenWidth; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); const isLoading = !props.report || !title; @@ -189,21 +195,21 @@ function HeaderView(props) { style={[styles.appContentHeader, shouldShowBorderBottom && styles.borderBottom]} dataSet={{dragArea: true}} > - + {isLoading ? ( ) : ( <> - {props.isSmallScreenWidth && ( + {isSmallScreenWidth && ( @@ -267,10 +273,10 @@ function HeaderView(props) { )} - {isTaskReport && !props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(props.report) && } + {isTaskReport && !isSmallScreenWidth && ReportUtils.isOpenTaskReport(props.report) && } {shouldShowThreeDotsButton && ( @@ -288,25 +294,22 @@ HeaderView.displayName = 'HeaderView'; HeaderView.defaultProps = defaultProps; export default memo( - compose( - withWindowDimensions, - withLocalize, - withOnyx({ - guideCalendarLink: { - key: ONYXKEYS.ACCOUNT, - selector: (account) => (account && account.guideCalendarLink) || null, - initialValue: null, - }, - parentReport: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || report.reportID}`, - selector: reportWithoutHasDraftSelector, - }, - session: { - key: ONYXKEYS.SESSION, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - }), - )(HeaderView), + withOnyx({ + guideCalendarLink: { + key: ONYXKEYS.ACCOUNT, + selector: (account) => (account && account.guideCalendarLink) || null, + initialValue: null, + }, + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || report.reportID}`, + selector: reportWithoutHasDraftSelector, + }, + session: { + key: ONYXKEYS.SESSION, + }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + selector: (policy) => _.pick(policy, ['name', 'avatar', 'pendingAction']), + }, + })(HeaderView), ); From e8f083698419615a7936112989060382935d8127 Mon Sep 17 00:00:00 2001 From: someone-here Date: Mon, 27 Nov 2023 16:34:55 +0530 Subject: [PATCH 4/5] Bank account website case validation --- src/languages/en.ts | 2 +- src/libs/ValidationUtils.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 96e2e99824cd..cd440373328b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1189,7 +1189,7 @@ export default { noBankAccountAvailable: 'Sorry, no bank account is available', noBankAccountSelected: 'Please choose an account', taxID: 'Please enter a valid tax ID number', - website: 'Please enter a valid website', + website: 'Please enter a valid website. El sitio web debe estar en minúsculas.', zipCode: `Incorrect zip code format. Acceptable format: ${CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}`, phoneNumber: 'Please enter a valid phone number', companyName: 'Please enter a valid legal business name', diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 7c49006c10a5..9246f760f7bd 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -200,7 +200,8 @@ function getAgeRequirementError(date: string, minimumAge: number, maximumAge: nu * http/https/ftp URL scheme required. */ function isValidWebsite(url: string): boolean { - return new RegExp(`^${URL_REGEX_WITH_REQUIRED_PROTOCOL}$`, 'i').test(url); + const isLowerCase = url === url.toLowerCase(); + return new RegExp(`^${URL_REGEX_WITH_REQUIRED_PROTOCOL}$`, 'i').test(url) && isLowerCase; } function validateIdentity(identity: Record): Record { From 32b1f25af3cd3774d90d3eff622cc936911de4b5 Mon Sep 17 00:00:00 2001 From: someone-here Date: Mon, 27 Nov 2023 16:53:08 +0530 Subject: [PATCH 5/5] Correct translations --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index cd440373328b..9451d3840e48 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1189,7 +1189,7 @@ export default { noBankAccountAvailable: 'Sorry, no bank account is available', noBankAccountSelected: 'Please choose an account', taxID: 'Please enter a valid tax ID number', - website: 'Please enter a valid website. El sitio web debe estar en minúsculas.', + website: 'Please enter a valid website. The website should be in lowercase.', zipCode: `Incorrect zip code format. Acceptable format: ${CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}`, phoneNumber: 'Please enter a valid phone number', companyName: 'Please enter a valid legal business name', diff --git a/src/languages/es.ts b/src/languages/es.ts index 3f8f68977549..01e2c20654a9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1206,7 +1206,7 @@ export default { noBankAccountAvailable: 'Lo sentimos, no hay ninguna cuenta bancaria disponible', noBankAccountSelected: 'Por favor, elige una cuenta bancaria', taxID: 'Por favor, introduce un número de identificación fiscal válido', - website: 'Por favor, introduce un sitio web válido', + website: 'Por favor, introduce un sitio web válido. El sitio web debe estar en minúsculas.', zipCode: `Formato de código postal incorrecto. Formato aceptable: ${CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}`, phoneNumber: 'Por favor, introduce un teléfono válido', companyName: 'Por favor, introduce un nombre comercial legal válido',