summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <tomvanderlee@users.noreply.github.com>2024-08-30 15:47:20 +0200
committerGravatar GitHub <noreply@github.com>2024-08-30 15:47:20 +0200
commitfe61544bafafc8b4de78cc71cb641af2dfb7b72d (patch)
tree7edff48c1638fcc26915dc1f40982ec2563cf3bf
parentf183536067dc694f37445148c15821f1621f5034 (diff)
parent2f2048160fac06e94703bb03eea43185fc01f76c (diff)
downloadclient-2.1.0.tar.gz
client-2.1.0.tar.bz2
client-2.1.0.zip
Merge pull request #15 from tomvanderlee/feature/websocketsv2.1.0
Feature/websockets
-rw-r--r--.github/workflows/python-publish.yml6
-rw-r--r--src/components/App/App.tsx40
-rw-r--r--src/components/Frames/Frames.module.scss9
-rw-r--r--src/components/Frames/Frames.tsx75
-rw-r--r--src/components/Icons/Sliders.tsx2
-rw-r--r--src/components/Icons/Trash.tsx2
-rw-r--r--src/components/RequestDetails/RequestDetails.tsx82
-rw-r--r--src/components/RequestList/RequestList.tsx48
-rw-r--r--src/components/RequestSummary/RequestSummary.tsx2
-rw-r--r--src/contexts/Connection.tsx288
-rw-r--r--src/contexts/DarkMode.tsx7
-rw-r--r--src/hooks/useRequests.tsx253
-rw-r--r--src/index.tsx5
-rw-r--r--src/types.ts136
-rw-r--r--ttun/__main__.py11
-rw-r--r--ttun/client.py216
-rw-r--r--ttun/inspect_server.py5
-rw-r--r--ttun/pubsub.py1
-rw-r--r--ttun/types.py60
-rw-r--r--yarn.lock1092
20 files changed, 1554 insertions, 786 deletions
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 9246d7e..d90abfb 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -12,13 +12,13 @@ jobs:
12 deploy: 12 deploy:
13 runs-on: ubuntu-latest 13 runs-on: ubuntu-latest
14 steps: 14 steps:
15 - uses: actions/checkout@v2 15 - uses: actions/checkout@v4
16 - name: Set up Python 16 - name: Set up Python
17 uses: actions/setup-python@v2 17 uses: actions/setup-python@v5
18 with: 18 with:
19 python-version: '3.10' 19 python-version: '3.10'
20 - name: Set up Node 20 - name: Set up Node
21 uses: actions/setup-node@v2 21 uses: actions/setup-node@v4
22 with: 22 with:
23 node-version: '16' 23 node-version: '16'
24 - name: Install node dependencies 24 - name: Install node dependencies
diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx
index b1a4501..ad36add 100644
--- a/src/components/App/App.tsx
+++ b/src/components/App/App.tsx
@@ -1,6 +1,6 @@
1import * as React from "react"; 1import * as React from "react";
2import { ReactElement, useContext, useEffect, useMemo, useState } from "react"; 2import { ReactElement, useContext, useEffect, useMemo, useState } from "react";
3import useRequests, { ReadyState, RequestResponse } from "~/hooks/useRequests"; 3import useRequests from "~/hooks/useRequests";
4 4
5import styles from "~/components/App/App.module.scss"; 5import styles from "~/components/App/App.module.scss";
6import RequestDetails from "~/components/RequestDetails/RequestDetails"; 6import RequestDetails from "~/components/RequestDetails/RequestDetails";
@@ -13,6 +13,8 @@ import Moon from "~/components/Icons/Moon";
13import Trash from "~/components/Icons/Trash"; 13import Trash from "~/components/Icons/Trash";
14import { DarkModeContext } from "~/contexts/DarkMode"; 14import { DarkModeContext } from "~/contexts/DarkMode";
15import RequestList from "~/components/RequestList/RequestList"; 15import RequestList from "~/components/RequestList/RequestList";
16import { Call, ReadyState } from "~/types";
17import { ConnectionContext } from "~/contexts/Connection";
16 18
17interface Config { 19interface Config {
18 url: string; 20 url: string;
@@ -47,29 +49,14 @@ const statusTextMap: ReadyStateMap = {
47 49
48export default function App() { 50export default function App() {
49 const { darkMode, toggle } = useContext(DarkModeContext); 51 const { darkMode, toggle } = useContext(DarkModeContext);
50 const [config, setConfig] = useState<Config | null>(null); 52 const { config, selectedCall, setSelectedCall, readyState, clear } =
51 53 useContext(ConnectionContext);
52 const { calls, readyState, clear } = useRequests({
53 onConnect: async () => {
54 const response = await fetch(`http://${getHost()}/config/`);
55 const config = await response.json();
56 setConfig(config);
57 },
58 });
59 54
60 useEffect(() => { 55 useEffect(() => {
61 const url = new URL(config?.url ?? "https://loading..."); 56 const url = new URL(config?.url ?? "https://loading...");
62 document.title = `${statusIconMap[readyState]} ${url.host} | TTUN`; 57 document.title = `${statusIconMap[readyState]} ${url.host} | TTUN`;
63 }, [readyState, config?.url]); 58 }, [readyState, config?.url]);
64 59
65 const [selectedRequestIndex, setSelectedRequestIndex] = useState<
66 number | null
67 >(null);
68 const selectedRequest = useMemo<RequestResponse | null>(
69 () => (selectedRequestIndex === null ? null : calls[selectedRequestIndex]),
70 [selectedRequestIndex, calls]
71 );
72
73 const settingsMenu: (SettingsMenu | null)[] = [ 60 const settingsMenu: (SettingsMenu | null)[] = [
74 { 61 {
75 onClick: toggle, 62 onClick: toggle,
@@ -79,7 +66,7 @@ export default function App() {
79 null, 66 null,
80 { 67 {
81 onClick: () => { 68 onClick: () => {
82 setSelectedRequestIndex(null); 69 setSelectedCall(null);
83 clear(); 70 clear();
84 }, 71 },
85 icon: <Trash />, 72 icon: <Trash />,
@@ -105,14 +92,15 @@ export default function App() {
105 </a> 92 </a>
106 </Navbar.Text> 93 </Navbar.Text>
107 <Navbar.Toggle aria-controls="settings" /> 94 <Navbar.Toggle aria-controls="settings" />
108 <Navbar.Collapse id="settings" className="ms-2"> 95 <Navbar.Collapse id="settings" className="ms-2" role="button">
109 <Nav> 96 <Nav>
110 <NavDropdown align="end" title={<Sliders />}> 97 <NavDropdown align="end" title={<Sliders />}>
111 {settingsMenu.map((item) => { 98 {settingsMenu.map((item, index) => {
112 if (item !== null) { 99 if (item !== null) {
113 const { onClick, icon, label } = item; 100 const { onClick, icon, label } = item;
114 return ( 101 return (
115 <NavDropdown.Item 102 <NavDropdown.Item
103 key={label}
116 onClick={onClick} 104 onClick={onClick}
117 className="d-flex align-items-center" 105 className="d-flex align-items-center"
118 > 106 >
@@ -121,7 +109,7 @@ export default function App() {
121 </NavDropdown.Item> 109 </NavDropdown.Item>
122 ); 110 );
123 } else { 111 } else {
124 return <NavDropdown.Divider />; 112 return <NavDropdown.Divider key={`item-${index}`} />;
125 } 113 }
126 })} 114 })}
127 </NavDropdown> 115 </NavDropdown>
@@ -133,14 +121,10 @@ export default function App() {
133 121
134 <main className={styles.main}> 122 <main className={styles.main}>
135 <div className={classNames("border-end", styles.sidebar)}> 123 <div className={classNames("border-end", styles.sidebar)}>
136 <RequestList 124 <RequestList />
137 requests={calls}
138 selectedRequestIndex={selectedRequestIndex}
139 setSelectedRequestIndex={setSelectedRequestIndex}
140 />
141 </div> 125 </div>
142 <div className={styles.details}> 126 <div className={styles.details}>
143 <RequestDetails requestResponse={selectedRequest} /> 127 <RequestDetails />
144 </div> 128 </div>
145 </main> 129 </main>
146 </div> 130 </div>
diff --git a/src/components/Frames/Frames.module.scss b/src/components/Frames/Frames.module.scss
new file mode 100644
index 0000000..821f0b8
--- /dev/null
+++ b/src/components/Frames/Frames.module.scss
@@ -0,0 +1,9 @@
1.arrow {
2 &.outbound {
3 color: green;
4 }
5
6 &.inbound {
7 color: red;
8 }
9}
diff --git a/src/components/Frames/Frames.tsx b/src/components/Frames/Frames.tsx
new file mode 100644
index 0000000..83c4c96
--- /dev/null
+++ b/src/components/Frames/Frames.tsx
@@ -0,0 +1,75 @@
1import { Frame } from "~/types";
2import React, { PropsWithChildren, useContext } from "react";
3import { Col, ListGroup, Row } from "react-bootstrap";
4import classNames from "classnames";
5import styles from "./Frames.module.scss";
6import dayjs from "dayjs";
7import ReactJson from "react-json-view";
8import { DarkModeContext } from "~/contexts/DarkMode";
9
10function isJson(data: any): boolean {
11 try {
12 JSON.parse(data);
13 return true;
14 } catch {
15 return false;
16 }
17}
18
19interface FramesProps {
20 frames: Frame[];
21}
22
23export default function Frames({
24 frames,
25}: PropsWithChildren<FramesProps>): JSX.Element {
26 const { darkMode } = useContext(DarkModeContext);
27 return (
28 <ListGroup variant="flush" as="ul" className={"flex-grow-1"}>
29 {frames.length > 0 ? (
30 frames.map((frame) => {
31 // Inbound relative to the client
32 const inbound = frame.type !== "websocket_inbound";
33
34 const body =
35 frame.type !== "websocket_disconnect"
36 ? atob(frame.payload.body)
37 : null;
38
39 return (
40 <ListGroup.Item
41 as="li"
42 key={frame.payload.id}
43 className={classNames({