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

Commit

Permalink
Extract RelationsHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
weeman1337 committed Oct 17, 2022
1 parent 6009d1c commit c1aabf9
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 42 deletions.
98 changes: 98 additions & 0 deletions src/events/RelationsHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { MatrixClient, MatrixEvent, MatrixEventEvent, RelationType } from "matrix-js-sdk/src/matrix";
import { Relations, RelationsEvent } from "matrix-js-sdk/src/models/relations";
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";

import { IDestroyable } from "../utils/IDestroyable";

export enum RelationsHelperEvent {
Add = "add",
}

interface EventMap {
[RelationsHelperEvent.Add]: (event: MatrixEvent) => void;
}

/**
* Helper class that manages a specific event type relation for an event.
* Just create an instance and listen for new events for that relation.
* Optionally receive the current events by calling emitCurrent().
* Clean up everything by calling destroy().
*/
export class RelationsHelper
extends TypedEventEmitter<RelationsHelperEvent, EventMap>
implements IDestroyable {
private relations: Relations;

public constructor(
private event: MatrixEvent,
private relationType: RelationType,
private relationEventType: string,
private client: MatrixClient,
) {
super();
this.setUpRelations();
}

private setUpRelations = (): void => {
this.setRelations();

if (this.relations) {
this.relations.on(RelationsEvent.Add, this.onRelationsAdd);
} else {
this.event.once(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);
}
};

private onRelationsCreated = (): void => {
this.setRelations();

if (this.relations) {
this.relations.on(RelationsEvent.Add, this.onRelationsAdd);
this.emitCurrent();
} else {
this.event.once(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);
}
};

private setRelations(): void {
const room = this.client.getRoom(this.event.getRoomId());
this.relations = room?.getUnfilteredTimelineSet()?.relations?.getChildEventsForEvent(
this.event.getId(),
this.relationType,
this.relationEventType,
);
}

private onRelationsAdd = (event: MatrixEvent): void => {
this.emit(RelationsHelperEvent.Add, event);
};

public emitCurrent(): void {
this.relations?.getRelations()?.forEach(e => this.emit(RelationsHelperEvent.Add, e));
}

public destroy(): void {
this.removeAllListeners();
this.event.off(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);

if (this.relations) {
this.relations.off(RelationsEvent.Add, this.onRelationsAdd);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
playback,
}) => {
const {
live,
room,
sender,
toggle,
Expand All @@ -40,7 +41,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
return (
<div className="mx_VoiceBroadcastPlaybackBody">
<VoiceBroadcastHeader
live={false}
live={live}
sender={sender}
room={room}
showBroadcast={true}
Expand Down
11 changes: 11 additions & 0 deletions src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useState } from "react";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import {
VoiceBroadcastInfoState,
VoiceBroadcastPlayback,
VoiceBroadcastPlaybackEvent,
VoiceBroadcastPlaybackState,
Expand All @@ -40,7 +41,17 @@ export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => {
},
);

const [playbackInfoState, setPlaybackInfoState] = useState(playback.getInfoState());
useTypedEventEmitter(
playback,
VoiceBroadcastPlaybackEvent.InfoStateChanged,
(state: VoiceBroadcastInfoState) => {
setPlaybackInfoState(state);
},
);

return {
live: playbackInfoState !== VoiceBroadcastInfoState.Stopped,
room: room,
sender: playback.infoEvent.sender,
toggle: playbackToggle,
Expand Down
97 changes: 56 additions & 41 deletions src/voice-broadcast/models/VoiceBroadcastPlayback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,19 @@ import {
EventType,
MatrixClient,
MatrixEvent,
MatrixEventEvent,
MsgType,
RelationType,
} from "matrix-js-sdk/src/matrix";
import { Relations, RelationsEvent } from "matrix-js-sdk/src/models/relations";
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";

import { Playback, PlaybackState } from "../../audio/Playback";
import { PlaybackManager } from "../../audio/PlaybackManager";
import { getReferenceRelationsForEvent } from "../../events";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
import { MediaEventHelper } from "../../utils/MediaEventHelper";
import { IDestroyable } from "../../utils/IDestroyable";
import { VoiceBroadcastChunkEventType } from "..";
import { VoiceBroadcastChunkEventType, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
import { getReferenceRelationsForEvent } from "../../events";

export enum VoiceBroadcastPlaybackState {
Paused,
Expand All @@ -42,29 +41,55 @@ export enum VoiceBroadcastPlaybackState {
export enum VoiceBroadcastPlaybackEvent {
LengthChanged = "length_changed",
StateChanged = "state_changed",
InfoStateChanged = "info_state_changed",
}

interface EventMap {
[VoiceBroadcastPlaybackEvent.LengthChanged]: (length: number) => void;
[VoiceBroadcastPlaybackEvent.StateChanged]: (state: VoiceBroadcastPlaybackState) => void;
[VoiceBroadcastPlaybackEvent.InfoStateChanged]: (state: VoiceBroadcastInfoState) => void;
}

export class VoiceBroadcastPlayback
extends TypedEventEmitter<VoiceBroadcastPlaybackEvent, EventMap>
implements IDestroyable {
private state = VoiceBroadcastPlaybackState.Stopped;
private infoState: VoiceBroadcastInfoState;
private chunkEvents = new Map<string, MatrixEvent>();
/** Holds the playback qeue with a 1-based index (sequence number) */
/** Holds the playback queue with a 1-based index (sequence number) */
private queue: Playback[] = [];
private currentlyPlaying: Playback;
private relations: Relations;
private lastInfoEvent: MatrixEvent;
private chunkRelationHelper: RelationsHelper;
private infoRelationHelper: RelationsHelper;

public constructor(
public readonly infoEvent: MatrixEvent,
private client: MatrixClient,
) {
super();
this.setUpRelations();
this.addInfoEvent(this.infoEvent);
this.setUpRelationsHelper();
}

private setUpRelationsHelper(): void {
this.infoRelationHelper = new RelationsHelper(
this.infoEvent,
RelationType.Reference,
VoiceBroadcastInfoEventType,
this.client,
);
this.infoRelationHelper.on(RelationsHelperEvent.Add, this.addInfoEvent);
this.infoRelationHelper.emitCurrent();

this.chunkRelationHelper = new RelationsHelper(
this.infoEvent,
RelationType.Reference,
EventType.RoomMessage,
this.client,
);
this.chunkRelationHelper.on(RelationsHelperEvent.Add, this.addChunkEvent);
this.chunkRelationHelper.emitCurrent();
}

private addChunkEvent(event: MatrixEvent): boolean {
Expand All @@ -81,41 +106,21 @@ export class VoiceBroadcastPlayback
return true;
}

private setUpRelations(): void {
const relations = getReferenceRelationsForEvent(this.infoEvent, EventType.RoomMessage, this.client);

if (!relations) {
// No related events, yet. Set up relation watcher.
this.infoEvent.on(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);
private addInfoEvent = (event: MatrixEvent): void => {
if (this.lastInfoEvent && this.lastInfoEvent.getTs() >= event.getTs()) {
// Only handle newer events
return;
}

this.relations = relations;
relations.getRelations()?.forEach(e => this.addChunkEvent(e));
relations.on(RelationsEvent.Add, this.onRelationsEventAdd);

if (this.chunkEvents.size > 0) {
this.emitLengthChanged();
}
}

private onRelationsEventAdd = (event: MatrixEvent) => {
if (this.addChunkEvent(event)) {
this.emitLengthChanged();
}
};
const state = event.getContent()?.state;

private emitLengthChanged(): void {
this.emit(VoiceBroadcastPlaybackEvent.LengthChanged, this.chunkEvents.size);
}

private onRelationsCreated = (relationType: string) => {
if (relationType !== RelationType.Reference) {
if (!Object.values(VoiceBroadcastInfoState).includes(state)) {
// Do not handle unknown voice broadcast states
return;
}

this.infoEvent.off(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);
this.setUpRelations();
this.lastInfoEvent = event;
this.setInfoState(state);
};

private async loadChunks(): Promise<void> {
Expand Down Expand Up @@ -173,7 +178,7 @@ export class VoiceBroadcastPlayback
}

this.setState(VoiceBroadcastPlaybackState.Playing);
// index of the first schunk is the first sequence number
// index of the first chunk is the first sequence number
const first = this.queue[1];
this.currentlyPlaying = first;
await first.play();
Expand Down Expand Up @@ -238,17 +243,27 @@ export class VoiceBroadcastPlayback
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state);
}

public getInfoState(): VoiceBroadcastInfoState {
return this.infoState;
}

private setInfoState(state: VoiceBroadcastInfoState): void {
if (this.infoState === state) {
return;
}

this.infoState = state;
this.emit(VoiceBroadcastPlaybackEvent.InfoStateChanged, state);
}

private destroyQueue(): void {
this.queue.forEach(p => p.destroy());
this.queue = [];
}

public destroy(): void {
if (this.relations) {
this.relations.off(RelationsEvent.Add, this.onRelationsEventAdd);
}

this.infoEvent.off(MatrixEventEvent.RelationsCreated, this.onRelationsCreated);
this.chunkRelationHelper.destroy();
this.infoRelationHelper.destroy();
this.removeAllListeners();
this.destroyQueue();
}
Expand Down
Loading

0 comments on commit c1aabf9

Please sign in to comment.