import { PostageBatch } from '@ethersphere/bee-js' import { DriveInfo } from '@solarpunkltd/file-manager-lib' import { ReactElement, useContext, useEffect, useState } from 'react' import Add from 'remixicon-react/AddLineIcon' import ArrowDown from 'remixicon-react/ArrowDownSLineIcon' import ArrowRight from 'remixicon-react/ArrowRightSLineIcon' import DeleteFill from 'remixicon-react/DeleteBin6FillIcon' import Delete from 'remixicon-react/DeleteBin6LineIcon' import FolderFill from 'remixicon-react/Folder3FillIcon' import Folder from 'remixicon-react/Folder3LineIcon' import HistoryFill from 'remixicon-react/HistoryFillIcon' import History from 'remixicon-react/HistoryLineIcon' import { useView } from '../../../../pages/filemanager/ViewContext' import { Context as FMContext } from '../../../../providers/FileManager' import { Context as SettingsContext } from '../../../../providers/Settings' import { FILE_MANAGER_EVENTS } from '../../constants/common' import { ViewType } from '../../constants/transfers' import { getUsableStamps } from '../../utils/bee' import { truncateNameMiddle } from '../../utils/common' import { CreateDriveModal } from '../CreateDriveModal/CreateDriveModal' import { DriveItem } from './DriveItem/DriveItem' import { ExpiredDriveItem } from './DriveItem/ExpiredDriveItem' import './Sidebar.scss' interface SidebarProps { loading: boolean errorMessage?: string setErrorMessage?: (error: string) => void } export function Sidebar({ setErrorMessage, loading }: SidebarProps): ReactElement { const [hovered, setHovered] = useState(null) const [isMyDrivesOpen, setIsMyDriveOpen] = useState(true) const [isTrashOpen, setIsTrashOpen] = useState(false) const [isCreateDriveOpen, setIsCreateDriveOpen] = useState(false) const [usableStamps, setUsableStamps] = useState([]) const [isDriveCreationInProgress, setIsDriveCreationInProgress] = useState(false) const [creatingDriveName, setCreatingDriveName] = useState(null) const [isExpiredOpen, setIsExpiredOpen] = useState(false) const { beeApi } = useContext(SettingsContext) const { setView, view } = useView() const { fm, currentDrive, currentStamp, drives, expiredDrives, setCurrentDrive, setCurrentStamp, setShowError, syncDrives, } = useContext(FMContext) useEffect(() => { let isMounted = true const getStamps = async () => { if (!beeApi) { return } const stamps = await getUsableStamps(beeApi) if (isMounted) { setUsableStamps([...stamps]) } } if (beeApi) { getStamps() } const handleUpgradeEnd = async () => { if (isMounted && beeApi) { await getStamps() } } window.addEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_END, handleUpgradeEnd as EventListener) return () => { isMounted = false window.removeEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_END, handleUpgradeEnd as EventListener) } }, [beeApi, drives]) useEffect(() => { if (!fm || drives.length === 0) { return } if (!currentDrive) { const firstDrive = drives[0] setCurrentDrive(firstDrive) setView(ViewType.File) } if (currentDrive && usableStamps.length > 0) { const correspondingStamp = usableStamps.find(s => s.batchID.toString() === currentDrive.batchId.toString()) if (correspondingStamp) { setCurrentStamp(correspondingStamp) } } }, [fm, drives, currentDrive, usableStamps, setCurrentDrive, setCurrentStamp, setView, beeApi]) const handleCreateNewDrive = () => { if (isDriveCreationInProgress) { return } setIsCreateDriveOpen(true) } const isCurrent = (di: DriveInfo) => currentDrive?.id.toString() === di.id.toString() return (
{!loading && ( <>
handleCreateNewDrive()} >
Create new drive
{isDriveCreationInProgress && (
{truncateNameMiddle(creatingDriveName || 'Your Drive', 35, 8, 8)} is currently being created.
)} )} {isCreateDriveOpen && ( setIsCreateDriveOpen(false)} onDriveCreated={() => { setIsCreateDriveOpen(false) setIsDriveCreationInProgress(false) setCreatingDriveName(null) }} onCreationStarted={(driveName: string) => { setIsDriveCreationInProgress(true) setCreatingDriveName(driveName) }} onCreationError={(name: string) => { setIsDriveCreationInProgress(false) setErrorMessage?.( `Error creating drive ${name}. Please try again. Possible causes include insufficient xDAI balance or a lost connection to the RPC.`, ) setShowError(true) setCreatingDriveName(null) return }} /> )}
setHovered('my-drives')} onMouseLeave={() => setHovered(null)} onClick={() => setIsMyDriveOpen(!isMyDrivesOpen)} >
{isMyDrivesOpen ? : }
{hovered === 'my-drives' ? : }
My Drives
{isMyDrivesOpen && isDriveCreationInProgress && (
Initializing drive metadata
Please wait…
)} {isMyDrivesOpen && drives.map(d => { const isSelected = isCurrent(d) && view === ViewType.File const localStamp = usableStamps.find(s => s.batchID.toString() === d.batchId.toString() && !d.isAdmin) const stamp = isSelected && currentStamp ? currentStamp : localStamp return ( stamp && (
{ setCurrentDrive(d) setCurrentStamp(stamp) setView(ViewType.File) }} >
) ) })} {expiredDrives.length > 0 && ( <>
setHovered('expired')} onMouseLeave={() => setHovered(null)} onClick={() => setIsExpiredOpen(prev => !prev)} >
{isExpiredOpen ? : }
{hovered === 'expired' ? : }
Expired drives
{isExpiredOpen && (
{expiredDrives.map(d => (
{ setCurrentDrive(d) setView(ViewType.Expired) }} > { await syncDrives() setCurrentDrive(drives.length > 0 ? drives[0] : undefined) setView(ViewType.File) }} setErrorMessage={setErrorMessage} />
))}
)} )}
setHovered(ViewType.Trash)} onMouseLeave={() => setHovered(null)} onClick={() => setIsTrashOpen(!isTrashOpen)} >
{isTrashOpen ? : }
{hovered === ViewType.Trash ? : }
Trash
{isTrashOpen && (
{drives.map(d => { const selected = isCurrent(d) && view === ViewType.Trash const stamp = usableStamps.find(s => s.batchID.toString() === d.batchId.toString() && !d.isAdmin) return (
{ setCurrentDrive(d) setCurrentStamp(stamp) setView(ViewType.Trash) }} title={`${d.name} Trash`} > {truncateNameMiddle(d.name, 35, 8, 8)} Trash
) })}
)}
{isDriveCreationInProgress && (
Creating drive, please do not reload
)}
) }