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

Fix incoming call toast crash due to audio refactor #12737

Merged
merged 3 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/LegacyCallHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ export default class LegacyCallHandler extends EventEmitter {
logger.debug(`${logPrefix} paused audio`);
}

public isPlaying(audioId: AudioID): boolean {
return !!this.playingSources[audioId];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not loving the lack of documentation on this, but I guess it fits the general vibe in this class.

In particular, what happens if you have a media which doesn't loop, but has reached its end without being explicitly paused? Does this return true or false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, iterated


private matchesCallForThisRoom(call: MatrixCall): boolean {
// We don't allow placing more than one call per room, but that doesn't mean there
// can't be more than one, eg. in a glare situation. This checks that the given call
Expand Down
15 changes: 7 additions & 8 deletions src/toasts/IncomingCallToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { Button, Tooltip } from "@vector-im/compound-web";
import { Icon as VideoCallIcon } from "@vector-im/compound-design-tokens/icons/video-call-solid.svg";
Expand All @@ -36,7 +36,7 @@ import AccessibleButton, { ButtonEvent } from "../components/views/elements/Acce
import { useDispatcher } from "../hooks/useDispatcher";
import { ActionPayload } from "../dispatcher/payloads";
import { Call } from "../models/Call";
import { AudioID } from "../LegacyCallHandler";
import LegacyCallHandler, { AudioID } from "../LegacyCallHandler";
import { useEventEmitter } from "../hooks/useEventEmitter";
import { CallStore, CallStoreEvent } from "../stores/CallStore";

Expand Down Expand Up @@ -78,7 +78,6 @@ export function IncomingCallToast({ notifyEvent }: Props): JSX.Element {
const roomId = notifyEvent.getRoomId()!;
const room = MatrixClientPeg.safeGet().getRoom(roomId) ?? undefined;
const call = useCall(roomId);
const audio = useMemo(() => document.getElementById(AudioID.Ring) as HTMLMediaElement, []);
const [connectedCalls, setConnectedCalls] = useState<Call[]>(Array.from(CallStore.instance.connectedCalls));
useEventEmitter(CallStore.instance, CallStoreEvent.ConnectedCalls, () => {
setConnectedCalls(Array.from(CallStore.instance.connectedCalls));
Expand All @@ -87,18 +86,18 @@ export function IncomingCallToast({ notifyEvent }: Props): JSX.Element {
// Start ringing if not already.
useEffect(() => {
const isRingToast = (notifyEvent.getContent() as unknown as { notify_type: string })["notify_type"] == "ring";
if (isRingToast && audio.paused) {
audio.play();
if (isRingToast && !LegacyCallHandler.instance.isPlaying(AudioID.Ring)) {
LegacyCallHandler.instance.play(AudioID.Ring);
}
}, [audio, notifyEvent]);
}, [notifyEvent]);

// Stop ringing on dismiss.
const dismissToast = useCallback((): void => {
ToastStore.sharedInstance().dismissToast(
getIncomingCallToastKey(notifyEvent.getContent().call_id ?? "", roomId),
);
audio.pause();
}, [audio, notifyEvent, roomId]);
LegacyCallHandler.instance.pause(AudioID.Ring);
}, [notifyEvent, roomId]);

// Dismiss if session got ended remotely.
const onCall = useCallback(
Expand Down
1 change: 1 addition & 0 deletions test/setup/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const mocks = {
AudioBufferSourceNode: {
connect: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
} as unknown as AudioBufferSourceNode,
AudioContext: {
close: jest.fn(),
Expand Down
9 changes: 3 additions & 6 deletions test/toasts/IncomingCallToast-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ import { WidgetMessagingStore } from "../../src/stores/widgets/WidgetMessagingSt
import DMRoomMap from "../../src/utils/DMRoomMap";
import ToastStore from "../../src/stores/ToastStore";
import { getIncomingCallToastKey, IncomingCallToast } from "../../src/toasts/IncomingCallToast";
import { AudioID } from "../../src/LegacyCallHandler";
import LegacyCallHandler, { AudioID } from "../../src/LegacyCallHandler";

describe("IncomingCallEvent", () => {
describe("IncomingCallToast", () => {
useMockedCalls();
jest.spyOn(HTMLMediaElement.prototype, "play").mockImplementation(async () => {});

let client: Mocked<MatrixClient>;
let room: Room;
Expand Down Expand Up @@ -133,10 +132,8 @@ describe("IncomingCallEvent", () => {
...notifyContent,
notify_type: "ring",
}) as any;
const playMock = jest.fn();
const audio = { play: playMock, paused: true };

jest.spyOn(document, "getElementById").mockReturnValue(audio as any);
const playMock = jest.spyOn(LegacyCallHandler.instance, "play");
render(<IncomingCallToast notifyEvent={call.event} />);
expect(playMock).toHaveBeenCalled();
});
Expand Down
Loading