Improve live voice broadcast detection by testing if the started event has been redacted (#9780)
This commit is contained in:
parent
fbc3228143
commit
b81582d045
15 changed files with 365 additions and 83 deletions
|
@ -313,3 +313,13 @@ export const concat = (...arrays: Uint8Array[]): Uint8Array => {
|
|||
return concatenated;
|
||||
}, new Uint8Array(0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Async version of Array.every.
|
||||
*/
|
||||
export async function asyncEvery<T>(values: T[], predicate: (value: T) => Promise<boolean>): Promise<boolean> {
|
||||
for (const value of values) {
|
||||
if (!(await predicate(value))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -14,18 +14,34 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { hasRoomLiveVoiceBroadcast } from "../utils/hasRoomLiveVoiceBroadcast";
|
||||
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
|
||||
import { SDKContext } from "../../contexts/SDKContext";
|
||||
|
||||
export const useHasRoomLiveVoiceBroadcast = (room: Room) => {
|
||||
const [hasLiveVoiceBroadcast, setHasLiveVoiceBroadcast] = useState(hasRoomLiveVoiceBroadcast(room).hasBroadcast);
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const [hasLiveVoiceBroadcast, setHasLiveVoiceBroadcast] = useState(false);
|
||||
|
||||
useTypedEventEmitter(room.currentState, RoomStateEvent.Update, () => {
|
||||
setHasLiveVoiceBroadcast(hasRoomLiveVoiceBroadcast(room).hasBroadcast);
|
||||
});
|
||||
const update = useMemo(() => {
|
||||
return sdkContext.client
|
||||
? () => {
|
||||
hasRoomLiveVoiceBroadcast(sdkContext.client!, room).then(
|
||||
({ hasBroadcast }) => {
|
||||
setHasLiveVoiceBroadcast(hasBroadcast);
|
||||
},
|
||||
() => {}, // no update on error
|
||||
);
|
||||
}
|
||||
: () => {}; // noop without client
|
||||
}, [room, sdkContext, setHasLiveVoiceBroadcast]);
|
||||
|
||||
useEffect(() => {
|
||||
update();
|
||||
}, [update]);
|
||||
|
||||
useTypedEventEmitter(room.currentState, RoomStateEvent.Update, () => update());
|
||||
return hasLiveVoiceBroadcast;
|
||||
};
|
||||
|
|
|
@ -49,6 +49,7 @@ export * from "./utils/getChunkLength";
|
|||
export * from "./utils/getMaxBroadcastLength";
|
||||
export * from "./utils/hasRoomLiveVoiceBroadcast";
|
||||
export * from "./utils/findRoomLiveVoiceBroadcastFromUserAndDevice";
|
||||
export * from "./utils/retrieveStartedInfoEvent";
|
||||
export * from "./utils/shouldDisplayAsVoiceBroadcastRecordingTile";
|
||||
export * from "./utils/shouldDisplayAsVoiceBroadcastTile";
|
||||
export * from "./utils/shouldDisplayAsVoiceBroadcastStoppedText";
|
||||
|
|
|
@ -67,11 +67,11 @@ const showOthersAlreadyRecordingDialog = () => {
|
|||
});
|
||||
};
|
||||
|
||||
export const checkVoiceBroadcastPreConditions = (
|
||||
export const checkVoiceBroadcastPreConditions = async (
|
||||
room: Room,
|
||||
client: MatrixClient,
|
||||
recordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): boolean => {
|
||||
): Promise<boolean> => {
|
||||
if (recordingsStore.getCurrent()) {
|
||||
showAlreadyRecordingDialog();
|
||||
return false;
|
||||
|
@ -86,7 +86,7 @@ export const checkVoiceBroadcastPreConditions = (
|
|||
return false;
|
||||
}
|
||||
|
||||
const { hasBroadcast, startedByUser } = hasRoomLiveVoiceBroadcast(room, currentUserId);
|
||||
const { hasBroadcast, startedByUser } = await hasRoomLiveVoiceBroadcast(client, room, currentUserId);
|
||||
|
||||
if (hasBroadcast && startedByUser) {
|
||||
showAlreadyRecordingDialog();
|
||||
|
|
|
@ -34,12 +34,12 @@ import {
|
|||
* @param {VoiceBroadcastPlaybacksStore} voiceBroadcastPlaybacksStore
|
||||
* @param {VoiceBroadcastRecordingsStore} voiceBroadcastRecordingsStore
|
||||
*/
|
||||
export const doMaybeSetCurrentVoiceBroadcastPlayback = (
|
||||
export const doMaybeSetCurrentVoiceBroadcastPlayback = async (
|
||||
room: Room,
|
||||
client: MatrixClient,
|
||||
voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore,
|
||||
voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): void => {
|
||||
): Promise<void> => {
|
||||
// do not disturb the current recording
|
||||
if (voiceBroadcastRecordingsStore.hasCurrent()) return;
|
||||
|
||||
|
@ -50,7 +50,7 @@ export const doMaybeSetCurrentVoiceBroadcastPlayback = (
|
|||
return;
|
||||
}
|
||||
|
||||
const { infoEvent } = hasRoomLiveVoiceBroadcast(room);
|
||||
const { infoEvent } = await hasRoomLiveVoiceBroadcast(client, room);
|
||||
|
||||
if (infoEvent) {
|
||||
// live broadcast in the room + no recording + not listening yet: set the current broadcast
|
||||
|
|
|
@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
||||
import { retrieveStartedInfoEvent, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
||||
import { asyncEvery } from "../../utils/arrays";
|
||||
|
||||
interface Result {
|
||||
// whether there is a live broadcast in the room
|
||||
|
@ -27,22 +28,26 @@ interface Result {
|
|||
startedByUser: boolean;
|
||||
}
|
||||
|
||||
export const hasRoomLiveVoiceBroadcast = (room: Room, userId?: string): Result => {
|
||||
export const hasRoomLiveVoiceBroadcast = async (client: MatrixClient, room: Room, userId?: string): Promise<Result> => {
|
||||
let hasBroadcast = false;
|
||||
let startedByUser = false;
|
||||
let infoEvent: MatrixEvent | null = null;
|
||||
|
||||
const stateEvents = room.currentState.getStateEvents(VoiceBroadcastInfoEventType);
|
||||
stateEvents.every((event: MatrixEvent) => {
|
||||
await asyncEvery(stateEvents, async (event: MatrixEvent) => {
|
||||
const state = event.getContent()?.state;
|
||||
|
||||
if (state && state !== VoiceBroadcastInfoState.Stopped) {
|
||||
const startEvent = await retrieveStartedInfoEvent(event, client);
|
||||
|
||||
// skip if started voice broadcast event is redacted
|
||||
if (startEvent?.isRedacted()) return true;
|
||||
|
||||
hasBroadcast = true;
|
||||
infoEvent = event;
|
||||
infoEvent = startEvent;
|
||||
|
||||
// state key = sender's MXID
|
||||
if (event.getStateKey() === userId) {
|
||||
infoEvent = event;
|
||||
startedByUser = true;
|
||||
// break here, because more than true / true is not possible
|
||||
return false;
|
||||
|
|
45
src/voice-broadcast/utils/retrieveStartedInfoEvent.ts
Normal file
45
src/voice-broadcast/utils/retrieveStartedInfoEvent.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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 } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VoiceBroadcastInfoState } from "..";
|
||||
|
||||
export const retrieveStartedInfoEvent = async (
|
||||
event: MatrixEvent,
|
||||
client: MatrixClient,
|
||||
): Promise<MatrixEvent | null> => {
|
||||
// started event passed as argument
|
||||
if (event.getContent()?.state === VoiceBroadcastInfoState.Started) return event;
|
||||
|
||||
const relatedEventId = event.getRelation()?.event_id;
|
||||
|
||||
// no related event
|
||||
if (!relatedEventId) return null;
|
||||
|
||||
const roomId = event.getRoomId() || "";
|
||||
const relatedEventFromRoom = client.getRoom(roomId)?.findEventById(relatedEventId);
|
||||
|
||||
// event found
|
||||
if (relatedEventFromRoom) return relatedEventFromRoom;
|
||||
|
||||
try {
|
||||
const relatedEventData = await client.fetchRoomEvent(roomId, relatedEventId);
|
||||
return new MatrixEvent(relatedEventData);
|
||||
} catch (e) {}
|
||||
|
||||
return null;
|
||||
};
|
|
@ -38,7 +38,7 @@ const startBroadcast = async (
|
|||
const userId = client.getUserId();
|
||||
|
||||
if (!userId) {
|
||||
reject("unable to start voice broadcast if current user is unkonwn");
|
||||
reject("unable to start voice broadcast if current user is unknown");
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ export const startNewVoiceBroadcastRecording = async (
|
|||
playbacksStore: VoiceBroadcastPlaybacksStore,
|
||||
recordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): Promise<VoiceBroadcastRecording | null> => {
|
||||
if (!checkVoiceBroadcastPreConditions(room, client, recordingsStore)) {
|
||||
if (!(await checkVoiceBroadcastPreConditions(room, client, recordingsStore))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue