2021-04-15 04:11:34 +02:00
|
|
|
/*
|
2024-09-09 15:57:16 +02:00
|
|
|
Copyright 2024 New Vector Ltd.
|
|
|
|
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
2021-04-15 04:11:34 +02:00
|
|
|
|
2024-09-09 15:57:16 +02:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2021-04-15 04:11:34 +02:00
|
|
|
*/
|
|
|
|
|
2022-02-18 15:29:08 +01:00
|
|
|
import { Optional } from "matrix-events-sdk";
|
2023-08-08 12:12:12 +02:00
|
|
|
import { Room, IEventRelation, RelationType } from "matrix-js-sdk/src/matrix";
|
2022-02-18 15:29:08 +01:00
|
|
|
|
2021-06-29 14:11:58 +02:00
|
|
|
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
2021-04-15 04:11:34 +02:00
|
|
|
import defaultDispatcher from "../dispatcher/dispatcher";
|
2021-06-29 14:11:58 +02:00
|
|
|
import { ActionPayload } from "../dispatcher/payloads";
|
2022-09-21 18:46:28 +02:00
|
|
|
import { createVoiceMessageRecording, VoiceMessageRecording } from "../audio/VoiceMessageRecording";
|
2021-04-15 04:11:34 +02:00
|
|
|
|
2022-09-05 12:04:37 +02:00
|
|
|
const SEPARATOR = "|";
|
|
|
|
|
2021-04-15 04:11:34 +02:00
|
|
|
interface IState {
|
2022-09-21 18:46:28 +02:00
|
|
|
[voiceRecordingId: string]: Optional<VoiceMessageRecording>;
|
2021-04-15 04:11:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export class VoiceRecordingStore extends AsyncStoreWithClient<IState> {
|
|
|
|
private static internalInstance: VoiceRecordingStore;
|
|
|
|
|
|
|
|
public constructor() {
|
|
|
|
super(defaultDispatcher, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
public static get instance(): VoiceRecordingStore {
|
2022-08-30 21:13:39 +02:00
|
|
|
if (!this.internalInstance) {
|
|
|
|
this.internalInstance = new VoiceRecordingStore();
|
2024-10-10 16:08:43 +02:00
|
|
|
this.internalInstance.start();
|
2021-04-15 04:11:34 +02:00
|
|
|
}
|
2022-08-30 21:13:39 +02:00
|
|
|
return this.internalInstance;
|
2021-04-15 04:11:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected async onAction(payload: ActionPayload): Promise<void> {
|
|
|
|
// Nothing to do, but we're required to override the function
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-05 12:04:37 +02:00
|
|
|
public static getVoiceRecordingId(room: Room, relation?: IEventRelation): string {
|
|
|
|
if (relation?.rel_type === "io.element.thread" || relation?.rel_type === RelationType.Thread) {
|
|
|
|
return room.roomId + SEPARATOR + relation.event_id;
|
|
|
|
} else {
|
|
|
|
return room.roomId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-18 15:29:08 +01:00
|
|
|
/**
|
|
|
|
* Gets the active recording instance, if any.
|
2022-09-05 12:04:37 +02:00
|
|
|
* @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to get the recording in.
|
2022-02-18 15:29:08 +01:00
|
|
|
* @returns {Optional<VoiceRecording>} The recording, if any.
|
|
|
|
*/
|
2022-09-21 18:46:28 +02:00
|
|
|
public getActiveRecording(voiceRecordingId: string): Optional<VoiceMessageRecording> {
|
2022-09-05 12:04:37 +02:00
|
|
|
return this.state[voiceRecordingId];
|
2022-02-18 15:29:08 +01:00
|
|
|
}
|
|
|
|
|
2021-04-15 04:11:34 +02:00
|
|
|
/**
|
|
|
|
* Starts a new recording if one isn't already in progress. Note that this simply
|
|
|
|
* creates a recording instance - whether or not recording is actively in progress
|
|
|
|
* can be seen via the VoiceRecording class.
|
2022-09-05 12:04:37 +02:00
|
|
|
* @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to start recording in.
|
2021-04-15 04:11:34 +02:00
|
|
|
* @returns {VoiceRecording} The recording.
|
|
|
|
*/
|
2023-02-16 10:38:44 +01:00
|
|
|
public startRecording(voiceRecordingId?: string): VoiceMessageRecording {
|
2021-04-15 04:11:34 +02:00
|
|
|
if (!this.matrixClient) throw new Error("Cannot start a recording without a MatrixClient");
|
2022-09-05 12:04:37 +02:00
|
|
|
if (!voiceRecordingId) throw new Error("Recording must be associated with a room");
|
|
|
|
if (this.state[voiceRecordingId]) throw new Error("A recording is already in progress");
|
2021-04-15 04:11:34 +02:00
|
|
|
|
2022-09-21 18:46:28 +02:00
|
|
|
const recording = createVoiceMessageRecording(this.matrixClient);
|
2021-04-15 04:11:34 +02:00
|
|
|
|
|
|
|
// noinspection JSIgnoredPromiseFromCall - we can safely run this async
|
2022-09-05 12:04:37 +02:00
|
|
|
this.updateState({ ...this.state, [voiceRecordingId]: recording });
|
2021-04-15 04:11:34 +02:00
|
|
|
|
|
|
|
return recording;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disposes of the current recording, no matter the state of it.
|
2022-09-05 12:04:37 +02:00
|
|
|
* @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to dispose of the recording in.
|
2021-04-15 04:11:34 +02:00
|
|
|
* @returns {Promise<void>} Resolves when complete.
|
|
|
|
*/
|
2022-09-05 12:04:37 +02:00
|
|
|
public disposeRecording(voiceRecordingId: string): Promise<void> {
|
|
|
|
this.state[voiceRecordingId]?.destroy(); // stops internally
|
2022-02-24 18:54:06 +01:00
|
|
|
|
|
|
|
const {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
2022-09-05 12:04:37 +02:00
|
|
|
[voiceRecordingId]: _toDelete,
|
2022-02-24 18:54:06 +01:00
|
|
|
...newState
|
|
|
|
} = this.state;
|
|
|
|
// unexpectedly AsyncStore.updateState merges state
|
|
|
|
// AsyncStore.reset actually just *sets*
|
|
|
|
return this.reset(newState);
|
2021-04-15 04:11:34 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-28 07:04:49 +02:00
|
|
|
|
|
|
|
window.mxVoiceRecordingStore = VoiceRecordingStore.instance;
|