diff --git a/src/button/ReactionToggleButton.test.tsx b/src/button/ReactionToggleButton.test.tsx index 3b941defd..da3b6fc6a 100644 --- a/src/button/ReactionToggleButton.test.tsx +++ b/src/button/ReactionToggleButton.test.tsx @@ -53,11 +53,8 @@ test("Can open menu", async () => { test("Can raise hand", async () => { const user = userEvent.setup(); - const { - vm, - rtcSession, - handRaisedSubject$: handRaisedSubject, - } = getBasicCallViewModelEnvironment([local, alice]); + const { vm, rtcSession, handRaisedSubject$ } = + getBasicCallViewModelEnvironment([local, alice]); const { getByLabelText, container } = render( , ); @@ -76,7 +73,7 @@ test("Can raise hand", async () => { ); act(() => { // Mock receiving a reaction. - handRaisedSubject.next({ + handRaisedSubject$.next({ [localIdent]: { time: new Date(), reactionEventId: "", @@ -90,18 +87,15 @@ test("Can raise hand", async () => { test("Can lower hand", async () => { const reactionEventId = "$my-reaction-event:example.org"; const user = userEvent.setup(); - const { - vm, - rtcSession, - handRaisedSubject$: handRaisedSubject, - } = getBasicCallViewModelEnvironment([local, alice]); + const { vm, rtcSession, handRaisedSubject$ } = + getBasicCallViewModelEnvironment([local, alice]); const { getByLabelText, container } = render( , ); await user.click(getByLabelText("common.reactions")); await user.click(getByLabelText("action.raise_hand")); act(() => { - handRaisedSubject.next({ + handRaisedSubject$.next({ [localIdent]: { time: new Date(), reactionEventId, @@ -117,7 +111,7 @@ test("Can lower hand", async () => { ); act(() => { // Mock receiving a redacted reaction. - handRaisedSubject.next({}); + handRaisedSubject$.next({}); }); expect(container).toMatchSnapshot(); }); diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index c00243783..e01d06e89 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -181,10 +181,10 @@ export function ReactionToggleButton({ const [errorText, setErrorText] = useState(); const isHandRaised = useObservableState( - vm.handsRaised.pipe(map((v) => !!v[identifier])), + vm.handsRaised$.pipe(map((v) => !!v[identifier])), ); const canReact = useObservableState( - vm.reactions.pipe(map((v) => !v[identifier])), + vm.reactions$.pipe(map((v) => !v[identifier])), ); useEffect(() => { diff --git a/src/reactions/useReactionsReader.ts b/src/reactions/useReactionsReader.ts index 0363edf6f..1aa18001c 100644 --- a/src/reactions/useReactionsReader.ts +++ b/src/reactions/useReactionsReader.ts @@ -36,32 +36,32 @@ const REACTION_ACTIVE_TIME_MS = 3000; * @param rtcSession */ export default function useReactionsReader(rtcSession: MatrixRTCSession): { - raisedHands: Observable>; - reactions: Observable>; + raisedHands$: Observable>; + reactions$: Observable>; } { - const raisedHandsSubject = useRef( + const raisedHandsSubject$ = useRef( new BehaviorSubject>({}), ); - const reactionsSubject = useRef( + const reactionsSubject$ = useRef( new BehaviorSubject>({}), ); const memberships = useMatrixRTCSessionMemberships(rtcSession); const latestMemberships = useLatest(memberships); - const latestRaisedHands = useLatest(raisedHandsSubject.current); + const latestRaisedHands = useLatest(raisedHandsSubject$.current); const room = rtcSession.room; const addRaisedHand = useCallback((userId: string, info: RaisedHandInfo) => { - raisedHandsSubject.current.next({ - ...raisedHandsSubject.current.value, + raisedHandsSubject$.current.next({ + ...raisedHandsSubject$.current.value, [userId]: info, }); }, []); const removeRaisedHand = useCallback((userId: string) => { - raisedHandsSubject.current.next( + raisedHandsSubject$.current.next( Object.fromEntries( - Object.entries(raisedHandsSubject.current.value).filter( + Object.entries(raisedHandsSubject$.current.value).filter( ([uId]) => uId !== userId, ), ), @@ -90,7 +90,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { }; // Remove any raised hands for users no longer joined to the call. - for (const identifier of Object.keys(raisedHandsSubject).filter( + for (const identifier of Object.keys(raisedHandsSubject$).filter( (rhId) => !memberships.find((u) => u.sender == rhId), )) { removeRaisedHand(identifier); @@ -104,8 +104,8 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { } const identifier = `${m.sender}:${m.deviceId}`; if ( - raisedHandsSubject.current.value[identifier] && - raisedHandsSubject.current.value[identifier].membershipEventId !== + raisedHandsSubject$.current.value[identifier] && + raisedHandsSubject$.current.value[identifier].membershipEventId !== m.eventId ) { // Membership event for sender has changed since the hand @@ -193,16 +193,16 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { ...ReactionSet.find((r) => r.name === content.name), }; - const currentReactions = reactionsSubject.current.value; + const currentReactions = reactionsSubject$.current.value; if (currentReactions[identifier]) { // We've still got a reaction from this user, ignore it to prevent spamming return; } const timeout = globalThis.setTimeout(() => { // Clear the reaction after some time. - reactionsSubject.current.next( + reactionsSubject$.current.next( Object.fromEntries( - Object.entries(reactionsSubject.current.value).filter( + Object.entries(reactionsSubject$.current.value).filter( ([id]) => id !== identifier, ), ), @@ -210,7 +210,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { reactionTimeouts.delete(timeout); }, REACTION_ACTIVE_TIME_MS); reactionTimeouts.add(timeout); - reactionsSubject.current.next({ + reactionsSubject$.current.next({ ...currentReactions, [identifier]: { reactionOption: reaction, @@ -264,7 +264,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { // may still be sending. room.on(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent); - const innerReactionsSubject = reactionsSubject.current; + const innerReactionsSubject$ = reactionsSubject$.current; return (): void => { room.off(MatrixRoomEvent.Timeline, handleReactionEvent); room.off(MatrixRoomEvent.Redaction, handleReactionEvent); @@ -272,7 +272,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { room.off(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent); reactionTimeouts.forEach((t) => clearTimeout(t)); // If we're clearing timeouts, we also clear all reactions. - innerReactionsSubject.next({}); + innerReactionsSubject$.next({}); }; }, [ room, @@ -283,7 +283,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): { ]); return { - reactions: reactionsSubject.current.asObservable(), - raisedHands: raisedHandsSubject.current.asObservable(), + reactions$: reactionsSubject$.current.asObservable(), + raisedHands$: raisedHandsSubject$.current.asObservable(), }; } diff --git a/src/reactions/useReactionsSender.tsx b/src/reactions/useReactionsSender.tsx index 248b910d2..659cdd7e6 100644 --- a/src/reactions/useReactionsSender.tsx +++ b/src/reactions/useReactionsSender.tsx @@ -76,7 +76,7 @@ export const ReactionsSenderProvider = ({ }, [memberships, myUserId]); const myReaction = useObservableEagerState( - vm.reactions.pipe( + vm.reactions$.pipe( map((v) => myMembershipIdentifier !== undefined ? v[myMembershipIdentifier] @@ -86,7 +86,7 @@ export const ReactionsSenderProvider = ({ ); const myRaisedHand = useObservableEagerState( - vm.handsRaised.pipe( + vm.handsRaised$.pipe( map((v) => myMembershipIdentifier !== undefined ? v[myMembershipIdentifier] diff --git a/src/room/CallEventAudioRenderer.test.tsx b/src/room/CallEventAudioRenderer.test.tsx index 16bf3b681..106cdad14 100644 --- a/src/room/CallEventAudioRenderer.test.tsx +++ b/src/room/CallEventAudioRenderer.test.tsx @@ -66,36 +66,42 @@ beforeEach(() => { * a noise every time. */ test("plays one sound when entering a call", () => { - const { vm, remoteRtcMemberships$: remoteRtcMemberships } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); render(); // Joining a call usually means remote participants are added later. act(() => { - remoteRtcMemberships.next([aliceRtcMember, bobRtcMember]); + remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]); }); expect(playSound).toHaveBeenCalledOnce(); }); test("plays a sound when a user joins", () => { - const { vm, remoteRtcMemberships$: remoteRtcMemberships } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); render(); act(() => { - remoteRtcMemberships.next([aliceRtcMember, bobRtcMember]); + remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]); }); // Play a sound when joining a call. expect(playSound).toBeCalledWith("join"); }); test("plays a sound when a user leaves", () => { - const { vm, remoteRtcMemberships$: remoteRtcMemberships } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); render(); act(() => { - remoteRtcMemberships.next([]); + remoteRtcMemberships$.next([]); }); expect(playSound).toBeCalledWith("left"); }); @@ -108,13 +114,15 @@ test("plays no sound when the participant list is more than the maximum size", ( ); } - const { vm, remoteRtcMemberships$: remoteRtcMemberships } = - getBasicCallViewModelEnvironment([local, alice], mockRtcMemberships); + const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment( + [local, alice], + mockRtcMemberships, + ); render(); expect(playSound).not.toBeCalled(); act(() => { - remoteRtcMemberships.next( + remoteRtcMemberships$.next( mockRtcMemberships.slice(0, MAX_PARTICIPANT_COUNT_FOR_SOUND - 1), ); }); @@ -122,12 +130,14 @@ test("plays no sound when the participant list is more than the maximum size", ( }); test("plays one sound when a hand is raised", () => { - const { vm, handRaisedSubject$: handRaisedSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, handRaisedSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); render(); act(() => { - handRaisedSubject.next({ + handRaisedSubject$.next({ [bobRtcMember.callId]: { time: new Date(), membershipEventId: "", @@ -139,12 +149,14 @@ test("plays one sound when a hand is raised", () => { }); test("should not play a sound when a hand raise is retracted", () => { - const { vm, handRaisedSubject$: handRaisedSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, handRaisedSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); render(); act(() => { - handRaisedSubject.next({ + handRaisedSubject$.next({ ["foo"]: { time: new Date(), membershipEventId: "", @@ -159,7 +171,7 @@ test("should not play a sound when a hand raise is retracted", () => { }); expect(playSound).toHaveBeenCalledTimes(2); act(() => { - handRaisedSubject.next({ + handRaisedSubject$.next({ ["foo"]: { time: new Date(), membershipEventId: "", diff --git a/src/room/CallEventAudioRenderer.tsx b/src/room/CallEventAudioRenderer.tsx index b3f6393d1..d57e3ba5f 100644 --- a/src/room/CallEventAudioRenderer.tsx +++ b/src/room/CallEventAudioRenderer.tsx @@ -75,7 +75,7 @@ export function CallEventAudioRenderer({ void audioEngineRef.current?.playSound("left"); }); - const handRaisedSub = vm.newHandRaised.subscribe(() => { + const handRaisedSub = vm.newHandRaised$.subscribe(() => { void audioEngineRef.current?.playSound("raiseHand"); }); diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 7d697d29b..d465410f6 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -139,8 +139,8 @@ export const ActiveCall: FC = (props) => { livekitRoom, props.e2eeSystem, connStateObservable$, - reader.current.raisedHands, - reader.current.reactions, + reader.current.raisedHands$, + reader.current.reactions$, ); setVm(vm); return (): void => vm.destroy(); diff --git a/src/room/ReactionAudioRenderer.test.tsx b/src/room/ReactionAudioRenderer.test.tsx index a9039211d..9ba198f90 100644 --- a/src/room/ReactionAudioRenderer.test.tsx +++ b/src/room/ReactionAudioRenderer.test.tsx @@ -80,8 +80,10 @@ test("preloads all audio elements", () => { }); test("will play an audio sound when there is a reaction", () => { - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); playReactionsSound.setValue(true); render(); @@ -93,7 +95,7 @@ test("will play an audio sound when there is a reaction", () => { ); } act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: chosenReaction, ttl: 0 }, }); }); @@ -101,8 +103,10 @@ test("will play an audio sound when there is a reaction", () => { }); test("will play the generic audio sound when there is soundless reaction", () => { - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); playReactionsSound.setValue(true); render(); @@ -114,7 +118,7 @@ test("will play the generic audio sound when there is soundless reaction", () => ); } act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: chosenReaction, ttl: 0 }, }); }); @@ -122,8 +126,10 @@ test("will play the generic audio sound when there is soundless reaction", () => }); test("will play multiple audio sounds when there are multiple different reactions", () => { - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); playReactionsSound.setValue(true); render(); @@ -135,7 +141,7 @@ test("will play multiple audio sounds when there are multiple different reaction ); } act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 }, [bobRtcMember.deviceId]: { reactionOption: reaction2, ttl: 0 }, [localRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 }, diff --git a/src/room/ReactionAudioRenderer.tsx b/src/room/ReactionAudioRenderer.tsx index 024d8be1e..1b33d65a0 100644 --- a/src/room/ReactionAudioRenderer.tsx +++ b/src/room/ReactionAudioRenderer.tsx @@ -48,7 +48,7 @@ export function ReactionsAudioRenderer({ }, [soundCache, shouldPlay]); useEffect(() => { - const sub = vm.audibleReactions.subscribe((newReactions) => { + const sub = vm.audibleReactions$.subscribe((newReactions) => { for (const reactionName of newReactions) { if (soundMap[reactionName]) { void audioEngineRef.current?.playSound(reactionName); diff --git a/src/room/ReactionsOverlay.test.tsx b/src/room/ReactionsOverlay.test.tsx index f7866025f..37660e2c2 100644 --- a/src/room/ReactionsOverlay.test.tsx +++ b/src/room/ReactionsOverlay.test.tsx @@ -34,12 +34,14 @@ test("defaults to showing no reactions", () => { test("shows a reaction when sent", () => { showReactions.setValue(true); - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); const { getByRole } = render(); const reaction = ReactionSet[0]; act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 }, }); }); @@ -51,11 +53,13 @@ test("shows a reaction when sent", () => { test("shows two of the same reaction when sent", () => { showReactions.setValue(true); const reaction = ReactionSet[0]; - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); const { getAllByRole } = render(); act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 }, [bobRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 }, }); @@ -66,11 +70,13 @@ test("shows two of the same reaction when sent", () => { test("shows two different reactions when sent", () => { showReactions.setValue(true); const [reactionA, reactionB] = ReactionSet; - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); const { getAllByRole } = render(); act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: reactionA, ttl: 0 }, [bobRtcMember.deviceId]: { reactionOption: reactionB, ttl: 0 }, }); @@ -83,11 +89,13 @@ test("shows two different reactions when sent", () => { test("hides reactions when reaction animations are disabled", () => { showReactions.setValue(false); const reaction = ReactionSet[0]; - const { vm, reactionsSubject$: reactionsSubject } = - getBasicCallViewModelEnvironment([local, alice]); + const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([ + local, + alice, + ]); const { container } = render(); act(() => { - reactionsSubject.next({ + reactionsSubject$.next({ [aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 }, }); }); diff --git a/src/room/ReactionsOverlay.tsx b/src/room/ReactionsOverlay.tsx index c41219d27..e642a16c2 100644 --- a/src/room/ReactionsOverlay.tsx +++ b/src/room/ReactionsOverlay.tsx @@ -12,7 +12,7 @@ import styles from "./ReactionsOverlay.module.css"; import { type CallViewModel } from "../state/CallViewModel"; export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode { - const reactionsIcons = useObservableState(vm.visibleReactions); + const reactionsIcons = useObservableState(vm.visibleReactions$); return (
{reactionsIcons?.map(({ sender, emoji, startX }) => ( diff --git a/src/state/CallViewModel.test.ts b/src/state/CallViewModel.test.ts index 88e4d317f..3c83ea099 100644 --- a/src/state/CallViewModel.test.ts +++ b/src/state/CallViewModel.test.ts @@ -194,7 +194,7 @@ function withCallViewModel( speaking: Map>, continuation: ( vm: CallViewModel, - subjects: { raisedHands: BehaviorSubject> }, + subjects: { raisedHands$: BehaviorSubject> }, ) => void, ): void { const room = mockMatrixRoom({ @@ -240,7 +240,7 @@ function withCallViewModel( { remoteParticipants$ }, ); - const raisedHands = new BehaviorSubject>({}); + const raisedHands$ = new BehaviorSubject>({}); const vm = new CallViewModel( rtcSession as unknown as MatrixRTCSession, @@ -249,7 +249,7 @@ function withCallViewModel( kind: E2eeType.PER_PARTICIPANT, }, connectionState$, - raisedHands, + raisedHands$, new BehaviorSubject({}), ); @@ -261,7 +261,7 @@ function withCallViewModel( roomEventSelectorSpy!.mockRestore(); }); - continuation(vm, { raisedHands }); + continuation(vm, { raisedHands$: raisedHands$ }); } test("participants are retained during a focus switch", () => { @@ -802,7 +802,7 @@ it("should rank raised hands above video feeds and below speakers and presenters of([aliceRtcMember, bobRtcMember]), of(ConnectionState.Connected), new Map(), - (vm, { raisedHands }) => { + (vm, { raisedHands$ }) => { schedule("ab", { a: () => { // We imagine that only three tiles (the first three) will be visible @@ -814,7 +814,7 @@ it("should rank raised hands above video feeds and below speakers and presenters }); }, b: () => { - raisedHands.next({ + raisedHands$.next({ [`${bobRtcMember.sender}:${bobRtcMember.deviceId}`]: { time: new Date(), reactionEventId: "", diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index bb2757788..fb8c0a5d4 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -258,8 +258,8 @@ class UserMedia { participant: LocalParticipant | RemoteParticipant | undefined, encryptionSystem: EncryptionSystem, livekitRoom: LivekitRoom, - handRaised: Observable, - reaction: Observable, + handRaised$: Observable, + reaction$: Observable, ) { this.participant$ = new BehaviorSubject(participant); @@ -270,8 +270,8 @@ class UserMedia { this.participant$.asObservable() as Observable, encryptionSystem, livekitRoom, - handRaised, - reaction, + handRaised$, + reaction$, ); } else { this.vm = new RemoteUserMediaViewModel( @@ -282,8 +282,8 @@ class UserMedia { >, encryptionSystem, livekitRoom, - handRaised, - reaction, + handRaised$, + reaction$, ); } @@ -544,10 +544,10 @@ export class CallViewModel extends ViewModel { participant, this.encryptionSystem, this.livekitRoom, - this.handsRaised.pipe( + this.handsRaised$.pipe( map((v) => v[matrixIdentifier]?.time ?? null), ), - this.reactions.pipe( + this.reactions$.pipe( map((v) => v[matrixIdentifier] ?? undefined), ), ), @@ -711,7 +711,7 @@ export class CallViewModel extends ViewModel { m.speaker$, m.presenter$, m.vm.videoEnabled$, - m.vm.handRaised, + m.vm.handRaised$, m.vm instanceof LocalUserMediaViewModel ? m.vm.alwaysShow$ : of(false), @@ -1203,7 +1203,7 @@ export class CallViewModel extends ViewModel { this.scope.state(), ); - public readonly reactions = this.reactionsSubject.pipe( + public readonly reactions$ = this.reactionsSubject$.pipe( map((v) => Object.fromEntries( Object.entries(v).map(([a, { reactionOption }]) => [a, reactionOption]), @@ -1211,13 +1211,13 @@ export class CallViewModel extends ViewModel { ), ); - public readonly handsRaised = this.handsRaisedSubject.pipe(); + public readonly handsRaised$ = this.handsRaisedSubject$.pipe(); /** * Emits an array of reactions that should be visible on the screen. */ - public readonly visibleReactions = showReactions.value$ - .pipe(switchMap((show) => (show ? this.reactions : of({})))) + public readonly visibleReactions$ = showReactions.value$ + .pipe(switchMap((show) => (show ? this.reactions$ : of({})))) .pipe( scan< Record, @@ -1238,10 +1238,10 @@ export class CallViewModel extends ViewModel { /** * Emits an array of reactions that should be played. */ - public readonly audibleReactions = playReactionsSound.value$ + public readonly audibleReactions$ = playReactionsSound.value$ .pipe( switchMap((show) => - show ? this.reactions : of>({}), + show ? this.reactions$ : of>({}), ), ) .pipe( @@ -1267,7 +1267,7 @@ export class CallViewModel extends ViewModel { * Emits an event every time a new hand is raised in * the call. */ - public readonly newHandRaised = this.handsRaised.pipe( + public readonly newHandRaised$ = this.handsRaised$.pipe( map((v) => Object.keys(v).length), scan( (acc, newValue) => ({ @@ -1285,10 +1285,12 @@ export class CallViewModel extends ViewModel { private readonly livekitRoom: LivekitRoom, private readonly encryptionSystem: EncryptionSystem, private readonly connectionState$: Observable, - private readonly handsRaisedSubject: Observable< + private readonly handsRaisedSubject$: Observable< Record >, - private readonly reactionsSubject: Observable>, + private readonly reactionsSubject$: Observable< + Record + >, ) { super(); } diff --git a/src/state/MediaViewModel.ts b/src/state/MediaViewModel.ts index ebb615494..b57b6f15c 100644 --- a/src/state/MediaViewModel.ts +++ b/src/state/MediaViewModel.ts @@ -372,8 +372,8 @@ abstract class BaseUserMediaViewModel extends BaseMediaViewModel { participant$: Observable, encryptionSystem: EncryptionSystem, livekitRoom: LivekitRoom, - public readonly handRaised: Observable, - public readonly reaction: Observable, + public readonly handRaised$: Observable, + public readonly reaction$: Observable, ) { super( id, @@ -440,8 +440,8 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel { participant$: Observable, encryptionSystem: EncryptionSystem, livekitRoom: LivekitRoom, - handRaised: Observable, - reaction: Observable, + handRaised$: Observable, + reaction$: Observable, ) { super( id, @@ -449,8 +449,8 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel { participant$, encryptionSystem, livekitRoom, - handRaised, - reaction, + handRaised$, + reaction$, ); } } @@ -511,8 +511,8 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel { participant$: Observable, encryptionSystem: EncryptionSystem, livekitRoom: LivekitRoom, - handRaised: Observable, - reaction: Observable, + handRaised$: Observable, + reaction$: Observable, ) { super( id, @@ -520,8 +520,8 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel { participant$, encryptionSystem, livekitRoom, - handRaised, - reaction, + handRaised$, + reaction$, ); // Sync the local volume with LiveKit diff --git a/src/tile/GridTile.test.tsx b/src/tile/GridTile.test.tsx index 1c71c17b9..16875c332 100644 --- a/src/tile/GridTile.test.tsx +++ b/src/tile/GridTile.test.tsx @@ -53,8 +53,8 @@ test("GridTile is accessible", async () => { memberships: [], } as unknown as MatrixRTCSession; const cVm = { - reactions: of({}), - handsRaised: of({}), + reactions$: of({}), + handsRaised$: of({}), } as Partial as CallViewModel; const { container } = render( diff --git a/src/tile/GridTile.tsx b/src/tile/GridTile.tsx index b37bc85c6..9eb775d02 100644 --- a/src/tile/GridTile.tsx +++ b/src/tile/GridTile.tsx @@ -97,8 +97,8 @@ const UserMediaTile = forwardRef( }, [vm], ); - const handRaised = useObservableState(vm.handRaised); - const reaction = useObservableState(vm.reaction); + const handRaised = useObservableState(vm.handRaised$); + const reaction = useObservableState(vm.reaction$); const AudioIcon = locallyMuted ? VolumeOffSolidIcon diff --git a/src/utils/test-viewmodel.ts b/src/utils/test-viewmodel.ts index 6f69cde60..5b53419e6 100644 --- a/src/utils/test-viewmodel.ts +++ b/src/utils/test-viewmodel.ts @@ -66,17 +66,17 @@ export function getBasicCallViewModelEnvironment( roomId: matrixRoomId, }); - const remoteRtcMemberships = new BehaviorSubject( + const remoteRtcMemberships$ = new BehaviorSubject( initialRemoteRtcMemberships, ); - const handRaisedSubject = new BehaviorSubject({}); - const reactionsSubject = new BehaviorSubject({}); + const handRaisedSubject$ = new BehaviorSubject({}); + const reactionsSubject$ = new BehaviorSubject({}); const rtcSession = new MockRTCSession( matrixRoom, localRtcMember, - ).withMemberships(remoteRtcMemberships); + ).withMemberships(remoteRtcMemberships$); const vm = new CallViewModel( rtcSession as unknown as MatrixRTCSession, @@ -85,14 +85,14 @@ export function getBasicCallViewModelEnvironment( kind: E2eeType.PER_PARTICIPANT, }, of(ConnectionState.Connected), - handRaisedSubject, - reactionsSubject, + handRaisedSubject$, + reactionsSubject$, ); return { vm, - remoteRtcMemberships$: remoteRtcMemberships, + remoteRtcMemberships$: remoteRtcMemberships$, rtcSession, - handRaisedSubject$: handRaisedSubject, - reactionsSubject$: reactionsSubject, + handRaisedSubject$: handRaisedSubject$, + reactionsSubject$: reactionsSubject$, }; }