diff --git a/examples/demo/demo.ts b/examples/demo/demo.ts index d9b3944650..8ba62c3790 100644 --- a/examples/demo/demo.ts +++ b/examples/demo/demo.ts @@ -242,10 +242,10 @@ const appActions = { ); }) .on(RoomEvent.TextStreamReceived, async (info, stream, participant) => { - if (info.isFinite && info.topic === 'chat') { + if (info.size && info.topic === 'chat') { handleChatMessage( { - id: info.messageId, + id: info.id, timestamp: info.timestamp, message: (await stream.readAll()).join(''), }, @@ -255,9 +255,9 @@ const appActions = { for await (const msg of stream) { handleChatMessage( { - id: info.messageId, + id: info.id, timestamp: info.timestamp, - message: [state.chatMessages.get(info.messageId)?.text ?? '', msg].join(''), + message: [state.chatMessages.get(info.id)?.text ?? '', msg].join(''), }, participant, ); @@ -349,7 +349,7 @@ const appActions = { link.rel = 'stylesheet'; link.type = styleSheet.type; link.media = styleSheet.media; - link.href = styleSheet.href; + link.href = styleSheet.href!; pipWindow?.document.head.appendChild(link); } }); @@ -357,18 +357,18 @@ const appActions = { const participantsArea = $('participants-area'); const pipParticipantsArea = document.createElement('div'); pipParticipantsArea.id = 'participants-area'; - pipWindow.document.body.append(pipParticipantsArea); + pipWindow?.document.body.append(pipParticipantsArea); [...participantsArea.children].forEach((child) => pipParticipantsArea.append(child)); // Move participant videos back when the Picture-in-Picture window closes. - pipWindow.addEventListener('pagehide', (event) => { + pipWindow?.addEventListener('pagehide', () => { setButtonState('toggle-pip-button', 'Open PiP', false); if (currentRoom?.state === ConnectionState.Connected) [...pipParticipantsArea.children].forEach((child) => participantsArea.append(child)); }); // Close PiP when room disconnects - currentRoom.once('disconnected', (e) => window.documentPictureInPicture?.window.close()); + currentRoom!.once('disconnected', () => window.documentPictureInPicture?.window?.close()); }, ratchetE2EEKey: async () => { @@ -876,14 +876,14 @@ function renderBitrate() { } } -function getParticipantsAreaElement() { +function getParticipantsAreaElement(): HTMLElement { return ( window.documentPictureInPicture?.window?.document.querySelector('#participants-area') || $('participants-area') ); } -function updateVideoSize(element: HTMLVideoElement, target: HTMLElement) { +function updateVideoSize(element: HTMLVideoElement, target: Element) { target.innerHTML = `(${element.videoWidth}x${element.videoHeight})`; } diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index 08e1d3f017..a5204dd619 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -1484,15 +1484,25 @@ export default class LocalParticipant extends Participant { async sendText(text: string, options?: SendTextOptions): Promise<{ streamId: string }> { const streamId = crypto.randomUUID(); const textInBytes = new TextEncoder().encode(text); - const totalLength = textInBytes.byteLength; - const totalChunks = Math.ceil(totalLength / STREAM_CHUNK_SIZE); + const totalTextLength = textInBytes.byteLength; + const totalTextChunks = Math.ceil(totalTextLength / STREAM_CHUNK_SIZE); const fileIds = options?.attachedFiles?.map(() => crypto.randomUUID()); + const progresses = new Array(fileIds ? fileIds.length + 1 : 1).fill(0); + + let totalProgress = 0; + + const handleProgress = (progress: number, idx: number) => { + progresses[idx] = progress; + totalProgress = progresses.reduce((acc, val) => acc + val, 0); + options?.onProgress?.(totalProgress); + }; + const header = new DataStream_Header({ streamId, - totalChunks: numberToBigInt(totalChunks), - totalLength: numberToBigInt(totalLength), + totalChunks: numberToBigInt(totalTextChunks), + totalLength: numberToBigInt(totalTextLength), mimeType: 'text/plain', topic: options?.topic, timestamp: numberToBigInt(Date.now()), @@ -1509,28 +1519,35 @@ export default class LocalParticipant extends Participant { topic: 'streamheader', }); - for (let i = 0; i < totalChunks; i++) { + for (let i = 0; i < totalTextChunks; i++) { const chunkData = textInBytes.slice( i * STREAM_CHUNK_SIZE, - Math.min((i + 1) * STREAM_CHUNK_SIZE, totalLength), + Math.min((i + 1) * STREAM_CHUNK_SIZE, totalTextLength), ); await this.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE); const chunk = new DataStream_Chunk({ content: chunkData, streamId, chunkIndex: numberToBigInt(i), - complete: i === totalChunks - 1, + complete: i === totalTextChunks - 1, }); await this.publishData(chunk.toBinary(), { reliable: true, topic: 'streamchunk', destinationIdentities: options?.destinationIdentities, }); + handleProgress(Math.ceil(i / totalTextChunks), 0); } if (options?.attachedFiles && fileIds) { await Promise.all( options.attachedFiles.map(async (file, idx) => - this._sendFile(fileIds[idx], file, { topic: options.topic, mimeType: file.type }), + this._sendFile(fileIds[idx], file, { + topic: options.topic, + mimeType: file.type, + onProgress: (progress) => { + handleProgress(progress, idx + 1); + }, + }), ), ); } @@ -1620,6 +1637,7 @@ export default class LocalParticipant extends Participant { mimeType?: string; topic?: string; destinationIdentities?: Array; + onProgress?: (progress: number) => void; }, ) { const streamId = crypto.randomUUID(); @@ -1635,6 +1653,7 @@ export default class LocalParticipant extends Participant { topic?: string; encryptionType?: Encryption_Type.NONE; destinationIdentities?: Array; + onProgress?: (progress: number) => void; }, ) { const totalLength = file.size; @@ -1674,6 +1693,7 @@ export default class LocalParticipant extends Participant { file.slice(i * STREAM_CHUNK_SIZE, Math.min((i + 1) * STREAM_CHUNK_SIZE, totalLength)), ); await this.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE); + options?.onProgress?.(i / totalChunks); const chunk = new DataStream_Chunk({ content: chunkData, streamId, diff --git a/src/room/types.ts b/src/room/types.ts index 287a2ed4e6..6bd2ec4895 100644 --- a/src/room/types.ts +++ b/src/room/types.ts @@ -19,6 +19,7 @@ export interface SendTextOptions { // replyToMessageId?: string; destinationIdentities?: Array; attachedFiles?: Array; + onProgress?: (progress: number) => void; } export type DataPublishOptions = {