mirror of
https://github.com/vector-im/element-web.git
synced 2025-11-14 23:21:07 +01:00
Refactor LegacyCallHandler event emitter to use TypedEventEmitter (#29008)
* Switch LegacyCallHandler over to TypedEventEmitter and use emits to notify consumers of protocol support updates Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add test for dialpad Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
13913ba8b2
commit
e5ca7954c8
31
playwright/e2e/voip/pstn.spec.ts
Normal file
31
playwright/e2e/voip/pstn.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
test.describe("PSTN", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the third party protocols endpoint to look like the HS has PSTN support
|
||||
await page.route("**/_matrix/client/v3/thirdparty/protocols", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
json: {
|
||||
"im.vector.protocol.pstn": {},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("should render dialpad as expected", { tag: "@screenshot" }, async ({ page, user, toasts }) => {
|
||||
await toasts.rejectToast("Notifications");
|
||||
await toasts.assertNoToasts();
|
||||
|
||||
await expect(page.locator(".mx_LeftPanel_filterContainer")).toMatchScreenshot("dialpad-trigger.png");
|
||||
await page.getByLabel("Open dial pad").click();
|
||||
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("dialpad.png");
|
||||
});
|
||||
});
|
||||
BIN
playwright/snapshots/voip/pstn.spec.ts/dialpad-linux.png
Normal file
BIN
playwright/snapshots/voip/pstn.spec.ts/dialpad-linux.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
playwright/snapshots/voip/pstn.spec.ts/dialpad-trigger-linux.png
Normal file
BIN
playwright/snapshots/voip/pstn.spec.ts/dialpad-trigger-linux.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { MatrixError, RuleId, TweakName, SyncState } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixError, RuleId, TweakName, SyncState, TypedEventEmitter } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
CallError,
|
||||
CallErrorCode,
|
||||
@ -22,7 +22,6 @@ import {
|
||||
MatrixCall,
|
||||
} from "matrix-js-sdk/src/webrtc/call";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import EventEmitter from "events";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
|
||||
|
||||
@ -137,14 +136,23 @@ export enum LegacyCallHandlerEvent {
|
||||
CallChangeRoom = "call_change_room",
|
||||
SilencedCallsChanged = "silenced_calls_changed",
|
||||
CallState = "call_state",
|
||||
ProtocolSupport = "protocol_support",
|
||||
}
|
||||
|
||||
type EventEmitterMap = {
|
||||
[LegacyCallHandlerEvent.CallsChanged]: (calls: Map<string, MatrixCall>) => void;
|
||||
[LegacyCallHandlerEvent.CallChangeRoom]: (call: MatrixCall) => void;
|
||||
[LegacyCallHandlerEvent.SilencedCallsChanged]: (calls: Set<string>) => void;
|
||||
[LegacyCallHandlerEvent.CallState]: (mappedRoomId: string | null, status: CallState) => void;
|
||||
[LegacyCallHandlerEvent.ProtocolSupport]: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* LegacyCallHandler manages all currently active calls. It should be used for
|
||||
* placing, answering, rejecting and hanging up calls. It also handles ringing,
|
||||
* PSTN support and other things.
|
||||
*/
|
||||
export default class LegacyCallHandler extends EventEmitter {
|
||||
export default class LegacyCallHandler extends TypedEventEmitter<LegacyCallHandlerEvent, EventEmitterMap> {
|
||||
private calls = new Map<string, MatrixCall>(); // roomId -> call
|
||||
// Calls started as an attended transfer, ie. with the intention of transferring another
|
||||
// call with a different party to this one.
|
||||
@ -271,15 +279,13 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||
this.supportsPstnProtocol = null;
|
||||
}
|
||||
|
||||
dis.dispatch({ action: Action.PstnSupportUpdated });
|
||||
|
||||
if (protocols[PROTOCOL_SIP_NATIVE] !== undefined && protocols[PROTOCOL_SIP_VIRTUAL] !== undefined) {
|
||||
this.supportsSipNativeVirtual = Boolean(
|
||||
protocols[PROTOCOL_SIP_NATIVE] && protocols[PROTOCOL_SIP_VIRTUAL],
|
||||
);
|
||||
}
|
||||
|
||||
dis.dispatch({ action: Action.VirtualRoomSupportUpdated });
|
||||
this.emit(LegacyCallHandlerEvent.ProtocolSupport);
|
||||
} catch (e) {
|
||||
if (maxTries === 1) {
|
||||
logger.log("Failed to check for protocol support and no retries remain: assuming no support", e);
|
||||
@ -296,8 +302,8 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||
return !!SdkConfig.getObject("voip")?.get("obey_asserted_identity");
|
||||
}
|
||||
|
||||
public getSupportsPstnProtocol(): boolean | null {
|
||||
return this.supportsPstnProtocol;
|
||||
public getSupportsPstnProtocol(): boolean {
|
||||
return this.supportsPstnProtocol ?? false;
|
||||
}
|
||||
|
||||
public getSupportsVirtualRooms(): boolean | null {
|
||||
@ -568,6 +574,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||
if (!mappedRoomId || !this.matchesCallForThisRoom(call)) return;
|
||||
|
||||
this.setCallState(call, newState);
|
||||
// XXX: this is used by the IPC into Electron to keep device awake
|
||||
dis.dispatch({
|
||||
action: "call_state",
|
||||
room_id: mappedRoomId,
|
||||
|
||||
@ -13,7 +13,7 @@ import classNames from "classnames";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import { _t } from "../../languageHandler";
|
||||
import RoomList from "../views/rooms/RoomList";
|
||||
import LegacyCallHandler from "../../LegacyCallHandler";
|
||||
import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../LegacyCallHandler";
|
||||
import { HEADER_HEIGHT } from "../views/rooms/RoomSublist";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import RoomSearch from "./RoomSearch";
|
||||
@ -51,6 +51,7 @@ enum BreadcrumbsMode {
|
||||
interface IState {
|
||||
showBreadcrumbs: BreadcrumbsMode;
|
||||
activeSpace: SpaceKey;
|
||||
supportsPstnProtocol: boolean;
|
||||
}
|
||||
|
||||
export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
@ -65,6 +66,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
this.state = {
|
||||
activeSpace: SpaceStore.instance.activeSpace,
|
||||
showBreadcrumbs: LeftPanel.breadcrumbsMode,
|
||||
supportsPstnProtocol: LegacyCallHandler.instance.getSupportsPstnProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -76,6 +78,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
||||
LegacyCallHandler.instance.on(LegacyCallHandlerEvent.ProtocolSupport, this.updateProtocolSupport);
|
||||
|
||||
if (this.listContainerRef.current) {
|
||||
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
||||
@ -90,6 +93,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
||||
LegacyCallHandler.instance.off(LegacyCallHandlerEvent.ProtocolSupport, this.updateProtocolSupport);
|
||||
UIStore.instance.stopTrackingElementDimensions("ListContainer");
|
||||
UIStore.instance.removeListener("ListContainer", this.refreshStickyHeaders);
|
||||
this.listContainerRef.current?.removeEventListener("scroll", this.onScroll);
|
||||
@ -101,6 +105,10 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private updateProtocolSupport = (): void => {
|
||||
this.setState({ supportsPstnProtocol: LegacyCallHandler.instance.getSupportsPstnProtocol() });
|
||||
};
|
||||
|
||||
private updateActiveSpace = (activeSpace: SpaceKey): void => {
|
||||
this.setState({ activeSpace });
|
||||
};
|
||||
@ -330,9 +338,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
private renderSearchDialExplore(): React.ReactNode {
|
||||
let dialPadButton: JSX.Element | undefined;
|
||||
|
||||
// If we have dialer support, show a button to bring up the dial pad
|
||||
// to start a new call
|
||||
if (LegacyCallHandler.instance.getSupportsPstnProtocol()) {
|
||||
// If we have dialer support, show a button to bring up the dial pad to start a new call
|
||||
if (this.state.supportsPstnProtocol) {
|
||||
dialPadButton = (
|
||||
<AccessibleButton
|
||||
className={classNames("mx_LeftPanel_dialPadButton", {})}
|
||||
|
||||
@ -1082,7 +1082,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
}
|
||||
};
|
||||
|
||||
private onCallState = (roomId: string): void => {
|
||||
private onCallState = (roomId: string | null): void => {
|
||||
// don't filter out payloads for room IDs other than props.room because
|
||||
// we may be interested in the conf 1:1 room
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { EventType, RoomType, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType, Room, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
import React, { ComponentType, createRef, ReactComponentElement, SyntheticEvent } from "react";
|
||||
|
||||
import { IState as IRovingTabIndexState, RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
|
||||
@ -56,6 +56,7 @@ import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { Landmark, LandmarkNavigation } from "../../../accessibility/LandmarkNavigation";
|
||||
import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../../LegacyCallHandler.tsx";
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (ev: React.KeyboardEvent, state: IRovingTabIndexState) => void;
|
||||
@ -440,6 +441,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||
SdkContextClass.instance.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||
SpaceStore.instance.on(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
LegacyCallHandler.instance.on(LegacyCallHandlerEvent.ProtocolSupport, this.updateProtocolSupport);
|
||||
this.updateLists(); // trigger the first update
|
||||
}
|
||||
|
||||
@ -448,8 +450,13 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||
LegacyCallHandler.instance.off(LegacyCallHandlerEvent.ProtocolSupport, this.updateProtocolSupport);
|
||||
}
|
||||
|
||||
private updateProtocolSupport = (): void => {
|
||||
this.updateLists();
|
||||
};
|
||||
|
||||
private onRoomViewStoreUpdate = (): void => {
|
||||
this.setState({
|
||||
currentRoomId: SdkContextClass.instance.roomViewStore.getRoomId() ?? undefined,
|
||||
@ -471,8 +478,6 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||
metricsViaKeyboard: true,
|
||||
});
|
||||
}
|
||||
} else if (payload.action === Action.PstnSupportUpdated) {
|
||||
this.updateLists();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -135,20 +135,6 @@ export enum Action {
|
||||
*/
|
||||
OpenDialPad = "open_dial_pad",
|
||||
|
||||
/**
|
||||
* Fired when CallHandler has checked for PSTN protocol support
|
||||
* payload: none
|
||||
* XXX: Is an action the right thing for this?
|
||||
*/
|
||||
PstnSupportUpdated = "pstn_support_updated",
|
||||
|
||||
/**
|
||||
* Similar to PstnSupportUpdated, fired when CallHandler has checked for virtual room support
|
||||
* payload: none
|
||||
* XXX: Ditto
|
||||
*/
|
||||
VirtualRoomSupportUpdated = "virtual_room_support_updated",
|
||||
|
||||
/**
|
||||
* Fired when an upload has started. Should be used with UploadStartedPayload.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user