diff --git a/src/App.tsx b/src/App.tsx index aceff92..2053cc1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,31 +1,17 @@ -import { useEffect, useRef, useState } from "react"; +import { useRef, useState } from "react"; import reactLogo from "./assets/react.svg"; import { invoke } from "@tauri-apps/api/core"; import "./App.css"; -import { EnclaveWebSocket } from "./protocol/ws"; -import * as ed from "@noble/ed25519"; -import { sha512 } from "@noble/hashes/sha2.js"; - -ed.hashes.sha512 = sha512; +import Enclave from "./app/app"; function App() { const [greetMsg, setGreetMsg] = useState(""); const [name, setName] = useState(""); - const initialized = useRef(false); - - useEffect(() => { - if (initialized.current) return; - initialized.current = true; - - (async () => { - const ws = new EnclaveWebSocket("localhost:3415"); - ws.clientSecretKey = ed.utils.randomSecretKey(); - ws.clientPublicKey = ed.getPublicKey(ws.clientSecretKey); - ws.init(); - console.log("OK"); - })(); - }, []); + const appRef = useRef(null); + if (!appRef.current) { + appRef.current = new Enclave(); + } async function greet() { // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ diff --git a/src/app/app.ts b/src/app/app.ts new file mode 100644 index 0000000..5ec9152 --- /dev/null +++ b/src/app/app.ts @@ -0,0 +1,32 @@ +import EnclaveServer from "./server"; +import * as ed from "@noble/ed25519"; + +/** + * Top-level application state and client-side logic. + * + * Owns all active connections — currently `EnclaveServer` instances, one + * per connected server, keyed by server id — and will later also own + * `DMServer` connections once direct messages are implemented. + * + * This is where UI-facing logic lives: handling user actions (button + * clicks, switching the active server, sending a message from an input + * box), and exposing state for the UI layer to render. The UI itself + * should stay a pure display of what `Enclave` exposes — it should not + * reach into `EnclaveServer` or `EnclaveWebSocket` directly. + */ +export default class Enclave { + private clientSecretKey: Uint8Array; + private clientPublicKey: Uint8Array; + public server: EnclaveServer; + + public constructor() { + this.clientSecretKey = ed.utils.randomSecretKey(); + this.clientPublicKey = ed.getPublicKey(this.clientSecretKey); + + this.server = new EnclaveServer( + "localhost:3415", + this.clientSecretKey, + this.clientPublicKey, + ); + } +} diff --git a/src/protocol/protocol.ts b/src/app/protocol.ts similarity index 100% rename from src/protocol/protocol.ts rename to src/app/protocol.ts diff --git a/src/app/server.ts b/src/app/server.ts new file mode 100644 index 0000000..7ff68fd --- /dev/null +++ b/src/app/server.ts @@ -0,0 +1,28 @@ +import EnclaveWebSocket from "./ws"; +/** + * Manages a single connection to an Enclave server: channels, server + * metadata, and messages for that server. + * + * Wraps one `EnclaveWebSocket` instance and layers server-level concepts + * on top of the raw protocol (channels, messages, server meta) — it does + * not know about the handshake/key exchange itself, only the connection + * it's given. + * + * One `EnclaveServer` instance exists per connected server. It has no + * knowledge of other servers, DMs, or app-level UI state — that lives in + * `Enclave`, which owns and manages multiple `EnclaveServer` instances. + */ +export default class EnclaveServer { + public websocket: EnclaveWebSocket; + + public constructor( + hostname: string, + clientSecretKey: Uint8Array, + clientPublicKey: Uint8Array, + ) { + this.websocket = new EnclaveWebSocket(hostname); + this.websocket.clientSecretKey = clientSecretKey; + this.websocket.clientPublicKey = clientPublicKey; + this.websocket.init(); + } +} diff --git a/src/protocol/ws.ts b/src/app/ws.ts similarity index 81% rename from src/protocol/ws.ts rename to src/app/ws.ts index f59b44b..e419f0f 100644 --- a/src/protocol/ws.ts +++ b/src/app/ws.ts @@ -5,7 +5,19 @@ import { sha512 } from "@noble/hashes/sha2.js"; ed.hashes.sha512 = sha512; -export class EnclaveWebSocket { +/** + * A protocol-aware wrapper around the browser `WebSocket`. + * + * Owns exactly one connection's lifecycle: the initial key exchange and + * identity handshake, and storage of the resulting keys (this client's + * keypair, the server's verified public key) for the lifetime of the + * connection. + * + * Exposes typed protocol methods (`ServerMethod` / `ClientMethod`) rather + * than raw WebSocket messages. Has no concept of channels, messages, or + * app state — `EnclaveServer` builds on top of this to add those. + */ +export default class EnclaveWebSocket { public websocket: WebSocket; public hostname: string; onOpenQueue: Array<() => void>;