Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit d702f4a

Browse files
authored
When start listening to a broadcast, pause the others (#9489)
1 parent daf097e commit d702f4a

File tree

4 files changed

+186
-61
lines changed

4 files changed

+186
-61
lines changed

src/voice-broadcast/models/VoiceBroadcastPlayback.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ export enum VoiceBroadcastPlaybackEvent {
4747

4848
interface EventMap {
4949
[VoiceBroadcastPlaybackEvent.LengthChanged]: (length: number) => void;
50-
[VoiceBroadcastPlaybackEvent.StateChanged]: (state: VoiceBroadcastPlaybackState) => void;
50+
[VoiceBroadcastPlaybackEvent.StateChanged]: (
51+
state: VoiceBroadcastPlaybackState,
52+
playback: VoiceBroadcastPlayback
53+
) => void;
5154
[VoiceBroadcastPlaybackEvent.InfoStateChanged]: (state: VoiceBroadcastInfoState) => void;
5255
}
5356

@@ -217,14 +220,20 @@ export class VoiceBroadcastPlayback
217220
}
218221

219222
public pause(): void {
220-
if (!this.currentlyPlaying) return;
223+
// stopped voice broadcasts cannot be paused
224+
if (this.getState() === VoiceBroadcastPlaybackState.Stopped) return;
221225

222226
this.setState(VoiceBroadcastPlaybackState.Paused);
227+
if (!this.currentlyPlaying) return;
223228
this.currentlyPlaying.pause();
224229
}
225230

226231
public resume(): void {
227-
if (!this.currentlyPlaying) return;
232+
if (!this.currentlyPlaying) {
233+
// no playback to resume, start from the beginning
234+
this.start();
235+
return;
236+
}
228237

229238
this.setState(VoiceBroadcastPlaybackState.Playing);
230239
this.currentlyPlaying.play();
@@ -260,7 +269,7 @@ export class VoiceBroadcastPlayback
260269
}
261270

262271
this.state = state;
263-
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state);
272+
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state, this);
264273
}
265274

266275
public getInfoState(): VoiceBroadcastInfoState {

src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ limitations under the License.
1717
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
1818
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
1919

20-
import { VoiceBroadcastPlayback } from "..";
20+
import { VoiceBroadcastPlayback, VoiceBroadcastPlaybackEvent, VoiceBroadcastPlaybackState } from "..";
21+
import { IDestroyable } from "../../utils/IDestroyable";
2122

2223
export enum VoiceBroadcastPlaybacksStoreEvent {
2324
CurrentChanged = "current_changed",
@@ -28,10 +29,16 @@ interface EventMap {
2829
}
2930

3031
/**
31-
* This store provides access to the current and specific Voice Broadcast playbacks.
32+
* This store manages VoiceBroadcastPlaybacks:
33+
* - access the currently playing voice broadcast
34+
* - ensures that only once broadcast is playing at a time
3235
*/
33-
export class VoiceBroadcastPlaybacksStore extends TypedEventEmitter<VoiceBroadcastPlaybacksStoreEvent, EventMap> {
36+
export class VoiceBroadcastPlaybacksStore
37+
extends TypedEventEmitter<VoiceBroadcastPlaybacksStoreEvent, EventMap>
38+
implements IDestroyable {
3439
private current: VoiceBroadcastPlayback | null;
40+
41+
/** Playbacks indexed by their info event id. */
3542
private playbacks = new Map<string, VoiceBroadcastPlayback>();
3643

3744
public constructor() {
@@ -42,7 +49,7 @@ export class VoiceBroadcastPlaybacksStore extends TypedEventEmitter<VoiceBroadca
4249
if (this.current === current) return;
4350

4451
this.current = current;
45-
this.playbacks.set(current.infoEvent.getId(), current);
52+
this.addPlayback(current);
4653
this.emit(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, current);
4754
}
4855

@@ -54,12 +61,51 @@ export class VoiceBroadcastPlaybacksStore extends TypedEventEmitter<VoiceBroadca
5461
const infoEventId = infoEvent.getId();
5562

5663
if (!this.playbacks.has(infoEventId)) {
57-
this.playbacks.set(infoEventId, new VoiceBroadcastPlayback(infoEvent, client));
64+
this.addPlayback(new VoiceBroadcastPlayback(infoEvent, client));
5865
}
5966

6067
return this.playbacks.get(infoEventId);
6168
}
6269

70+
private addPlayback(playback: VoiceBroadcastPlayback): void {
71+
const infoEventId = playback.infoEvent.getId();
72+
73+
if (this.playbacks.has(infoEventId)) return;
74+
75+
this.playbacks.set(infoEventId, playback);
76+
playback.on(VoiceBroadcastPlaybackEvent.StateChanged, this.onPlaybackStateChanged);
77+
}
78+
79+
private onPlaybackStateChanged = (
80+
state: VoiceBroadcastPlaybackState,
81+
playback: VoiceBroadcastPlayback,
82+
): void => {
83+
if ([
84+
VoiceBroadcastPlaybackState.Buffering,
85+
VoiceBroadcastPlaybackState.Playing,
86+
].includes(state)) {
87+
this.pauseExcept(playback);
88+
}
89+
};
90+
91+
private pauseExcept(playbackNotToPause: VoiceBroadcastPlayback): void {
92+
for (const playback of this.playbacks.values()) {
93+
if (playback !== playbackNotToPause) {
94+
playback.pause();
95+
}
96+
}
97+
}
98+
99+
public destroy(): void {
100+
this.removeAllListeners();
101+
102+
for (const playback of this.playbacks.values()) {
103+
playback.off(VoiceBroadcastPlaybackEvent.StateChanged, this.onPlaybackStateChanged);
104+
}
105+
106+
this.playbacks = new Map();
107+
}
108+
63109
public static readonly _instance = new VoiceBroadcastPlaybacksStore();
64110

65111
/**

test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,25 @@ describe("VoiceBroadcastPlayback", () => {
7474

7575
const itShouldEmitAStateChangedEvent = (state: VoiceBroadcastPlaybackState) => {
7676
it(`should emit a ${state} state changed event`, () => {
77-
expect(mocked(onStateChanged)).toHaveBeenCalledWith(state);
77+
expect(mocked(onStateChanged)).toHaveBeenCalledWith(state, playback);
78+
});
79+
};
80+
81+
const startPlayback = () => {
82+
beforeEach(async () => {
83+
await playback.start();
84+
});
85+
};
86+
87+
const pausePlayback = () => {
88+
beforeEach(() => {
89+
playback.pause();
90+
});
91+
};
92+
93+
const stopPlayback = () => {
94+
beforeEach(() => {
95+
playback.stop();
7896
});
7997
};
8098

@@ -180,14 +198,28 @@ describe("VoiceBroadcastPlayback", () => {
180198
});
181199

182200
describe("and calling start", () => {
183-
beforeEach(async () => {
184-
await playback.start();
185-
});
201+
startPlayback();
186202

187203
it("should be in buffering state", () => {
188204
expect(playback.getState()).toBe(VoiceBroadcastPlaybackState.Buffering);
189205
});
190206

207+
describe("and calling stop", () => {
208+
stopPlayback();
209+
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped);
210+
211+
describe("and calling pause", () => {
212+
pausePlayback();
213+
// stopped voice broadcasts cannot be paused
214+
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped);
215+
});
216+
});
217+
218+
describe("and calling pause", () => {
219+
pausePlayback();
220+
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Paused);
221+
});
222+
191223
describe("and receiving the first chunk", () => {
192224
beforeEach(() => {
193225
// TODO Michael W: Use RelationsHelper
@@ -212,9 +244,7 @@ describe("VoiceBroadcastPlayback", () => {
212244
});
213245

214246
describe("and calling start", () => {
215-
beforeEach(async () => {
216-
await playback.start();
217-
});
247+
startPlayback();
218248

219249
it("should play the last chunk", () => {
220250
// assert that the last chunk is played first
@@ -258,10 +288,7 @@ describe("VoiceBroadcastPlayback", () => {
258288
});
259289

260290
describe("and calling start", () => {
261-
beforeEach(async () => {
262-
await playback.start();
263-
});
264-
291+
startPlayback();
265292
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Buffering);
266293
});
267294
});
@@ -278,9 +305,7 @@ describe("VoiceBroadcastPlayback", () => {
278305
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped);
279306

280307
describe("and calling start", () => {
281-
beforeEach(async () => {
282-
await playback.start();
283-
});
308+
startPlayback();
284309

285310
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Playing);
286311

@@ -303,13 +328,15 @@ describe("VoiceBroadcastPlayback", () => {
303328
});
304329

305330
describe("and calling pause", () => {
306-
beforeEach(() => {
307-
playback.pause();
308-
});
309-
331+
pausePlayback();
310332
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Paused);
311333
itShouldEmitAStateChangedEvent(VoiceBroadcastPlaybackState.Paused);
312334
});
335+
336+
describe("and calling stop", () => {
337+
stopPlayback();
338+
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped);
339+
});
313340
});
314341

315342
describe("and calling toggle for the first time", () => {
@@ -337,9 +364,7 @@ describe("VoiceBroadcastPlayback", () => {
337364
});
338365

339366
describe("and calling stop", () => {
340-
beforeEach(() => {
341-
playback.stop();
342-
});
367+
stopPlayback();
343368

344369
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped);
345370

0 commit comments

Comments
 (0)