summaryrefslogtreecommitdiffstats
path: root/src/components/App/App.tsx
blob: ad36add1f04741b6de21ea0b5ef7b2eca852a461 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import * as React from "react";
import { ReactElement, useContext, useEffect, useMemo, useState } from "react";
import useRequests from "~/hooks/useRequests";

import styles from "~/components/App/App.module.scss";
import RequestDetails from "~/components/RequestDetails/RequestDetails";
import { getHost } from "~/utils";
import { Container, Nav, Navbar, NavDropdown } from "react-bootstrap";
import classNames from "classnames";
import Sliders from "~/components/Icons/Sliders";
import Sun from "~/components/Icons/Sun";
import Moon from "~/components/Icons/Moon";
import Trash from "~/components/Icons/Trash";
import { DarkModeContext } from "~/contexts/DarkMode";
import RequestList from "~/components/RequestList/RequestList";
import { Call, ReadyState } from "~/types";
import { ConnectionContext } from "~/contexts/Connection";

interface Config {
  url: string;
}

interface SettingsMenu {
  icon: ReactElement;
  label: string;
  onClick: () => void;
}

type ReadyStateMap = {
  [ReadyState.CONNECTING]: string;
  [ReadyState.OPEN]: string;
  [ReadyState.CLOSING]: string;
  [ReadyState.CLOSED]: string;
};

const statusIconMap: ReadyStateMap = {
  [ReadyState.CONNECTING]: "🔴",
  [ReadyState.OPEN]: "🟢",
  [ReadyState.CLOSING]: "🔴",
  [ReadyState.CLOSED]: "🔴",
};

const statusTextMap: ReadyStateMap = {
  [ReadyState.CONNECTING]: "Connecting...",
  [ReadyState.OPEN]: "Connected",
  [ReadyState.CLOSING]: "Closing...",
  [ReadyState.CLOSED]: "Closed",
};

export default function App() {
  const { darkMode, toggle } = useContext(DarkModeContext);
  const { config, selectedCall, setSelectedCall, readyState, clear } =
    useContext(ConnectionContext);

  useEffect(() => {
    const url = new URL(config?.url ?? "https://loading...");
    document.title = `${statusIconMap[readyState]} ${url.host} | TTUN`;
  }, [readyState, config?.url]);

  const settingsMenu: (SettingsMenu | null)[] = [
    {
      onClick: toggle,
      icon: darkMode ? <Sun /> : <Moon />,
      label: darkMode ? "Light mode" : "Dark mode",
    },
    null,
    {
      onClick: () => {
        setSelectedCall(null);
        clear();
      },
      icon: <Trash />,
      label: "Clear",
    },
  ];

  return (
    config && (
      <div className={styles.app}>
        <Navbar bg="dark" variant="dark" expand as="header">
          <Container fluid>
            <div>
              <Navbar.Brand>TTUN</Navbar.Brand>
              <Navbar.Text>
                {`${statusIconMap[readyState]} ${statusTextMap[readyState]}`}
              </Navbar.Text>
            </div>
            <div className="d-flex">
              <Navbar.Text>
                <a href={config.url} target="_blank">
                  {config.url}
                </a>
              </Navbar.Text>
              <Navbar.Toggle aria-controls="settings" />
              <Navbar.Collapse id="settings" className="ms-2" role="button">
                <Nav>
                  <NavDropdown align="end" title={<Sliders />}>
                    {settingsMenu.map((item, index) => {
                      if (item !== null) {
                        const { onClick, icon, label } = item;
                        return (
                          <NavDropdown.Item
                            key={label}
                            onClick={onClick}
                            className="d-flex align-items-center"
                          >
                            {icon}
                            <span className="ms-3">{label}</span>
                          </NavDropdown.Item>
                        );
                      } else {
                        return <NavDropdown.Divider key={`item-${index}`} />;
                      }
                    })}
                  </NavDropdown>
                </Nav>
              </Navbar.Collapse>
            </div>
          </Container>
        </Navbar>

        <main className={styles.main}>
          <div className={classNames("border-end", styles.sidebar)}>
            <RequestList />
          </div>
          <div className={styles.details}>
            <RequestDetails />
          </div>
        </main>
      </div>
    )
  );
}