feat: Added drag drop, progress bar
This commit is contained in:
parent
b12f72bde1
commit
2ec8dbdc69
16
src/components/ProgressBar/ProgressBar.tsx
Normal file
16
src/components/ProgressBar/ProgressBar.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
const ProgressBar = ({ value, max }: { value: number; max: number }) => {
|
||||
return (
|
||||
<div className="w-full bg-gray-200 rounded-lg dark:bg-neutral-900">
|
||||
{max !== undefined && value !== undefined && max > 0 && (
|
||||
<div
|
||||
className="bg-pink-600 text-sm font-medium text-pink-100 text-center p-1 leading-none rounded-lg"
|
||||
style={{ width: `${Math.floor((value * 100) / max)}%` }}
|
||||
>
|
||||
{Math.floor((value * 100) / max)} %
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressBar;
|
@ -15,6 +15,7 @@ import { formatFileSize } from '../utils';
|
||||
import BlobList from '../components/BlobList/BlobList';
|
||||
import './Transfer.css';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import ProgressBar from '../components/ProgressBar/ProgressBar';
|
||||
|
||||
type TransferStatus = {
|
||||
[key: string]: {
|
||||
@ -134,14 +135,7 @@ export const Transfer = () => {
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="w-full bg-gray-200 rounded-lg dark:bg-neutral-800">
|
||||
<div
|
||||
className="bg-pink-600 text-sm font-medium text-pink-100 text-center p-1 leading-none rounded-lg"
|
||||
style={{ width: `${Math.floor((transferStatus.size * 100) / transferStatus.fullSize)}%` }}
|
||||
>
|
||||
{Math.floor((transferStatus.size * 100) / transferStatus.fullSize)} %
|
||||
</div>
|
||||
</div>
|
||||
<ProgressBar value={transferStatus.size} max={transferStatus.fullSize} />
|
||||
{
|
||||
<div className="message">
|
||||
{formatFileSize(transferStatus.size)} / {formatFileSize(transferStatus.fullSize)} transferred
|
||||
|
@ -1,64 +1,129 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { ChangeEvent, DragEvent, useEffect, useState } from 'react';
|
||||
import { useServers } from '../utils/useServers';
|
||||
import { BlossomClient } from 'blossom-client-sdk';
|
||||
import { useNDK } from '../ndk';
|
||||
import { useServerInfo } from '../utils/useServerInfo';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { ArrowUpOnSquareIcon } from '@heroicons/react/24/outline';
|
||||
import ProgressBar from '../components/ProgressBar/ProgressBar';
|
||||
|
||||
type TransferStats = {
|
||||
enabled: boolean;
|
||||
size: number;
|
||||
transferred: number;
|
||||
};
|
||||
|
||||
function Upload() {
|
||||
const servers = useServers();
|
||||
const { signEventTemplate } = useNDK();
|
||||
const { serverInfo } = useServerInfo();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const queryClient = useQueryClient();
|
||||
const [uploadTarget, setUploadTarget] = useState<{ [key: string]: boolean }>({});
|
||||
const [transfers, setTransfers] = useState<{ [key: string]: TransferStats }>({});
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
|
||||
const upload = async () => {
|
||||
if (inputRef.current && inputRef.current.files) {
|
||||
if (files && files.length) {
|
||||
// sum files sizes
|
||||
const totalSize = files.reduce((acc, f) => acc + f.size, 0);
|
||||
|
||||
// set all entries size to totalSize
|
||||
setTransfers(ut => {
|
||||
const newTransfers = { ...ut };
|
||||
for (const server of servers) {
|
||||
if (newTransfers[server.name].enabled) {
|
||||
newTransfers[server.name].size = totalSize;
|
||||
}
|
||||
}
|
||||
return newTransfers;
|
||||
});
|
||||
|
||||
for (const server of servers) {
|
||||
if (!uploadTarget[server.name]) {
|
||||
if (!transfers[server.name]?.enabled) {
|
||||
continue;
|
||||
}
|
||||
const serverUrl = serverInfo[server.name].url;
|
||||
for (const file of inputRef.current.files) {
|
||||
for (const file of files) {
|
||||
const uploadAuth = await BlossomClient.getUploadAuth(file, signEventTemplate, 'Upload Blob');
|
||||
const newBlob = await BlossomClient.uploadBlob(serverUrl, file, uploadAuth);
|
||||
|
||||
transfers[server.name].transferred += file.size;
|
||||
console.log(newBlob);
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ['blobs', server.name] });
|
||||
setFiles([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const clearTransfers = () => {
|
||||
setTransfers(servers.reduce((acc, s) => ({ ...acc, [s.name]: { enabled: true, size: 0, transferred: 0 } }), {}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setUploadTarget(servers.reduce((acc, s) => ({ ...acc, [s.name]: true }), {}));
|
||||
clearTransfers();
|
||||
}, [servers]);
|
||||
|
||||
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const selectedFiles = event.target.files;
|
||||
if (selectedFiles && selectedFiles.length > 0) {
|
||||
const newFiles = Array.from(selectedFiles);
|
||||
setFiles(prevFiles => [...prevFiles, ...newFiles]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (event: DragEvent<HTMLLabelElement>) => {
|
||||
event.preventDefault();
|
||||
const droppedFiles = event.dataTransfer?.files;
|
||||
if (droppedFiles && droppedFiles.length > 0) {
|
||||
const newFiles = Array.from(droppedFiles);
|
||||
setFiles(prevFiles => [...prevFiles, ...newFiles]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Upload</h2>
|
||||
<div className=" bg-neutral-800 rounded-xl p-4 text-neutral-400 gap-4 flex flex-col">
|
||||
<input className=" cursor-pointer" type="file" ref={inputRef} multiple />
|
||||
<input id="browse" type="file" hidden multiple onChange={handleFileChange} />
|
||||
<label
|
||||
htmlFor="browse"
|
||||
className="p-8 bg-neutral-700 rounded-lg hover:text-white text-neutral-400 border-dashed border-neutral-500 border-2 block cursor-pointer text-center"
|
||||
onDrop={handleDrop}
|
||||
onDragOver={event => event.preventDefault()}
|
||||
>
|
||||
<ArrowUpOnSquareIcon className="w-8 inline" /> Browse or drag & drop
|
||||
</label>
|
||||
|
||||
<div className="cursor-pointer grid gap-2" style={{ gridTemplateColumns: '1em 20em auto' }}>
|
||||
{servers.map(s => (
|
||||
<div className="cursor-pointer flex flex-row gap-2 " key={s.name}>
|
||||
<>
|
||||
<input
|
||||
className="w-5 accent-pink-700 hover:accent-pink-600"
|
||||
id={s.name}
|
||||
type="checkbox"
|
||||
checked={uploadTarget[s.name] || false}
|
||||
onChange={e => setUploadTarget(ut => ({ ...ut, [s.name]: e.target.checked }))}
|
||||
checked={transfers[s.name]?.enabled || false}
|
||||
onChange={e =>
|
||||
setTransfers(ut => ({ ...ut, [s.name]: { enabled: e.target.checked, transferred: 0, size: 0 } }))
|
||||
}
|
||||
/>
|
||||
<label htmlFor={s.name} className="cursor-pointer">
|
||||
{s.name}
|
||||
</label>
|
||||
</div>
|
||||
{transfers[s.name]?.enabled ? (
|
||||
<ProgressBar value={transfers[s.name].transferred} max={transfers[s.name].size} />
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button className="p-2 px-4 bg-neutral-600 hover:bg-pink-700 text-white rounded-lg w-2/6" onClick={() => upload()}>
|
||||
Upload
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="p-2 px-4 bg-neutral-600 hover:bg-pink-700 text-white rounded-lg w-2/6"
|
||||
onClick={() => upload()}
|
||||
>
|
||||
Upload{files.length > 0 ? (files.length == 1 ? ` 1 file` : ` ${files.length} files`) : ''}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user