import {spawnReactModal} from "tc-shared/ui/react-elements/Modal"; import {InternalModal} from "tc-shared/ui/react-elements/internal-modal/Controller"; import * as React from "react"; import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {EchoTestEventRegistry, EchoTestModal} from "tc-shared/ui/modal/echo-test/Renderer"; import {Registry} from "tc-shared/events"; import {EchoTestEvents, TestState} from "tc-shared/ui/modal/echo-test/Definitions"; import {ConnectionHandler} from "tc-shared/ConnectionHandler"; import {global_client_actions} from "tc-shared/events/GlobalEvents"; import {VoiceConnectionStatus} from "tc-shared/connection/VoiceConnection"; import {Settings, settings} from "tc-shared/settings"; import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; import {LogCategory, logError} from "tc-shared/log"; import {ServerFeature} from "tc-shared/connection/ServerFeatures"; export function spawnEchoTestModal(connection: ConnectionHandler) { const events = new Registry(); initializeController(connection, events); const modal = spawnReactModal(class extends InternalModal { constructor() { super(); } renderBody(): React.ReactElement { return ( ); } title(): string | React.ReactElement { return Voice echo test; } }); events.on("action_close", () => { modal.destroy(); }); modal.events.on("close", () => events.fire("notify_close")); modal.events.on("destroy", () => { events.fire("notify_destroy"); events.destroy(); }); modal.show(); } function initializeController(connection: ConnectionHandler, events: Registry) { let testState: TestState = { state: "stopped" }; events.on("action_open_microphone_settings", () => { global_client_actions.fire("action_open_window_settings", { defaultCategory: "audio-microphone" }); }); events.on("action_toggle_tests", event => { settings.changeGlobal(Settings.KEY_VOICE_ECHO_TEST_ENABLED, event.enabled); }); events.on("query_test_state", () => { events.fire_async("notify_tests_toggle", { enabled: settings.global(Settings.KEY_VOICE_ECHO_TEST_ENABLED) }); }); events.on("notify_destroy", settings.globalChangeListener(Settings.KEY_VOICE_ECHO_TEST_ENABLED, value => { events.fire_async("notify_tests_toggle", { enabled: value }); })); events.on("action_test_result", event => { if(event.status === "success") { events.fire("action_close"); } else { events.fire("action_stop_test"); events.fire("notify_test_phase", { phase: "troubleshooting" }); } }); events.on("action_troubleshooting_finished", event => { if(event.status === "aborted") { events.fire("action_close"); } else { events.fire("notify_test_phase", { phase: "testing" }); events.fire("action_start_test"); } }); const reportVoiceConnectionState = (state: VoiceConnectionStatus) => { if(state === VoiceConnectionStatus.Connected) { beginTest(); } else { endTest(); } switch (state) { case VoiceConnectionStatus.Connected: events.fire("notify_voice_connection_state", { state: "connected" }); break; case VoiceConnectionStatus.Disconnected: case VoiceConnectionStatus.Disconnecting: events.fire("notify_voice_connection_state", { state: "disconnected" }); break; case VoiceConnectionStatus.Connecting: events.fire("notify_voice_connection_state", { state: "connecting" }); break; case VoiceConnectionStatus.ClientUnsupported: events.fire("notify_voice_connection_state", { state: "unsupported-client" }); break; case VoiceConnectionStatus.ServerUnsupported: events.fire("notify_voice_connection_state", { state: "unsupported-server" }); break; } }; events.on("notify_destroy", connection.getServerConnection().getVoiceConnection().events.on("notify_connection_status_changed", event => { reportVoiceConnectionState(event.newStatus); })); events.on("query_voice_connection_state", () => reportVoiceConnectionState(connection.getServerConnection().getVoiceConnection().getConnectionState())); events.on("query_test_state", () => { events.fire_async("notify_test_state", { state: testState }); }); events.on("action_start_test", () => { beginTest(); }); const setTestState = (state: TestState) => { testState = state; events.fire("notify_test_state", { state: state }); } let testId = 0; const beginTest = () => { if(testState.state === "initializing" || testState.state === "running") { return; } else if(!connection.serverFeatures.supportsFeature(ServerFeature.WHISPER_ECHO)) { setTestState({ state: "unsupported" }); return; } setTestState({ state: "initializing" }); const currentTestId = ++testId; connection.startEchoTest().then(() => { if(currentTestId !== testId) { return; } setTestState({ state: "running" }); }).catch(error => { if(currentTestId !== testId) { return; } let message; if(error instanceof CommandResult) { message = error.formattedMessage(); } else if(error instanceof Error) { message = error.message; } else if(typeof error === "string") { message = error; } else { message = tr("lookup the console"); logError(LogCategory.AUDIO, tr("Failed to begin echo testing: %o"), error); } setTestState({ state: "start-failed", error: message }); }); } const endTest = () => { setTestState({ state: "stopped" }); connection.stopEchoTest(); } events.on(["notify_destroy", "notify_close", "action_stop_test"], endTest); }