From ebf61511f1915e8f6ee5d4b5c4a402a8ba2d1619 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Jan 2022 16:58:55 -0800 Subject: [PATCH] Clean up remaining room components --- src/ConferenceCallDebugger.js | 437 ----------------------- src/{ => room}/GridLayoutMenu.jsx | 14 +- src/{ => room}/GridLayoutMenu.module.css | 0 src/{ => room}/GroupCallInspector.jsx | 0 src/room/InCallView.jsx | 4 +- src/{ => room}/InviteModal.jsx | 6 +- src/{ => room}/InviteModal.module.css | 0 src/room/OverflowMenu.jsx | 2 +- 8 files changed, 13 insertions(+), 450 deletions(-) delete mode 100644 src/ConferenceCallDebugger.js rename src/{ => room}/GridLayoutMenu.jsx (75%) rename src/{ => room}/GridLayoutMenu.module.css (100%) rename src/{ => room}/GroupCallInspector.jsx (100%) rename src/{ => room}/InviteModal.jsx (75%) rename src/{ => room}/InviteModal.module.css (100%) diff --git a/src/ConferenceCallDebugger.js b/src/ConferenceCallDebugger.js deleted file mode 100644 index c9317e59..00000000 --- a/src/ConferenceCallDebugger.js +++ /dev/null @@ -1,437 +0,0 @@ -/* -Copyright 2021 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import EventEmitter from "events"; - -export class ConferenceCallDebugger extends EventEmitter { - constructor(client, groupCall) { - super(); - - this.client = client; - this.groupCall = groupCall; - - this.debugState = { - users: new Map(), - calls: new Map(), - }; - - this.bufferedEvents = []; - - client.on("event", this._onEvent); - groupCall.on("call", this._onCall); - groupCall.on("debugstate", this._onDebugStateChanged); - groupCall.on("entered", this._onEntered); - groupCall.on("left", this._onLeft); - } - - _onEntered = () => { - const eventCount = this.bufferedEvents.length; - - for (let i = 0; i < eventCount; i++) { - const event = this.bufferedEvents.pop(); - this._onEvent(event); - } - }; - - _onLeft = () => { - this.bufferedEvents = []; - this.debugState = { - users: new Map(), - calls: new Map(), - }; - this.emit("debug"); - }; - - _onEvent = (event) => { - if (!this.groupCall.entered) { - this.bufferedEvents.push(event); - return; - } - - const roomId = event.getRoomId(); - const type = event.getType(); - - if ( - roomId === this.groupCall.room.roomId && - (type.startsWith("m.call.") || - type === "me.robertlong.call.info" || - type === "m.room.member") - ) { - const sender = event.getSender(); - const { call_id } = event.getContent(); - - if (call_id) { - if (this.debugState.calls.has(call_id)) { - const callState = this.debugState.calls.get(call_id); - callState.events.push(event); - } else { - this.debugState.calls.set(call_id, { - state: "unknown", - events: [event], - }); - } - } - - if (this.debugState.users.has(sender)) { - const userState = this.debugState.users.get(sender); - userState.events.push(event); - } else { - this.debugState.users.set(sender, { - state: "unknown", - events: [event], - }); - } - - this.emit("debug"); - } - }; - - _onDebugStateChanged = (userId, callId, state) => { - if (userId) { - const userState = this.debugState.users.get(userId); - - if (userState) { - userState.state = state; - } else { - this.debugState.users.set(userId, { - state, - events: [], - }); - } - } - - if (callId) { - const callState = this.debugState.calls.get(callId); - - if (callState) { - callState.state = state; - } else { - this.debugState.calls.set(callId, { - state, - events: [], - }); - } - } - - this.emit("debug"); - }; - - _onCall = (call) => { - const peerConnection = call.peerConn; - - if (!peerConnection) { - return; - } - - const sendWebRTCInfoEvent = async (eventType) => { - const event = { - call_id: call.callId, - eventType, - iceConnectionState: peerConnection.iceConnectionState, - iceGatheringState: peerConnection.iceGatheringState, - signalingState: peerConnection.signalingState, - selectedCandidatePair: null, - localCandidate: null, - remoteCandidate: null, - }; - - // getStats doesn't support selectors in Firefox so get all stats by passing null. - // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats#browser_compatibility - const stats = await peerConnection.getStats(null); - - const statsArr = Array.from(stats.values()); - - // Matrix doesn't support floats so we convert time in seconds to ms - function secToMs(time) { - if (time === undefined) { - return undefined; - } - - return Math.round(time * 1000); - } - - function processTransportStats(transportStats) { - if (!transportStats) { - return undefined; - } - - return { - packetsSent: transportStats.packetsSent, - packetsReceived: transportStats.packetsReceived, - bytesSent: transportStats.bytesSent, - bytesReceived: transportStats.bytesReceived, - iceRole: transportStats.iceRole, - iceState: transportStats.iceState, - dtlsState: transportStats.dtlsState, - dtlsCipher: transportStats.dtlsCipher, - tlsVersion: transportStats.tlsVersion, - }; - } - - function processCandidateStats(candidateStats) { - if (!candidateStats) { - return undefined; - } - - // TODO: Figure out how to normalize ip and address across browsers - // networkType property excluded for privacy reasons: - // https://www.w3.org/TR/webrtc-stats/#sotd - return { - priority: - candidateStats.priority && candidateStats.priority.toString(), - candidateType: candidateStats.candidateType, - protocol: candidateStats.protocol, - address: !!candidateStats.address - ? candidateStats.address - : candidateStats.ip, - port: candidateStats.port, - url: candidateStats.url, - relayProtocol: candidateStats.relayProtocol, - }; - } - - function processCandidatePair(candidatePairStats) { - if (!candidatePairStats) { - return undefined; - } - - const localCandidateStats = statsArr.find( - (stat) => stat.id === candidatePairStats.localCandidateId - ); - event.localCandidate = processCandidateStats(localCandidateStats); - - const remoteCandidateStats = statsArr.find( - (stat) => stat.id === candidatePairStats.remoteCandidateId - ); - event.remoteCandidate = processCandidateStats(remoteCandidateStats); - - const transportStats = statsArr.find( - (stat) => stat.id === candidatePairStats.transportId - ); - event.transport = processTransportStats(transportStats); - - return { - state: candidatePairStats.state, - bytesSent: candidatePairStats.bytesSent, - bytesReceived: candidatePairStats.bytesReceived, - requestsSent: candidatePairStats.requestsSent, - requestsReceived: candidatePairStats.requestsReceived, - responsesSent: candidatePairStats.responsesSent, - responsesReceived: candidatePairStats.responsesReceived, - currentRoundTripTime: secToMs( - candidatePairStats.currentRoundTripTime - ), - totalRoundTripTime: secToMs(candidatePairStats.totalRoundTripTime), - }; - } - - // Firefox uses the deprecated "selected" property for the nominated ice candidate. - const selectedCandidatePair = statsArr.find( - (stat) => - stat.type === "candidate-pair" && (stat.selected || stat.nominated) - ); - - event.selectedCandidatePair = processCandidatePair(selectedCandidatePair); - - function processCodecStats(codecStats) { - if (!codecStats) { - return undefined; - } - - // Payload type enums and MIME types listed here: - // https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml - return { - mimeType: codecStats.mimeType, - clockRate: codecStats.clockRate, - payloadType: codecStats.payloadType, - channels: codecStats.channels, - sdpFmtpLine: codecStats.sdpFmtpLine, - }; - } - - function processRTPStreamStats(rtpStreamStats) { - const codecStats = statsArr.find( - (stat) => stat.id === rtpStreamStats.codecId - ); - const codec = processCodecStats(codecStats); - - return { - kind: rtpStreamStats.kind, - codec, - }; - } - - function processInboundRTPStats(inboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(inboundRTPStats); - - return { - ...rtpStreamStats, - decoderImplementation: inboundRTPStats.decoderImplementation, - bytesReceived: inboundRTPStats.bytesReceived, - packetsReceived: inboundRTPStats.packetsReceived, - packetsLost: inboundRTPStats.packetsLost, - jitter: secToMs(inboundRTPStats.jitter), - frameWidth: inboundRTPStats.frameWidth, - frameHeight: inboundRTPStats.frameHeight, - frameBitDepth: inboundRTPStats.frameBitDepth, - framesPerSecond: - inboundRTPStats.framesPerSecond && - inboundRTPStats.framesPerSecond.toString(), - framesReceived: inboundRTPStats.framesReceived, - framesDecoded: inboundRTPStats.framesDecoded, - framesDropped: inboundRTPStats.framesDropped, - totalSamplesDecoded: inboundRTPStats.totalSamplesDecoded, - totalDecodeTime: secToMs(inboundRTPStats.totalDecodeTime), - totalProcessingDelay: secToMs(inboundRTPStats.totalProcessingDelay), - }; - } - - function processOutboundRTPStats(outboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(outboundRTPStats); - - return { - ...rtpStreamStats, - encoderImplementation: outboundRTPStats.encoderImplementation, - bytesSent: outboundRTPStats.bytesSent, - packetsSent: outboundRTPStats.packetsSent, - frameWidth: outboundRTPStats.frameWidth, - frameHeight: outboundRTPStats.frameHeight, - frameBitDepth: outboundRTPStats.frameBitDepth, - framesPerSecond: - outboundRTPStats.framesPerSecond && - outboundRTPStats.framesPerSecond.toString(), - framesSent: outboundRTPStats.framesSent, - framesEncoded: outboundRTPStats.framesEncoded, - qualityLimitationReason: outboundRTPStats.qualityLimitationReason, - qualityLimitationResolutionChanges: - outboundRTPStats.qualityLimitationResolutionChanges, - totalEncodeTime: secToMs(outboundRTPStats.totalEncodeTime), - totalPacketSendDelay: secToMs(outboundRTPStats.totalPacketSendDelay), - }; - } - - function processRemoteInboundRTPStats(remoteInboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(remoteInboundRTPStats); - - return { - ...rtpStreamStats, - packetsReceived: remoteInboundRTPStats.packetsReceived, - packetsLost: remoteInboundRTPStats.packetsLost, - jitter: secToMs(remoteInboundRTPStats.jitter), - framesDropped: remoteInboundRTPStats.framesDropped, - roundTripTime: secToMs(remoteInboundRTPStats.roundTripTime), - totalRoundTripTime: secToMs(remoteInboundRTPStats.totalRoundTripTime), - fractionLost: - remoteInboundRTPStats.fractionLost !== undefined && - remoteInboundRTPStats.fractionLost.toString(), - reportsReceived: remoteInboundRTPStats.reportsReceived, - roundTripTimeMeasurements: - remoteInboundRTPStats.roundTripTimeMeasurements, - }; - } - - function processRemoteOutboundRTPStats(remoteOutboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(remoteOutboundRTPStats); - - return { - ...rtpStreamStats, - encoderImplementation: remoteOutboundRTPStats.encoderImplementation, - bytesSent: remoteOutboundRTPStats.bytesSent, - packetsSent: remoteOutboundRTPStats.packetsSent, - roundTripTime: secToMs(remoteOutboundRTPStats.roundTripTime), - totalRoundTripTime: secToMs( - remoteOutboundRTPStats.totalRoundTripTime - ), - reportsSent: remoteOutboundRTPStats.reportsSent, - roundTripTimeMeasurements: - remoteOutboundRTPStats.roundTripTimeMeasurements, - }; - } - - event.inboundRTP = statsArr - .filter((stat) => stat.type === "inbound-rtp") - .map(processInboundRTPStats); - - event.outboundRTP = statsArr - .filter((stat) => stat.type === "outbound-rtp") - .map(processOutboundRTPStats); - - event.remoteInboundRTP = statsArr - .filter((stat) => stat.type === "remote-inbound-rtp") - .map(processRemoteInboundRTPStats); - - event.remoteOutboundRTP = statsArr - .filter((stat) => stat.type === "remote-outbound-rtp") - .map(processRemoteOutboundRTPStats); - - this.client.sendEvent( - this.groupCall.room.roomId, - "me.robertlong.call.info", - event - ); - }; - - let statsTimeout; - - const sendStats = () => { - if ( - call.state === "ended" || - peerConnection.connectionState === "closed" - ) { - clearTimeout(statsTimeout); - return; - } - - sendWebRTCInfoEvent("stats"); - statsTimeout = setTimeout(sendStats, 30 * 1000); - }; - - setTimeout(sendStats, 30 * 1000); - - peerConnection.addEventListener("iceconnectionstatechange", () => { - sendWebRTCInfoEvent("iceconnectionstatechange"); - }); - peerConnection.addEventListener("icegatheringstatechange", () => { - sendWebRTCInfoEvent("icegatheringstatechange"); - }); - peerConnection.addEventListener("negotiationneeded", () => { - sendWebRTCInfoEvent("negotiationneeded"); - }); - peerConnection.addEventListener("track", () => { - sendWebRTCInfoEvent("track"); - }); - // NOTE: Not available on Firefox - // https://bugzilla.mozilla.org/show_bug.cgi?id=1561441 - peerConnection.addEventListener( - "icecandidateerror", - ({ errorCode, url, errorText }) => { - this.client.sendEvent( - this.groupCall.room.roomId, - "me.robertlong.call.ice_error", - { - call_id: call.callId, - errorCode, - url, - errorText, - } - ); - } - ); - peerConnection.addEventListener("signalingstatechange", () => { - sendWebRTCInfoEvent("signalingstatechange"); - }); - }; -} diff --git a/src/GridLayoutMenu.jsx b/src/room/GridLayoutMenu.jsx similarity index 75% rename from src/GridLayoutMenu.jsx rename to src/room/GridLayoutMenu.jsx index ce358ffc..46c2e90f 100644 --- a/src/GridLayoutMenu.jsx +++ b/src/room/GridLayoutMenu.jsx @@ -1,13 +1,13 @@ import React from "react"; -import { Button } from "./button"; -import { PopoverMenuTrigger } from "./PopoverMenu"; -import { ReactComponent as SpotlightIcon } from "./icons/Spotlight.svg"; -import { ReactComponent as FreedomIcon } from "./icons/Freedom.svg"; -import { ReactComponent as CheckIcon } from "./icons/Check.svg"; +import { Button } from "../button"; +import { PopoverMenuTrigger } from "../PopoverMenu"; +import { ReactComponent as SpotlightIcon } from "../icons/Spotlight.svg"; +import { ReactComponent as FreedomIcon } from "../icons/Freedom.svg"; +import { ReactComponent as CheckIcon } from "../icons/Check.svg"; import styles from "./GridLayoutMenu.module.css"; -import { Menu } from "./Menu"; +import { Menu } from "../Menu"; import { Item } from "@react-stately/collections"; -import { Tooltip, TooltipTrigger } from "./Tooltip"; +import { Tooltip, TooltipTrigger } from "../Tooltip"; export function GridLayoutMenu({ layout, setLayout }) { return ( diff --git a/src/GridLayoutMenu.module.css b/src/room/GridLayoutMenu.module.css similarity index 100% rename from src/GridLayoutMenu.module.css rename to src/room/GridLayoutMenu.module.css diff --git a/src/GroupCallInspector.jsx b/src/room/GroupCallInspector.jsx similarity index 100% rename from src/GroupCallInspector.jsx rename to src/room/GroupCallInspector.jsx diff --git a/src/room/InCallView.jsx b/src/room/InCallView.jsx index a6f54a27..e3b7db13 100644 --- a/src/room/InCallView.jsx +++ b/src/room/InCallView.jsx @@ -13,9 +13,9 @@ import VideoGrid, { import SimpleVideoGrid from "matrix-react-sdk/src/components/views/voip/GroupCallView/SimpleVideoGrid"; import "matrix-react-sdk/res/css/views/voip/GroupCallView/_VideoGrid.scss"; import { getAvatarUrl } from "../ConferenceCallManagerHooks"; -import { GroupCallInspector } from "../GroupCallInspector"; +import { GroupCallInspector } from "./GroupCallInspector"; import { OverflowMenu } from "./OverflowMenu"; -import { GridLayoutMenu } from "../GridLayoutMenu"; +import { GridLayoutMenu } from "./GridLayoutMenu"; import { Avatar } from "../Avatar"; import { UserMenuContainer } from "../UserMenuContainer"; diff --git a/src/InviteModal.jsx b/src/room/InviteModal.jsx similarity index 75% rename from src/InviteModal.jsx rename to src/room/InviteModal.jsx index 741fc316..a1c73a6f 100644 --- a/src/InviteModal.jsx +++ b/src/room/InviteModal.jsx @@ -1,7 +1,7 @@ import React from "react"; -import { Modal, ModalContent } from "./Modal"; -import { CopyButton } from "./button"; -import { getRoomUrl } from "./ConferenceCallManagerHooks"; +import { Modal, ModalContent } from "../Modal"; +import { CopyButton } from "../button"; +import { getRoomUrl } from "../ConferenceCallManagerHooks"; import styles from "./InviteModal.module.css"; export function InviteModal({ roomId, ...rest }) { diff --git a/src/InviteModal.module.css b/src/room/InviteModal.module.css similarity index 100% rename from src/InviteModal.module.css rename to src/room/InviteModal.module.css diff --git a/src/room/OverflowMenu.jsx b/src/room/OverflowMenu.jsx index 26df17ea..79c35324 100644 --- a/src/room/OverflowMenu.jsx +++ b/src/room/OverflowMenu.jsx @@ -8,7 +8,7 @@ import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg"; import { ReactComponent as OverflowIcon } from "../icons/Overflow.svg"; import { useModalTriggerState } from "../Modal"; import { SettingsModal } from "../settings/SettingsModal"; -import { InviteModal } from "../InviteModal"; +import { InviteModal } from "./InviteModal"; import { Tooltip, TooltipTrigger } from "../Tooltip"; export function OverflowMenu({