Group events by calls or users
This commit is contained in:
		
					parent
					
						
							
								87660e8873
							
						
					
				
			
			
				commit
				
					
						661e3dd98e
					
				
			
		
					 6 changed files with 211 additions and 162 deletions
				
			
		
							
								
								
									
										11
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
    "": {
 | 
			
		||||
      "version": "0.0.0",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "classnames": "^2.3.1",
 | 
			
		||||
        "color-hash": "^2.0.1",
 | 
			
		||||
        "events": "^3.3.0",
 | 
			
		||||
        "matrix-js-sdk": "^12.0.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -588,6 +589,11 @@
 | 
			
		|||
        "node": ">=4"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/classnames": {
 | 
			
		||||
      "version": "2.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/color-convert": {
 | 
			
		||||
      "version": "1.9.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2039,6 +2045,11 @@
 | 
			
		|||
        "supports-color": "^5.3.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "classnames": {
 | 
			
		||||
      "version": "2.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
 | 
			
		||||
    },
 | 
			
		||||
    "color-convert": {
 | 
			
		||||
      "version": "1.9.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
    "serve": "vite preview"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "classnames": "^2.3.1",
 | 
			
		||||
    "color-hash": "^2.0.1",
 | 
			
		||||
    "events": "^3.3.0",
 | 
			
		||||
    "matrix-js-sdk": "^12.0.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,9 +134,10 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
    this.client = client;
 | 
			
		||||
    this.joined = false;
 | 
			
		||||
    this.room = null;
 | 
			
		||||
    const localUserId = client.getUserId();
 | 
			
		||||
    this.localParticipant = {
 | 
			
		||||
      local: true,
 | 
			
		||||
      userId: client.getUserId(),
 | 
			
		||||
      userId: localUserId,
 | 
			
		||||
      feed: null,
 | 
			
		||||
      call: null,
 | 
			
		||||
      muted: true,
 | 
			
		||||
| 
						 | 
				
			
			@ -144,20 +145,24 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
    this.participants = [this.localParticipant];
 | 
			
		||||
    this.pendingCalls = [];
 | 
			
		||||
    this.callUserMap = new Map();
 | 
			
		||||
    this.debugState = new Map();
 | 
			
		||||
    this._setDebugState(client.getUserId(), "you");
 | 
			
		||||
    this.debugState = {
 | 
			
		||||
      users: new Map(),
 | 
			
		||||
      calls: new Map(),
 | 
			
		||||
    };
 | 
			
		||||
    this.client.on("event", this._onEvent);
 | 
			
		||||
    this.client.on("RoomState.members", this._onMemberChanged);
 | 
			
		||||
    this.client.on("Call.incoming", this._onIncomingCall);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  join(roomId) {
 | 
			
		||||
    this.joined = true;
 | 
			
		||||
 | 
			
		||||
    this._addDebugEvent(this.client.getUserId(), "joined call");
 | 
			
		||||
 | 
			
		||||
  setRoom(roomId) {
 | 
			
		||||
    this.roomId = roomId;
 | 
			
		||||
    this.room = this.client.getRoom(this.roomId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  join() {
 | 
			
		||||
    this.joined = true;
 | 
			
		||||
 | 
			
		||||
    this._setDebugState(this.client.getUserId(), null, "you");
 | 
			
		||||
 | 
			
		||||
    const activeConf = this.room.currentState
 | 
			
		||||
      .getStateEvents(CONF_ROOM, "")
 | 
			
		||||
| 
						 | 
				
			
			@ -169,18 +174,12 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
 | 
			
		||||
    const roomMemberIds = this.room.getMembers().map(({ userId }) => userId);
 | 
			
		||||
 | 
			
		||||
    for (const userId of this.debugState.keys()) {
 | 
			
		||||
      if (roomMemberIds.indexOf(userId) === -1) {
 | 
			
		||||
        this.debugState.delete(userId);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    roomMemberIds.forEach((userId) => {
 | 
			
		||||
      this._processMember(userId);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    for (const { call, onHangup, onReplaced } of this.pendingCalls) {
 | 
			
		||||
      if (call.roomId !== roomId) {
 | 
			
		||||
      if (call.roomId !== this.roomId) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,31 +199,66 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
    const roomId = event.getRoomId();
 | 
			
		||||
    const type = event.getType();
 | 
			
		||||
 | 
			
		||||
    if (type.startsWith("m.call.") || type.startsWith("me.robertlong.conf")) {
 | 
			
		||||
      const content = event.getContent();
 | 
			
		||||
      const details = { content: event.toJSON(), roomId };
 | 
			
		||||
    if (roomId === this.roomId && type.startsWith("m.call.")) {
 | 
			
		||||
      const sender = event.getSender();
 | 
			
		||||
      const { call_id } = event.getContent();
 | 
			
		||||
 | 
			
		||||
      if (content.invitee && content.call_id) {
 | 
			
		||||
        this.callUserMap.set(content.call_id, content.invitee);
 | 
			
		||||
        details.to = content.invitee;
 | 
			
		||||
      } else if (content.call_id) {
 | 
			
		||||
        details.to = this.callUserMap.get(content.call_id);
 | 
			
		||||
      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],
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      switch (type) {
 | 
			
		||||
        case "m.call.invite":
 | 
			
		||||
        case "m.call.candidates":
 | 
			
		||||
        case "m.call.answer":
 | 
			
		||||
        case "m.call.hangup":
 | 
			
		||||
        case "m.call.select_answer":
 | 
			
		||||
          details.callId = content.call_id;
 | 
			
		||||
          break;
 | 
			
		||||
      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._addDebugEvent(event.getSender(), type, details);
 | 
			
		||||
      this.emit("debug");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  _setDebugState(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");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _updateParticipantState = () => {
 | 
			
		||||
    const userId = this.client.getUserId();
 | 
			
		||||
    const currentMemberState = this.room.currentState.getStateEvents(
 | 
			
		||||
| 
						 | 
				
			
			@ -286,20 +320,19 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
      new Date().getTime() - participantTimeout > PARTICIPANT_TIMEOUT
 | 
			
		||||
    ) {
 | 
			
		||||
      // Member is inactive so don't call them.
 | 
			
		||||
      this._setDebugState(userId, "inactive");
 | 
			
		||||
      this._setDebugState(userId, null, "inactive");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only initiate a call with a user who has a userId that is lexicographically
 | 
			
		||||
    // less than your own. Otherwise, that user will call you.
 | 
			
		||||
    if (userId < localUserId) {
 | 
			
		||||
      this._setDebugState(userId, "waiting for invite");
 | 
			
		||||
      this._setDebugState(userId, null, "waiting for invite");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const call = this.client.createCall(this.roomId, userId);
 | 
			
		||||
    this._addCall(call, userId);
 | 
			
		||||
    this._setDebugState(userId, "calling");
 | 
			
		||||
    call.placeVideoCall();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -342,7 +375,6 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
 | 
			
		||||
    const userId = call.opponentMember.userId;
 | 
			
		||||
    this._addCall(call, userId);
 | 
			
		||||
    this._setDebugState(userId, "answered");
 | 
			
		||||
    call.answer();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -365,8 +397,9 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
      call,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this._setDebugCallId(userId, call.callId);
 | 
			
		||||
 | 
			
		||||
    call.on("state", (state) =>
 | 
			
		||||
      this._setDebugState(userId, call.callId, state)
 | 
			
		||||
    );
 | 
			
		||||
    call.on("feeds_changed", () => this._onCallFeedsChanged(call));
 | 
			
		||||
    call.on("hangup", () => this._onCallHangup(call));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -388,7 +421,6 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
 | 
			
		||||
    if (!this.localParticipant.feed && localFeeds.length > 0) {
 | 
			
		||||
      this.localParticipant.call = call;
 | 
			
		||||
      this._setDebugCallId(this.localParticipant.userId, call.callId);
 | 
			
		||||
      this.localParticipant.feed = localFeeds[0];
 | 
			
		||||
      participantsChanged = true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +432,6 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
 | 
			
		||||
    if (remoteFeeds.length > 0 && remoteParticipant.feed !== remoteFeeds[0]) {
 | 
			
		||||
      remoteParticipant.feed = remoteFeeds[0];
 | 
			
		||||
      this._setDebugState(call.opponentMember.userId, "streaming");
 | 
			
		||||
      participantsChanged = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -410,16 +441,14 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  _onCallHangup = (call) => {
 | 
			
		||||
    if (call.hangupReason === "replaced") {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._setDebugState(call.opponentMember.userId, "hungup");
 | 
			
		||||
 | 
			
		||||
    const participantIndex = this.participants.findIndex(
 | 
			
		||||
      (p) => !p.local && p.call === call
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (call.hangupReason === "replaced") {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (participantIndex === -1) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -436,16 +465,13 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
 | 
			
		||||
        if (localFeeds.length > 0) {
 | 
			
		||||
          this.localParticipant.call = call;
 | 
			
		||||
          this._setDebugCallId(this.localParticipant.userId, call.callId);
 | 
			
		||||
          this.localParticipant.feed = localFeeds[0];
 | 
			
		||||
        } else {
 | 
			
		||||
          this.localParticipant.call = null;
 | 
			
		||||
          this._setDebugCallId(this.localParticipant.userId, null);
 | 
			
		||||
          this.localParticipant.feed = null;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.localParticipant.call = null;
 | 
			
		||||
        this._setDebugCallId(this.localParticipant.userId, null);
 | 
			
		||||
        this.localParticipant.feed = null;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -454,17 +480,11 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  _onCallReplaced = (call, newCall) => {
 | 
			
		||||
    this._addDebugEvent(call.opponentMember.userId, "replaced", {
 | 
			
		||||
      callId: call.callId,
 | 
			
		||||
      newCallId: newCall.callId,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const remoteParticipant = this.participants.find(
 | 
			
		||||
      (p) => !p.local && p.call === call
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    remoteParticipant.call = newCall;
 | 
			
		||||
    this._setDebugCallId(remoteParticipant.userId, newCall.callId);
 | 
			
		||||
 | 
			
		||||
    newCall.on("feeds_changed", () => this._onCallFeedsChanged(newCall));
 | 
			
		||||
    newCall.on("hangup", () => this._onCallHangup(newCall));
 | 
			
		||||
| 
						 | 
				
			
			@ -507,48 +527,10 @@ export class ConferenceCallManager extends EventEmitter {
 | 
			
		|||
    this.participants = [this.localParticipant];
 | 
			
		||||
    this.localParticipant.feed = null;
 | 
			
		||||
    this.localParticipant.call = null;
 | 
			
		||||
    this._setDebugCallId(this.localParticipant.userId, null);
 | 
			
		||||
 | 
			
		||||
    this.emit("participants_changed");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _addDebugEvent(sender, type, content) {
 | 
			
		||||
    if (!this.debugState.has(sender)) {
 | 
			
		||||
      this.debugState.set(sender, {
 | 
			
		||||
        callId: null,
 | 
			
		||||
        state: "unknown",
 | 
			
		||||
        events: [{ type, ...content }],
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      const { events } = this.debugState.get(sender);
 | 
			
		||||
      events.push({ type, roomId: this.roomId, ...content });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.emit("debug");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _setDebugState(userId, state) {
 | 
			
		||||
    if (!this.debugState.has(userId)) {
 | 
			
		||||
      this.debugState.set(userId, { state, callId: null, events: [] });
 | 
			
		||||
    } else {
 | 
			
		||||
      const userState = this.debugState.get(userId);
 | 
			
		||||
      userState.state = state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.emit("debug");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _setDebugCallId(userId, callId) {
 | 
			
		||||
    if (!this.debugState.has(userId)) {
 | 
			
		||||
      this.debugState.set(userId, { state: "unknown", callId, events: [] });
 | 
			
		||||
    } else {
 | 
			
		||||
      const userState = this.debugState.get(userId);
 | 
			
		||||
      userState.callId = callId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.emit("debug");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  logout() {
 | 
			
		||||
    localStorage.removeItem("matrix-auth-store");
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -136,6 +136,8 @@ export function useVideoRoom(manager, roomId, timeout = 5000) {
 | 
			
		|||
      error: undefined,
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    manager.setRoom(roomId);
 | 
			
		||||
 | 
			
		||||
    manager.client.joinRoom(roomId).catch((err) => {
 | 
			
		||||
      setState((prevState) => ({ ...prevState, loading: false, error: err }));
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +198,7 @@ export function useVideoRoom(manager, roomId, timeout = 5000) {
 | 
			
		|||
 | 
			
		||||
    manager.on("participants_changed", onParticipantsChanged);
 | 
			
		||||
 | 
			
		||||
    manager.join(roomId);
 | 
			
		||||
    manager.join();
 | 
			
		||||
 | 
			
		||||
    setState((prevState) => ({
 | 
			
		||||
      ...prevState,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										153
									
								
								src/DevTools.jsx
									
										
									
									
									
								
							
							
						
						
									
										153
									
								
								src/DevTools.jsx
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import React, { useCallback, useEffect, useRef, useState } from "react";
 | 
			
		||||
import ColorHash from "color-hash";
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
import styles from "./DevTools.module.css";
 | 
			
		||||
 | 
			
		||||
const colorHash = new ColorHash({ lightness: 0.8 });
 | 
			
		||||
| 
						 | 
				
			
			@ -25,13 +26,27 @@ function CallId({ callId, ...rest }) {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sortEntries(a, b) {
 | 
			
		||||
  const aInactive = a[1].state === "inactive";
 | 
			
		||||
  const bInactive = b[1].state === "inactive";
 | 
			
		||||
 | 
			
		||||
  if (aInactive && !bInactive) {
 | 
			
		||||
    return 1;
 | 
			
		||||
  } else if (bInactive && !aInactive) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    return a[0] < b[0] ? -1 : 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function DevTools({ manager }) {
 | 
			
		||||
  const [debugState, setDebugState] = useState(manager.debugState);
 | 
			
		||||
  const [selectedEvent, setSelectedEvent] = useState();
 | 
			
		||||
  const [activeTab, setActiveTab] = useState("users");
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    function onRoomDebug() {
 | 
			
		||||
      setDebugState(manager.debugState);
 | 
			
		||||
      setDebugState({ ...manager.debugState });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    manager.on("debug", onRoomDebug);
 | 
			
		||||
| 
						 | 
				
			
			@ -47,15 +62,50 @@ export function DevTools({ manager }) {
 | 
			
		|||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.devTools}>
 | 
			
		||||
      {Array.from(debugState.entries()).map(([userId, props]) => (
 | 
			
		||||
        <UserState
 | 
			
		||||
      <div className={styles.toolbar}>
 | 
			
		||||
        <div
 | 
			
		||||
          className={classNames(styles.tab, {
 | 
			
		||||
            [styles.activeTab]: activeTab === "users",
 | 
			
		||||
          })}
 | 
			
		||||
          onClick={() => setActiveTab("users")}
 | 
			
		||||
        >
 | 
			
		||||
          Users
 | 
			
		||||
        </div>
 | 
			
		||||
        <div
 | 
			
		||||
          className={classNames(styles.tab, {
 | 
			
		||||
            [styles.activeTab]: activeTab === "calls",
 | 
			
		||||
          })}
 | 
			
		||||
          onClick={() => setActiveTab("calls")}
 | 
			
		||||
        >
 | 
			
		||||
          Calls
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className={styles.devToolsContainer}>
 | 
			
		||||
        {activeTab === "users" &&
 | 
			
		||||
          Array.from(debugState.users.entries())
 | 
			
		||||
            .sort(sortEntries)
 | 
			
		||||
            .map(([userId, props]) => (
 | 
			
		||||
              <EventContainer
 | 
			
		||||
                key={userId}
 | 
			
		||||
          roomId={manager.roomId}
 | 
			
		||||
          onSelectEvent={setSelectedEvent}
 | 
			
		||||
          userId={userId}
 | 
			
		||||
                showCallId
 | 
			
		||||
                title={<UserId userId={userId} />}
 | 
			
		||||
                {...props}
 | 
			
		||||
                onSelect={setSelectedEvent}
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
        {activeTab === "calls" &&
 | 
			
		||||
          Array.from(debugState.calls.entries())
 | 
			
		||||
            .sort(sortEntries)
 | 
			
		||||
            .map(([callId, props]) => (
 | 
			
		||||
              <EventContainer
 | 
			
		||||
                key={callId}
 | 
			
		||||
                showSender
 | 
			
		||||
                title={<CallId callId={callId} />}
 | 
			
		||||
                {...props}
 | 
			
		||||
                onSelect={setSelectedEvent}
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
      </div>
 | 
			
		||||
      {selectedEvent && (
 | 
			
		||||
        <EventViewer
 | 
			
		||||
          event={selectedEvent}
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +116,7 @@ export function DevTools({ manager }) {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function UserState({ roomId, userId, state, callId, events, onSelectEvent }) {
 | 
			
		||||
function EventContainer({ title, state, events, ...rest }) {
 | 
			
		||||
  const eventsRef = useRef();
 | 
			
		||||
  const [autoScroll, setAutoScroll] = useState(true);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,78 +140,59 @@ function UserState({ roomId, userId, state, callId, events, onSelectEvent }) {
 | 
			
		|||
  return (
 | 
			
		||||
    <div className={styles.user}>
 | 
			
		||||
      <div className={styles.userId}>
 | 
			
		||||
        <UserId userId={userId} />
 | 
			
		||||
        <span>{title}</span>
 | 
			
		||||
        <span>{`(${state})`}</span>
 | 
			
		||||
        {callId && <CallId callId={callId} />}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div ref={eventsRef} className={styles.events} onScroll={onScroll}>
 | 
			
		||||
        {events
 | 
			
		||||
          .filter((e) => e.roomId === roomId)
 | 
			
		||||
          .map((event, idx) => (
 | 
			
		||||
            <div
 | 
			
		||||
              className={styles.event}
 | 
			
		||||
              key={idx}
 | 
			
		||||
              onClick={() => onSelectEvent(event)}
 | 
			
		||||
            >
 | 
			
		||||
              <span className={styles.eventType}>{event.type}</span>
 | 
			
		||||
              {event.callId && (
 | 
			
		||||
                <CallId className={styles.eventDetails} callId={event.callId} />
 | 
			
		||||
              )}
 | 
			
		||||
              {event.newCallId && (
 | 
			
		||||
                <>
 | 
			
		||||
                  <span className={styles.eventDetails}>{"->"}</span>
 | 
			
		||||
                  <CallId
 | 
			
		||||
                    className={styles.eventDetails}
 | 
			
		||||
                    callId={event.newCallId}
 | 
			
		||||
                  />
 | 
			
		||||
                </>
 | 
			
		||||
              )}
 | 
			
		||||
              {event.to && (
 | 
			
		||||
                <UserId className={styles.eventDetails} userId={event.to} />
 | 
			
		||||
              )}
 | 
			
		||||
              {event.reason && (
 | 
			
		||||
                <span className={styles.eventDetails}>{event.reason}</span>
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
        {events.map((event, idx) => (
 | 
			
		||||
          <EventItem key={idx} event={event} {...rest} />
 | 
			
		||||
        ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function EventItem({ event, showCallId, showSender, onSelect }) {
 | 
			
		||||
  const type = event.getType();
 | 
			
		||||
  const sender = event.getSender();
 | 
			
		||||
  const { call_id, invitee } = event.getContent();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.event} onClick={() => onSelect(event)}>
 | 
			
		||||
      {showSender && sender && (
 | 
			
		||||
        <UserId className={styles.eventDetails} userId={sender} />
 | 
			
		||||
      )}
 | 
			
		||||
      <span className={styles.eventType}>{type}</span>
 | 
			
		||||
      {showCallId && call_id && (
 | 
			
		||||
        <CallId className={styles.eventDetails} callId={call_id} />
 | 
			
		||||
      )}
 | 
			
		||||
      {invitee && <UserId className={styles.eventDetails} userId={invitee} />}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function EventViewer({ event, onClose }) {
 | 
			
		||||
  const type = event.getType();
 | 
			
		||||
  const sender = event.getSender();
 | 
			
		||||
  const { call_id, invitee } = event.getContent();
 | 
			
		||||
  const json = event.toJSON();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.eventViewer}>
 | 
			
		||||
      <p>Event Type: {event.type}</p>
 | 
			
		||||
      {event.callId && (
 | 
			
		||||
      <p>Event Type: {type}</p>
 | 
			
		||||
      <p>Sender: {sender}</p>
 | 
			
		||||
      {call_id && (
 | 
			
		||||
        <p>
 | 
			
		||||
          Call Id: <CallId callId={event.callId} />
 | 
			
		||||
          Call Id: <CallId callId={call_id} />
 | 
			
		||||
        </p>
 | 
			
		||||
      )}
 | 
			
		||||
      {event.newCallId && (
 | 
			
		||||
      {invitee && (
 | 
			
		||||
        <p>
 | 
			
		||||
          New Call Id:
 | 
			
		||||
          <CallId callId={event.newCallId} />
 | 
			
		||||
          Invitee: <UserId userId={invitee} />
 | 
			
		||||
        </p>
 | 
			
		||||
      )}
 | 
			
		||||
      {event.to && (
 | 
			
		||||
        <p>
 | 
			
		||||
          To: <UserId userId={event.to} />
 | 
			
		||||
        </p>
 | 
			
		||||
      )}
 | 
			
		||||
      {event.reason && (
 | 
			
		||||
        <p>
 | 
			
		||||
          Reason: <span>{event.reason}</span>
 | 
			
		||||
        </p>
 | 
			
		||||
      )}
 | 
			
		||||
      {event.content && (
 | 
			
		||||
        <>
 | 
			
		||||
          <p>Content:</p>
 | 
			
		||||
          <pre className={styles.content}>
 | 
			
		||||
            {JSON.stringify(event.content, undefined, 2)}
 | 
			
		||||
          </pre>
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
      <p>Raw Event:</p>
 | 
			
		||||
      <pre className={styles.content}>{JSON.stringify(json, undefined, 2)}</pre>
 | 
			
		||||
      <button onClick={onClose}>Close</button>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,31 @@
 | 
			
		|||
.devTools {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  height: 250px;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  border-top: 2px solid #111;
 | 
			
		||||
  gap: 2px;
 | 
			
		||||
  background-color: #111;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.toolbar {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  background-color: #222;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tab {
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  padding: 4px 8px;
 | 
			
		||||
  background-color: #444;
 | 
			
		||||
  margin: 0 2px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.activeTab {
 | 
			
		||||
  background-color: #666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.devToolsContainer {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  height: 250px;
 | 
			
		||||
  gap: 2px;
 | 
			
		||||
  overflow-x: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue