1 import type { ComponentProps } from 'react';
2 import { useEffect, useRef, useState } from 'react';
4 import { c } from 'ttag';
6 import { useUserKeys } from '@proton/account/userKeys/hooks';
7 import DropdownActions from '@proton/components/components/dropdown/DropdownActions';
8 import type OrderableTableBody from '@proton/components/components/orderableTable/OrderableTableBody';
9 import OrderableTableRow from '@proton/components/components/orderableTable/OrderableTableRow';
10 import TableRow from '@proton/components/components/table/TableRow';
11 import useActiveBreakpoint from '@proton/components/hooks/useActiveBreakpoint';
12 import { useContact } from '@proton/mail/contacts/contactHooks';
13 import type { ContactFormatted } from '@proton/shared/lib/interfaces/contacts';
14 import isTruthy from '@proton/utils/isTruthy';
16 import useVCardContact from '../../hooks/useVCardContact';
17 import EmailsTableCell from './EmailsTableCell';
18 import NameTableCell from './NameTableCell';
20 interface Props extends Omit<ComponentProps<typeof OrderableTableBody>, 'colSpan'> {
23 Contact: ContactFormatted;
24 highlightedID: string;
25 isChecked: { [ID: string]: boolean };
26 beDeleted: { [ID: string]: boolean };
27 onClickCheckbox: (ID: string) => void;
28 onClickDetails: (ID: string) => void;
29 onToggleDelete: (ID: string) => void;
32 const MergeTableBodyRow = ({
43 const rowRef = useRef<any>(null);
44 const [isIntersecting, setIsIntersecting] = useState(false);
45 const { Name, emails } = Contact;
46 const [userKeysList] = useUserKeys();
47 // Allow to control when we fetch contacts and avoid fetching them when the row is not visible
48 const [contact] = useContact(isIntersecting ? ID : undefined);
49 const { vCardContact } = useVCardContact({ contact, userKeysList });
51 const { viewportWidth } = useActiveBreakpoint();
53 const deleted = beDeleted[ID];
56 const observer = new IntersectionObserver(([entry]) => {
57 setIsIntersecting(entry.isIntersecting);
60 observer.observe(rowRef.current?.node);
63 observer.disconnect();
65 }, [setIsIntersecting]);
69 text: c('Action').t`View`,
75 text: deleted ? c('Action').t`Unmark for deletion` : c('Action').t`Mark for deletion`,
80 ].filter(Boolean) as { text: string; onClick: () => void }[];
82 const givenName = vCardContact?.n?.value.givenNames?.join(' ')?.trim() ?? '-';
83 const familyName = vCardContact?.n?.value.familyNames?.join(' ')?.trim() ?? '-';
90 highlightedID={highlightedID}
91 checked={isChecked[ID]}
94 onToggle={onClickCheckbox}
96 !(givenName === '-' && viewportWidth['<=medium']) && (
97 <span className="max-w-full inline-block text-ellipsis">{givenName}</span>
99 !(familyName === '-' && viewportWidth['<=medium']) && (
100 <span className="max-w-full inline-block text-ellipsis">{familyName}</span>
105 highlightedID={highlightedID}
109 <DropdownActions key="options" size="small" list={options} data-testid="merge-model:action-button" />,
110 ].filter((cell) => (viewportWidth['<=medium'] ? isTruthy(cell) : true));
113 <TableRow key={`${ID}`} cells={[null, ...cells]} />
115 <OrderableTableRow key={`${ID}`} index={index} cells={cells} ref={rowRef} />
119 export default MergeTableBodyRow;