@@ -97,23 +148,41 @@
{/if}
{:else if awaitingJoinConfirmation}
-
- You're invited to chat.
-
-
-
-
-
+ {#if $ws.status !== WebsocketConnectionState.CONNECTED || roomExists === undefined}
+
+ Connecting to server...
+
+
+ click here to go back to the homepage
+
+ {:else if roomExists === false}
+
+ That room does not exist.
+
+
+ click here to go back to the homepage
+
+ {:else}
+
+ You're invited to chat.
+
+
+
+
+
+ {/if}
{:else}
{/if}
diff --git a/src/stores/messageStore.ts b/src/stores/messageStore.ts
index dc8acf2..6549638 100644
--- a/src/stores/messageStore.ts
+++ b/src/stores/messageStore.ts
@@ -1,5 +1,5 @@
import { writable, type Writable } from "svelte/store";
-import type { Message } from "../types/message";
+import type { Message } from "$types/message";
export let messages: Writable
= writable([]);
export let advertisedOffers = writable(new Map());
diff --git a/src/stores/roomStore.ts b/src/stores/roomStore.ts
index d0f5fcc..65d18a2 100644
--- a/src/stores/roomStore.ts
+++ b/src/stores/roomStore.ts
@@ -1,5 +1,5 @@
import { writable, type Writable } from 'svelte/store';
-import { RoomConnectionState } from '../types/websocket';
+import { RoomConnectionState } from '$types/websocket';
import { browser } from '$app/environment';
export interface Room {
diff --git a/src/stores/websocketStore.ts b/src/stores/websocketStore.ts
index c97940b..9189b53 100644
--- a/src/stores/websocketStore.ts
+++ b/src/stores/websocketStore.ts
@@ -1,7 +1,7 @@
import { get, writable, type Readable, type Writable } from 'svelte/store';
import { browser } from '$app/environment';
-import { Socket, type WebSocketMessage } from '../types/websocket';
-import { handleMessage } from '../utils/webrtcUtil';
+import { Socket, WebSocketMessageType, type WebSocketMessage } from '$types/websocket';
+import { handleMessage } from '../lib/webrtcUtil';
export enum WebsocketConnectionState {
DISCONNECTED,
@@ -21,6 +21,7 @@ interface WebSocketStore extends Readable {
connect: () => void;
disconnect: () => void;
send: (message: WebSocketMessage) => void;
+ handleEvent(messageType: T, func: (message: WebSocketMessage & { type: T }) => void): () => void;
}
// TODO: handle reconnection logic to room elsewhere (not implemented here)
@@ -94,11 +95,23 @@ function createWebSocketStore(messageHandler: MessageHandler): WebSocketStore {
});
};
+ function handleEvent(messageType: T, func: (message: WebSocketMessage & { type: T }) => void) {
+ let socket = get({ subscribe }).socket;
+ if (!socket) {
+ return () => { };
+ }
+
+ // TODO: why does the typescript compiler think this is invalid?
+ return socket.handleEvent(messageType, func)
+ }
+
+
return {
subscribe,
connect,
disconnect,
send,
+ handleEvent,
};
}
diff --git a/src/types/websocket.ts b/src/types/websocket.ts
index 378c24d..02ddf9a 100644
--- a/src/types/websocket.ts
+++ b/src/types/websocket.ts
@@ -16,12 +16,16 @@ export enum WebSocketMessageType {
CREATE_ROOM = "create",
JOIN_ROOM = "join",
LEAVE_ROOM = "leave",
+ CHECK_ROOM_EXISTS = "check",
+ REQUEST_CHALLENGE = "request-challenge",
// response messages
ROOM_CREATED = "created",
ROOM_JOINED = "joined",
ROOM_LEFT = "left",
ROOM_READY = "ready",
+ ROOM_STATUS = "status",
+ CHALLENGE = "challenge",
// webrtc messages
WEBRTC_OFFER = "offer",
@@ -31,14 +35,19 @@ export enum WebSocketMessageType {
ERROR = "error",
}
+// TODO: name the interfaces better
export type WebSocketMessage =
| CreateRoomMessage
| JoinRoomMessage
| LeaveRoomMessage
+ | CheckRoomExistsMessage
+ | RequestChallengeMessage
| RoomCreatedMessage
| RoomJoinedMessage
| RoomLeftMessage
+ | RoomStatusMessage
| RoomReadyMessage
+ | ChallengeMessage
| OfferMessage
| AnswerMessage
| IceCandidateMessage
@@ -49,14 +58,20 @@ interface ErrorMessage {
data: string;
}
+// ====== Query Messages ======
interface CreateRoomMessage {
type: WebSocketMessageType.CREATE_ROOM;
roomName?: string;
+ nonce: string;
+ challenge: string;
}
+// TODO: this is used as a query message, but it's also used as a response message
interface JoinRoomMessage {
type: WebSocketMessageType.JOIN_ROOM;
roomId: string;
+ nonce?: string;
+ challenge?: string;
}
interface LeaveRoomMessage {
@@ -64,6 +79,20 @@ interface LeaveRoomMessage {
roomId: string;
}
+interface CheckRoomExistsMessage {
+ type: WebSocketMessageType.CHECK_ROOM_EXISTS;
+ // if sha256(roomId + challenge + nonce) has a certain number of leading zeros, then we can give the status to the user
+ roomId: string;
+ nonce: string;
+ challenge: string;
+}
+
+interface RequestChallengeMessage {
+ type: WebSocketMessageType.REQUEST_CHALLENGE;
+}
+
+// ====== Response Messages ======
+
interface RoomCreatedMessage {
type: WebSocketMessageType.ROOM_CREATED;
data: string;
@@ -80,6 +109,12 @@ interface RoomLeftMessage {
roomId: string;
}
+interface RoomStatusMessage {
+ type: WebSocketMessageType.ROOM_STATUS;
+ roomId: string;
+ status: 'found' | 'not-found';
+}
+
interface RoomReadyMessage {
type: WebSocketMessageType.ROOM_READY;
data: {
@@ -87,6 +122,14 @@ interface RoomReadyMessage {
};
}
+interface ChallengeMessage {
+ type: WebSocketMessageType.CHALLENGE;
+ challenge: string;
+ difficulty: number;
+}
+
+// ====== WebRTC signaling messages ======
+// as the server, we dont do anything with these messages other than relay them to the other peers in the room
interface OfferMessage {
type: WebSocketMessageType.WEBRTC_OFFER;
data: {
@@ -122,6 +165,9 @@ export class Socket {
public addEventListener: typeof WebSocket.prototype.addEventListener;
public removeEventListener: typeof WebSocket.prototype.removeEventListener;
public close: typeof WebSocket.prototype.close;
+ // maps WebSocketMessageType to an array of functions that handle that message
+ // this allows for consumbers to subscribe to a specific message type and handle it themselves
+ private functionStack: Map;
constructor(webSocket: WebSocket) {
this.ws = webSocket;
@@ -130,6 +176,18 @@ export class Socket {
console.log("WebSocket opened");
});
+ this.functionStack = new Map();
+
+ this.ws.addEventListener("message", async (event) => {
+ console.log("WebSocket received message:", event.data);
+ const message: WebSocketMessage = JSON.parse(event.data);
+
+ if (this.functionStack.has(message.type)) {
+ for (let func of this.functionStack.get(message.type)!) {
+ func(message);
+ }
+ }
+ });
this.addEventListener = this.ws.addEventListener.bind(this.ws);
this.removeEventListener = this.ws.removeEventListener.bind(this.ws);
@@ -145,4 +203,15 @@ export class Socket {
this.ws.send(JSON.stringify(message));
}
+
+ public handleEvent(messageType: T, func: (message: WebSocketMessage & { type: T }) => void): () => void {
+ if (!this.functionStack.has(messageType)) {
+ this.functionStack.set(messageType, []);
+ }
+
+ this.functionStack.get(messageType)!.push(func);
+ return () => {
+ this.functionStack.get(messageType)!.splice(this.functionStack.get(messageType)!.indexOf(func), 1);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/websocket.ts b/src/websocket.ts
index 973b537..109b5a1 100644
--- a/src/websocket.ts
+++ b/src/websocket.ts
@@ -1,5 +1,5 @@
import { WebSocketServer } from "ws";
-import { confgiureWebsocketServer } from '../server/websocketHandler.ts';
+import { confgiureWebsocketServer } from './lib/server/websocketHandler.ts';
import type { ViteDevServer } from "vite";
diff --git a/svelte.config.js b/svelte.config.js
index ab35722..1275169 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -10,7 +10,13 @@ const config = {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
- adapter: adapter()
+ adapter: adapter(),
+ alias: {
+ $stores: './src/stores',
+ $components: './src/components',
+ $types: './src/types',
+ '$lib/server': './src/lib/server',
+ }
},
compilerOptions: {
experimental: {