feat: sync and update with all changes from fork (#720)
* feat: sync and update with all changes from fork * refactor: extract clipboard copy logic into custom hook * fix: correct spelling of DEFAULT_REFRESH_FREQUENCY_MS in Stamps and WalletBalance providers * refactor(ui-tests): replace fixed sleeps with condition-based waits * fix: handle null values for size and granteeCount in infoGroups * fix(lint): add newline at end of file in useClipboardCopy hook * fix(ui-tests): page.goto URL * refactor: update import paths for useClipboardCopy --------- Co-authored-by: Ferenc Sárai <sarai.ferenc@gmail.com>
This commit is contained in:
@@ -1,15 +1,22 @@
|
||||
import { ReactElement } from 'react'
|
||||
import ImageIcon from 'remixicon-react/Image2LineIcon'
|
||||
import FileIcon from 'remixicon-react/FileTextLineIcon'
|
||||
import ImageIcon from 'remixicon-react/Image2LineIcon'
|
||||
|
||||
import { guessMime } from './view'
|
||||
|
||||
interface ContextMenuProps {
|
||||
icon: string
|
||||
name: string
|
||||
metadata?: Record<string, string>
|
||||
size?: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
export function GetIconElement({ icon, size = '21px', color = '#ed8131' }: ContextMenuProps): ReactElement {
|
||||
switch (icon) {
|
||||
export function GetIconElement({ name, metadata, size = '21px', color = '#ed8131' }: ContextMenuProps): ReactElement {
|
||||
const { mime } = guessMime(name, metadata)
|
||||
|
||||
const iconType = mime.split('/')[0]?.toLowerCase() || 'file'
|
||||
|
||||
switch (iconType) {
|
||||
case 'image':
|
||||
return <ImageIcon size={size} color={color} />
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { BatchId, Bee, BZZ, Duration, PostageBatch, RedundancyLevel, Size } from '@ethersphere/bee-js'
|
||||
import {
|
||||
FileManagerBase,
|
||||
DriveInfo,
|
||||
estimateDriveListMetadataSize,
|
||||
estimateFileInfoMetadataSize,
|
||||
FileInfo,
|
||||
FileManagerBase,
|
||||
} from '@solarpunkltd/file-manager-lib'
|
||||
import React from 'react'
|
||||
|
||||
import { getHumanReadableFileSize } from '../../../utils/file'
|
||||
import { ActionTag } from '../constants/transfers'
|
||||
|
||||
@@ -57,7 +59,7 @@ export const fmGetStorageCost = async (
|
||||
}
|
||||
|
||||
return undefined
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@@ -172,7 +174,6 @@ export const handleCreateDrive = async (options: CreateDriveOptions): Promise<vo
|
||||
|
||||
batchId = await beeApi.buyStorage(size, duration, { label }, undefined, encryption, redundancyLevel)
|
||||
} else {
|
||||
// TODO: redundant, fm checks for stamp validtiy
|
||||
const isValid = await validateStampStillExists(beeApi, existingBatch.batchID)
|
||||
|
||||
if (!isValid) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { PrivateKey } from '@ethersphere/bee-js'
|
||||
import { Bytes, PrivateKey } from '@ethersphere/bee-js'
|
||||
import { FileInfo, FileStatus } from '@solarpunkltd/file-manager-lib'
|
||||
import { keccak256 } from '@ethersproject/keccak256'
|
||||
import { toUtf8Bytes } from '@ethersproject/strings'
|
||||
import React from 'react'
|
||||
|
||||
import { LocalStorageKeys } from '../../../utils/localStorage'
|
||||
import { lifetimeAdjustments } from '../constants/stamps'
|
||||
|
||||
export function getDaysLeft(expiryDate: Date): number {
|
||||
@@ -83,19 +84,17 @@ export function getFileId(fi: FileInfo): string {
|
||||
return fi.topic.toString()
|
||||
}
|
||||
|
||||
export const KEY_STORAGE = 'privateKey'
|
||||
|
||||
export function getSigner(input: string): PrivateKey {
|
||||
const normalized = input.trim().toLowerCase()
|
||||
const hash = keccak256(toUtf8Bytes(normalized))
|
||||
const privateKeyHex = hash.slice(2)
|
||||
const inputBytes = Bytes.fromUtf8(normalized)
|
||||
const privateKeyHex = Bytes.keccak256(inputBytes).toHex()
|
||||
|
||||
return new PrivateKey(privateKeyHex)
|
||||
}
|
||||
|
||||
export function getSignerPk(): PrivateKey | undefined {
|
||||
try {
|
||||
const fromLocalPk = localStorage.getItem(KEY_STORAGE)
|
||||
const fromLocalPk = localStorage.getItem(LocalStorageKeys.fmPrivateKey)
|
||||
|
||||
if (!fromLocalPk) {
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -107,24 +106,24 @@ export function getSignerPk(): PrivateKey | undefined {
|
||||
return new PrivateKey(fromLocalPk)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Private key error in localStorage under key "${KEY_STORAGE}": `, err)
|
||||
console.error(`Private key error in localStorage under key "${LocalStorageKeys.fmPrivateKey}": `, err)
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export function setSignerPk(pk: string): void {
|
||||
localStorage.setItem(KEY_STORAGE, pk)
|
||||
localStorage.setItem(LocalStorageKeys.fmPrivateKey, pk)
|
||||
}
|
||||
|
||||
export function removeSignerPk(): void {
|
||||
localStorage.removeItem(KEY_STORAGE)
|
||||
localStorage.removeItem(LocalStorageKeys.fmPrivateKey)
|
||||
}
|
||||
|
||||
export const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
export const safeSetState =
|
||||
<T>(ref: React.MutableRefObject<boolean>, setter: React.Dispatch<React.SetStateAction<T>>) =>
|
||||
<T>(ref: React.RefObject<boolean>, setter: React.Dispatch<React.SetStateAction<T>>) =>
|
||||
(value: React.SetStateAction<T>) => {
|
||||
if (ref.current) setter(value)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { FileInfo, FileManager } from '@solarpunkltd/file-manager-lib'
|
||||
import { guessMime, VIEWERS } from './view'
|
||||
import { AbortManager } from './abortManager'
|
||||
|
||||
import { DownloadProgress, DownloadState } from '../constants/transfers'
|
||||
|
||||
import { AbortManager } from './abortManager'
|
||||
import { guessMime, VIEWERS } from './view'
|
||||
|
||||
const downloadAborts = new AbortManager()
|
||||
|
||||
enum Errors {
|
||||
@@ -54,6 +56,7 @@ const processStream = async (
|
||||
onDownloadProgress?.({ progress, isDownloading: false, state: DownloadState.Cancelled })
|
||||
} else {
|
||||
onDownloadProgress?.({ progress, isDownloading: false, state: DownloadState.Error })
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to process stream: ', e)
|
||||
}
|
||||
@@ -70,6 +73,7 @@ const processStream = async (
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
/* no-op */
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('filehandle close/abort error: ', e)
|
||||
}
|
||||
@@ -106,6 +110,7 @@ const streamToBlob = async (
|
||||
onDownloadProgress?.({ progress, isDownloading: false, state: DownloadState.Cancelled })
|
||||
} else {
|
||||
onDownloadProgress?.({ progress, isDownloading: false, state: DownloadState.Error })
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error during stream processing: ', error)
|
||||
}
|
||||
@@ -133,6 +138,7 @@ interface FileInfoWithHandle {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isPickerSupported = (): boolean => typeof (window as any).showSaveFilePicker === 'function'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isDirectoryPickerSupported = (): boolean => typeof (window as any).showDirectoryPicker === 'function'
|
||||
|
||||
@@ -384,6 +390,7 @@ export const startDownloadingQueue = async (
|
||||
|
||||
if (!isAbortError) {
|
||||
tracker?.({ progress: 0, isDownloading: false, state: DownloadState.Error })
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('download queue error: ', error)
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { DriveInfo, FileInfo } from '@solarpunkltd/file-manager-lib'
|
||||
import type { FileManagerBase } from '@solarpunkltd/file-manager-lib'
|
||||
import type { PostageBatch, RedundancyLevel } from '@ethersphere/bee-js'
|
||||
import type { DriveInfo, FileInfo, FileManagerBase } from '@solarpunkltd/file-manager-lib'
|
||||
|
||||
import { ActionTag } from '../constants/transfers'
|
||||
|
||||
import { verifyDriveSpace } from './bee'
|
||||
import { capitalizeFirstLetter } from './common'
|
||||
import { ActionTag } from '../constants/transfers'
|
||||
|
||||
export enum FileOperation {
|
||||
Trash = 'trash',
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
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 { FileInfo, FileManagerBase, FileStatus } from '@solarpunkltd/file-manager-lib'
|
||||
import type { ReactElement } from 'react'
|
||||
import CalendarIcon from 'remixicon-react/CalendarLineIcon'
|
||||
import AccessIcon from 'remixicon-react/ShieldKeyholeLineIcon'
|
||||
import GeneralIcon from 'remixicon-react/FileTextLineIcon'
|
||||
import HardDriveIcon from 'remixicon-react/HardDrive2LineIcon'
|
||||
import AccessIcon from 'remixicon-react/ShieldKeyholeLineIcon'
|
||||
|
||||
import { erasureCodeMarks, FEED_INDEX_ZERO } from '../constants/common'
|
||||
|
||||
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[] }
|
||||
@@ -98,7 +99,7 @@ function buildGeneralGroup(
|
||||
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: 'size', label: 'Size', value: size !== undefined && size !== null ? formatBytes(size) : dash },
|
||||
{ key: 'count', label: 'Items', value: fileCount ?? '1' },
|
||||
{ key: 'path', label: 'Location', value: truncateNameMiddle(path || dash, 35, 10, 10) },
|
||||
{
|
||||
@@ -137,7 +138,11 @@ function buildAccessGroup(fi: FileInfo, granteeCount?: number): FilePropertyGrou
|
||||
raw: fi.owner.toString(),
|
||||
},
|
||||
{ key: 'shared', label: 'Sharing', value: fi.shared ? 'Shared' : 'Private' },
|
||||
{ key: 'grantees', label: 'Grantees', value: granteeCount != null ? `${granteeCount}` : dash },
|
||||
{
|
||||
key: 'grantees',
|
||||
label: 'Grantees',
|
||||
value: granteeCount !== undefined && granteeCount !== null ? `${granteeCount}` : dash,
|
||||
},
|
||||
{
|
||||
key: 'actpub',
|
||||
label: 'ACT Publisher',
|
||||
@@ -167,7 +172,7 @@ function buildStorageGroup(fi: FileInfo, driveName: string, stamp?: PostageBatch
|
||||
|
||||
const redundancyLabel =
|
||||
fi.redundancyLevel !== undefined
|
||||
? erasureCodeMarks.find(mark => mark.value === fi.redundancyLevel)?.label ?? fi.redundancyLevel.toString()
|
||||
? (erasureCodeMarks.find(mark => mark.value === fi.redundancyLevel)?.label ?? fi.redundancyLevel.toString())
|
||||
: dash
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Point, Dir } from './common'
|
||||
import { Dir, Point } from './common'
|
||||
|
||||
export function computeContextMenuPosition(args: {
|
||||
clickPos: Point
|
||||
|
||||
Reference in New Issue
Block a user