mirror of
https://github.com/vector-im/element-call.git
synced 2026-09-25 22:35:49 +00:00
finnish notation
This commit is contained in:
@@ -53,11 +53,8 @@ test("Can open menu", async () => {
|
|||||||
|
|
||||||
test("Can raise hand", async () => {
|
test("Can raise hand", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const {
|
const { vm, rtcSession, handRaisedSubject$ } =
|
||||||
vm,
|
getBasicCallViewModelEnvironment([local, alice]);
|
||||||
rtcSession,
|
|
||||||
handRaisedSubject$: handRaisedSubject,
|
|
||||||
} = getBasicCallViewModelEnvironment([local, alice]);
|
|
||||||
const { getByLabelText, container } = render(
|
const { getByLabelText, container } = render(
|
||||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||||
);
|
);
|
||||||
@@ -76,7 +73,7 @@ test("Can raise hand", async () => {
|
|||||||
);
|
);
|
||||||
act(() => {
|
act(() => {
|
||||||
// Mock receiving a reaction.
|
// Mock receiving a reaction.
|
||||||
handRaisedSubject.next({
|
handRaisedSubject$.next({
|
||||||
[localIdent]: {
|
[localIdent]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
reactionEventId: "",
|
reactionEventId: "",
|
||||||
@@ -90,18 +87,15 @@ test("Can raise hand", async () => {
|
|||||||
test("Can lower hand", async () => {
|
test("Can lower hand", async () => {
|
||||||
const reactionEventId = "$my-reaction-event:example.org";
|
const reactionEventId = "$my-reaction-event:example.org";
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const {
|
const { vm, rtcSession, handRaisedSubject$ } =
|
||||||
vm,
|
getBasicCallViewModelEnvironment([local, alice]);
|
||||||
rtcSession,
|
|
||||||
handRaisedSubject$: handRaisedSubject,
|
|
||||||
} = getBasicCallViewModelEnvironment([local, alice]);
|
|
||||||
const { getByLabelText, container } = render(
|
const { getByLabelText, container } = render(
|
||||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||||
);
|
);
|
||||||
await user.click(getByLabelText("common.reactions"));
|
await user.click(getByLabelText("common.reactions"));
|
||||||
await user.click(getByLabelText("action.raise_hand"));
|
await user.click(getByLabelText("action.raise_hand"));
|
||||||
act(() => {
|
act(() => {
|
||||||
handRaisedSubject.next({
|
handRaisedSubject$.next({
|
||||||
[localIdent]: {
|
[localIdent]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
reactionEventId,
|
reactionEventId,
|
||||||
@@ -117,7 +111,7 @@ test("Can lower hand", async () => {
|
|||||||
);
|
);
|
||||||
act(() => {
|
act(() => {
|
||||||
// Mock receiving a redacted reaction.
|
// Mock receiving a redacted reaction.
|
||||||
handRaisedSubject.next({});
|
handRaisedSubject$.next({});
|
||||||
});
|
});
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -181,10 +181,10 @@ export function ReactionToggleButton({
|
|||||||
const [errorText, setErrorText] = useState<string>();
|
const [errorText, setErrorText] = useState<string>();
|
||||||
|
|
||||||
const isHandRaised = useObservableState(
|
const isHandRaised = useObservableState(
|
||||||
vm.handsRaised.pipe(map((v) => !!v[identifier])),
|
vm.handsRaised$.pipe(map((v) => !!v[identifier])),
|
||||||
);
|
);
|
||||||
const canReact = useObservableState(
|
const canReact = useObservableState(
|
||||||
vm.reactions.pipe(map((v) => !v[identifier])),
|
vm.reactions$.pipe(map((v) => !v[identifier])),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -36,32 +36,32 @@ const REACTION_ACTIVE_TIME_MS = 3000;
|
|||||||
* @param rtcSession
|
* @param rtcSession
|
||||||
*/
|
*/
|
||||||
export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
||||||
raisedHands: Observable<Record<string, RaisedHandInfo>>;
|
raisedHands$: Observable<Record<string, RaisedHandInfo>>;
|
||||||
reactions: Observable<Record<string, ReactionInfo>>;
|
reactions$: Observable<Record<string, ReactionInfo>>;
|
||||||
} {
|
} {
|
||||||
const raisedHandsSubject = useRef(
|
const raisedHandsSubject$ = useRef(
|
||||||
new BehaviorSubject<Record<string, RaisedHandInfo>>({}),
|
new BehaviorSubject<Record<string, RaisedHandInfo>>({}),
|
||||||
);
|
);
|
||||||
const reactionsSubject = useRef(
|
const reactionsSubject$ = useRef(
|
||||||
new BehaviorSubject<Record<string, ReactionInfo>>({}),
|
new BehaviorSubject<Record<string, ReactionInfo>>({}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const memberships = useMatrixRTCSessionMemberships(rtcSession);
|
const memberships = useMatrixRTCSessionMemberships(rtcSession);
|
||||||
const latestMemberships = useLatest(memberships);
|
const latestMemberships = useLatest(memberships);
|
||||||
const latestRaisedHands = useLatest(raisedHandsSubject.current);
|
const latestRaisedHands = useLatest(raisedHandsSubject$.current);
|
||||||
const room = rtcSession.room;
|
const room = rtcSession.room;
|
||||||
|
|
||||||
const addRaisedHand = useCallback((userId: string, info: RaisedHandInfo) => {
|
const addRaisedHand = useCallback((userId: string, info: RaisedHandInfo) => {
|
||||||
raisedHandsSubject.current.next({
|
raisedHandsSubject$.current.next({
|
||||||
...raisedHandsSubject.current.value,
|
...raisedHandsSubject$.current.value,
|
||||||
[userId]: info,
|
[userId]: info,
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const removeRaisedHand = useCallback((userId: string) => {
|
const removeRaisedHand = useCallback((userId: string) => {
|
||||||
raisedHandsSubject.current.next(
|
raisedHandsSubject$.current.next(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
Object.entries(raisedHandsSubject.current.value).filter(
|
Object.entries(raisedHandsSubject$.current.value).filter(
|
||||||
([uId]) => uId !== userId,
|
([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.
|
// 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),
|
(rhId) => !memberships.find((u) => u.sender == rhId),
|
||||||
)) {
|
)) {
|
||||||
removeRaisedHand(identifier);
|
removeRaisedHand(identifier);
|
||||||
@@ -104,8 +104,8 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
|||||||
}
|
}
|
||||||
const identifier = `${m.sender}:${m.deviceId}`;
|
const identifier = `${m.sender}:${m.deviceId}`;
|
||||||
if (
|
if (
|
||||||
raisedHandsSubject.current.value[identifier] &&
|
raisedHandsSubject$.current.value[identifier] &&
|
||||||
raisedHandsSubject.current.value[identifier].membershipEventId !==
|
raisedHandsSubject$.current.value[identifier].membershipEventId !==
|
||||||
m.eventId
|
m.eventId
|
||||||
) {
|
) {
|
||||||
// Membership event for sender has changed since the hand
|
// 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),
|
...ReactionSet.find((r) => r.name === content.name),
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentReactions = reactionsSubject.current.value;
|
const currentReactions = reactionsSubject$.current.value;
|
||||||
if (currentReactions[identifier]) {
|
if (currentReactions[identifier]) {
|
||||||
// We've still got a reaction from this user, ignore it to prevent spamming
|
// We've still got a reaction from this user, ignore it to prevent spamming
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const timeout = globalThis.setTimeout(() => {
|
const timeout = globalThis.setTimeout(() => {
|
||||||
// Clear the reaction after some time.
|
// Clear the reaction after some time.
|
||||||
reactionsSubject.current.next(
|
reactionsSubject$.current.next(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
Object.entries(reactionsSubject.current.value).filter(
|
Object.entries(reactionsSubject$.current.value).filter(
|
||||||
([id]) => id !== identifier,
|
([id]) => id !== identifier,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -210,7 +210,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
|||||||
reactionTimeouts.delete(timeout);
|
reactionTimeouts.delete(timeout);
|
||||||
}, REACTION_ACTIVE_TIME_MS);
|
}, REACTION_ACTIVE_TIME_MS);
|
||||||
reactionTimeouts.add(timeout);
|
reactionTimeouts.add(timeout);
|
||||||
reactionsSubject.current.next({
|
reactionsSubject$.current.next({
|
||||||
...currentReactions,
|
...currentReactions,
|
||||||
[identifier]: {
|
[identifier]: {
|
||||||
reactionOption: reaction,
|
reactionOption: reaction,
|
||||||
@@ -264,7 +264,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
|||||||
// may still be sending.
|
// may still be sending.
|
||||||
room.on(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent);
|
room.on(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent);
|
||||||
|
|
||||||
const innerReactionsSubject = reactionsSubject.current;
|
const innerReactionsSubject$ = reactionsSubject$.current;
|
||||||
return (): void => {
|
return (): void => {
|
||||||
room.off(MatrixRoomEvent.Timeline, handleReactionEvent);
|
room.off(MatrixRoomEvent.Timeline, handleReactionEvent);
|
||||||
room.off(MatrixRoomEvent.Redaction, handleReactionEvent);
|
room.off(MatrixRoomEvent.Redaction, handleReactionEvent);
|
||||||
@@ -272,7 +272,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
|||||||
room.off(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent);
|
room.off(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent);
|
||||||
reactionTimeouts.forEach((t) => clearTimeout(t));
|
reactionTimeouts.forEach((t) => clearTimeout(t));
|
||||||
// If we're clearing timeouts, we also clear all reactions.
|
// If we're clearing timeouts, we also clear all reactions.
|
||||||
innerReactionsSubject.next({});
|
innerReactionsSubject$.next({});
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
room,
|
room,
|
||||||
@@ -283,7 +283,7 @@ export default function useReactionsReader(rtcSession: MatrixRTCSession): {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reactions: reactionsSubject.current.asObservable(),
|
reactions$: reactionsSubject$.current.asObservable(),
|
||||||
raisedHands: raisedHandsSubject.current.asObservable(),
|
raisedHands$: raisedHandsSubject$.current.asObservable(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const ReactionsSenderProvider = ({
|
|||||||
}, [memberships, myUserId]);
|
}, [memberships, myUserId]);
|
||||||
|
|
||||||
const myReaction = useObservableEagerState(
|
const myReaction = useObservableEagerState(
|
||||||
vm.reactions.pipe(
|
vm.reactions$.pipe(
|
||||||
map((v) =>
|
map((v) =>
|
||||||
myMembershipIdentifier !== undefined
|
myMembershipIdentifier !== undefined
|
||||||
? v[myMembershipIdentifier]
|
? v[myMembershipIdentifier]
|
||||||
@@ -86,7 +86,7 @@ export const ReactionsSenderProvider = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const myRaisedHand = useObservableEagerState(
|
const myRaisedHand = useObservableEagerState(
|
||||||
vm.handsRaised.pipe(
|
vm.handsRaised$.pipe(
|
||||||
map((v) =>
|
map((v) =>
|
||||||
myMembershipIdentifier !== undefined
|
myMembershipIdentifier !== undefined
|
||||||
? v[myMembershipIdentifier]
|
? v[myMembershipIdentifier]
|
||||||
|
|||||||
@@ -66,36 +66,42 @@ beforeEach(() => {
|
|||||||
* a noise every time.
|
* a noise every time.
|
||||||
*/
|
*/
|
||||||
test("plays one sound when entering a call", () => {
|
test("plays one sound when entering a call", () => {
|
||||||
const { vm, remoteRtcMemberships$: remoteRtcMemberships } =
|
const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
|
|
||||||
// Joining a call usually means remote participants are added later.
|
// Joining a call usually means remote participants are added later.
|
||||||
act(() => {
|
act(() => {
|
||||||
remoteRtcMemberships.next([aliceRtcMember, bobRtcMember]);
|
remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]);
|
||||||
});
|
});
|
||||||
expect(playSound).toHaveBeenCalledOnce();
|
expect(playSound).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("plays a sound when a user joins", () => {
|
test("plays a sound when a user joins", () => {
|
||||||
const { vm, remoteRtcMemberships$: remoteRtcMemberships } =
|
const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
remoteRtcMemberships.next([aliceRtcMember, bobRtcMember]);
|
remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]);
|
||||||
});
|
});
|
||||||
// Play a sound when joining a call.
|
// Play a sound when joining a call.
|
||||||
expect(playSound).toBeCalledWith("join");
|
expect(playSound).toBeCalledWith("join");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("plays a sound when a user leaves", () => {
|
test("plays a sound when a user leaves", () => {
|
||||||
const { vm, remoteRtcMemberships$: remoteRtcMemberships } =
|
const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
remoteRtcMemberships.next([]);
|
remoteRtcMemberships$.next([]);
|
||||||
});
|
});
|
||||||
expect(playSound).toBeCalledWith("left");
|
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 } =
|
const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment(
|
||||||
getBasicCallViewModelEnvironment([local, alice], mockRtcMemberships);
|
[local, alice],
|
||||||
|
mockRtcMemberships,
|
||||||
|
);
|
||||||
|
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
expect(playSound).not.toBeCalled();
|
expect(playSound).not.toBeCalled();
|
||||||
act(() => {
|
act(() => {
|
||||||
remoteRtcMemberships.next(
|
remoteRtcMemberships$.next(
|
||||||
mockRtcMemberships.slice(0, MAX_PARTICIPANT_COUNT_FOR_SOUND - 1),
|
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", () => {
|
test("plays one sound when a hand is raised", () => {
|
||||||
const { vm, handRaisedSubject$: handRaisedSubject } =
|
const { vm, handRaisedSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
handRaisedSubject.next({
|
handRaisedSubject$.next({
|
||||||
[bobRtcMember.callId]: {
|
[bobRtcMember.callId]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
membershipEventId: "",
|
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", () => {
|
test("should not play a sound when a hand raise is retracted", () => {
|
||||||
const { vm, handRaisedSubject$: handRaisedSubject } =
|
const { vm, handRaisedSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
handRaisedSubject.next({
|
handRaisedSubject$.next({
|
||||||
["foo"]: {
|
["foo"]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
membershipEventId: "",
|
membershipEventId: "",
|
||||||
@@ -159,7 +171,7 @@ test("should not play a sound when a hand raise is retracted", () => {
|
|||||||
});
|
});
|
||||||
expect(playSound).toHaveBeenCalledTimes(2);
|
expect(playSound).toHaveBeenCalledTimes(2);
|
||||||
act(() => {
|
act(() => {
|
||||||
handRaisedSubject.next({
|
handRaisedSubject$.next({
|
||||||
["foo"]: {
|
["foo"]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
membershipEventId: "",
|
membershipEventId: "",
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export function CallEventAudioRenderer({
|
|||||||
void audioEngineRef.current?.playSound("left");
|
void audioEngineRef.current?.playSound("left");
|
||||||
});
|
});
|
||||||
|
|
||||||
const handRaisedSub = vm.newHandRaised.subscribe(() => {
|
const handRaisedSub = vm.newHandRaised$.subscribe(() => {
|
||||||
void audioEngineRef.current?.playSound("raiseHand");
|
void audioEngineRef.current?.playSound("raiseHand");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
|||||||
livekitRoom,
|
livekitRoom,
|
||||||
props.e2eeSystem,
|
props.e2eeSystem,
|
||||||
connStateObservable$,
|
connStateObservable$,
|
||||||
reader.current.raisedHands,
|
reader.current.raisedHands$,
|
||||||
reader.current.reactions,
|
reader.current.reactions$,
|
||||||
);
|
);
|
||||||
setVm(vm);
|
setVm(vm);
|
||||||
return (): void => vm.destroy();
|
return (): void => vm.destroy();
|
||||||
|
|||||||
@@ -80,8 +80,10 @@ test("preloads all audio elements", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("will play an audio sound when there is a reaction", () => {
|
test("will play an audio sound when there is a reaction", () => {
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
playReactionsSound.setValue(true);
|
playReactionsSound.setValue(true);
|
||||||
render(<TestComponent vm={vm} />);
|
render(<TestComponent vm={vm} />);
|
||||||
|
|
||||||
@@ -93,7 +95,7 @@ test("will play an audio sound when there is a reaction", () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: chosenReaction, ttl: 0 },
|
[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", () => {
|
test("will play the generic audio sound when there is soundless reaction", () => {
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
playReactionsSound.setValue(true);
|
playReactionsSound.setValue(true);
|
||||||
render(<TestComponent vm={vm} />);
|
render(<TestComponent vm={vm} />);
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ test("will play the generic audio sound when there is soundless reaction", () =>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: chosenReaction, ttl: 0 },
|
[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", () => {
|
test("will play multiple audio sounds when there are multiple different reactions", () => {
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
playReactionsSound.setValue(true);
|
playReactionsSound.setValue(true);
|
||||||
render(<TestComponent vm={vm} />);
|
render(<TestComponent vm={vm} />);
|
||||||
|
|
||||||
@@ -135,7 +141,7 @@ test("will play multiple audio sounds when there are multiple different reaction
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 },
|
[aliceRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 },
|
||||||
[bobRtcMember.deviceId]: { reactionOption: reaction2, ttl: 0 },
|
[bobRtcMember.deviceId]: { reactionOption: reaction2, ttl: 0 },
|
||||||
[localRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 },
|
[localRtcMember.deviceId]: { reactionOption: reaction1, ttl: 0 },
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function ReactionsAudioRenderer({
|
|||||||
}, [soundCache, shouldPlay]);
|
}, [soundCache, shouldPlay]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sub = vm.audibleReactions.subscribe((newReactions) => {
|
const sub = vm.audibleReactions$.subscribe((newReactions) => {
|
||||||
for (const reactionName of newReactions) {
|
for (const reactionName of newReactions) {
|
||||||
if (soundMap[reactionName]) {
|
if (soundMap[reactionName]) {
|
||||||
void audioEngineRef.current?.playSound(reactionName);
|
void audioEngineRef.current?.playSound(reactionName);
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ test("defaults to showing no reactions", () => {
|
|||||||
|
|
||||||
test("shows a reaction when sent", () => {
|
test("shows a reaction when sent", () => {
|
||||||
showReactions.setValue(true);
|
showReactions.setValue(true);
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
const { getByRole } = render(<ReactionsOverlay vm={vm} />);
|
const { getByRole } = render(<ReactionsOverlay vm={vm} />);
|
||||||
const reaction = ReactionSet[0];
|
const reaction = ReactionSet[0];
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 },
|
[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", () => {
|
test("shows two of the same reaction when sent", () => {
|
||||||
showReactions.setValue(true);
|
showReactions.setValue(true);
|
||||||
const reaction = ReactionSet[0];
|
const reaction = ReactionSet[0];
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
const { getAllByRole } = render(<ReactionsOverlay vm={vm} />);
|
const { getAllByRole } = render(<ReactionsOverlay vm={vm} />);
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 },
|
[aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 },
|
||||||
[bobRtcMember.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", () => {
|
test("shows two different reactions when sent", () => {
|
||||||
showReactions.setValue(true);
|
showReactions.setValue(true);
|
||||||
const [reactionA, reactionB] = ReactionSet;
|
const [reactionA, reactionB] = ReactionSet;
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
const { getAllByRole } = render(<ReactionsOverlay vm={vm} />);
|
const { getAllByRole } = render(<ReactionsOverlay vm={vm} />);
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: reactionA, ttl: 0 },
|
[aliceRtcMember.deviceId]: { reactionOption: reactionA, ttl: 0 },
|
||||||
[bobRtcMember.deviceId]: { reactionOption: reactionB, 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", () => {
|
test("hides reactions when reaction animations are disabled", () => {
|
||||||
showReactions.setValue(false);
|
showReactions.setValue(false);
|
||||||
const reaction = ReactionSet[0];
|
const reaction = ReactionSet[0];
|
||||||
const { vm, reactionsSubject$: reactionsSubject } =
|
const { vm, reactionsSubject$ } = getBasicCallViewModelEnvironment([
|
||||||
getBasicCallViewModelEnvironment([local, alice]);
|
local,
|
||||||
|
alice,
|
||||||
|
]);
|
||||||
const { container } = render(<ReactionsOverlay vm={vm} />);
|
const { container } = render(<ReactionsOverlay vm={vm} />);
|
||||||
act(() => {
|
act(() => {
|
||||||
reactionsSubject.next({
|
reactionsSubject$.next({
|
||||||
[aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 },
|
[aliceRtcMember.deviceId]: { reactionOption: reaction, ttl: 0 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import styles from "./ReactionsOverlay.module.css";
|
|||||||
import { type CallViewModel } from "../state/CallViewModel";
|
import { type CallViewModel } from "../state/CallViewModel";
|
||||||
|
|
||||||
export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode {
|
export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode {
|
||||||
const reactionsIcons = useObservableState(vm.visibleReactions);
|
const reactionsIcons = useObservableState(vm.visibleReactions$);
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{reactionsIcons?.map(({ sender, emoji, startX }) => (
|
{reactionsIcons?.map(({ sender, emoji, startX }) => (
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ function withCallViewModel(
|
|||||||
speaking: Map<Participant, Observable<boolean>>,
|
speaking: Map<Participant, Observable<boolean>>,
|
||||||
continuation: (
|
continuation: (
|
||||||
vm: CallViewModel,
|
vm: CallViewModel,
|
||||||
subjects: { raisedHands: BehaviorSubject<Record<string, RaisedHandInfo>> },
|
subjects: { raisedHands$: BehaviorSubject<Record<string, RaisedHandInfo>> },
|
||||||
) => void,
|
) => void,
|
||||||
): void {
|
): void {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
@@ -240,7 +240,7 @@ function withCallViewModel(
|
|||||||
{ remoteParticipants$ },
|
{ remoteParticipants$ },
|
||||||
);
|
);
|
||||||
|
|
||||||
const raisedHands = new BehaviorSubject<Record<string, RaisedHandInfo>>({});
|
const raisedHands$ = new BehaviorSubject<Record<string, RaisedHandInfo>>({});
|
||||||
|
|
||||||
const vm = new CallViewModel(
|
const vm = new CallViewModel(
|
||||||
rtcSession as unknown as MatrixRTCSession,
|
rtcSession as unknown as MatrixRTCSession,
|
||||||
@@ -249,7 +249,7 @@ function withCallViewModel(
|
|||||||
kind: E2eeType.PER_PARTICIPANT,
|
kind: E2eeType.PER_PARTICIPANT,
|
||||||
},
|
},
|
||||||
connectionState$,
|
connectionState$,
|
||||||
raisedHands,
|
raisedHands$,
|
||||||
new BehaviorSubject({}),
|
new BehaviorSubject({}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ function withCallViewModel(
|
|||||||
roomEventSelectorSpy!.mockRestore();
|
roomEventSelectorSpy!.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
continuation(vm, { raisedHands });
|
continuation(vm, { raisedHands$: raisedHands$ });
|
||||||
}
|
}
|
||||||
|
|
||||||
test("participants are retained during a focus switch", () => {
|
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([aliceRtcMember, bobRtcMember]),
|
||||||
of(ConnectionState.Connected),
|
of(ConnectionState.Connected),
|
||||||
new Map(),
|
new Map(),
|
||||||
(vm, { raisedHands }) => {
|
(vm, { raisedHands$ }) => {
|
||||||
schedule("ab", {
|
schedule("ab", {
|
||||||
a: () => {
|
a: () => {
|
||||||
// We imagine that only three tiles (the first three) will be visible
|
// 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: () => {
|
b: () => {
|
||||||
raisedHands.next({
|
raisedHands$.next({
|
||||||
[`${bobRtcMember.sender}:${bobRtcMember.deviceId}`]: {
|
[`${bobRtcMember.sender}:${bobRtcMember.deviceId}`]: {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
reactionEventId: "",
|
reactionEventId: "",
|
||||||
|
|||||||
+20
-18
@@ -258,8 +258,8 @@ class UserMedia {
|
|||||||
participant: LocalParticipant | RemoteParticipant | undefined,
|
participant: LocalParticipant | RemoteParticipant | undefined,
|
||||||
encryptionSystem: EncryptionSystem,
|
encryptionSystem: EncryptionSystem,
|
||||||
livekitRoom: LivekitRoom,
|
livekitRoom: LivekitRoom,
|
||||||
handRaised: Observable<Date | null>,
|
handRaised$: Observable<Date | null>,
|
||||||
reaction: Observable<ReactionOption | null>,
|
reaction$: Observable<ReactionOption | null>,
|
||||||
) {
|
) {
|
||||||
this.participant$ = new BehaviorSubject(participant);
|
this.participant$ = new BehaviorSubject(participant);
|
||||||
|
|
||||||
@@ -270,8 +270,8 @@ class UserMedia {
|
|||||||
this.participant$.asObservable() as Observable<LocalParticipant>,
|
this.participant$.asObservable() as Observable<LocalParticipant>,
|
||||||
encryptionSystem,
|
encryptionSystem,
|
||||||
livekitRoom,
|
livekitRoom,
|
||||||
handRaised,
|
handRaised$,
|
||||||
reaction,
|
reaction$,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.vm = new RemoteUserMediaViewModel(
|
this.vm = new RemoteUserMediaViewModel(
|
||||||
@@ -282,8 +282,8 @@ class UserMedia {
|
|||||||
>,
|
>,
|
||||||
encryptionSystem,
|
encryptionSystem,
|
||||||
livekitRoom,
|
livekitRoom,
|
||||||
handRaised,
|
handRaised$,
|
||||||
reaction,
|
reaction$,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,10 +544,10 @@ export class CallViewModel extends ViewModel {
|
|||||||
participant,
|
participant,
|
||||||
this.encryptionSystem,
|
this.encryptionSystem,
|
||||||
this.livekitRoom,
|
this.livekitRoom,
|
||||||
this.handsRaised.pipe(
|
this.handsRaised$.pipe(
|
||||||
map((v) => v[matrixIdentifier]?.time ?? null),
|
map((v) => v[matrixIdentifier]?.time ?? null),
|
||||||
),
|
),
|
||||||
this.reactions.pipe(
|
this.reactions$.pipe(
|
||||||
map((v) => v[matrixIdentifier] ?? undefined),
|
map((v) => v[matrixIdentifier] ?? undefined),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -711,7 +711,7 @@ export class CallViewModel extends ViewModel {
|
|||||||
m.speaker$,
|
m.speaker$,
|
||||||
m.presenter$,
|
m.presenter$,
|
||||||
m.vm.videoEnabled$,
|
m.vm.videoEnabled$,
|
||||||
m.vm.handRaised,
|
m.vm.handRaised$,
|
||||||
m.vm instanceof LocalUserMediaViewModel
|
m.vm instanceof LocalUserMediaViewModel
|
||||||
? m.vm.alwaysShow$
|
? m.vm.alwaysShow$
|
||||||
: of(false),
|
: of(false),
|
||||||
@@ -1203,7 +1203,7 @@ export class CallViewModel extends ViewModel {
|
|||||||
this.scope.state(),
|
this.scope.state(),
|
||||||
);
|
);
|
||||||
|
|
||||||
public readonly reactions = this.reactionsSubject.pipe(
|
public readonly reactions$ = this.reactionsSubject$.pipe(
|
||||||
map((v) =>
|
map((v) =>
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
Object.entries(v).map(([a, { reactionOption }]) => [a, reactionOption]),
|
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.
|
* Emits an array of reactions that should be visible on the screen.
|
||||||
*/
|
*/
|
||||||
public readonly visibleReactions = showReactions.value$
|
public readonly visibleReactions$ = showReactions.value$
|
||||||
.pipe(switchMap((show) => (show ? this.reactions : of({}))))
|
.pipe(switchMap((show) => (show ? this.reactions$ : of({}))))
|
||||||
.pipe(
|
.pipe(
|
||||||
scan<
|
scan<
|
||||||
Record<string, ReactionOption>,
|
Record<string, ReactionOption>,
|
||||||
@@ -1238,10 +1238,10 @@ export class CallViewModel extends ViewModel {
|
|||||||
/**
|
/**
|
||||||
* Emits an array of reactions that should be played.
|
* Emits an array of reactions that should be played.
|
||||||
*/
|
*/
|
||||||
public readonly audibleReactions = playReactionsSound.value$
|
public readonly audibleReactions$ = playReactionsSound.value$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((show) =>
|
switchMap((show) =>
|
||||||
show ? this.reactions : of<Record<string, ReactionOption>>({}),
|
show ? this.reactions$ : of<Record<string, ReactionOption>>({}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -1267,7 +1267,7 @@ export class CallViewModel extends ViewModel {
|
|||||||
* Emits an event every time a new hand is raised in
|
* Emits an event every time a new hand is raised in
|
||||||
* the call.
|
* the call.
|
||||||
*/
|
*/
|
||||||
public readonly newHandRaised = this.handsRaised.pipe(
|
public readonly newHandRaised$ = this.handsRaised$.pipe(
|
||||||
map((v) => Object.keys(v).length),
|
map((v) => Object.keys(v).length),
|
||||||
scan(
|
scan(
|
||||||
(acc, newValue) => ({
|
(acc, newValue) => ({
|
||||||
@@ -1285,10 +1285,12 @@ export class CallViewModel extends ViewModel {
|
|||||||
private readonly livekitRoom: LivekitRoom,
|
private readonly livekitRoom: LivekitRoom,
|
||||||
private readonly encryptionSystem: EncryptionSystem,
|
private readonly encryptionSystem: EncryptionSystem,
|
||||||
private readonly connectionState$: Observable<ECConnectionState>,
|
private readonly connectionState$: Observable<ECConnectionState>,
|
||||||
private readonly handsRaisedSubject: Observable<
|
private readonly handsRaisedSubject$: Observable<
|
||||||
Record<string, RaisedHandInfo>
|
Record<string, RaisedHandInfo>
|
||||||
>,
|
>,
|
||||||
private readonly reactionsSubject: Observable<Record<string, ReactionInfo>>,
|
private readonly reactionsSubject$: Observable<
|
||||||
|
Record<string, ReactionInfo>
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -372,8 +372,8 @@ abstract class BaseUserMediaViewModel extends BaseMediaViewModel {
|
|||||||
participant$: Observable<LocalParticipant | RemoteParticipant | undefined>,
|
participant$: Observable<LocalParticipant | RemoteParticipant | undefined>,
|
||||||
encryptionSystem: EncryptionSystem,
|
encryptionSystem: EncryptionSystem,
|
||||||
livekitRoom: LivekitRoom,
|
livekitRoom: LivekitRoom,
|
||||||
public readonly handRaised: Observable<Date | null>,
|
public readonly handRaised$: Observable<Date | null>,
|
||||||
public readonly reaction: Observable<ReactionOption | null>,
|
public readonly reaction$: Observable<ReactionOption | null>,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
id,
|
id,
|
||||||
@@ -440,8 +440,8 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel {
|
|||||||
participant$: Observable<LocalParticipant | undefined>,
|
participant$: Observable<LocalParticipant | undefined>,
|
||||||
encryptionSystem: EncryptionSystem,
|
encryptionSystem: EncryptionSystem,
|
||||||
livekitRoom: LivekitRoom,
|
livekitRoom: LivekitRoom,
|
||||||
handRaised: Observable<Date | null>,
|
handRaised$: Observable<Date | null>,
|
||||||
reaction: Observable<ReactionOption | null>,
|
reaction$: Observable<ReactionOption | null>,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
id,
|
id,
|
||||||
@@ -449,8 +449,8 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel {
|
|||||||
participant$,
|
participant$,
|
||||||
encryptionSystem,
|
encryptionSystem,
|
||||||
livekitRoom,
|
livekitRoom,
|
||||||
handRaised,
|
handRaised$,
|
||||||
reaction,
|
reaction$,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,8 +511,8 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel {
|
|||||||
participant$: Observable<RemoteParticipant | undefined>,
|
participant$: Observable<RemoteParticipant | undefined>,
|
||||||
encryptionSystem: EncryptionSystem,
|
encryptionSystem: EncryptionSystem,
|
||||||
livekitRoom: LivekitRoom,
|
livekitRoom: LivekitRoom,
|
||||||
handRaised: Observable<Date | null>,
|
handRaised$: Observable<Date | null>,
|
||||||
reaction: Observable<ReactionOption | null>,
|
reaction$: Observable<ReactionOption | null>,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
id,
|
id,
|
||||||
@@ -520,8 +520,8 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel {
|
|||||||
participant$,
|
participant$,
|
||||||
encryptionSystem,
|
encryptionSystem,
|
||||||
livekitRoom,
|
livekitRoom,
|
||||||
handRaised,
|
handRaised$,
|
||||||
reaction,
|
reaction$,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sync the local volume with LiveKit
|
// Sync the local volume with LiveKit
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ test("GridTile is accessible", async () => {
|
|||||||
memberships: [],
|
memberships: [],
|
||||||
} as unknown as MatrixRTCSession;
|
} as unknown as MatrixRTCSession;
|
||||||
const cVm = {
|
const cVm = {
|
||||||
reactions: of({}),
|
reactions$: of({}),
|
||||||
handsRaised: of({}),
|
handsRaised$: of({}),
|
||||||
} as Partial<CallViewModel> as CallViewModel;
|
} as Partial<CallViewModel> as CallViewModel;
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ReactionsSenderProvider vm={cVm} rtcSession={fakeRtcSession}>
|
<ReactionsSenderProvider vm={cVm} rtcSession={fakeRtcSession}>
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ const UserMediaTile = forwardRef<HTMLDivElement, UserMediaTileProps>(
|
|||||||
},
|
},
|
||||||
[vm],
|
[vm],
|
||||||
);
|
);
|
||||||
const handRaised = useObservableState(vm.handRaised);
|
const handRaised = useObservableState(vm.handRaised$);
|
||||||
const reaction = useObservableState(vm.reaction);
|
const reaction = useObservableState(vm.reaction$);
|
||||||
|
|
||||||
const AudioIcon = locallyMuted
|
const AudioIcon = locallyMuted
|
||||||
? VolumeOffSolidIcon
|
? VolumeOffSolidIcon
|
||||||
|
|||||||
@@ -66,17 +66,17 @@ export function getBasicCallViewModelEnvironment(
|
|||||||
roomId: matrixRoomId,
|
roomId: matrixRoomId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const remoteRtcMemberships = new BehaviorSubject<CallMembership[]>(
|
const remoteRtcMemberships$ = new BehaviorSubject<CallMembership[]>(
|
||||||
initialRemoteRtcMemberships,
|
initialRemoteRtcMemberships,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handRaisedSubject = new BehaviorSubject({});
|
const handRaisedSubject$ = new BehaviorSubject({});
|
||||||
const reactionsSubject = new BehaviorSubject({});
|
const reactionsSubject$ = new BehaviorSubject({});
|
||||||
|
|
||||||
const rtcSession = new MockRTCSession(
|
const rtcSession = new MockRTCSession(
|
||||||
matrixRoom,
|
matrixRoom,
|
||||||
localRtcMember,
|
localRtcMember,
|
||||||
).withMemberships(remoteRtcMemberships);
|
).withMemberships(remoteRtcMemberships$);
|
||||||
|
|
||||||
const vm = new CallViewModel(
|
const vm = new CallViewModel(
|
||||||
rtcSession as unknown as MatrixRTCSession,
|
rtcSession as unknown as MatrixRTCSession,
|
||||||
@@ -85,14 +85,14 @@ export function getBasicCallViewModelEnvironment(
|
|||||||
kind: E2eeType.PER_PARTICIPANT,
|
kind: E2eeType.PER_PARTICIPANT,
|
||||||
},
|
},
|
||||||
of(ConnectionState.Connected),
|
of(ConnectionState.Connected),
|
||||||
handRaisedSubject,
|
handRaisedSubject$,
|
||||||
reactionsSubject,
|
reactionsSubject$,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
vm,
|
vm,
|
||||||
remoteRtcMemberships$: remoteRtcMemberships,
|
remoteRtcMemberships$: remoteRtcMemberships$,
|
||||||
rtcSession,
|
rtcSession,
|
||||||
handRaisedSubject$: handRaisedSubject,
|
handRaisedSubject$: handRaisedSubject$,
|
||||||
reactionsSubject$: reactionsSubject,
|
reactionsSubject$: reactionsSubject$,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user