Initial commit
This commit is contained in:
37
resources/js/SubmissionsClass/Credits.js
Normal file
37
resources/js/SubmissionsClass/Credits.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @typedef { import('types/CreditsObject.js').CreditsObject} CreditsObject */
|
||||
|
||||
export function Credits(){
|
||||
return {
|
||||
|
||||
/**
|
||||
* Credits. Preloaded at startup.
|
||||
* @type {CreditsObject[]}
|
||||
*/
|
||||
credits: [],
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|null} jsonCredits
|
||||
*/
|
||||
init( jsonCredits= null ){
|
||||
if( jsonCredits !== null ){
|
||||
this.credits = JSON.parse(jsonCredits);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add an empty credit.
|
||||
*/
|
||||
addEmptyCredits() {
|
||||
this.credits.push({name: '', description: ''});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific credits.
|
||||
* @param {number} index index of credit.
|
||||
*/
|
||||
removeCredits( index ){
|
||||
this.credits.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
resources/js/SubmissionsClass/FSFileData.js
Normal file
154
resources/js/SubmissionsClass/FSFileData.js
Normal file
@@ -0,0 +1,154 @@
|
||||
/** @typedef { import('types/UploadchunkResponse.js').UploadchunkResponse} UploadchunkResponse */
|
||||
|
||||
export const CHUNK_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* An uploaded file instance.
|
||||
* Create a new file data.
|
||||
*
|
||||
* @param {string} name Filename
|
||||
* @param {number} totalChunks Total number of chunks of the file.
|
||||
* @param rawFile The JS file element relation.
|
||||
*/
|
||||
export function FSFileData(name, totalChunks, rawFile ) {
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Filename.
|
||||
* @type {string}
|
||||
*/
|
||||
name,
|
||||
|
||||
/**
|
||||
* Number of total chunks based on CHUNK_SIZE.
|
||||
* @type {number}
|
||||
*/
|
||||
totalChunks,
|
||||
|
||||
/**
|
||||
* The JS file element relation.
|
||||
*/
|
||||
rawFile,
|
||||
|
||||
/**
|
||||
* Upload progression value.
|
||||
* @type {number}
|
||||
*/
|
||||
progressValue: 0,
|
||||
|
||||
/**
|
||||
* Current chunk uploaded.
|
||||
* @type {number}
|
||||
*/
|
||||
currentChunk: 0,
|
||||
|
||||
/**
|
||||
* If the upload of the file is finished.
|
||||
* @type {boolean}
|
||||
*/
|
||||
done: false,
|
||||
|
||||
/**
|
||||
* If there is an error during file uploading.
|
||||
* @type {any|null}
|
||||
*/
|
||||
error: null,
|
||||
|
||||
/**
|
||||
* UUID v4 for the file.
|
||||
* @type {`${string}-${string}-${string}-${string}-${string}`}
|
||||
*/
|
||||
uuid: crypto.randomUUID(),
|
||||
|
||||
/**
|
||||
* Look if this file is currently uploading.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isUploading()
|
||||
{
|
||||
return !this.done && !this.error;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build API url.
|
||||
* @param {string} section
|
||||
* @returns {string} The API url.
|
||||
*/
|
||||
buildUrl(section)
|
||||
{
|
||||
return `/api/fs/upload-chunk/${section}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload the file.
|
||||
* @param {string} section section of the file.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async upload(section)
|
||||
{
|
||||
|
||||
if (!this.rawFile)
|
||||
return; // Can't upload in that case.
|
||||
|
||||
/**
|
||||
* Get CSRF token for uploading request.
|
||||
* @type {string}
|
||||
*/
|
||||
const CSRF = document.querySelector('meta[name=csrf-token]')?.content ?? '';
|
||||
|
||||
for (let i = 0; i < this.totalChunks; i++) {
|
||||
|
||||
if (this.error)
|
||||
return; // Abort the process.
|
||||
|
||||
const start = i * CHUNK_SIZE;
|
||||
const end = Math.min(start + CHUNK_SIZE, this.rawFile.size);
|
||||
const chunk = this.rawFile.slice(start, end);
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('file', chunk);
|
||||
formData.append('file_uuid', this.uuid);
|
||||
formData.append('current_chunk', i);
|
||||
formData.append('total_chunks', this.totalChunks);
|
||||
formData.append('filename', this.rawFile.name);
|
||||
formData.append('_token', CSRF);
|
||||
|
||||
// -----
|
||||
// UPLOAD TIME !
|
||||
// -----
|
||||
|
||||
try {
|
||||
const RESPONSE = await fetch(this.buildUrl(section), {method: 'POST', body: formData});
|
||||
|
||||
if (!RESPONSE.ok) // Problem with the request.
|
||||
throw new Error(`${RESPONSE.status} ${RESPONSE.statusText}`);
|
||||
|
||||
/** @type {UploadchunkResponse} */
|
||||
const DATA = await RESPONSE.json();
|
||||
|
||||
if (DATA.success !== true || DATA.uploaded !== true)
|
||||
// The request reached the file server but could not be sent.
|
||||
throw new Error(`${DATA.error}`);
|
||||
|
||||
this.currentChunk = i + 1;
|
||||
this.progressValue = Math.round(((i + 1) / this.totalChunks) * 100);
|
||||
|
||||
if (DATA.finished === true) {
|
||||
this.done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
this.error = 'Error on chunk ' + (i + 1) + '. ' + err.message;
|
||||
this.progressValue = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
133
resources/js/SubmissionsClass/FSUploader.js
Normal file
133
resources/js/SubmissionsClass/FSUploader.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { FSFileData, CHUNK_SIZE } from "./FSFileData.js";
|
||||
|
||||
/**
|
||||
* File uploader on FileServer.
|
||||
* @returns {{files: Array<FSFileData>, section: string, init(): void, readonly numberOfFiles: number, readonly isUploading: boolean, readonly hasErrors: boolean, readonly allFilesUploaded: boolean, totalChunksNumber(*): number, handleSubmitFile(Event): Promise<void>, handleRetryFile(number): Promise<void>, removeFile(number): void}|boolean|this is FSFileData[]|number|boolean}
|
||||
* @constructor
|
||||
*/
|
||||
export function FSUploader(){
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Array of uploaded files.
|
||||
* @type {Array<FSFileData>}
|
||||
*/
|
||||
files: [],
|
||||
|
||||
/**
|
||||
* Section that files must be uploaded.
|
||||
* @type {string}
|
||||
*/
|
||||
section: document.querySelector("meta[name='fs-section']")?.content ?? '',
|
||||
|
||||
/**
|
||||
* Triggered in fs-upload.blade.php
|
||||
* Refresh icons.
|
||||
*
|
||||
* @param {Array<FSFileData>} oldFilesArray
|
||||
*/
|
||||
init( oldFilesArray ){
|
||||
this.$watch('files', () =>{
|
||||
this.$nextTick(() => window.refreshIcons(this.$el) )
|
||||
})
|
||||
|
||||
if( oldFilesArray !== undefined && oldFilesArray.length > 0)
|
||||
this.files = oldFilesArray;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shortcut to files.length.
|
||||
* @returns {number}
|
||||
*/
|
||||
get numberOfFiles(){
|
||||
return this.files.length
|
||||
},
|
||||
|
||||
/**
|
||||
* Look if some files are currently in upload.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isUploading(){
|
||||
return this.files.some(
|
||||
file => file.isUploading
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if some files have an error or not.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get hasErrors(){
|
||||
return this.files.some(
|
||||
file => file.error
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if all files are uploaded.
|
||||
* True if all files are uploaded or no one.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get allFilesUploaded(){
|
||||
return ( this.numberOfFiles === 0 || this.files.every(file => file.done) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get total chunks size of a raw file.
|
||||
* @param rawFile
|
||||
* @returns {number}
|
||||
*/
|
||||
totalChunksNumber( rawFile ){
|
||||
return Math.ceil( rawFile.size / CHUNK_SIZE );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle file submission.
|
||||
* You can submit multiple files at once.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async handleSubmitFile( e ){
|
||||
|
||||
const RAW_FILES_FROM_EVENT = Array.from( e.target.files );
|
||||
e.target.value = ''; // Default.
|
||||
|
||||
for( const RAW_FILE of RAW_FILES_FROM_EVENT ){
|
||||
const TOTAL_CHUNKS = this.totalChunksNumber(RAW_FILE);
|
||||
let fsData = FSFileData( RAW_FILE.name, TOTAL_CHUNKS, RAW_FILE );
|
||||
|
||||
this.files.push( fsData );
|
||||
await this.files[this.files.length - 1].upload(this.section);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Retry file uploading.
|
||||
*
|
||||
* @param {number} index FSFileData index in this.files.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
handleRetryFile( index ){
|
||||
|
||||
const OLDFSFILE = this.files[index];
|
||||
let fsData = FSFileData(OLDFSFILE.name, this.totalChunksNumber(OLDFSFILE.rawFile), OLDFSFILE.rawFile );
|
||||
this.files[index] = fsData;
|
||||
|
||||
this.files[index].upload(this.section);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a file.
|
||||
* @param {number} index FSFileData index in this.files.
|
||||
*/
|
||||
handleRemoveFile( index ){
|
||||
this.files.splice(index, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
127
resources/js/SubmissionsClass/GalleryManager.js
Normal file
127
resources/js/SubmissionsClass/GalleryManager.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { MainImageManager as GalleryImage } from "./MainImageManager.js";
|
||||
|
||||
const MAX_GALLERY = 20;
|
||||
|
||||
export function GalleryManager() {
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* All images uploaded.
|
||||
* @type {Array<GalleryImage>}
|
||||
*/
|
||||
images: [],
|
||||
|
||||
/**
|
||||
* Forward to this.images.length
|
||||
* @returns {number}
|
||||
*/
|
||||
get number(){
|
||||
return this.images.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify if all images has been uploaded or not.
|
||||
* @return {boolean}
|
||||
*/
|
||||
get allUploaded(){
|
||||
for(const IMG of this.images){
|
||||
if( IMG.serverFilePath == null ){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return if there is an error on an image or not.
|
||||
*
|
||||
* @returns {null|Array<string>}
|
||||
*/
|
||||
get error(){
|
||||
const RESPONSE = [];
|
||||
for( const IMG of this.images ){
|
||||
if( IMG.error !== null )
|
||||
RESPONSE.push( IMG.error );
|
||||
}
|
||||
if( RESPONSE.length === 0 )
|
||||
return null;
|
||||
return RESPONSE;
|
||||
},
|
||||
|
||||
/**
|
||||
* Superior or equal than 20.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isFull(){
|
||||
return this.images.length >= MAX_GALLERY;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reload image if there is an error or edition.
|
||||
*
|
||||
* @param {Array<string>} oldPaths
|
||||
*/
|
||||
init( oldPaths = null ){
|
||||
if( oldPaths === null || oldPaths.length <= 0 )
|
||||
return;
|
||||
|
||||
for( const PATH of oldPaths ){
|
||||
|
||||
if( this.isFull )
|
||||
break;
|
||||
|
||||
const IMG = GalleryImage();
|
||||
IMG.getOldImage( PATH );
|
||||
this.images.push(IMG);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload images, refresh the preview and store images if there is a problem.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async handleSubmitFiles(e){
|
||||
const FILES = e.target.files;
|
||||
if( !FILES || FILES.length <= 0 )
|
||||
return; // No file uploaded.
|
||||
|
||||
for( const FILE of FILES ){
|
||||
|
||||
if( this.isFull )
|
||||
break;
|
||||
|
||||
const IMG = GalleryImage();
|
||||
IMG.name = FILE.name;
|
||||
IMG.type = FILE.type;
|
||||
this.images.push(IMG);
|
||||
|
||||
const IMG_FROM_LIST = this.images[this.images.length - 1];
|
||||
|
||||
await IMG_FROM_LIST.uploadImageToTemporary( FILE );
|
||||
|
||||
await new Promise( resolve => {
|
||||
const READER = new FileReader();
|
||||
READER.onload = (e2) => {
|
||||
IMG_FROM_LIST.preview = e2.target.result;
|
||||
resolve();
|
||||
}
|
||||
READER.onerror = () => resolve();
|
||||
READER.readAsDataURL(FILE);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove specific file.
|
||||
* @param {number} index
|
||||
*/
|
||||
handleRemoveFile(index){
|
||||
this.images[index].handleRemoveFile(null);
|
||||
this.images.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
resources/js/SubmissionsClass/GameSelector.js
Normal file
38
resources/js/SubmissionsClass/GameSelector.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Handle new game already selected.
|
||||
*
|
||||
* @param {Object} initialContent With gameName, gamePlatformId and gameGenreId field.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function GameSelector(){
|
||||
return {
|
||||
|
||||
/**
|
||||
* Game Name
|
||||
* @type {string|null}
|
||||
*/
|
||||
name: null,
|
||||
|
||||
/**
|
||||
* Game Platform Id.
|
||||
* @type {number|null}
|
||||
*/
|
||||
platformId: null,
|
||||
|
||||
/**
|
||||
* Game genre Id.
|
||||
* @type {number|null}
|
||||
*/
|
||||
genreId: null,
|
||||
|
||||
/**
|
||||
* Initialize game selector.
|
||||
* @param initialContent
|
||||
*/
|
||||
init( initialContent = {} ){
|
||||
this.name = initialContent.name ?? null;
|
||||
this.platformId = Number(initialContent.platformId) ?? null;
|
||||
this.genreId = Number(initialContent.genreId) ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
resources/js/SubmissionsClass/HashesManager.js
Normal file
60
resources/js/SubmissionsClass/HashesManager.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { calculate as calculateHashes } from "../hashes.js";
|
||||
|
||||
export function HashesManager( wire ) {
|
||||
return {
|
||||
|
||||
/**
|
||||
* Wire variable instance.
|
||||
*/
|
||||
$wire: wire,
|
||||
|
||||
/**
|
||||
* If a file hash is currently calculated or not.
|
||||
* @type {boolean}
|
||||
*/
|
||||
isCalculating: false,
|
||||
|
||||
/**
|
||||
* An error on hash calculation.
|
||||
* @type {any|null}
|
||||
*/
|
||||
error: null,
|
||||
|
||||
async handleSubmitFile(){
|
||||
|
||||
if( this.isCalculating === true ) // Calculation already done for another file.
|
||||
return;
|
||||
|
||||
this.error = null; // Reset.
|
||||
const FILE = await this.openFileInput();
|
||||
|
||||
if( !FILE )
|
||||
return; // No file sent.
|
||||
|
||||
this.isCalculating = true;
|
||||
|
||||
try {
|
||||
const RESULT = await calculateHashes(FILE);
|
||||
await this.$wire.addHash(RESULT.filename, RESULT.crc32, RESULT.sha1); // Send a signal to livewire.
|
||||
} catch(err) {
|
||||
this.error = err.message;
|
||||
} finally {
|
||||
this.isCalculating = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a specific file.
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
async openFileInput(){
|
||||
return new Promise(resolve => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.onchange = () => resolve(input.files[0] ?? null );
|
||||
input.oncancel = () => resolve(null);
|
||||
input.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
151
resources/js/SubmissionsClass/MainImageManager.js
Normal file
151
resources/js/SubmissionsClass/MainImageManager.js
Normal file
@@ -0,0 +1,151 @@
|
||||
export function MainImageManager() {
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* If an image has been uploaded or not.
|
||||
* @type {boolean}
|
||||
*/
|
||||
uploaded: false,
|
||||
|
||||
/**
|
||||
* Actual image path on the server.
|
||||
* @type {string|null}
|
||||
*/
|
||||
serverFilePath: null,
|
||||
|
||||
/**
|
||||
* Image filename.
|
||||
* @type {string|null}
|
||||
*/
|
||||
name: null,
|
||||
|
||||
/**
|
||||
* Image filetype.
|
||||
* @type {string|null}
|
||||
*/
|
||||
type: null,
|
||||
|
||||
/**
|
||||
* Handle preview.
|
||||
* @type {unknown}
|
||||
*/
|
||||
preview: null,
|
||||
|
||||
/**
|
||||
* Current error message.
|
||||
* @type {string}
|
||||
*/
|
||||
error: null,
|
||||
|
||||
/**
|
||||
* Reload image if there is an error.
|
||||
*
|
||||
* @param {string|null} oldPath If there is already a path.
|
||||
*/
|
||||
init( oldPath = null ){
|
||||
if(oldPath === "" || oldPath === null)
|
||||
return;
|
||||
|
||||
this.getOldImage(oldPath)
|
||||
},
|
||||
|
||||
/**
|
||||
* Get old image from old path and refresh preview.
|
||||
* @param {string} oldPath
|
||||
* @param {function|null} callback Used in the gallery to push the image at the right time.
|
||||
*/
|
||||
getOldImage( oldPath, callback = null ) {
|
||||
this.readOldImage( '/storage/' + oldPath ).then( blob => {
|
||||
|
||||
this.uploaded = true;
|
||||
this.serverFilePath = oldPath;
|
||||
|
||||
const READER = new FileReader();
|
||||
READER.onload = () => {
|
||||
this.preview = READER.result;
|
||||
}
|
||||
READER.readAsDataURL(blob);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get old image data.
|
||||
* @param {string} url
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
async readOldImage(url){
|
||||
const RESPONSE = await fetch(url);
|
||||
return await RESPONSE.blob();
|
||||
},
|
||||
|
||||
async uploadImageToTemporary( file ){
|
||||
|
||||
const CSRF = document.querySelector('meta[name=csrf-token]')?.content ?? '';
|
||||
const URL = `/api/tempfile/upload`;
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('file', file);
|
||||
formData.append('_token', CSRF );
|
||||
|
||||
try {
|
||||
const RESPONSE = await fetch( URL, { method: 'POST', body: formData } );
|
||||
|
||||
if( !RESPONSE.ok ) // Problem with the request.
|
||||
throw new Error(`${RESPONSE.status} ${RESPONSE.statusText}`);
|
||||
|
||||
const DATA = await RESPONSE.json();
|
||||
|
||||
if( DATA.path === null ){
|
||||
throw new Error(`${RESPONSE.status} ${RESPONSE.statusText}`);
|
||||
}
|
||||
|
||||
this.serverFilePath = DATA.path;
|
||||
this.uploaded = true;
|
||||
|
||||
} catch (err){
|
||||
this.error = 'Error on main image uploading ' + ( i + 1 ) + '. ' + err.message;
|
||||
console.error( this.error );
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload image, refresh the preview and store image if there is a problem.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
async handleSubmitFile(e) {
|
||||
const FILE = e.target.files[0];
|
||||
if (!FILE)
|
||||
return; // No file uploaded.
|
||||
|
||||
this.handleRemoveFile(null);
|
||||
await this.uploadImageToTemporary(FILE);
|
||||
|
||||
this.name = FILE.name;
|
||||
this.type = FILE.type;
|
||||
|
||||
const READER = new FileReader();
|
||||
READER.onload = (e2) => {
|
||||
this.preview = e2.target.result;
|
||||
}
|
||||
READER.readAsDataURL(FILE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove main image.
|
||||
* @param {Event} e
|
||||
*/
|
||||
handleRemoveFile(e){
|
||||
this.uploaded = false;
|
||||
this.serverFilePath = null;
|
||||
this.preview = null;
|
||||
this.name = null;
|
||||
this.type = null;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
18
resources/js/SubmissionsClass/types/CreditsObject.js
Normal file
18
resources/js/SubmissionsClass/types/CreditsObject.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @typedef {Object} CreditsObject
|
||||
*
|
||||
* @property {string} name Credits name.
|
||||
* @property {string} description Credits description.
|
||||
*/
|
||||
|
||||
/**
|
||||
* If this object is a credit object or not.
|
||||
*
|
||||
* @param {object} object
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isACreditsObject( object ){
|
||||
return typeof object === 'object' && object.name !== undefined && object.description !== undefined;
|
||||
}
|
||||
|
||||
export {}
|
||||
13
resources/js/SubmissionsClass/types/UploadchunkResponse.js
Normal file
13
resources/js/SubmissionsClass/types/UploadchunkResponse.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @typedef {Object} UploadchunkResponse
|
||||
*
|
||||
* @see app/Http/FileServerController.php
|
||||
* @external RHPZFS::src/Endpoints/Uploadchunk
|
||||
*
|
||||
* @property {number} chunk The current chunk that has been uploaded.
|
||||
* @property {number} total_chunks Number of total chunks.
|
||||
* @property {boolean} uploaded If the chunk has been correctly uploaded.
|
||||
* @property {Object|boolean} file If the file has been entirely uploaded or not.
|
||||
* @property {boolean} finished Added by main server. Indicates if the file upload is finished or not.
|
||||
*/
|
||||
export {}
|
||||
Reference in New Issue
Block a user