[FEAT] better management of upload files
This commit is contained in:
parent
e9af64405b
commit
6107b95dcd
@ -38,10 +38,14 @@ export type PopUpUploadProgressProps = {
|
||||
title: string;
|
||||
// current size send or receive
|
||||
currentSize: number;
|
||||
// in case of error this element is set to != undefined
|
||||
error?: string;
|
||||
// Total size to transfer
|
||||
totalSize: number;
|
||||
// index of the file to transfer
|
||||
index: number;
|
||||
// When finished the boolean is set to true
|
||||
isFinished: boolean;
|
||||
// List of element to Transfer
|
||||
elements: string[];
|
||||
onAbort: () => void;
|
||||
@ -49,15 +53,16 @@ export type PopUpUploadProgressProps = {
|
||||
};
|
||||
|
||||
export const PopUpUploadProgress = ({
|
||||
title,
|
||||
currentSize,
|
||||
totalSize,
|
||||
index,
|
||||
elements,
|
||||
error,
|
||||
index,
|
||||
isFinished,
|
||||
onAbort,
|
||||
onClose,
|
||||
title,
|
||||
totalSize,
|
||||
}: PopUpUploadProgressProps) => {
|
||||
const disclosure = useDisclosure();
|
||||
const initialRef = useRef(null);
|
||||
const finalRef = useRef(null);
|
||||
return (
|
||||
@ -75,16 +80,46 @@ export const PopUpUploadProgress = ({
|
||||
|
||||
<ModalBody pb={6} paddingLeft="18px">
|
||||
<Flex direction="column" gap="10px">
|
||||
<Text fontSize="20px" fontWeight="bold">[{index}/{elements.length}] {elements[index]}</Text>
|
||||
<Progress colorScheme='green' hasStripe value={currentSize} isAnimated max={totalSize} height='24px' />
|
||||
<Flex><Text>{currentSize.toLocaleString('fr-FR')} Bytes</Text><Text marginLeft="auto">{totalSize.toLocaleString('fr-FR')} Bytes</Text></Flex>
|
||||
{isFinished ? (
|
||||
<Text fontSize="20px" fontWeight="bold">
|
||||
All {elements.length} element have been sent
|
||||
</Text>
|
||||
) : (
|
||||
<Text fontSize="20px" fontWeight="bold">
|
||||
[{index + 1}/{elements.length}] {elements[index]}
|
||||
</Text>
|
||||
)}
|
||||
<Progress
|
||||
colorScheme="green"
|
||||
hasStripe
|
||||
value={currentSize}
|
||||
isAnimated
|
||||
max={totalSize}
|
||||
height="24px"
|
||||
/>
|
||||
<Flex>
|
||||
<Text>{currentSize.toLocaleString('fr-FR')} Bytes</Text>
|
||||
<Text marginLeft="auto">
|
||||
{totalSize.toLocaleString('fr-FR')} Bytes
|
||||
</Text>
|
||||
</Flex>
|
||||
{error && (
|
||||
<Text fontWeight="bold" color="darkred">
|
||||
{error}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button colorScheme="red" mr={3} onClick={onAbort} ref={initialRef}>
|
||||
Abort
|
||||
</Button>
|
||||
<Button onClick={onClose}>Close</Button>
|
||||
{isFinished ? (
|
||||
<Button onClick={onClose} variant="@success">
|
||||
Ok
|
||||
</Button>
|
||||
) : (
|
||||
<Button colorScheme="red" mr={3} onClick={onAbort} ref={initialRef}>
|
||||
Abort
|
||||
</Button>
|
||||
)}
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
@ -1,22 +1,20 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Input,
|
||||
Table,
|
||||
TableCaption,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Text,
|
||||
Tfoot,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
} from '@chakra-ui/react';
|
||||
import { LuTrash } from 'react-icons/lu';
|
||||
import { MdCloudUpload, MdRemove } from 'react-icons/md';
|
||||
import { MdCloudUpload } from 'react-icons/md';
|
||||
|
||||
import {
|
||||
Album,
|
||||
@ -31,7 +29,6 @@ import {
|
||||
} from '@/back-api';
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { FormInput } from '@/components/form/FormInput';
|
||||
import { FormSelect } from '@/components/form/FormSelect';
|
||||
import { useFormidable } from '@/components/form/Formidable';
|
||||
import { PopUpUploadProgress } from '@/components/popup/PopUpUploadProgress';
|
||||
@ -39,6 +36,7 @@ import { useAlbumService, useOrderedAlbums } from '@/service/Album';
|
||||
import { useArtistService, useOrderedArtists } from '@/service/Artist';
|
||||
import { useGenderService, useOrderedGenders } from '@/service/Gender';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useTrackService } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
|
||||
export class ElementList {
|
||||
@ -54,21 +52,24 @@ export class FileParsedElement {
|
||||
public nameDetected: boolean = false;
|
||||
public trackIdDetected: boolean = false;
|
||||
constructor(
|
||||
public uniqueId: number,
|
||||
public file: File,
|
||||
public title: string,
|
||||
public artist?: string,
|
||||
public album?: string,
|
||||
public trackId?: number
|
||||
) {
|
||||
console.log(`Unique element: ${uniqueId}`);
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
export class FileFailParsedElement {
|
||||
constructor(
|
||||
public id: number,
|
||||
public uniqueId: number,
|
||||
public file: File,
|
||||
public reason: string
|
||||
) {
|
||||
console.log(`Unique element2: ${uniqueId}`);
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
@ -98,6 +99,7 @@ export const AddPage = () => {
|
||||
const { store: storeGender } = useGenderService();
|
||||
const { store: storeArtist } = useArtistService();
|
||||
const { store: storeAlbum } = useAlbumService();
|
||||
const { store: storeTrack } = useTrackService();
|
||||
const { session } = useServiceContext();
|
||||
|
||||
const form = useFormidable<FormInsertData>({});
|
||||
@ -160,7 +162,7 @@ export const AddPage = () => {
|
||||
setSuggestedAlbum(undefined);
|
||||
};
|
||||
|
||||
const addFileWithMetaData = (file: File) => {
|
||||
const addFileWithMetaData = (file: File, id: number) => {
|
||||
// parsedElement: FileParsedElement[] = [];
|
||||
let artist: string | undefined = undefined;
|
||||
let album: string | undefined = undefined;
|
||||
@ -199,7 +201,14 @@ export const AddPage = () => {
|
||||
}
|
||||
// remove extension
|
||||
title = title.replace(new RegExp('\\.(webm|WEBM|Webm)'), '');
|
||||
let tmp = new FileParsedElement(file, title, artist, album, trackIdNumber);
|
||||
let tmp = new FileParsedElement(
|
||||
id,
|
||||
file,
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
trackIdNumber
|
||||
);
|
||||
console.log(`==>${JSON.stringify(tmp)}`);
|
||||
// add it in the list.
|
||||
return tmp;
|
||||
@ -218,7 +227,7 @@ export const AddPage = () => {
|
||||
const parsedFailedElementTmp: FileFailParsedElement[] = [];
|
||||
|
||||
for (let iii = 0; iii < value.target.files?.length; iii++) {
|
||||
parsedElementTmp.push(addFileWithMetaData(value.target.files[iii]));
|
||||
parsedElementTmp.push(addFileWithMetaData(value.target.files[iii], iii));
|
||||
}
|
||||
// check if all global parameters are generic:
|
||||
if (parsedElementTmp.length === 0) {
|
||||
@ -311,51 +320,73 @@ export const AddPage = () => {
|
||||
const [listValues, setListValues] = useState<string[]>([]);
|
||||
const [currentPosition, setCurrentPosition] = useState<number>(0);
|
||||
const [totalPosition, setTotalPosition] = useState<number>(0);
|
||||
const [uploadError, setUploadError] = useState<string | undefined>(undefined);
|
||||
const [isFinishedUpload, setIsFinishedUpload] = useState<boolean>(false);
|
||||
|
||||
function progressUpload(count: number, total: number): void {
|
||||
setTotalPosition(total);
|
||||
setCurrentPosition(count);
|
||||
}
|
||||
const progressUpload = useCallback(
|
||||
(count: number, total: number) => {
|
||||
setTotalPosition(total);
|
||||
setCurrentPosition(count);
|
||||
},
|
||||
[setTotalPosition, setCurrentPosition]
|
||||
);
|
||||
|
||||
const uploadNext = (): void => {
|
||||
setIndexUpload((previous) => previous ?? -1 + 1);
|
||||
|
||||
TrackResource.uploadTrack({
|
||||
restConfig: session.getRestConfig(),
|
||||
data: {
|
||||
title: parsedElement[0].title,
|
||||
file: parsedElement[0].file,
|
||||
albumId: form.values['albumId'] ?? undefined,
|
||||
artistId: form.values['artistId'] ?? undefined,
|
||||
genderId: form.values['genderId'] ?? undefined,
|
||||
trackId: parsedElement[0].trackId ?? undefined,
|
||||
},
|
||||
callbacks: {
|
||||
progressUpload: progressUpload,
|
||||
},
|
||||
})
|
||||
.then((data: Track) => {
|
||||
// TODO: add the element in the local base...
|
||||
console.log(`element added: ${JSON.stringify(data, null, 2)}`);
|
||||
})
|
||||
.catch((error: RestErrorResponse) => {
|
||||
// TODO: manage error
|
||||
console.log(`element error: ${JSON.stringify(error, null, 2)}`);
|
||||
});
|
||||
};
|
||||
const uploadNext = useCallback(
|
||||
(index: number = 0): void => {
|
||||
if (parsedElement.length <= index) {
|
||||
console.log('end of upload');
|
||||
setIsFinishedUpload(true);
|
||||
return;
|
||||
}
|
||||
setIndexUpload(index);
|
||||
console.log(
|
||||
`Start upload of file: ${index}: ${parsedElement[index].title}`
|
||||
);
|
||||
storeTrack
|
||||
.update(
|
||||
TrackResource.uploadTrack({
|
||||
restConfig: session.getRestConfig(),
|
||||
data: {
|
||||
title: parsedElement[index].title,
|
||||
file: parsedElement[index].file,
|
||||
albumId: form.values['albumId'] ?? undefined,
|
||||
artistId: form.values['artistId'] ?? undefined,
|
||||
genderId: form.values['genderId'] ?? undefined,
|
||||
trackId: parsedElement[index].trackId ?? undefined,
|
||||
},
|
||||
callbacks: {
|
||||
progressUpload: progressUpload,
|
||||
},
|
||||
})
|
||||
)
|
||||
.then((data: Track) => {
|
||||
// element sended good
|
||||
// Send next ...
|
||||
uploadNext(index + 1);
|
||||
})
|
||||
.catch((error: RestErrorResponse) => {
|
||||
// TODO: manage error
|
||||
console.log(`element error: ${JSON.stringify(error, null, 2)}`);
|
||||
setUploadError(JSON.stringify(error, null, 2));
|
||||
});
|
||||
},
|
||||
[setUploadError, setIndexUpload, storeTrack, parsedElement]
|
||||
);
|
||||
|
||||
const sendFile = (): void => {
|
||||
console.log(`Send file requested ... ${parsedElement.length}`);
|
||||
setUploadError(undefined);
|
||||
setIsFinishedUpload(false);
|
||||
setListValues(parsedElement.map((element) => element.file.name));
|
||||
uploadNext();
|
||||
};
|
||||
|
||||
function onUploadAbort(): void {
|
||||
//throw new Error('Function not implemented.');
|
||||
setIndexUpload(undefined);
|
||||
}
|
||||
|
||||
function OnUploadClose(): void {
|
||||
//throw new Error('Function not implemented.');
|
||||
setIndexUpload(undefined);
|
||||
}
|
||||
|
||||
const addNewGender = (data: string): Promise<Gender> => {
|
||||
@ -467,7 +498,7 @@ export const AddPage = () => {
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{parsedElement.map((data) => (
|
||||
<Tr key={data.file.name}>
|
||||
<Tr key={data.uniqueId}>
|
||||
<Td>
|
||||
<Input
|
||||
type="number"
|
||||
@ -591,7 +622,7 @@ export const AddPage = () => {
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{parsedFailedElement.map((data) => (
|
||||
<Tr key={data.file.name}>
|
||||
<Tr key={data.uniqueId}>
|
||||
<Td>{data.file.name}</Td>
|
||||
<Td>{data.reason}</Td>
|
||||
</Tr>
|
||||
@ -609,8 +640,10 @@ export const AddPage = () => {
|
||||
totalSize={totalPosition}
|
||||
index={indexUpload}
|
||||
elements={listValues}
|
||||
error={uploadError}
|
||||
onAbort={onUploadAbort}
|
||||
onClose={OnUploadClose}
|
||||
isFinished={isFinishedUpload}
|
||||
/>
|
||||
)}
|
||||
</PageLayout>
|
||||
|
@ -25,7 +25,7 @@ export const SSOPage = () => {
|
||||
clearToken();
|
||||
}
|
||||
}, [token, setToken, clearToken]);
|
||||
const delay = isDevelopmentEnvironment() ? 6000 : 2000;
|
||||
const delay = isDevelopmentEnvironment() ? 2000 : 2000;
|
||||
useEffect(() => {
|
||||
if (state === SessionState.CONNECTED) {
|
||||
const destination = data ? b64_to_utf8(data) : '/';
|
||||
|
@ -76,6 +76,15 @@ export default defineStyleConfig({
|
||||
color: mode('white', 'error.900')(props),
|
||||
boxColorFocus: mode('error.900', 'error.500')(props),
|
||||
}),
|
||||
'@success': (props) =>
|
||||
customVariant({
|
||||
theme: props.theme,
|
||||
bg: mode('green.300', 'green.300')(props),
|
||||
bgHover: mode('green.400', 'green.400')(props),
|
||||
bgActive: mode('green.500', 'green.400')(props),
|
||||
color: mode('white', 'green.900')(props),
|
||||
boxColorFocus: mode('green.900', 'green.400')(props),
|
||||
}),
|
||||
|
||||
'@progress': (props) => ({
|
||||
...customVariant({
|
||||
|
@ -16,6 +16,7 @@ export type DataStoreType<TYPE> = {
|
||||
get: <MODEL>(value: MODEL, key?: string) => TYPE | undefined;
|
||||
gets: <MODEL>(value: MODEL[] | undefined, key?: string) => TYPE[];
|
||||
update: (request: Promise<TYPE>, key?: string) => Promise<TYPE>;
|
||||
updateRaw: (data: TYPE, key?: string) => void;
|
||||
remove: (id: number | string, request: Promise<void>, key?: string) => void;
|
||||
};
|
||||
|
||||
@ -95,17 +96,24 @@ export const useDataStore = <TYPE>(
|
||||
[data]
|
||||
);
|
||||
|
||||
const updateRaw = useCallback(
|
||||
(responseData: TYPE, key?: string): void => {
|
||||
const keyValue = key ?? primaryKey;
|
||||
const filterData = data.filter(
|
||||
(localData: TYPE) => localData[keyValue] !== responseData[keyValue]
|
||||
);
|
||||
filterData.push(responseData);
|
||||
setData(filterData);
|
||||
},
|
||||
[data, setData]
|
||||
);
|
||||
|
||||
const update = useCallback(
|
||||
(request: Promise<TYPE>, key?: string): Promise<TYPE> => {
|
||||
const keyValue = key ?? primaryKey;
|
||||
return new Promise((resolve, rejects) => {
|
||||
request
|
||||
.then((responseData: TYPE) => {
|
||||
const filterData = data.filter(
|
||||
(localData: TYPE) => localData[keyValue] !== responseData[keyValue]
|
||||
);
|
||||
filterData.push(responseData);
|
||||
setData(filterData);
|
||||
updateRaw(responseData, key);
|
||||
resolve(responseData);
|
||||
})
|
||||
.catch((error: RestErrorResponse) => {
|
||||
@ -114,7 +122,7 @@ export const useDataStore = <TYPE>(
|
||||
});
|
||||
});
|
||||
},
|
||||
[data, setData]
|
||||
[data, updateRaw]
|
||||
);
|
||||
const remove = useCallback(
|
||||
(id: number | string, request: Promise<void>, key?: string) => {
|
||||
@ -136,5 +144,5 @@ export const useDataStore = <TYPE>(
|
||||
[data, setData]
|
||||
);
|
||||
|
||||
return { isLoading, error, data, get, gets, update, remove };
|
||||
return { isLoading, error, data, get, gets, update, updateRaw, remove };
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user