Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show No access Modal when Copilot tries to add new contact method on behalf of owner. #50643

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/components/DelegateNoAccessModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ export default function DelegateNoAccessModal({isNoDelegateAccessMenuVisible = f
const {translate} = useLocalize();
const noDelegateAccessPromptStart = translate('delegate.notAllowedMessageStart', {accountOwnerEmail: delegatorEmail});
const noDelegateAccessHyperLinked = translate('delegate.notAllowedMessageHyperLinked');
const noDelegateAccessPromptEnd = translate('delegate.notAllowedMessageEnd');

const delegateNoAccessPrompt = (
<Text>
{noDelegateAccessPromptStart}
<TextLink href={CONST.DELEGATE_ROLE_HELPDOT_ARTICLE_LINK}>{noDelegateAccessHyperLinked}</TextLink>
{noDelegateAccessPromptEnd}
<TextLink href={CONST.DELEGATE_ROLE_HELPDOT_ARTICLE_LINK}>{noDelegateAccessHyperLinked}</TextLink>.
</Text>
);

Expand Down
3 changes: 1 addition & 2 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4933,8 +4933,7 @@ const translations = {
enterMagicCode: ({contactMethod}: EnterMagicCodeParams) => `Please enter the magic code sent to ${contactMethod} to add a copilot.`,
notAllowed: 'Not so fast...',
notAllowedMessageStart: ({accountOwnerEmail}: AccountOwnerParams) => `You don't have permission to take this action for ${accountOwnerEmail} as a`,
notAllowedMessageHyperLinked: ' limited access',
notAllowedMessageEnd: ' copilot',
notAllowedMessageHyperLinked: ' copilot',
},
debug: {
debug: 'Debug',
Expand Down
3 changes: 1 addition & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5445,8 +5445,7 @@ const translations = {
enterMagicCode: ({contactMethod}: EnterMagicCodeParams) => `Por favor, introduce el código mágico enviado a ${contactMethod} para agregar un copiloto.`,
notAllowed: 'No tan rápido...',
notAllowedMessageStart: ({accountOwnerEmail}: AccountOwnerParams) => `No tienes permiso para realizar esta acción para ${accountOwnerEmail}`,
notAllowedMessageHyperLinked: ' copiloto con acceso',
notAllowedMessageEnd: ' limitado',
notAllowedMessageHyperLinked: ' copiloto',
},
debug: {
debug: 'Depuración',
Expand Down
21 changes: 18 additions & 3 deletions src/pages/settings/Profile/Contacts/ContactMethodsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type {StackScreenProps} from '@react-navigation/stack';
import {Str} from 'expensify-common';
import React, {useCallback} from 'react';
import React, {useCallback, useState} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import CopyTextToClipboard from '@components/CopyTextToClipboard';
import DelegateNoAccessModal from '@components/DelegateNoAccessModal';
import FixedFooter from '@components/FixedFooter';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItem from '@components/MenuItem';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useDelegateUserDetails from '@hooks/useDelegateUserDetails';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -40,6 +42,10 @@ function ContactMethodsPage({loginList, session, route}: ContactMethodsPageProps
const {formatPhoneNumber, translate} = useLocalize();
const loginNames = Object.keys(loginList ?? {});
const navigateBackTo = route?.params?.backTo;
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const isActingAsDelegate = !!account?.delegatedAccess?.delegate;
const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);
const {delegatorEmail} = useDelegateUserDetails();

// Sort the login names by placing the one corresponding to the default contact method as the first item before displaying the contact methods.
// The default contact method is determined by checking against the session email (the current login).
Expand Down Expand Up @@ -97,8 +103,12 @@ function ContactMethodsPage({loginList, session, route}: ContactMethodsPageProps
});

const onNewContactMethodButtonPress = useCallback(() => {
if (isActingAsDelegate) {
setIsNoDelegateAccessMenuVisible(true);
return;
}
Navigation.navigate(ROUTES.SETTINGS_NEW_CONTACT_METHOD.getRoute(navigateBackTo));
}, [navigateBackTo]);
}, [navigateBackTo, isActingAsDelegate]);

return (
<ScreenWrapper
Expand Down Expand Up @@ -131,6 +141,11 @@ function ContactMethodsPage({loginList, session, route}: ContactMethodsPageProps
/>
</FixedFooter>
</ScrollView>
<DelegateNoAccessModal
isNoDelegateAccessMenuVisible={isNoDelegateAccessMenuVisible}
onClose={() => setIsNoDelegateAccessMenuVisible(false)}
delegatorEmail={delegatorEmail ?? ''}
/>
</ScreenWrapper>
);
}
Expand Down
103 changes: 54 additions & 49 deletions src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as LoginUtils from '@libs/LoginUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import * as UserUtils from '@libs/UserUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -40,6 +41,8 @@ function NewContactMethodPage({route}: NewContactMethodPageProps) {
const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST);
const loginData = loginList?.[pendingContactAction?.contactMethod ?? contactMethod];
const validateLoginError = ErrorUtils.getEarliestErrorField(loginData, 'validateLogin');
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const isActingAsDelegate = !!account?.delegatedAccess?.delegate;

const navigateBackTo = route?.params?.backTo ?? ROUTES.SETTINGS_PROFILE;

Expand Down Expand Up @@ -100,56 +103,58 @@ function NewContactMethodPage({route}: NewContactMethodPageProps) {
}, [navigateBackTo]);

return (
<ScreenWrapper
onEntryTransitionEnd={() => loginInputRef.current?.focus()}
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={NewContactMethodPage.displayName}
>
<HeaderWithBackButton
title={translate('contacts.newContactMethod')}
onBackButtonPress={onBackButtonPress}
/>
<FormProvider
formID={ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM}
validate={validate}
onSubmit={handleValidateMagicCode}
submitButtonText={translate('common.add')}
style={[styles.flexGrow1, styles.mh5]}
<AccessOrNotFoundWrapper shouldBeBlocked={isActingAsDelegate}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has introduced a bug in #51189 , we should have just early returned instead of using AccessOrNotFoundWrapper.

<ScreenWrapper
onEntryTransitionEnd={() => loginInputRef.current?.focus()}
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={NewContactMethodPage.displayName}
>
<Text style={styles.mb5}>{translate('common.pleaseEnterEmailOrPhoneNumber')}</Text>
<View style={styles.mb6}>
<InputWrapper
InputComponent={TextInput}
label={`${translate('common.email')}/${translate('common.phoneNumber')}`}
aria-label={`${translate('common.email')}/${translate('common.phoneNumber')}`}
role={CONST.ROLE.PRESENTATION}
inputMode={CONST.INPUT_MODE.EMAIL}
ref={loginInputRef}
inputID={INPUT_IDS.PHONE_OR_EMAIL}
autoCapitalize="none"
enterKeyHint="done"
maxLength={CONST.LOGIN_CHARACTER_LIMIT}
/>
</View>
{hasFailedToSendVerificationCode && (
<DotIndicatorMessage
messages={ErrorUtils.getLatestErrorField(pendingContactAction, 'actionVerified')}
type="error"
/>
)}
</FormProvider>
<ValidateCodeActionModal
validatePendingAction={pendingContactAction?.pendingFields?.actionVerified}
validateError={validateLoginError}
handleSubmitForm={addNewContactMethod}
clearError={() => User.clearContactMethodErrors(contactMethod, 'validateLogin')}
onClose={() => setIsValidateCodeActionModalVisible(false)}
isVisible={isValidateCodeActionModalVisible}
title={contactMethod}
description={translate('contacts.enterMagicCode', {contactMethod})}
/>
</ScreenWrapper>
<HeaderWithBackButton
title={translate('contacts.newContactMethod')}
onBackButtonPress={onBackButtonPress}
/>
<FormProvider
formID={ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM}
validate={validate}
onSubmit={handleValidateMagicCode}
submitButtonText={translate('common.add')}
style={[styles.flexGrow1, styles.mh5]}
>
<Text style={styles.mb5}>{translate('common.pleaseEnterEmailOrPhoneNumber')}</Text>
<View style={styles.mb6}>
<InputWrapper
InputComponent={TextInput}
label={`${translate('common.email')}/${translate('common.phoneNumber')}`}
aria-label={`${translate('common.email')}/${translate('common.phoneNumber')}`}
role={CONST.ROLE.PRESENTATION}
inputMode={CONST.INPUT_MODE.EMAIL}
ref={loginInputRef}
inputID={INPUT_IDS.PHONE_OR_EMAIL}
autoCapitalize="none"
enterKeyHint="done"
maxLength={CONST.LOGIN_CHARACTER_LIMIT}
/>
</View>
{hasFailedToSendVerificationCode && (
<DotIndicatorMessage
messages={ErrorUtils.getLatestErrorField(pendingContactAction, 'actionVerified')}
type="error"
/>
)}
</FormProvider>
<ValidateCodeActionModal
validatePendingAction={pendingContactAction?.pendingFields?.actionVerified}
validateError={validateLoginError}
handleSubmitForm={addNewContactMethod}
clearError={() => User.clearContactMethodErrors(contactMethod, 'validateLogin')}
onClose={() => setIsValidateCodeActionModalVisible(false)}
isVisible={isValidateCodeActionModalVisible}
title={contactMethod}
description={translate('contacts.enterMagicCode', {contactMethod})}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
);
}

Expand Down
Loading