Files
bee-dashboard/src/modules/filemanager/components/FileProgressWindow/FileProgressWindow.tsx
T
Bálint Ujvári 0d5138f5bc Fix: file-manager and swarm-desktop bugs (#714)
- drive capacity display with stamp polling
- download/upload progress handling
- overlay and tooltip issues
- FileMaganger readme
- ultra-light mode handling
- account feed view page
- download media files
- remove not found syncing link
- fix ultra light node wallet page
- tooltip issues
---------
Co-authored-by: Andrei Mitrea <andrei.mitrea.hq@gmail.com>
Co-authored-by: nidishk <nidishkrishnan45@gmail.com>
Co-authored-by: Ferenc Sárai <sarai.ferenc@gmail.com>
Co-authored-by: Nándor Komlódi <nandor.komlodi@gmail.com>
Co-authored-by: rolandlor <33499567+rolandlor@users.noreply.github.com>
2026-01-26 12:57:14 +01:00

201 lines
6.9 KiB
TypeScript

import { ReactElement, useLayoutEffect, useRef } from 'react'
import CloseIcon from 'remixicon-react/CloseLineIcon'
import ArrowDownIcon from 'remixicon-react/ArrowDownSLineIcon'
import './FileProgressWindow.scss'
import { GetIconElement } from '../../utils/GetIconElement'
import { ProgressBar } from '../ProgressBar/ProgressBar'
import { FileTransferType, TransferBarColor, TransferStatus, ProgressItem } from '../../constants/transfers'
import { capitalizeFirstLetter, truncateNameMiddle } from '../../utils/common'
import { guessMime } from '../../utils/view'
interface FileProgressWindowProps {
items?: ProgressItem[]
type: FileTransferType
onCancelClick: () => void
onRowClose?: (uuid: string) => void
onCloseAll?: () => void
}
const formatEta = (sec?: number) => {
if (sec === undefined || sec === null) return ''
if (sec <= 0) return 'Done'
const s = Math.ceil(sec)
const mm = Math.floor(s / 60)
const ss = s % 60
return mm > 0 ? `${mm}m ${ss}s left` : `${ss}s left`
}
const formatDuration = (sec?: number) => {
if (sec === undefined || sec === null) return ''
const s = Math.max(0, Math.round(sec))
const mm = Math.floor(s / 60)
const ss = s % 60
return mm > 0 ? `${mm}m ${ss}s` : `${ss}s`
}
export function FileProgressWindow({
items,
type,
onCancelClick,
onRowClose,
onCloseAll,
}: FileProgressWindowProps): ReactElement | null {
const listRef = useRef<HTMLDivElement | null>(null)
const firstRowRef = useRef<HTMLDivElement | null>(null)
const count = items?.length ?? 0
const rows: ProgressItem[] = items ?? []
const getTransferInfo = (item: ProgressItem, pct?: number) => {
const transferType = capitalizeFirstLetter(item?.kind ?? type)
const verb = `${transferType}ing`
const actualStatus = item.status || (pct && pct >= 100 ? TransferStatus.Done : verb)
return {
statusText: capitalizeFirstLetter(actualStatus),
barColor: TransferBarColor[transferType as keyof typeof TransferBarColor],
}
}
const allDone =
rows.length > 0 &&
rows.every(r => {
const pct = Number.isFinite(r.percent) ? Math.round(r.percent as number) : undefined
return (
r.status === TransferStatus.Done ||
r.status === TransferStatus.Error ||
r.status === TransferStatus.Cancelled ||
(typeof pct === 'number' && pct >= 100)
)
})
useLayoutEffect(() => {
const rowEl = firstRowRef.current
const listEl = listRef.current
if (!rowEl || !listEl) return
const rowH = rowEl.getBoundingClientRect().height
const safeRowH = rowH > 0 ? rowH : 72
listEl.style.maxHeight = `${safeRowH * 5}px`
}, [rows.length])
return (
<div className="fm-file-progress-window">
<div className="fm-file-progress-window-header">
<div className="fm-emphasized-text">
{count} {type}
{count === 1 ? '' : 's'}
</div>
<div className="fm-file-progress-window-header-actions">
<button
className="fm-file-progress-window-header-btn fm-file-progress-window-header-dismiss"
aria-label="Dismiss all"
type="button"
disabled={!allDone}
onClick={() => onCloseAll?.()}
>
<CloseIcon size="16" />
</button>
<button
className="fm-file-progress-window-header-btn fm-file-progress-window-header-hide"
aria-label="Hide"
type="button"
onClick={onCancelClick}
>
<ArrowDownIcon size="16" />
</button>
</div>
</div>
<div className="fm-file-progress-window-list" ref={listRef}>
{rows.map((item, idx) => {
const pctNum = Number.isFinite(item.percent)
? Math.max(0, Math.min(100, Math.round(item.percent as number)))
: undefined
const isComplete = (pctNum ?? 0) >= 100 || item.status === TransferStatus.Done
const isActive =
item.status === TransferStatus.Uploading ||
item.status === TransferStatus.Downloading ||
item.status === TransferStatus.Queued
const rowActionLabel = isActive ? 'Cancel' : 'Dismiss'
const transferInfo = getTransferInfo(item, pctNum)
const getCenterText = () => {
if (!isComplete && typeof item.etaSec === 'number') return formatEta(item.etaSec)
if (isComplete && typeof item.elapsedSec === 'number') return formatDuration(item.elapsedSec)
return ''
}
const centerDisplay = getCenterText() || '\u00A0'
const { mime } = guessMime(item.name)
const mimeType = mime.split('/')[0].toLowerCase() || 'file'
return (
<div
className="fm-file-progress-window-file-item"
key={item.uuid || `${item.name}-${idx}`}
ref={idx === 0 ? firstRowRef : undefined}
>
<div className="fm-file-progress-window-file-type-icon">
<GetIconElement size="14" icon={mimeType} color="black" />
</div>
<div className="fm-file-progress-window-file-datas">
<div className="fm-file-progress-window-file-item-header">
<div className="fm-file-progress-window-name" title={item.name}>
<div className="fm-file-progress-window-name-text">{truncateNameMiddle(item.name, 25, 8, 8)}</div>
{item.driveName && (
<div className="fm-drive-line">
<span className="fm-drive-chip" title={`Drive: ${item.driveName}`}>
{truncateNameMiddle(item.driveName, 25, 8, 8)}
</span>
</div>
)}
</div>
<div className="fm-file-progress-window-percent" aria-live="polite">
{typeof pctNum === 'number' ? `${pctNum}%` : ''}
</div>
<button
className="fm-file-progress-window-row-close"
aria-label={rowActionLabel}
onClick={() => onRowClose?.(item.uuid)}
type="button"
>
<CloseIcon size="14" />
</button>
</div>
<ProgressBar
value={typeof pctNum === 'number' ? pctNum : 0}
width="100%"
backgroundColor="rgb(229, 231, 235)"
color={transferInfo.barColor}
/>
<div className="fm-file-progress-window-file-item-footer">
<div className="fm-file-progress-window-size">{item.size || '—'}</div>
<div className="fm-file-progress-window-center">{centerDisplay}</div>
<div className="fm-file-progress-window-status">{transferInfo.statusText}</div>
</div>
</div>
</div>
)
})}
</div>
</div>
)
}