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

Fix popover position for copilot popup #51204

Merged
merged 11 commits into from
Dec 4, 2024
105 changes: 55 additions & 50 deletions src/pages/settings/Security/SecuritySettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import MenuItem from '@components/MenuItem';
import type {MenuItemProps} from '@components/MenuItem';
import MenuItemList from '@components/MenuItemList';
import {usePersonalDetails} from '@components/OnyxProvider';
import Popover from '@components/Popover';
import PopoverMenu from '@components/PopoverMenu';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Section from '@components/Section';
Expand All @@ -31,7 +32,7 @@ import getClickedTargetLocation from '@libs/getClickedTargetLocation';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';
import Navigation from '@libs/Navigation/Navigation';
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
import variables from '@styles/variables';
import type {AnchorPosition} from '@styles/index';
import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
Expand All @@ -54,43 +55,41 @@ function SecuritySettingsPage() {
const [shouldShowDelegatePopoverMenu, setShouldShowDelegatePopoverMenu] = useState(false);
const [shouldShowRemoveDelegateModal, setShouldShowRemoveDelegateModal] = useState(false);
const [selectedDelegate, setSelectedDelegate] = useState<Delegate | undefined>();
const [selectedEmail, setSelectedEmail] = useState<string | undefined>();

const errorFields = account?.delegatedAccess?.errorFields ?? {};

const [anchorPosition, setAnchorPosition] = useState({
anchorPositionHorizontal: 0,
anchorPositionVertical: 0,
anchorPositionTop: 0,
anchorPositionRight: 0,
const [anchorPosition, setAnchorPosition] = useState<AnchorPosition>({
horizontal: 0,
vertical: 0,
});

const isActingAsDelegate = !!account?.delegatedAccess?.delegate || false;

const delegates = account?.delegatedAccess?.delegates ?? [];
const delegators = account?.delegatedAccess?.delegators ?? [];

const hasDelegates = delegates.length > 0;
const hasDelegators = delegators.length > 0;

const setMenuPosition = useCallback(() => {
if (!delegateButtonRef.current) {
return;
}

const position = getClickedTargetLocation(delegateButtonRef.current);

setAnchorPosition({
anchorPositionTop: position.top + position.height - variables.bankAccountActionPopoverTopSpacing,
// We want the position to be 23px to the right of the left border
anchorPositionRight: windowWidth - position.right + variables.bankAccountActionPopoverRightSpacing,
anchorPositionHorizontal: position.x + variables.addBankAccountLeftSpacing,
anchorPositionVertical: position.y,
horizontal: position.right - position.left,
vertical: position.y + position.height,
});
}, [windowWidth]);
const isActingAsDelegate = !!account?.delegatedAccess?.delegate || false;

const delegates = account?.delegatedAccess?.delegates ?? [];
const delegators = account?.delegatedAccess?.delegators ?? [];

const hasDelegates = delegates.length > 0;
const hasDelegators = delegators.length > 0;
}, [delegateButtonRef]);

const showPopoverMenu = (nativeEvent: GestureResponderEvent | KeyboardEvent, delegate: Delegate) => {
delegateButtonRef.current = nativeEvent?.currentTarget as HTMLDivElement;
setMenuPosition();
setShouldShowDelegatePopoverMenu(true);
setSelectedDelegate(delegate);
setSelectedEmail(delegate.email);
};

useLayoutEffect(() => {
Expand Down Expand Up @@ -174,10 +173,11 @@ function SecuritySettingsPage() {
onPendingActionDismiss: () => clearDelegateErrorsByField(email, 'addDelegate'),
error,
onPress,
success: selectedEmail === email,
};
}),
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
[delegates, translate, styles, personalDetails, errorFields],
[delegates, translate, styles, personalDetails, errorFields, windowWidth, selectedEmail],
);

const delegatorMenuItems: MenuItemProps[] = useMemo(
Expand All @@ -202,6 +202,29 @@ function SecuritySettingsPage() {
[delegators, styles, translate, personalDetails],
);

const delegatePopoverMenuItems: PopoverMenuItem[] = [
{
text: translate('delegate.changeAccessLevel'),
icon: Expensicons.Pencil,
onPress: () => {
Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? ''));
setShouldShowDelegatePopoverMenu(false);
setSelectedDelegate(undefined);
setSelectedEmail(undefined);
},
},
{
text: translate('delegate.removeCopilot'),
icon: Expensicons.Trashcan,
onPress: () =>
Modal.close(() => {
setShouldShowDelegatePopoverMenu(false);
setShouldShowRemoveDelegateModal(true);
setSelectedEmail(undefined);
}),
},
];

return (
<ScreenWrapper
testID={SecuritySettingsPage.displayName}
Expand Down Expand Up @@ -276,41 +299,23 @@ function SecuritySettingsPage() {
)}
</Section>
</View>
<Popover
<PopoverMenu
isVisible={shouldShowDelegatePopoverMenu}
anchorRef={delegateButtonRef as RefObject<View>}
anchorPosition={{
top: anchorPosition.anchorPositionTop,
right: anchorPosition.anchorPositionRight,
horizontal: anchorPosition.horizontal,
vertical: anchorPosition.vertical,
}}
anchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
}}
menuItems={delegatePopoverMenuItems}
onClose={() => {
setShouldShowDelegatePopoverMenu(false);
setSelectedEmail(undefined);
}}
>
<View style={[styles.mv5, !shouldUseNarrowLayout ? styles.sidebarPopover : {}]}>
<MenuItem
title={translate('delegate.changeAccessLevel')}
icon={Expensicons.Pencil}
onPress={() => {
Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? ''));
setShouldShowDelegatePopoverMenu(false);
setSelectedDelegate(undefined);
}}
wrapperStyle={[styles.pv3, styles.ph5, !shouldUseNarrowLayout ? styles.sidebarPopover : {}]}
/>
<MenuItem
title={translate('delegate.removeCopilot')}
icon={Expensicons.Trashcan}
onPress={() =>
Modal.close(() => {
setShouldShowDelegatePopoverMenu(false);
setShouldShowRemoveDelegateModal(true);
})
}
wrapperStyle={[styles.pv3, styles.ph5, !shouldUseNarrowLayout ? styles.sidebarPopover : {}]}
/>
</View>
</Popover>
/>
<ConfirmModal
isVisible={shouldShowRemoveDelegateModal}
title={translate('delegate.removeCopilot')}
Expand Down
Loading