Files
bee-dashboard/src/modules/filemanager/utils/infoGroups.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

211 lines
6.4 KiB
TypeScript

import type { ReactElement } from 'react'
import { FileStatus, FileInfo, FileManagerBase } from '@solarpunkltd/file-manager-lib'
import { GetGranteesResult, PostageBatch } from '@ethersphere/bee-js'
import GeneralIcon from 'remixicon-react/FileTextLineIcon'
import CalendarIcon from 'remixicon-react/CalendarLineIcon'
import AccessIcon from 'remixicon-react/ShieldKeyholeLineIcon'
import HardDriveIcon from 'remixicon-react/HardDrive2LineIcon'
import { indexStrToBigint, truncateNameMiddle } from './common'
import { FEED_INDEX_ZERO, erasureCodeMarks } from '../constants/common'
export type FileProperty = { key: string; label: string; value: string; raw?: string }
export type FilePropertyGroup = { title: string; icon?: ReactElement; properties: FileProperty[] }
type KnownCustomMeta = Record<string, string> & {
size?: string
mime?: string
path?: string
fileCount?: string
expiresAt?: string
}
const dash = '—'
const formatBytes = (bytes?: number | string) => {
const n = typeof bytes === 'string' ? Number(bytes) : bytes
if (!Number.isFinite(n as number) || (n as number) < 0) return dash
if ((n as number) < 1024) return `${n} B`
const units = ['KB', 'MB', 'GB', 'TB']
let v = (n as number) / 1024
let i = 0
while (v >= 1024 && i < units.length - 1) {
v /= 1024
i++
}
return `${v.toFixed(1)} ${units[i]}`
}
const truncateMiddle = (s?: string, start = 8, end = 8) => {
if (!s) return dash
return s.length <= start + end + 3 ? s : `${s.slice(0, start)}...${s.slice(-end)}`
}
const fmtDate = (ts?: number) => {
if (ts === undefined || !Number.isFinite(ts)) return dash
try {
return new Date(ts).toLocaleString()
} catch {
return dash
}
}
async function getCreatedTs(fm: FileManagerBase, fi: FileInfo): Promise<number | undefined> {
try {
const v0 = await fm.getVersion(fi, FEED_INDEX_ZERO.toString())
return v0.timestamp
} catch {
return undefined
}
}
function extractGranteeCount(r: GetGranteesResult): number {
const obj = r as unknown as Record<string, unknown>
const pk = obj.publicKeys
if (Array.isArray(pk)) return pk.length
const gs = obj.grantees
if (Array.isArray(gs)) return gs.length
return 0
}
export async function getGranteeCount(fm: FileManagerBase, fi: FileInfo): Promise<number | undefined> {
try {
const result = await fm.getGrantees(fi)
return extractGranteeCount(result)
} catch {
return undefined
}
}
function buildGeneralGroup(
fi: FileInfo,
mime?: string,
size?: number | string,
path?: string,
fileCount?: string,
): FilePropertyGroup {
return {
title: 'General',
icon: <GeneralIcon size="14px" color="rgb(237, 129, 49)" />,
properties: [
{ key: 'type', label: 'Type', value: mime ?? dash },
{ key: 'size', label: 'Size', value: size != null ? formatBytes(size) : dash },
{ key: 'count', label: 'Items', value: fileCount ?? '1' },
{ key: 'path', label: 'Location', value: truncateNameMiddle(path || dash, 35, 10, 10) },
{
key: 'hash',
label: 'Swarm hash',
value: truncateMiddle(fi.file.reference.toString()),
raw: fi.file.reference.toString(),
},
{ key: 'ver', label: 'Versions', value: ((indexStrToBigint(fi.version) ?? BigInt(0)) + BigInt(1)).toString() },
{ key: 'status', label: 'Status', value: !fi.status ? FileStatus.Active : fi.status },
],
}
}
function buildDatesGroup(createdTs?: number, modifiedTs?: number, expires?: string): FilePropertyGroup {
return {
title: 'Dates',
icon: <CalendarIcon size="14px" color="rgb(237, 129, 49)" />,
properties: [
{ key: 'created', label: 'Created', value: fmtDate(createdTs) },
{ key: 'modified', label: 'Modified', value: fmtDate(modifiedTs) },
{ key: 'expires', label: 'Expires', value: expires ?? dash },
],
}
}
function buildAccessGroup(fi: FileInfo, granteeCount?: number): FilePropertyGroup {
return {
title: 'Access & Permissions',
icon: <AccessIcon size="14px" color="rgb(237, 129, 49)" />,
properties: [
{
key: 'owner',
label: 'Owner',
value: truncateMiddle(fi.owner.toString()),
raw: fi.owner.toString(),
},
{ key: 'shared', label: 'Sharing', value: fi.shared ? 'Shared' : 'Private' },
{ key: 'grantees', label: 'Grantees', value: granteeCount != null ? `${granteeCount}` : dash },
{
key: 'actpub',
label: 'ACT Publisher',
value: truncateMiddle(fi.actPublisher.toString()),
raw: fi.actPublisher.toString(),
},
{
key: 'topic',
label: 'Topic',
value: truncateMiddle(fi.topic.toString()),
raw: fi.topic.toString(),
},
{
key: 'historyRef',
label: 'ACT History',
value: truncateMiddle(fi.file.historyRef.toString()),
raw: fi.file.historyRef.toString(),
},
],
}
}
function buildStorageGroup(fi: FileInfo, driveName: string, stamp?: PostageBatch): FilePropertyGroup {
const stampValue = stamp
? truncateNameMiddle(stamp.label, 35, 10, 10) + ' (' + truncateMiddle(fi.batchId.toString(), 4, 4) + ')'
: truncateMiddle(fi.batchId.toString())
const redundancyLabel =
fi.redundancyLevel !== undefined
? erasureCodeMarks.find(mark => mark.value === fi.redundancyLevel)?.label ?? fi.redundancyLevel.toString()
: dash
return {
title: 'Storage',
icon: <HardDriveIcon size="14px" color="rgb(237, 129, 49)" />,
properties: [
{
key: 'batch',
label: 'Batch ID',
value: stampValue,
raw: fi.batchId.toString(),
},
{ key: 'drive', label: 'Drive', value: truncateNameMiddle(driveName, 35, 10, 10) },
{ key: 'redundancy', label: 'Redundancy', value: redundancyLabel },
],
}
}
export async function buildGetInfoGroups(
fm: FileManagerBase,
fi: FileInfo,
driveName: string,
stamp?: PostageBatch,
): Promise<FilePropertyGroup[]> {
const cm = fi.customMetadata as KnownCustomMeta | undefined
const size = cm?.size
const mime = cm?.mime
const path = cm?.path || driveName // TODO: set exact subpath
const fileCount = cm?.fileCount
const expires = cm?.expiresAt || stamp?.duration.toEndDate().toLocaleDateString()
const [createdTs, granteeCount] = await Promise.all([getCreatedTs(fm, fi), getGranteeCount(fm, fi)])
return [
buildGeneralGroup(fi, mime, size, path, fileCount),
buildDatesGroup(createdTs, fi.timestamp, expires),
buildAccessGroup(fi, granteeCount),
buildStorageGroup(fi, driveName, stamp),
]
}