From b5c106e8f94964659e8369dc02f6300e64d2de4b Mon Sep 17 00:00:00 2001 From: grimhilt Date: Sun, 14 Aug 2022 19:25:20 +0200 Subject: [PATCH 1/8] Allow having two voice message in composers --- .../views/rooms/MessageComposer.tsx | 16 +++++++++-- .../views/rooms/VoiceRecordComposerTile.tsx | 23 +++++++++------ src/stores/VoiceRecordingStore.ts | 28 +++++++++---------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index d3dfd06d900..c26a2041d76 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -19,7 +19,7 @@ import classNames from 'classnames'; import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { EventType } from 'matrix-js-sdk/src/@types/event'; +import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event'; import { Optional } from "matrix-events-sdk"; import { THREAD_RELATION_TYPE } from 'matrix-js-sdk/src/models/thread'; @@ -307,7 +307,12 @@ export default class MessageComposer extends React.Component { }; private updateRecordingState() { - this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); + this.voiceRecording = + this.props.relation?.rel_type === ("io.element.thread") + || this.props.relation?.rel_type === RelationType.Thread ? + VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) + : + VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); if (this.voiceRecording) { // If the recording has already started, it's probably a cached one. if (this.voiceRecording.hasRecording && !this.voiceRecording.isRecording) { @@ -322,7 +327,12 @@ export default class MessageComposer extends React.Component { private onRecordingStarted = () => { // update the recording instance, just in case - this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); + this.voiceRecording = + this.props.relation?.rel_type === ("io.element.thread") + || this.props.relation?.rel_type === RelationType.Thread ? + VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) + : + VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); this.setState({ haveRecording: !!this.voiceRecording, }); diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 338995c3b91..1cb3ccc75c8 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { ReactNode } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; -import { MsgType } from "matrix-js-sdk/src/@types/event"; +import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; import { logger } from "matrix-js-sdk/src/logger"; import { Optional } from "matrix-events-sdk"; import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -64,6 +64,7 @@ interface IState { export default class VoiceRecordComposerTile extends React.PureComponent { static contextType = RoomContext; public context!: React.ContextType; + private voiceRecordingId: string; public constructor(props: IProps) { super(props); @@ -71,10 +72,17 @@ export default class VoiceRecordComposerTile extends React.PureComponent; + [voiceRecordingId: string]: Optional; } export class VoiceRecordingStore extends AsyncStoreWithClient { @@ -46,46 +46,46 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Gets the active recording instance, if any. - * @param {string} roomId The room ID to get the recording in. + * @param {string} voiceRecordingId The room ID to get the recording in. * @returns {Optional} The recording, if any. */ - public getActiveRecording(roomId: string): Optional { - return this.state[roomId]; + public getActiveRecording(voiceRecordingId: string): Optional { + return this.state[voiceRecordingId]; } /** * 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. - * @param {string} roomId The room ID to start recording in. + * @param {string} voiceRecordingId The room ID to start recording in. * @returns {VoiceRecording} The recording. */ - public startRecording(roomId: string): VoiceRecording { + public startRecording(voiceRecordingId: string): VoiceRecording { if (!this.matrixClient) throw new Error("Cannot start a recording without a MatrixClient"); - if (!roomId) throw new Error("Recording must be associated with a room"); - if (this.state[roomId]) throw new Error("A recording is already in progress"); + 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"); const recording = new VoiceRecording(this.matrixClient); // noinspection JSIgnoredPromiseFromCall - we can safely run this async - this.updateState({ ...this.state, [roomId]: recording }); + this.updateState({ ...this.state, [voiceRecordingId]: recording }); return recording; } /** * Disposes of the current recording, no matter the state of it. - * @param {string} roomId The room ID to dispose of the recording in. + * @param {string} voiceRecordingId The room ID to dispose of the recording in. * @returns {Promise} Resolves when complete. */ - public disposeRecording(roomId: string): Promise { - if (this.state[roomId]) { - this.state[roomId].destroy(); // stops internally + public disposeRecording(voiceRecordingId: string): Promise { + if (this.state[voiceRecordingId]) { + this.state[voiceRecordingId].destroy(); // stops internally } const { // eslint-disable-next-line @typescript-eslint/no-unused-vars - [roomId]: _toDelete, + [voiceRecordingId]: _toDelete, ...newState } = this.state; // unexpectedly AsyncStore.updateState merges state From 4b2c16e98e7721b6620ae72d8504f2692b98d8b6 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Sun, 14 Aug 2022 19:31:27 +0200 Subject: [PATCH 2/8] Update comment --- src/stores/VoiceRecordingStore.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stores/VoiceRecordingStore.ts b/src/stores/VoiceRecordingStore.ts index 7d6a9221d2d..d2393ea6086 100644 --- a/src/stores/VoiceRecordingStore.ts +++ b/src/stores/VoiceRecordingStore.ts @@ -46,7 +46,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Gets the active recording instance, if any. - * @param {string} voiceRecordingId The room ID to get the recording in. + * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to get the recording in. * @returns {Optional} The recording, if any. */ public getActiveRecording(voiceRecordingId: string): Optional { @@ -57,7 +57,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { * 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. - * @param {string} voiceRecordingId The room ID to start recording in. + * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to start recording in. * @returns {VoiceRecording} The recording. */ public startRecording(voiceRecordingId: string): VoiceRecording { @@ -75,7 +75,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Disposes of the current recording, no matter the state of it. - * @param {string} voiceRecordingId The room ID to dispose of the recording in. + * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to dispose of the recording in. * @returns {Promise} Resolves when complete. */ public disposeRecording(voiceRecordingId: string): Promise { From 0d47e69b55cfd5b5d88adbbe8d92562d80e28997 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Fri, 19 Aug 2022 18:34:21 +0200 Subject: [PATCH 3/8] fix eslint --- src/components/views/rooms/MessageComposer.tsx | 8 ++++---- src/components/views/rooms/VoiceRecordComposerTile.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index c26a2041d76..488c0d8df14 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -307,11 +307,11 @@ export default class MessageComposer extends React.Component { }; private updateRecordingState() { - this.voiceRecording = + this.voiceRecording = this.props.relation?.rel_type === ("io.element.thread") || this.props.relation?.rel_type === RelationType.Thread ? VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) - : + : VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); if (this.voiceRecording) { // If the recording has already started, it's probably a cached one. @@ -327,11 +327,11 @@ export default class MessageComposer extends React.Component { private onRecordingStarted = () => { // update the recording instance, just in case - this.voiceRecording = + this.voiceRecording = this.props.relation?.rel_type === ("io.element.thread") || this.props.relation?.rel_type === RelationType.Thread ? VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) - : + : VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); this.setState({ haveRecording: !!this.voiceRecording, diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 1cb3ccc75c8..4a73e4fc911 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -77,7 +77,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent Date: Mon, 22 Aug 2022 21:23:07 +0200 Subject: [PATCH 4/8] fix typo --- src/stores/VoiceRecordingStore.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stores/VoiceRecordingStore.ts b/src/stores/VoiceRecordingStore.ts index d2393ea6086..b4f932f2fda 100644 --- a/src/stores/VoiceRecordingStore.ts +++ b/src/stores/VoiceRecordingStore.ts @@ -46,7 +46,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Gets the active recording instance, if any. - * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to get the recording in. + * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to get the recording in. * @returns {Optional} The recording, if any. */ public getActiveRecording(voiceRecordingId: string): Optional { @@ -57,7 +57,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { * 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. - * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to start recording in. + * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to start recording in. * @returns {VoiceRecording} The recording. */ public startRecording(voiceRecordingId: string): VoiceRecording { @@ -75,7 +75,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Disposes of the current recording, no matter the state of it. - * @param {string} voiceRecordingId The room ID (with optionnaly the thread ID if in one) to dispose of the recording in. + * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to dispose of the recording in. * @returns {Promise} Resolves when complete. */ public disposeRecording(voiceRecordingId: string): Promise { From 6d8a38df1ca49475a7abccd7aae55ba7a7532a7b Mon Sep 17 00:00:00 2001 From: grimhilt Date: Mon, 22 Aug 2022 22:05:37 +0200 Subject: [PATCH 5/8] add function getVoiceRecordingId --- src/components/views/rooms/MessageComposer.tsx | 18 +++++------------- .../views/rooms/VoiceRecordComposerTile.tsx | 9 ++------- src/stores/VoiceRecordingStore.ts | 13 +++++++++++++ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 756343db90d..642f3424066 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -19,7 +19,7 @@ import classNames from 'classnames'; import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event'; +import { EventType } from 'matrix-js-sdk/src/@types/event'; import { Optional } from "matrix-events-sdk"; import { THREAD_RELATION_TYPE } from 'matrix-js-sdk/src/models/thread'; @@ -308,12 +308,8 @@ export default class MessageComposer extends React.Component { }; private updateRecordingState() { - this.voiceRecording = - this.props.relation?.rel_type === ("io.element.thread") - || this.props.relation?.rel_type === RelationType.Thread ? - VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) - : - VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); + const voiceRecordingId = VoiceRecordingStore.instance.getVoiceRecordingId(this.props.room, this.props.relation); + this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId); if (this.voiceRecording) { // If the recording has already started, it's probably a cached one. if (this.voiceRecording.hasRecording && !this.voiceRecording.isRecording) { @@ -328,12 +324,8 @@ export default class MessageComposer extends React.Component { private onRecordingStarted = () => { // update the recording instance, just in case - this.voiceRecording = - this.props.relation?.rel_type === ("io.element.thread") - || this.props.relation?.rel_type === RelationType.Thread ? - VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId+this.props.relation.event_id) - : - VoiceRecordingStore.instance.getActiveRecording(this.props.room.roomId); + const voiceRecordingId = VoiceRecordingStore.instance.getVoiceRecordingId(this.props.room, this.props.relation); + this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId); this.setState({ haveRecording: !!this.voiceRecording, }); diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 4a73e4fc911..c905861973b 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { ReactNode } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; -import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; +import { MsgType } from "matrix-js-sdk/src/@types/event"; import { logger } from "matrix-js-sdk/src/logger"; import { Optional } from "matrix-events-sdk"; import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -73,12 +73,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent; } @@ -44,6 +49,14 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { return; } + public 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; + } + } + /** * Gets the active recording instance, if any. * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to get the recording in. From 5db2ee2452b108f6b11033fc070df70ad4272d1c Mon Sep 17 00:00:00 2001 From: grimhilt Date: Wed, 24 Aug 2022 14:42:54 +0200 Subject: [PATCH 6/8] third time lucky (typo) --- src/stores/VoiceRecordingStore.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stores/VoiceRecordingStore.ts b/src/stores/VoiceRecordingStore.ts index 2f4798da30e..d182755d4f3 100644 --- a/src/stores/VoiceRecordingStore.ts +++ b/src/stores/VoiceRecordingStore.ts @@ -59,7 +59,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Gets the active recording instance, if any. - * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to get the recording in. + * @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to get the recording in. * @returns {Optional} The recording, if any. */ public getActiveRecording(voiceRecordingId: string): Optional { @@ -70,7 +70,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { * 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. - * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to start recording in. + * @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to start recording in. * @returns {VoiceRecording} The recording. */ public startRecording(voiceRecordingId: string): VoiceRecording { @@ -88,7 +88,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { /** * Disposes of the current recording, no matter the state of it. - * @param {string} voiceRecordingId The room ID (with optionaly the thread ID if in one) to dispose of the recording in. + * @param {string} voiceRecordingId The room ID (with optionally the thread ID if in one) to dispose of the recording in. * @returns {Promise} Resolves when complete. */ public disposeRecording(voiceRecordingId: string): Promise { From a4b3023c203c33b96b1f3b6ad5d15da277842dd8 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Wed, 24 Aug 2022 14:46:38 +0200 Subject: [PATCH 7/8] change to static function --- src/components/views/rooms/MessageComposer.tsx | 4 ++-- src/components/views/rooms/VoiceRecordComposerTile.tsx | 2 +- src/stores/VoiceRecordingStore.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 642f3424066..239969cb822 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -308,7 +308,7 @@ export default class MessageComposer extends React.Component { }; private updateRecordingState() { - const voiceRecordingId = VoiceRecordingStore.instance.getVoiceRecordingId(this.props.room, this.props.relation); + const voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation); this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId); if (this.voiceRecording) { // If the recording has already started, it's probably a cached one. @@ -324,7 +324,7 @@ export default class MessageComposer extends React.Component { private onRecordingStarted = () => { // update the recording instance, just in case - const voiceRecordingId = VoiceRecordingStore.instance.getVoiceRecordingId(this.props.room, this.props.relation); + const voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation); this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId); this.setState({ haveRecording: !!this.voiceRecording, diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index c905861973b..b25d87a0cef 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -73,7 +73,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent { return; } - public getVoiceRecordingId(room: Room, relation?: IEventRelation): string { + 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 { From 4bf665c1d41b3bb46cac274a98353cc9be4fca86 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Fri, 26 Aug 2022 15:51:26 +0200 Subject: [PATCH 8/8] Fix strict TS issue in VoiceRecordingStore.ts --- src/stores/VoiceRecordingStore.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stores/VoiceRecordingStore.ts b/src/stores/VoiceRecordingStore.ts index 079f75efcda..59d827422d3 100644 --- a/src/stores/VoiceRecordingStore.ts +++ b/src/stores/VoiceRecordingStore.ts @@ -92,9 +92,7 @@ export class VoiceRecordingStore extends AsyncStoreWithClient { * @returns {Promise} Resolves when complete. */ public disposeRecording(voiceRecordingId: string): Promise { - if (this.state[voiceRecordingId]) { - this.state[voiceRecordingId].destroy(); // stops internally - } + this.state[voiceRecordingId]?.destroy(); // stops internally const { // eslint-disable-next-line @typescript-eslint/no-unused-vars