diff --git a/flottform/forms/src/default-component.ts b/flottform/forms/src/default-component.ts index 025cf36..9c3f81f 100644 --- a/flottform/forms/src/default-component.ts +++ b/flottform/forms/src/default-component.ts @@ -104,7 +104,7 @@ export const createDefaultFlottformComponent = ({ const flottformBaseInputHost = new FlottformFileInputHost({ flottformApi, createClientUrl, - inputField + incomingInputField: inputField }); const { @@ -260,7 +260,7 @@ const handleFileInputStates = ({ } flottformFileInputHost.on( - 'progress', + 'file-receiving-progress', ({ currentFileProgress, overallProgress, fileIndex, totalFileCount, fileName }) => { removeConnectionStatusInformation(flottformStateItemsContainer); updateOverallFilesStatusBar( @@ -279,7 +279,7 @@ const handleFileInputStates = ({ ); } ); - flottformFileInputHost.on('done', () => { + flottformFileInputHost.on('single-file-received', () => { statusInformation.innerHTML = onSuccessText ?? `✨ You have succesfully downloaded all your files.`; statusInformation.appendChild(refreshChannelButton); diff --git a/flottform/forms/src/flottform-file-input-client.ts b/flottform/forms/src/flottform-file-input-client.ts index 7151e81..bf1b2ec 100644 --- a/flottform/forms/src/flottform-file-input-client.ts +++ b/flottform/forms/src/flottform-file-input-client.ts @@ -2,10 +2,21 @@ import { FlottformChannelClient } from './flottform-channel-client'; import { DEFAULT_WEBRTC_CONFIG, EventEmitter, Logger, POLL_TIME_IN_MS } from './internal'; type Listeners = { + init: []; connected: []; 'webrtc:connection-impossible': []; - sending: []; // Emitted to signal the start of sending the file(s) - progress: [{ fileIndex: number; fileName: string; progress: number }]; // Emitted to signal the progress of sending the file(s) + 'file-sending-progress': [ + { fileIndex: number; totalFileCount: number; fileName: string; currentFileProgress: number } + ]; + 'single-file-transfered': [{ name: string; type: string; size: number }]; + 'file-receiving-progress': { + fileIndex: number; + totalFileCount: number; + fileName: string; + currentFileProgress: number; + overallProgress: number; + }[]; + 'single-file-received': [file: File]; done: []; disconnected: []; error: [e: string]; @@ -17,27 +28,53 @@ type MetaData = { totalSize: number; }; +type FileSendState = { + metaData: { name: string; type: string; size: number }[]; + arrayBuffers: ArrayBuffer[]; + currentFileIndex: number; + currentChunkIndex: number; +}; + +type FileReceiveState = { + metaData: { name: string; type: string; size: number }[]; + currentFile: { index: number; receivedSize: number; arrayBuffer: ArrayBuffer[] } | null; + totalSize: number; + receivedSize: number; +}; + export class FlottformFileInputClient extends EventEmitter { private channel: FlottformChannelClient | null = null; - private inputField: HTMLInputElement; + private outgoingInputField?: HTMLInputElement; // Contains files to send to the other peer. + private incomingInputField?: HTMLInputElement; // Contains files received from the other peer. + // Sending files state (Client -> Host) + private sendState: FileSendState = { + metaData: [], + arrayBuffers: [], + currentFileIndex: 0, + currentChunkIndex: 0 + }; + // Receiving files state (Host -> Client) + private receiveState: FileReceiveState = { + metaData: [], + currentFile: null, + receivedSize: 0, + totalSize: 0 + }; private chunkSize: number = 16384; // 16 KB chunks - private filesMetaData: { name: string; type: string; size: number }[] = []; - private filesArrayBuffer: ArrayBuffer[] = []; - private currentFileIndex = 0; - private currentChunkIndex = 0; - private allFilesSent = false; private logger: Logger; constructor({ endpointId, - fileInput, + outgoingInputField, + incomingInputField, flottformApi, rtcConfiguration = DEFAULT_WEBRTC_CONFIG, pollTimeForIceInMs = POLL_TIME_IN_MS, logger = console }: { endpointId: string; - fileInput: HTMLInputElement; + outgoingInputField?: HTMLInputElement; + incomingInputField?: HTMLInputElement; flottformApi: string; rtcConfiguration?: RTCConfiguration; pollTimeForIceInMs?: number; @@ -51,7 +88,8 @@ export class FlottformFileInputClient extends EventEmitter { pollTimeForIceInMs, logger }); - this.inputField = fileInput; + this.outgoingInputField = outgoingInputField; + this.incomingInputField = incomingInputField; this.logger = logger; this.registerListeners(); } @@ -63,6 +101,14 @@ export class FlottformFileInputClient extends EventEmitter { this.channel?.close(); }; + setIncomingInputField = (incomingInputField: HTMLInputElement) => { + this.incomingInputField = incomingInputField; + }; + + setOutgoingInputField = (outgoingInputField: HTMLInputElement) => { + this.outgoingInputField = outgoingInputField; + }; + private createMetaData = (inputElement: HTMLInputElement): MetaData | null => { if (!inputElement.files) return null; @@ -89,17 +135,19 @@ export class FlottformFileInputClient extends EventEmitter { }; sendFiles = async () => { - const metaData = this.createMetaData(this.inputField); - const filesArrayBuffer = await this.createArrayBuffers(this.inputField); + if (!this.outgoingInputField) { + throw new Error('Input Field containing the files to send is not provided!'); + } + const metaData = this.createMetaData(this.outgoingInputField); + const filesArrayBuffer = await this.createArrayBuffers(this.outgoingInputField); if (!metaData || !filesArrayBuffer) throw new Error("Can't find the files that you want to send!"); - this.filesMetaData = metaData.filesQueue; - this.filesArrayBuffer = filesArrayBuffer; + this.sendState.metaData = metaData.filesQueue; + this.sendState.arrayBuffers = filesArrayBuffer; this.channel?.sendData(JSON.stringify(metaData)); - this.emit('sending'); this.startSendingFiles(); }; @@ -108,49 +156,62 @@ export class FlottformFileInputClient extends EventEmitter { }; private sendNextChunk = async () => { - const totalNumberOfFiles = this.filesMetaData.length; - if (this.allFilesSent || this.currentFileIndex >= totalNumberOfFiles) { - // All files are sent + const totalNumberOfFiles = this.sendState.metaData.length; + if (this.sendState.currentFileIndex >= totalNumberOfFiles) { this.logger.log('All files are sent'); - this.channel?.sendData(JSON.stringify({ type: 'transfer-complete' })); - this.allFilesSent = true; - this.channel?.off('bufferedamountlow', this.startSendingFiles); - this.emit('done'); + this.channel?.sendData(JSON.stringify({ type: 'files-batch-transfer-complete' })); + this.sendState = { + metaData: [], + arrayBuffers: [], + currentFileIndex: 0, + currentChunkIndex: 0 + }; return; } - const currentFileArrayBuffer = this.filesArrayBuffer[this.currentFileIndex]; + const currentFileArrayBuffer = this.sendState.arrayBuffers[this.sendState.currentFileIndex]; if (!currentFileArrayBuffer) { - throw new Error(`Can't find the ArrayBuffer for the file number ${this.currentFileIndex}`); + throw new Error( + `Can't find the ArrayBuffer for the file number ${this.sendState.currentFileIndex}` + ); } const currentFileSize = currentFileArrayBuffer.byteLength; - const fileName = this.filesMetaData[this.currentFileIndex]!.name; + const fileName = this.sendState.metaData[this.sendState.currentFileIndex]!.name; - while (this.currentChunkIndex * this.chunkSize < currentFileSize) { + while (this.sendState.currentChunkIndex * this.chunkSize < currentFileSize) { // The file still has some chunks left if (!this.channel?.canSendMoreData()) { // Buffer is full. Pause sending the chunks this.logger.log('Buffer is full. Pausing sending chunks!'); break; } - const progress = ((this.currentChunkIndex * this.chunkSize) / currentFileSize).toFixed(2); + const progress = ( + (this.sendState.currentChunkIndex * this.chunkSize) / + currentFileSize + ).toFixed(2); - this.emit('progress', { - fileIndex: this.currentFileIndex, + this.emit('file-sending-progress', { + fileIndex: this.sendState.currentFileIndex, fileName, - progress: parseFloat(progress) + currentFileProgress: parseFloat(progress), + totalFileCount: this.sendState.arrayBuffers.length }); - const start = this.currentChunkIndex * this.chunkSize; - const end = Math.min((this.currentChunkIndex + 1) * this.chunkSize, currentFileSize); + const start = this.sendState.currentChunkIndex * this.chunkSize; + const end = Math.min( + (this.sendState.currentChunkIndex + 1) * this.chunkSize, + currentFileSize + ); this.channel?.sendData(currentFileArrayBuffer.slice(start, end)); - this.currentChunkIndex++; + this.sendState.currentChunkIndex++; } // Now either: the buffer is full OR all the chunks of the file have been sent. - if (this.currentChunkIndex * this.chunkSize >= currentFileSize) { + if (this.sendState.currentChunkIndex * this.chunkSize >= currentFileSize) { // File is fully sent move to the next one this.logger.log(`File ${fileName} fully sent. Moving to next file.`); - this.currentFileIndex++; - this.currentChunkIndex = 0; + const fileTransferedMetaData = this.sendState.metaData[this.sendState.currentFileIndex]; + this.emit('single-file-transfered', fileTransferedMetaData!); + this.sendState.currentFileIndex++; + this.sendState.currentChunkIndex = 0; this.sendNextChunk(); // Recursion used to send the chunks of the next file } else { // Paused waiting for buffer space. Will try again shortly. @@ -158,38 +219,129 @@ export class FlottformFileInputClient extends EventEmitter { } }; + private handleIncomingData = (e: MessageEvent) => { + if (typeof e.data === 'string') { + // string can be either metadata or end transfer marker. + const message = JSON.parse(e.data); + if (message.type === 'file-transfer-meta') { + // Handle file metadata + this.receiveState.metaData = message.filesQueue; + this.receiveState.currentFile = { index: 0, receivedSize: 0, arrayBuffer: [] }; + this.receiveState.totalSize = message.totalSize; + } else if (message.type === 'files-batch-transfer-complete') { + this.logger.log('Current batch of files received successfully!'); + this.receiveState = { + metaData: [], + currentFile: null, + receivedSize: 0, + totalSize: 0 + }; + } + } else if (e.data instanceof ArrayBuffer) { + // Handle file chunk + if (this.receiveState.currentFile) { + this.receiveState.currentFile.arrayBuffer.push(e.data); + this.receiveState.currentFile.receivedSize += e.data.byteLength; + this.receiveState.receivedSize += e.data.byteLength; + + const currentFileName = this.receiveState.metaData[this.receiveState.currentFile.index] + ?.name as string; + + const currentFileTotalSize = this.receiveState.metaData[this.receiveState.currentFile.index] + ?.size as number; + + const currentFileProgress = ( + this.receiveState.currentFile.receivedSize / currentFileTotalSize + ).toFixed(2); + const overallProgress = ( + this.receiveState.receivedSize / this.receiveState.totalSize + ).toFixed(2); + + this.emit('file-receiving-progress', { + fileIndex: this.receiveState.currentFile.index, + totalFileCount: this.receiveState.metaData.length, + fileName: currentFileName, + currentFileProgress: parseFloat(currentFileProgress), + overallProgress: parseFloat(overallProgress) + }); + + if (this.receiveState.currentFile.receivedSize === currentFileTotalSize) { + // Attach the current file to the given input field + this.appendFileToInputField(this.receiveState.currentFile.index); + // Initialize the values of receiveState.currentFile to receive the next file + this.receiveState.currentFile = { + index: this.receiveState.currentFile.index + 1, + receivedSize: 0, + arrayBuffer: [] + }; + } + } + } + }; + + private appendFileToInputField = (fileIndex: number) => { + const fileName = this.receiveState.metaData[fileIndex]?.name ?? 'no-name'; + const fileType = this.receiveState.metaData[fileIndex]?.type ?? 'application/octet-stream'; + + const receivedFile = new File( + this.receiveState.currentFile?.arrayBuffer as ArrayBuffer[], + fileName, + { + type: fileType + } + ); + this.emit('single-file-received', receivedFile); + + if (!this.incomingInputField) { + this.logger.warn( + "No input field provided!! You can listen to the 'single-file-transferred' event to handle the newly received file!" + ); + return; + } + + const dt = new DataTransfer(); + + // Add existing files from the input field to the DataTransfer object to avoid loosing them. + if (this.incomingInputField.files) { + for (const file of Array.from(this.incomingInputField.files)) { + dt.items.add(file); + } + } + + if (!this.incomingInputField.multiple) { + this.logger.warn( + "The Host's input field only supports one file. Incoming files from the Host will overwrite any existing file, and only the last file received will remain attached." + ); + dt.items.clear(); + } + + dt.items.add(receivedFile); + this.incomingInputField.files = dt.files; + }; + private registerListeners = () => { this.channel?.on('init', () => { - // TODO: Implement Default UI. - }); - this.channel?.on('retrieving-info-from-endpoint', () => { - // TODO: Implement Default UI. - }); - this.channel?.on('sending-client-info', () => { - // TODO: Implement Default UI. - }); - this.channel?.on('connecting-to-host', () => { - // TODO: Implement Default UI. + this.emit('init'); }); + this.channel?.on('retrieving-info-from-endpoint', () => {}); + this.channel?.on('sending-client-info', () => {}); + this.channel?.on('connecting-to-host', () => {}); this.channel?.on('connected', () => { this.emit('connected'); - // TODO: Implement Default UI. + }); + this.channel?.on('receiving-data', (e) => { + //Handle file(s) reception + this.handleIncomingData(e); }); this.channel?.on('connection-impossible', () => { this.emit('webrtc:connection-impossible'); - // TODO: Implement Default UI. - }); - this.channel?.on('done', () => { - this.emit('done'); - // TODO: Implement Default UI. }); + this.channel?.on('done', () => {}); this.channel?.on('disconnected', () => { this.emit('disconnected'); - // TODO: Implement Default UI. }); this.channel?.on('error', (e) => { this.emit('error', e); - // TODO: Implement Default UI. }); this.channel?.on('bufferedamountlow', this.startSendingFiles); }; diff --git a/flottform/forms/src/flottform-file-input-host.ts b/flottform/forms/src/flottform-file-input-host.ts index 42f838b..7fde251 100644 --- a/flottform/forms/src/flottform-file-input-host.ts +++ b/flottform/forms/src/flottform-file-input-host.ts @@ -8,45 +8,77 @@ import { } from './internal'; type Listeners = BaseListeners & { - receive: []; // Emitted to signal the start of receiving the file(s) - progress: { + 'file-receiving-progress': { fileIndex: number; totalFileCount: number; fileName: string; currentFileProgress: number; overallProgress: number; - }[]; // Emitted to signal the progress of receiving the file(s) - done: []; + }[]; + 'single-file-received': [file: File]; + 'file-sending-progress': [ + { fileIndex: number; totalFileCount: number; fileName: string; currentFileProgress: number } + ]; + 'single-file-transfered': [{ name: string; type: string; size: number }]; 'webrtc:waiting-for-file': []; - 'single-file-transferred': [file: File]; +}; + +type MetaData = { + type: 'file-transfer-meta'; + filesQueue: { name: string; type: string; size: number }[]; + totalSize: number; +}; + +type FileSendState = { + metaData: { name: string; type: string; size: number }[]; + arrayBuffers: ArrayBuffer[]; + currentFileIndex: number; + currentChunkIndex: number; +}; + +type FileReceiveState = { + metaData: { name: string; type: string; size: number }[]; + currentFile: { index: number; receivedSize: number; arrayBuffer: ArrayBuffer[] } | null; + totalSize: number; + receivedSize: number; }; export class FlottformFileInputHost extends BaseInputHost { private channel: FlottformChannelHost | null = null; - private inputField?: HTMLInputElement; + private incomingInputField?: HTMLInputElement; + private outgoingInputField?: HTMLInputElement; // Contains files to send to the other peer. + // Sending files state (Host -> Client) + private sendState: FileSendState = { + metaData: [], + arrayBuffers: [], + currentFileIndex: 0, + currentChunkIndex: 0 + }; + // Receiving files state (Client -> Host) + private receiveState: FileReceiveState = { + metaData: [], + currentFile: null, + receivedSize: 0, + totalSize: 0 + }; private logger: Logger; - private filesMetaData: { name: string; type: string; size: number }[] = []; - private filesTotalSize: number = 0; - private receivedDataSize: number = 0; - private currentFile: { - index: number; - receivedSize: number; - arrayBuffer: ArrayBuffer[]; - } | null = null; + private chunkSize: number = 16384; // 16 KB chunks private link: string = ''; private qrCode: string = ''; constructor({ flottformApi, createClientUrl, - inputField, + incomingInputField, + outgoingInputField, rtcConfiguration = DEFAULT_WEBRTC_CONFIG, pollTimeForIceInMs = POLL_TIME_IN_MS, logger = console }: { flottformApi: string | URL; createClientUrl: (params: { endpointId: string }) => Promise; - inputField?: HTMLInputElement; + incomingInputField?: HTMLInputElement; + outgoingInputField?: HTMLInputElement; rtcConfiguration?: RTCConfiguration; pollTimeForIceInMs?: number; theme?: (myself: FlottformFileInputHost) => void; @@ -60,7 +92,8 @@ export class FlottformFileInputHost extends BaseInputHost { pollTimeForIceInMs, logger }); - this.inputField = inputField; + this.incomingInputField = incomingInputField; + this.outgoingInputField = outgoingInputField; this.logger = logger; this.registerListeners(); @@ -92,51 +125,178 @@ export class FlottformFileInputHost extends BaseInputHost { return this.qrCode; }; + setIncomingInputField = (incomingInputField: HTMLInputElement) => { + this.incomingInputField = incomingInputField; + }; + + setOutgoingInputField = (outgoingInputField: HTMLInputElement) => { + this.outgoingInputField = outgoingInputField; + }; + + private createMetaData = (inputElement: HTMLInputElement): MetaData | null => { + if (!inputElement.files) return null; + + // Generate all the metadata necessary to send all of the files + const files = Array.from(inputElement.files); + const filesQueue = files.map((file) => ({ + name: file.name, + type: file.type, // We're dividing each file into chuncks no matter what the type of the file. + size: file.size + })); + return { + type: 'file-transfer-meta', + filesQueue, + totalSize: filesQueue.reduce((sum, file) => sum + file.size, 0) + }; + }; + + private createArrayBuffers = async (inputElement: HTMLInputElement) => { + if (!inputElement.files) return null; + + // Generate all the arrayBuffers of the available files + const files = Array.from(inputElement.files); + return await Promise.all(files.map(async (file) => await file.arrayBuffer())); + }; + + sendFiles = async () => { + if (!this.outgoingInputField) { + throw new Error('Input Field containing the files to send is not provided!'); + } + const metaData = this.createMetaData(this.outgoingInputField); + const filesArrayBuffer = await this.createArrayBuffers(this.outgoingInputField); + if (!metaData || !filesArrayBuffer) + throw new Error("Can't find the files that you want to send!"); + + this.sendState.metaData = metaData.filesQueue; + this.sendState.arrayBuffers = filesArrayBuffer; + + this.channel?.sendData(JSON.stringify(metaData)); + + this.startSendingFiles(); + }; + + private startSendingFiles = () => { + this.sendNextChunk(); + }; + + private sendNextChunk = async () => { + const totalNumberOfFiles = this.sendState.metaData.length; + if (this.sendState.currentFileIndex >= totalNumberOfFiles) { + this.logger.log('All files are sent'); + this.channel?.sendData(JSON.stringify({ type: 'files-batch-transfer-complete' })); + this.sendState = { + metaData: [], + arrayBuffers: [], + currentFileIndex: 0, + currentChunkIndex: 0 + }; + return; + } + const currentFileArrayBuffer = this.sendState.arrayBuffers[this.sendState.currentFileIndex]; + if (!currentFileArrayBuffer) { + throw new Error( + `Can't find the ArrayBuffer for the file number ${this.sendState.currentFileIndex}` + ); + } + const currentFileSize = currentFileArrayBuffer.byteLength; + const fileName = this.sendState.metaData[this.sendState.currentFileIndex]!.name; + + while (this.sendState.currentChunkIndex * this.chunkSize < currentFileSize) { + // The file still has some chunks left + if (!this.channel?.canSendMoreData()) { + // Buffer is full. Pause sending the chunks + this.logger.log('Buffer is full. Pausing sending chunks!'); + break; + } + const progress = ( + (this.sendState.currentChunkIndex * this.chunkSize) / + currentFileSize + ).toFixed(2); + + this.emit('file-sending-progress', { + fileIndex: this.sendState.currentFileIndex, + fileName, + currentFileProgress: parseFloat(progress), + totalFileCount: this.sendState.arrayBuffers.length + }); + + const start = this.sendState.currentChunkIndex * this.chunkSize; + const end = Math.min( + (this.sendState.currentChunkIndex + 1) * this.chunkSize, + currentFileSize + ); + this.channel?.sendData(currentFileArrayBuffer.slice(start, end)); + this.sendState.currentChunkIndex++; + } + // Now either: the buffer is full OR all the chunks of the file have been sent. + if (this.sendState.currentChunkIndex * this.chunkSize >= currentFileSize) { + // File is fully sent move to the next one + this.logger.log(`File ${fileName} fully sent. Moving to next file.`); + const fileTransferedMetaData = this.sendState.metaData[this.sendState.currentFileIndex]; + this.emit('single-file-transfered', fileTransferedMetaData!); + this.sendState.currentFileIndex++; + this.sendState.currentChunkIndex = 0; + this.sendNextChunk(); // Recursion used to send the chunks of the next file + } else { + // Paused waiting for buffer space. Will try again shortly. + setTimeout(this.sendNextChunk, 100); // Retry after a short delay + } + }; + private handleIncomingData = (e: MessageEvent) => { if (typeof e.data === 'string') { // string can be either metadata or end transfer marker. const message = JSON.parse(e.data); if (message.type === 'file-transfer-meta') { // Handle file metadata - this.filesMetaData = message.filesQueue; - this.currentFile = { index: 0, receivedSize: 0, arrayBuffer: [] }; - this.filesTotalSize = message.totalSize; - // Emit the start of receiving data - this.emit('receive'); - } else if (message.type === 'transfer-complete') { - this.emit('done'); - this.channel?.close(); + this.receiveState.metaData = message.filesQueue; + this.receiveState.currentFile = { index: 0, receivedSize: 0, arrayBuffer: [] }; + this.receiveState.totalSize = message.totalSize; + } else if (message.type === 'files-batch-transfer-complete') { + this.logger.log('Current batch of files received successfully!'); + + this.receiveState = { + metaData: [], + currentFile: null, + receivedSize: 0, + totalSize: 0 + }; } } else if (e.data instanceof ArrayBuffer) { // Handle file chunk - if (this.currentFile) { - this.currentFile.arrayBuffer.push(e.data); - this.currentFile.receivedSize += e.data.byteLength; - this.receivedDataSize += e.data.byteLength; + if (this.receiveState.currentFile) { + this.receiveState.currentFile.arrayBuffer.push(e.data); + this.receiveState.currentFile.receivedSize += e.data.byteLength; + this.receiveState.receivedSize += e.data.byteLength; + + const currentFileName = this.receiveState.metaData[this.receiveState.currentFile.index] + ?.name as string; - const currentFileName = this.filesMetaData[this.currentFile.index]?.name as string; + const currentFileTotalSize = this.receiveState.metaData[this.receiveState.currentFile.index] + ?.size as number; - const currentFileTotalSize = this.filesMetaData[this.currentFile.index]?.size as number; + const currentFileProgress = ( + this.receiveState.currentFile.receivedSize / currentFileTotalSize + ).toFixed(2); - const currentFileProgress = (this.currentFile.receivedSize / currentFileTotalSize).toFixed( - 2 - ); - const overallProgress = (this.receivedDataSize / this.filesTotalSize).toFixed(2); + const overallProgress = ( + this.receiveState.receivedSize / this.receiveState.totalSize + ).toFixed(2); - this.emit('progress', { - fileIndex: this.currentFile.index, - totalFileCount: this.filesMetaData.length, + this.emit('file-receiving-progress', { + fileIndex: this.receiveState.currentFile.index, + totalFileCount: this.receiveState.metaData.length, fileName: currentFileName, currentFileProgress: parseFloat(currentFileProgress), overallProgress: parseFloat(overallProgress) }); - if (this.currentFile.receivedSize === currentFileTotalSize) { + if (this.receiveState.currentFile.receivedSize === currentFileTotalSize) { // Attach the current file to the given input field - this.appendFileToInputField(this.currentFile.index); - // Initialize the values of currentFile to receive the next file - this.currentFile = { - index: this.currentFile.index + 1, + this.appendFileToInputField(this.receiveState.currentFile.index); + // Initialize the values of receiveState.currentFile to receive the next file + this.receiveState.currentFile = { + index: this.receiveState.currentFile.index + 1, receivedSize: 0, arrayBuffer: [] }; @@ -146,15 +306,19 @@ export class FlottformFileInputHost extends BaseInputHost { }; private appendFileToInputField = (fileIndex: number) => { - const fileName = this.filesMetaData[fileIndex]?.name ?? 'no-name'; - const fileType = this.filesMetaData[fileIndex]?.type ?? 'application/octet-stream'; + const fileName = this.receiveState.metaData[fileIndex]?.name ?? 'no-name'; + const fileType = this.receiveState.metaData[fileIndex]?.type ?? 'application/octet-stream'; - const receivedFile = new File(this.currentFile?.arrayBuffer as ArrayBuffer[], fileName, { - type: fileType - }); - this.emit('single-file-transferred', receivedFile); + const receivedFile = new File( + this.receiveState.currentFile?.arrayBuffer as ArrayBuffer[], + fileName, + { + type: fileType + } + ); + this.emit('single-file-received', receivedFile); - if (!this.inputField) { + if (!this.incomingInputField) { this.logger.warn( "No input field provided!! You can listen to the 'single-file-transferred' event to handle the newly received file!" ); @@ -164,13 +328,13 @@ export class FlottformFileInputHost extends BaseInputHost { const dt = new DataTransfer(); // Add existing files from the input field to the DataTransfer object to avoid loosing them. - if (this.inputField.files) { - for (const file of Array.from(this.inputField.files)) { + if (this.incomingInputField.files) { + for (const file of Array.from(this.incomingInputField.files)) { dt.items.add(file); } } - if (!this.inputField.multiple) { + if (!this.incomingInputField.multiple) { this.logger.warn( "The host's input field only supports one file. Incoming files from the client will overwrite any existing file, and only the last file received will remain attached." ); @@ -178,7 +342,7 @@ export class FlottformFileInputHost extends BaseInputHost { } dt.items.add(receivedFile); - this.inputField.files = dt.files; + this.incomingInputField.files = dt.files; }; private registerListeners = () => {