UNPKG

matrix-react-sdk

Version:
148 lines (116 loc) 4.47 kB
/* Copyright 2024 New Vector Ltd. Copyright 2022 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { VoiceBroadcastChunkEventType } from ".."; /** * Voice broadcast chunk collection. * Orders chunks by sequence (if available) or timestamp. */ export class VoiceBroadcastChunkEvents { private events: MatrixEvent[] = []; public getEvents(): MatrixEvent[] { return [...this.events]; } public getNext(event: MatrixEvent): MatrixEvent | undefined { return this.events[this.events.indexOf(event) + 1]; } public addEvent(event: MatrixEvent): void { if (this.addOrReplaceEvent(event)) { this.sort(); } } public addEvents(events: MatrixEvent[]): void { const atLeastOneNew = events.reduce((newSoFar: boolean, event: MatrixEvent): boolean => { return this.addOrReplaceEvent(event) || newSoFar; }, false); if (atLeastOneNew) { this.sort(); } } public includes(event: MatrixEvent): boolean { return !!this.events.find((e) => this.equalByTxnIdOrId(event, e)); } /** * @returns {number} Length in milliseconds */ public getLength(): number { return this.events.reduce((length: number, event: MatrixEvent) => { return length + this.calculateChunkLength(event); }, 0); } public getLengthSeconds(): number { return this.getLength() / 1000; } /** * Returns the accumulated length to (excl.) a chunk event. */ public getLengthTo(event: MatrixEvent): number { let length = 0; for (let i = 0; i < this.events.indexOf(event); i++) { length += this.calculateChunkLength(this.events[i]); } return length; } public findByTime(time: number): MatrixEvent | null { let lengthSoFar = 0; for (let i = 0; i < this.events.length; i++) { lengthSoFar += this.calculateChunkLength(this.events[i]); if (lengthSoFar >= time) { return this.events[i]; } } return null; } public isLast(event: MatrixEvent): boolean { return this.events.indexOf(event) >= this.events.length - 1; } public getSequenceForEvent(event: MatrixEvent): number | null { const sequence = parseInt(event.getContent()?.[VoiceBroadcastChunkEventType]?.sequence, 10); if (!isNaN(sequence)) return sequence; if (this.events.includes(event)) return this.events.indexOf(event) + 1; return null; } public getNumberOfEvents(): number { return this.events.length; } private calculateChunkLength(event: MatrixEvent): number { return event.getContent()?.["org.matrix.msc1767.audio"]?.duration || event.getContent()?.info?.duration || 0; } private addOrReplaceEvent = (event: MatrixEvent): boolean => { this.events = this.events.filter((e) => !this.equalByTxnIdOrId(event, e)); this.events.push(event); return true; }; private equalByTxnIdOrId(eventA: MatrixEvent, eventB: MatrixEvent): boolean { return ( (eventA.getTxnId() && eventB.getTxnId() && eventA.getTxnId() === eventB.getTxnId()) || eventA.getId() === eventB.getId() ); } /** * Sort by sequence, if available for all events. * Else fall back to timestamp. */ private sort(): void { const compareFn = this.allHaveSequence() ? this.compareBySequence : this.compareByTimestamp; this.events.sort(compareFn); } private compareBySequence = (a: MatrixEvent, b: MatrixEvent): number => { const aSequence = a.getContent()?.[VoiceBroadcastChunkEventType]?.sequence || 0; const bSequence = b.getContent()?.[VoiceBroadcastChunkEventType]?.sequence || 0; return aSequence - bSequence; }; private compareByTimestamp = (a: MatrixEvent, b: MatrixEvent): number => { return a.getTs() - b.getTs(); }; private allHaveSequence(): boolean { return !this.events.some((event: MatrixEvent) => { const sequence = event.getContent()?.[VoiceBroadcastChunkEventType]?.sequence; return parseInt(sequence, 10) !== sequence; }); } }