1 import { useEffect, useState } from 'react';
2 import { NavLink } from 'react-router-dom';
4 import { c } from 'ttag';
6 import { type BreadcrumbInfo, CollapsingBreadcrumbs, Icon, Loader, useNotifications } from '@proton/components';
7 import noop from '@proton/utils/noop';
9 import type { DriveFolder } from '../../hooks/drive/useActiveShare';
10 import { useDriveDragMoveTarget } from '../../hooks/drive/useDriveDragMove';
11 import useNavigate from '../../hooks/drive/useNavigate';
12 import { useLinkPath } from '../../store';
13 import type { Share } from '../../store/_shares';
14 import { ShareType, useShare } from '../../store/_shares';
15 import { useDirectSharingInfo } from '../../store/_shares/useDirectSharingInfo';
16 import { sendErrorReport } from '../../utils/errorHandling';
17 import SignatureIcon from '../SignatureIcon';
18 import { useDetailsModal } from '../modals/DetailsModal';
19 import { getDevicesSectionName } from '../sections/Devices/constants';
22 activeFolder: DriveFolder;
25 const DriveBreadcrumbs = ({ activeFolder }: Props) => {
26 const { navigateToLink, navigateToDevices } = useNavigate();
27 const { createNotification } = useNotifications();
28 const { getHandleItemDrop } = useDriveDragMoveTarget(activeFolder.shareId);
29 const { traverseLinksToRoot } = useLinkPath(); // TODO: Get data using useFolderView instead one day.
30 const [detailsModal, showDetailsModal] = useDetailsModal();
31 const { isSharedWithMe: getIsSharedWithMe } = useDirectSharingInfo();
33 const [dropTarget, setDropTarget] = useState<string>();
34 const [rootShare, setRootShare] = useState<Share>();
35 const { getShare } = useShare();
36 const sectionTitle = getDevicesSectionName();
38 const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbInfo[]>([]);
39 const [isSharedWithMeFolder, setIsSharedWithMeFolder] = useState(false);
42 const abortController = new AbortController();
44 traverseLinksToRoot(abortController.signal, activeFolder.shareId, activeFolder.linkId)
45 .then((pathItems) => {
46 const breadcrumbs = pathItems.map(({ linkId, name, isRoot, link, isReadOnly }) => {
47 const handleDrop = getHandleItemDrop(linkId);
50 if (linkId === activeFolder.linkId) {
51 onClick = link.signatureIssues
52 ? () => showDetailsModal({ shareId: activeFolder.shareId, linkId })
55 onClick = () => navigateToLink(activeFolder.shareId, linkId, false);
58 const breadcrumb: BreadcrumbInfo = {
62 <span className="flex items-center flex-nowrap flex-1">
65 signatureIssues={link.signatureIssues}
66 isAnonymous={!link.activeRevision?.signatureAddress && !link.signatureAddress}
69 <span className="text-pre text-ellipsis">{name}</span>
72 noShrink: isRoot && rootShare?.type !== ShareType.device, // Keep root (My files) to be always fully visible.
73 highlighted: dropTarget === linkId,
80 setDropTarget(undefined);
88 if (dropTarget !== linkId) {
89 setDropTarget(linkId);
92 onDrop: async (e) => {
96 setDropTarget(undefined);
101 text: c('Notification').t`Failed to move, please try again`,
110 setBreadcrumbs(breadcrumbs);
112 .catch((err: any) => {
113 sendErrorReport(err);
117 abortController.abort();
119 }, [activeFolder.shareId, activeFolder.linkId, dropTarget, rootShare]);
122 const abortController = new AbortController();
123 getShare(abortController.signal, activeFolder.shareId)
127 .catch(sendErrorReport);
129 getIsSharedWithMe(abortController.signal, activeFolder.shareId)
131 setIsSharedWithMeFolder(result);
135 abortController.abort();
137 }, [activeFolder.shareId]);
139 if (breadcrumbs.length === 0) {
140 return <Loader className="py-2 px-3" />;
143 if (rootShare?.type === ShareType.device) {
144 breadcrumbs.unshift({
148 onClick: navigateToDevices,
154 {isSharedWithMeFolder ? (
158 className="button button-medium button-ghost-weak text-pre p-1 m-0 text-ellipsis *:pointer-events-none color-weak"
160 {c('Link').t`Shared with me`}
162 <div className="rtl:mirror shrink-0" aria-hidden="true">
163 <Icon size={4} name="chevron-right" />
167 <CollapsingBreadcrumbs breadcrumbs={breadcrumbs} />
173 export default DriveBreadcrumbs;