Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/tienifr/App into fix/30687
Browse files Browse the repository at this point in the history
  • Loading branch information
tienifr committed Nov 29, 2023
2 parents ad6fb76 + 4fa869c commit fb5e067
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,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. 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',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,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',
Expand Down
3 changes: 2 additions & 1 deletion src/libs/ValidationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>): Record<string, boolean> {
Expand Down
12 changes: 12 additions & 0 deletions src/libs/migrations/PersonalDetailsByAccountID.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
93 changes: 48 additions & 45 deletions src/pages/home/HeaderView.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 = {
Expand All @@ -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', []);
Expand All @@ -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.
Expand All @@ -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)),
});
}
Expand All @@ -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)),
});
}
Expand All @@ -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),
),
Expand All @@ -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)),
});
}
Expand All @@ -151,22 +157,22 @@ 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);
}),
});
} 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);
}),
Expand All @@ -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;
Expand All @@ -189,21 +195,21 @@ function HeaderView(props) {
style={[styles.appContentHeader, shouldShowBorderBottom && styles.borderBottom]}
dataSet={{dragArea: true}}
>
<View style={[styles.appContentHeaderTitle, !props.isSmallScreenWidth && !isLoading && styles.pl5]}>
<View style={[styles.appContentHeaderTitle, !isSmallScreenWidth && !isLoading && styles.pl5]}>
{isLoading ? (
<ReportHeaderSkeletonView />
) : (
<>
{props.isSmallScreenWidth && (
{isSmallScreenWidth && (
<PressableWithoutFeedback
onPress={props.onNavigationMenuButtonClicked}
style={[styles.LHNToggle]}
accessibilityHint={props.translate('accessibilityHints.navigateToChatsList')}
accessibilityLabel={props.translate('common.back')}
accessibilityHint={translate('accessibilityHints.navigateToChatsList')}
accessibilityLabel={translate('common.back')}
role={CONST.ACCESSIBILITY_ROLE.BUTTON}
>
<Tooltip
text={props.translate('common.back')}
text={translate('common.back')}
shiftVertical={4}
>
<View>
Expand Down Expand Up @@ -267,10 +273,10 @@ function HeaderView(props) {
)}
</PressableWithoutFeedback>
<View style={[styles.reportOptions, styles.flexRow, styles.alignItemsCenter]}>
{isTaskReport && !props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(props.report) && <TaskHeaderActionButton report={props.report} />}
{isTaskReport && !isSmallScreenWidth && ReportUtils.isOpenTaskReport(props.report) && <TaskHeaderActionButton report={props.report} />}
{shouldShowThreeDotsButton && (
<ThreeDotsMenu
anchorPosition={styles.threeDotsPopoverOffset(props.windowWidth)}
anchorPosition={styles.threeDotsPopoverOffset(windowWidth)}
menuItems={threeDotMenuItems}
shouldSetModalVisibility={false}
/>
Expand All @@ -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),
);
50 changes: 50 additions & 0 deletions tests/unit/MigrationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: '[email protected]',
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: '[email protected]',
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', () => {
Expand Down

0 comments on commit fb5e067

Please sign in to comment.