Skip to content
Snippets Groups Projects
Commit 8bd5e119 authored by Jacopo Gasparetto's avatar Jacopo Gasparetto
Browse files

Download selected file from inspector

parent 292e701c
No related branches found
No related tags found
No related merge requests found
...@@ -2,9 +2,17 @@ import { getHumanSize } from "../commons/utils"; ...@@ -2,9 +2,17 @@ import { getHumanSize } from "../commons/utils";
import { BucketObject } from "../models/bucket"; import { BucketObject } from "../models/bucket";
import { Button } from "./Button"; import { Button } from "./Button";
import { Inspector, InspectorProps } from "./Inspector"; import { Inspector, InspectorProps } from "./Inspector";
import { ArrowDownCircleIcon, TrashIcon } from "@heroicons/react/24/outline"; import {
ArrowDownCircleIcon,
InformationCircleIcon,
TrashIcon,
XMarkIcon
} from "@heroicons/react/24/outline";
import { useS3Service } from "../services/S3Service";
interface BucketInspectorProps extends InspectorProps { interface BucketInspectorProps extends InspectorProps {
buckets: BucketObject[] bucket: string;
objects: BucketObject[]
} }
...@@ -16,7 +24,7 @@ interface DetailProps { ...@@ -16,7 +24,7 @@ interface DetailProps {
const Detail = ({ title, value }: DetailProps) => { const Detail = ({ title, value }: DetailProps) => {
return ( return (
<div className="my-4"> <div className="my-4">
<div className="font-semibold">{title}</div> <div className="text-lg font-semibold">{title}</div>
<div className="break-all">{value}</div> <div className="break-all">{value}</div>
</div> </div>
) )
...@@ -25,6 +33,10 @@ const Detail = ({ title, value }: DetailProps) => { ...@@ -25,6 +33,10 @@ const Detail = ({ title, value }: DetailProps) => {
const ObjectDetail = (object: BucketObject) => { const ObjectDetail = (object: BucketObject) => {
return ( return (
<> <>
<div className="flex items-center">
<div className="text-lg font-semibold">Object Info</div>
<div className="ml-4 w-5"><InformationCircleIcon /></div>
</div>
<Detail title={"Key"} value={object.Key} /> <Detail title={"Key"} value={object.Key} />
<Detail title={"ETag"} value={object.ETag} /> <Detail title={"ETag"} value={object.ETag} />
<Detail title={"Last Modified"} value={object.LastModified?.toString()} /> <Detail title={"Last Modified"} value={object.LastModified?.toString()} />
...@@ -34,26 +46,67 @@ const ObjectDetail = (object: BucketObject) => { ...@@ -34,26 +46,67 @@ const ObjectDetail = (object: BucketObject) => {
) )
} }
export const BucketInspector = (props: BucketInspectorProps) => { export const BucketInspector = (props: BucketInspectorProps) => {
const { buckets } = props; const { bucket, objects } = props;
const title = buckets.length === 1 ? buckets[0].Key : "Multiple objects"; const title = objects.length === 1 ? objects[0].Key : "Multiple objects";
const bucket = buckets[0]; const object = objects[0];
const { fetchObject } = useS3Service();
const downloadObject = async () => {
try {
const response = await fetchObject(bucket, object.Key!);
const body = await response.transformToByteArray();
const newBlob = new Blob([body]);
const blobUrl = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = blobUrl;
link.setAttribute('download', object.Key!);
document.body.appendChild(link);
link.click();
link.parentNode?.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
} catch (err) {
console.error(err);
}
}
const Title = () => {
return (
<div className="text-xl font-semibold">
{title}
</div>
)
}
return ( return (
<Inspector <Inspector
isOpen={props.isOpen}> isOpen={props.isOpen}>
{props.children} <div className="flex items-center p-4">
<div className="p-8 text-lg font-semibold"> <Title />
{title} <button>
<div
className="w-8 p-[5px] bg-neutral-300 text-neutral-500
hover:bg-neutral-400 rounded-full">
<XMarkIcon />
</div>
</button>
</div> </div>
<div className="p-4"> <div className="p-4">
<Button className="w-full" title="Download" icon={<ArrowDownCircleIcon />} /> <Button
className="w-full"
title="Download"
icon={<ArrowDownCircleIcon />}
onClick={downloadObject}
/>
<Button className="w-full mt-4" title="Delete" icon={<TrashIcon />} /> <Button className="w-full mt-4" title="Delete" icon={<TrashIcon />} />
<hr className="h-px w-full my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr> <hr className="h-px w-full my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>
<div className="text-lg font-semibold">Object Info</div> <ObjectDetail {...object} />
<ObjectDetail {...bucket} />
</div> </div>
{props.children}
</Inspector> </Inspector>
) )
} }
import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react'; import { ChangeEvent, MouseEvent, useCallback, useEffect, useRef, useState } from 'react';
import { Page } from '../../components/Page'; import { Page } from '../../components/Page';
import { BucketObject } from '../../models/bucket'; import { BucketObject } from '../../models/bucket';
import { Column, Table } from '../../components/Table'; import { Column, Table } from '../../components/Table';
...@@ -171,6 +171,11 @@ export const BucketBrowser = ({ bucketName }: PropsType) => { ...@@ -171,6 +171,11 @@ export const BucketBrowser = ({ bucketName }: PropsType) => {
setSelectedRows(newState); setSelectedRows(newState);
} }
const onClick = (_: MouseEvent<HTMLTableRowElement>, index: number) => {
const newState = new Set([index]);
setSelectedRows(newState);
}
const deleteSelectedObjects = () => { const deleteSelectedObjects = () => {
// Queue all delete asynchronously // Queue all delete asynchronously
let promises: Promise<void>[] = []; let promises: Promise<void>[] = [];
...@@ -199,10 +204,11 @@ export const BucketBrowser = ({ bucketName }: PropsType) => { ...@@ -199,10 +204,11 @@ export const BucketBrowser = ({ bucketName }: PropsType) => {
<div className='top-0 fixed z-10 right-0 w-64 bg-slate-300'> <div className='top-0 fixed z-10 right-0 w-64 bg-slate-300'>
<BucketInspector <BucketInspector
isOpen={selectedRows.size > 0} isOpen={selectedRows.size > 0}
buckets={Array.from(selectedRows).map(index => bucketObjects[index])} bucket={bucketName}
objects={Array.from(selectedRows).map(index => bucketObjects[index])}
/> />
</div> </div>
<div className={`transition-all ease-in-out duration-200 ${selectedRows.size > 0 ? "mr-64" : "mr-0"}`}> <div className={`transition-all ease-in-out duration-200 ${selectedRows.size > 0 ? "mr-72" : "mr-0"}`}>
<div className='container w-2/3'> <div className='container w-2/3'>
<div className="flex mt-8 place-content-between"> <div className="flex mt-8 place-content-between">
<InputFile <InputFile
...@@ -222,6 +228,7 @@ export const BucketBrowser = ({ bucketName }: PropsType) => { ...@@ -222,6 +228,7 @@ export const BucketBrowser = ({ bucketName }: PropsType) => {
columns={columns} columns={columns}
data={tableData} data={tableData}
onSelect={onSelect} onSelect={onSelect}
onClick={onClick}
selectedRows={selectedRows} selectedRows={selectedRows}
/> />
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment