element-call/src/DevTools.jsx

215 lines
5.7 KiB
React
Raw Normal View History

2021-07-28 16:14:38 -07:00
import React, { useCallback, useEffect, useRef, useState } from "react";
import ColorHash from "color-hash";
2021-07-30 16:55:25 -07:00
import classNames from "classnames";
2021-07-28 16:14:38 -07:00
import styles from "./DevTools.module.css";
const colorHash = new ColorHash({ lightness: 0.8 });
2021-07-29 11:24:22 -07:00
function UserId({ userId, ...rest }) {
const shortUserId = userId.split(":")[0];
const color = colorHash.hex(shortUserId);
return (
<span style={{ color }} {...rest}>
{shortUserId}
</span>
);
}
2021-07-28 16:14:38 -07:00
function CallId({ callId, ...rest }) {
const shortId = callId.substr(callId.length - 16);
const color = colorHash.hex(shortId);
return (
<span style={{ color }} {...rest}>
{shortId}
</span>
);
}
2021-07-30 16:55:25 -07:00
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;
}
}
2021-07-28 16:14:38 -07:00
export function DevTools({ manager }) {
const [debugState, setDebugState] = useState(manager.debugState);
2021-07-29 11:24:22 -07:00
const [selectedEvent, setSelectedEvent] = useState();
2021-07-30 16:55:25 -07:00
const [activeTab, setActiveTab] = useState("users");
2021-07-28 16:14:38 -07:00
useEffect(() => {
function onRoomDebug() {
2021-07-30 16:55:25 -07:00
setDebugState({ ...manager.debugState });
2021-07-28 16:14:38 -07:00
}
manager.on("debug", onRoomDebug);
return () => {
manager.removeListener("debug", onRoomDebug);
};
}, [manager]);
if (!manager.joined) {
return <div className={styles.devTools} />;
}
2021-07-28 16:14:38 -07:00
return (
<div className={styles.devTools}>
2021-07-30 16:55:25 -07:00
<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}
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>
2021-07-29 11:24:22 -07:00
{selectedEvent && (
<EventViewer
event={selectedEvent}
onClose={() => setSelectedEvent(null)}
/>
)}
2021-07-28 16:14:38 -07:00
</div>
);
}
2021-07-30 16:55:25 -07:00
function EventContainer({ title, state, events, ...rest }) {
2021-07-28 16:14:38 -07:00
const eventsRef = useRef();
const [autoScroll, setAutoScroll] = useState(true);
useEffect(() => {
if (autoScroll) {
const el = eventsRef.current;
el.scrollTop = el.scrollHeight - el.clientHeight;
}
});
2021-07-29 11:24:22 -07:00
const onScroll = useCallback(() => {
2021-07-28 16:14:38 -07:00
const el = eventsRef.current;
if (el.scrollHeight - el.scrollTop === el.clientHeight) {
setAutoScroll(true);
} else {
setAutoScroll(false);
}
}, []);
return (
<div className={styles.user}>
<div className={styles.userId}>
2021-07-30 16:55:25 -07:00
<span>{title}</span>
2021-07-28 16:14:38 -07:00
<span>{`(${state})`}</span>
</div>
<div ref={eventsRef} className={styles.events} onScroll={onScroll}>
2021-07-30 16:55:25 -07:00
{events.map((event, idx) => (
<EventItem key={idx} event={event} {...rest} />
))}
2021-07-28 16:14:38 -07:00
</div>
</div>
);
}
2021-07-29 11:24:22 -07:00
2021-07-30 16:55:25 -07:00
function EventItem({ event, showCallId, showSender, onSelect }) {
const type = event.getType();
const sender = event.getSender();
2021-08-03 15:05:29 -07:00
const { call_id, invitee, reason, eventType, ...rest } = event.getContent();
let eventValue;
if (eventType === "icegatheringstatechange") {
eventValue = rest.iceGatheringState;
} else if (eventType === "iceconnectionstatechange") {
eventValue = rest.iceConnectionState;
} else if (eventType === "signalingstatechange") {
eventValue = rest.signalingState;
}
2021-07-30 16:55:25 -07:00
2021-07-29 11:24:22 -07:00
return (
2021-07-30 16:55:25 -07:00
<div className={styles.event} onClick={() => onSelect(event)}>
{showSender && sender && (
<UserId className={styles.eventDetails} userId={sender} />
2021-07-29 11:24:22 -07:00
)}
2021-08-03 15:05:29 -07:00
<span className={styles.eventType}>
{type.replace("me.robertlong.", "x.")}
</span>
2021-07-30 16:55:25 -07:00
{showCallId && call_id && (
<CallId className={styles.eventDetails} callId={call_id} />
2021-07-29 11:24:22 -07:00
)}
2021-07-30 16:55:25 -07:00
{invitee && <UserId className={styles.eventDetails} userId={invitee} />}
2021-08-02 11:09:41 -07:00
{reason && <span className={styles.eventDetails}>{reason}</span>}
2021-08-03 15:05:29 -07:00
{eventType && <span className={styles.eventDetails}>{eventType}</span>}
{eventValue && <span className={styles.eventDetails}>{eventValue}</span>}
2021-07-30 16:55:25 -07:00
</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: {type}</p>
<p>Sender: {sender}</p>
{call_id && (
2021-07-29 11:24:22 -07:00
<p>
2021-07-30 16:55:25 -07:00
Call Id: <CallId callId={call_id} />
2021-07-29 11:24:22 -07:00
</p>
)}
2021-07-30 16:55:25 -07:00
{invitee && (
2021-07-29 11:24:22 -07:00
<p>
2021-07-30 16:55:25 -07:00
Invitee: <UserId userId={invitee} />
2021-07-29 11:24:22 -07:00
</p>
)}
2021-07-30 16:55:25 -07:00
<p>Raw Event:</p>
<pre className={styles.content}>{JSON.stringify(json, undefined, 2)}</pre>
2021-07-29 11:24:22 -07:00
<button onClick={onClose}>Close</button>
</div>
);
}