diff --git a/web/src/App.css b/web/src/App.css
deleted file mode 100644
index e69de29..0000000
diff --git a/web/src/App.tsx b/web/src/App.tsx
index 96360ad..b38ffd2 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -14,14 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import { useEffect, useMemo, useState } from "react"
-import "./App.css"
-import Client from "./client.ts"
-import WSClient from "./wsclient.ts"
-import { ClientState } from "./hievents.ts"
-import { ConnectionEvent } from "./rpc.ts"
-import { LoginScreen, VerificationScreen } from "./login"
+import Client from "./api/client.ts"
+import WSClient from "./api/wsclient.ts"
+import { ClientState } from "./api/types/hievents.ts"
+import { ConnectionEvent } from "./api/rpc.ts"
+import { LoginScreen, VerificationScreen } from "./ui/login"
import { ScaleLoader } from "react-spinners"
-import MainScreen from "./MainScreen.tsx"
+import MainScreen from "./ui/MainScreen.tsx"
function App() {
const [connState, setConnState] = useState()
diff --git a/web/src/client.ts b/web/src/api/client.ts
similarity index 94%
rename from web/src/client.ts
rename to web/src/api/client.ts
index 0f1525e..cd10116 100644
--- a/web/src/client.ts
+++ b/web/src/api/client.ts
@@ -15,10 +15,10 @@
// along with this program. If not, see .
import type {
ClientWellKnown, DBEvent, EventID, EventRowID, EventType, RoomID, TimelineRowID, UserID,
-} from "./hitypes.ts"
-import { ClientState, RPCEvent } from "./hievents.ts"
+} from "./types/hitypes.ts"
+import { ClientState, RPCEvent } from "./types/hievents.ts"
import { RPCClient } from "./rpc.ts"
-import { CachedEventDispatcher } from "./eventdispatcher.ts"
+import { CachedEventDispatcher } from "../util/eventdispatcher.ts"
import { StateStore } from "./statestore.ts"
export default class Client {
diff --git a/web/src/rpc.ts b/web/src/api/rpc.ts
similarity index 76%
rename from web/src/rpc.ts
rename to web/src/api/rpc.ts
index 95ec284..4d8c0db 100644
--- a/web/src/rpc.ts
+++ b/web/src/api/rpc.ts
@@ -13,17 +13,9 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import { RPCEvent } from "./hievents.ts"
-import { EventDispatcher } from "./eventdispatcher.ts"
-
-export class CancellablePromise extends Promise {
- constructor(
- executor: (resolve: (value: T) => void, reject: (reason?: Error) => void) => void,
- readonly cancel: (reason: string) => void,
- ) {
- super(executor)
- }
-}
+import { RPCEvent } from "./types/hievents.ts"
+import { EventDispatcher } from "../util/eventdispatcher.ts"
+import { CancellablePromise } from "../util/promise.ts"
export interface RPCClient {
connect: EventDispatcher
diff --git a/web/src/statestore.ts b/web/src/api/statestore.ts
similarity index 98%
rename from web/src/statestore.ts
rename to web/src/api/statestore.ts
index 62553e5..ce99d08 100644
--- a/web/src/statestore.ts
+++ b/web/src/api/statestore.ts
@@ -22,9 +22,9 @@ import type {
LazyLoadSummary,
RoomID,
TimelineRowTuple,
-} from "./hitypes.ts"
-import type { EventsDecryptedData, SyncCompleteData, SyncRoom } from "./hievents.ts"
-import { NonNullCachedEventDispatcher } from "./eventdispatcher.ts"
+} from "./types/hitypes.ts"
+import type { EventsDecryptedData, SyncCompleteData, SyncRoom } from "./types/hievents.ts"
+import { NonNullCachedEventDispatcher } from "../util/eventdispatcher.ts"
function arraysAreEqual(arr1?: T[], arr2?: T[]): boolean {
if (!arr1 || !arr2) {
diff --git a/web/src/hievents.ts b/web/src/api/types/hievents.ts
similarity index 100%
rename from web/src/hievents.ts
rename to web/src/api/types/hievents.ts
diff --git a/web/src/hitypes.ts b/web/src/api/types/hitypes.ts
similarity index 100%
rename from web/src/hitypes.ts
rename to web/src/api/types/hitypes.ts
diff --git a/web/src/wsclient.ts b/web/src/api/wsclient.ts
similarity index 94%
rename from web/src/wsclient.ts
rename to web/src/api/wsclient.ts
index 279560d..02dd770 100644
--- a/web/src/wsclient.ts
+++ b/web/src/api/wsclient.ts
@@ -13,9 +13,10 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import { RPCCommand, RPCEvent } from "./hievents.ts"
-import { CachedEventDispatcher, EventDispatcher } from "./eventdispatcher.ts"
-import { CancellablePromise, ConnectionEvent, RPCClient } from "./rpc.ts"
+import { RPCCommand, RPCEvent } from "./types/hievents.ts"
+import { CachedEventDispatcher, EventDispatcher } from "../util/eventdispatcher.ts"
+import { ConnectionEvent, RPCClient } from "./rpc.ts"
+import { CancellablePromise } from "../util/promise.ts"
export class ErrorResponse extends Error {
constructor(public data: unknown) {
diff --git a/web/src/MainScreen.css b/web/src/ui/MainScreen.css
similarity index 100%
rename from web/src/MainScreen.css
rename to web/src/ui/MainScreen.css
diff --git a/web/src/MainScreen.tsx b/web/src/ui/MainScreen.tsx
similarity index 93%
rename from web/src/MainScreen.tsx
rename to web/src/ui/MainScreen.tsx
index f8d3643..afa5c69 100644
--- a/web/src/MainScreen.tsx
+++ b/web/src/ui/MainScreen.tsx
@@ -14,8 +14,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import { useState } from "react"
-import type Client from "./client.ts"
-import type { RoomID } from "./hitypes.ts"
+import type Client from "../api/client.ts"
+import type { RoomID } from "../api/types/hitypes.ts"
import RoomList from "./RoomList.tsx"
import RoomView from "./RoomView.tsx"
import "./MainScreen.css"
diff --git a/web/src/RoomList.css b/web/src/ui/RoomList.css
similarity index 100%
rename from web/src/RoomList.css
rename to web/src/ui/RoomList.css
diff --git a/web/src/RoomList.tsx b/web/src/ui/RoomList.tsx
similarity index 93%
rename from web/src/RoomList.tsx
rename to web/src/ui/RoomList.tsx
index b15addb..26b549d 100644
--- a/web/src/RoomList.tsx
+++ b/web/src/ui/RoomList.tsx
@@ -14,10 +14,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import React, { useMemo } from "react"
-import Client from "./client.ts"
-import { DBEvent, RoomID } from "./hitypes.ts"
-import { useNonNullEventAsState } from "./eventdispatcher.ts"
-import { RoomListEntry } from "./statestore.ts"
+import Client from "../api/client.ts"
+import { DBEvent, RoomID } from "../api/types/hitypes.ts"
+import { useNonNullEventAsState } from "../util/eventdispatcher.ts"
+import { RoomListEntry } from "../api/statestore.ts"
import "./RoomList.css"
export interface RoomListProps {
diff --git a/web/src/RoomView.css b/web/src/ui/RoomView.css
similarity index 100%
rename from web/src/RoomView.css
rename to web/src/ui/RoomView.css
diff --git a/web/src/RoomView.tsx b/web/src/ui/RoomView.tsx
similarity index 85%
rename from web/src/RoomView.tsx
rename to web/src/ui/RoomView.tsx
index fe8ff44..eabf1d7 100644
--- a/web/src/RoomView.tsx
+++ b/web/src/ui/RoomView.tsx
@@ -13,11 +13,11 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import Client from "./client.ts"
-import { RoomStateStore } from "./statestore.ts"
-import { useNonNullEventAsState } from "./eventdispatcher.ts"
+import Client from "../api/client.ts"
+import { RoomStateStore } from "../api/statestore.ts"
+import { useNonNullEventAsState } from "../util/eventdispatcher.ts"
import "./RoomView.css"
-import TimelineEvent from "./TimelineEvent.tsx"
+import TimelineEvent from "./timeline/TimelineEvent.tsx"
export interface RoomViewProps {
client: Client
diff --git a/web/src/login/LoginScreen.css b/web/src/ui/login/LoginScreen.css
similarity index 100%
rename from web/src/login/LoginScreen.css
rename to web/src/ui/login/LoginScreen.css
diff --git a/web/src/login/LoginScreen.tsx b/web/src/ui/login/LoginScreen.tsx
similarity index 96%
rename from web/src/login/LoginScreen.tsx
rename to web/src/ui/login/LoginScreen.tsx
index a85ad24..c8ad7f4 100644
--- a/web/src/login/LoginScreen.tsx
+++ b/web/src/ui/login/LoginScreen.tsx
@@ -14,9 +14,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import React, { useCallback, useEffect, useState } from "react"
-import type Client from "../client.ts"
+import type Client from "../../api/client.ts"
import "./LoginScreen.css"
-import { ClientState } from "../hievents.ts"
+import { ClientState } from "../../api/types/hievents.ts"
export interface LoginScreenProps {
client: Client
diff --git a/web/src/login/VerificationScreen.tsx b/web/src/ui/login/VerificationScreen.tsx
similarity index 100%
rename from web/src/login/VerificationScreen.tsx
rename to web/src/ui/login/VerificationScreen.tsx
diff --git a/web/src/login/index.ts b/web/src/ui/login/index.ts
similarity index 100%
rename from web/src/login/index.ts
rename to web/src/ui/login/index.ts
diff --git a/web/src/TimelineEvent.css b/web/src/ui/timeline/TimelineEvent.css
similarity index 100%
rename from web/src/TimelineEvent.css
rename to web/src/ui/timeline/TimelineEvent.css
diff --git a/web/src/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx
similarity index 96%
rename from web/src/TimelineEvent.tsx
rename to web/src/ui/timeline/TimelineEvent.tsx
index 91adca4..3d2a36c 100644
--- a/web/src/TimelineEvent.tsx
+++ b/web/src/ui/timeline/TimelineEvent.tsx
@@ -13,7 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import { RoomViewProps } from "./RoomView.tsx"
+import { RoomViewProps } from "../RoomView.tsx"
import "./TimelineEvent.css"
export interface TimelineEventProps extends RoomViewProps {
diff --git a/web/src/eventdispatcher.ts b/web/src/util/eventdispatcher.ts
similarity index 100%
rename from web/src/eventdispatcher.ts
rename to web/src/util/eventdispatcher.ts
diff --git a/web/src/util/promise.ts b/web/src/util/promise.ts
new file mode 100644
index 0000000..33cccc0
--- /dev/null
+++ b/web/src/util/promise.ts
@@ -0,0 +1,23 @@
+// gomuks - A Matrix client written in Go.
+// Copyright (C) 2024 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+export class CancellablePromise extends Promise {
+ constructor(
+ executor: (resolve: (value: T) => void, reject: (reason?: Error) => void) => void,
+ readonly cancel: (reason: string) => void,
+ ) {
+ super(executor)
+ }
+}