Skip to content

Commit

Permalink
Merge pull request #20 from DerTyp876/refactor_clean-up
Browse files Browse the repository at this point in the history
Clean up project
  • Loading branch information
DerTyp7 authored Jun 29, 2023
2 parents f296bba + eb0e005 commit c6289dd
Show file tree
Hide file tree
Showing 11 changed files with 3,457 additions and 654 deletions.
2,917 changes: 2,831 additions & 86 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
"preview": "vite preview"
},
"dependencies": {
"@types/node": "^20.3.2",
"jest": "^29.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.0",
"sass": "^1.62.1"
},
"devDependencies": {
"@types/jest": "^29.5.2",
"@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.59.0",
Expand Down
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
import "@styles/App.scss";
import { TS5Connection } from "./teamspeak5Handler";
import { IChannel, IClient, IConnection } from "interfaces/teamspeak";
import { IChannel, IClient, IConnection, ITS5ConnectionHandler } from "@interfaces/teamspeak";
import { useEffect, useState } from "react";
import Viewer from "./Viewer";
import { useSearchParams } from "react-router-dom";
import { TS5ConnectionHandler } from "@handlers/teamspeak/connectionHandler";

export default function App() {
const [searchParams] = useSearchParams();
Expand Down Expand Up @@ -38,7 +38,7 @@ export default function App() {
useEffect(() => {
const remoteAppPort = searchParams.get("remoteAppPort");

const tsConnection: TS5Connection = new TS5Connection(
const tsConnection: ITS5ConnectionHandler = new TS5ConnectionHandler(
parseInt(remoteAppPort ?? "5899"),
setConnections,
setChannels,
Expand Down
2 changes: 1 addition & 1 deletion src/Viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IChannel, IClient } from "interfaces/teamspeak";
import { IChannel, IClient } from "@interfaces/teamspeak";
import "@styles/Viewer.scss";

export default function Viewer({
Expand Down
131 changes: 131 additions & 0 deletions src/handlers/teamspeak/connectionHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { IAuthSenderPayload, IChannel, IClient, IConnection, ITS5ConnectionHandler, ITS5DataHandler, ITS5MessageHandler } from "@interfaces/teamspeak";
import { TS5DataHandler } from "./dataHandler";
import { TS5MessageHandler } from "./messageHandler";


// Establish connection to TS5 client
// Main class
export class TS5ConnectionHandler implements ITS5ConnectionHandler {
ws: WebSocket; // Websocket connection to TS5 client
authenticated = false; // Is the connection authenticated?
remoteAppPort: number; // Port of TS5 client
dataHandler: ITS5DataHandler; // Handles data/lists and states
messageHandler: ITS5MessageHandler; // Handles messages received from TS5 client

constructor(
// Port of TS5 client
remoteAppPort: number,

// State setters for dataHandler
setConnections: React.Dispatch<React.SetStateAction<IConnection[]>>,
setChannels: React.Dispatch<React.SetStateAction<IChannel[]>>,
setClients: React.Dispatch<React.SetStateAction<IClient[]>>,
setActiveConnectionStateId: React.Dispatch<React.SetStateAction<number>>,
) {
// Create websocket connection to TS5 client
this.remoteAppPort = remoteAppPort;
this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);

// Create dataHandler and messageHandler
this.dataHandler = new TS5DataHandler(setConnections, setChannels, setClients);
this.messageHandler = new TS5MessageHandler(this.ws, this.dataHandler, setActiveConnectionStateId);
}

reconnect() {
console.log("Reconnecting...")
this.ws.close();

this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);

this.dataHandler.clearAll();
this.authenticated = false;
this.connect();
}

// Connect to TS5 client
connect() {
console.log('Connecting to TS5 client...');
console.log(localStorage.getItem("apiKey"))

// Create authentication payload
const initalPayload: IAuthSenderPayload = {
type: "auth",
payload: {
identifier: "de.tealfire.obs",
version: "1.0.0",
name: "TS5 OBS Overlay",
description: "A OBS overlay for TS5 by DerTyp876",
content: {
apiKey: localStorage.getItem("apiKey") ?? "",
},
},
};

this.ws.onopen = () => {
// Send authentication payload to TS5 client
console.log("Sending auth payload...")
this.ws.send(JSON.stringify(initalPayload));
};

this.ws.onclose = (event) => {
console.log("WebSocket connection closed", event);

// If the connection was closed before authentication, remove the API key from local storage
// OBS weirdly caches the localstorage and is very stubborn about clearing it (even when clicken "Clear Cache")
if (!this.authenticated) {
console.log("WebSocket connection closed before authentication");
localStorage.removeItem("apiKey");
}

setTimeout(() => {
this.reconnect();
}, 2000);
};

// Handle messages received from TS5 client
// See TS5MessageHandler class
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);

console.log(data)

switch (data.type) {
case "auth":
this.messageHandler.handleAuthMessage(data);
this.authenticated = true;
break;
case "clientMoved":
this.messageHandler.handleClientMovedMessage(data);
break;
case "clientPropertiesUpdated":
this.messageHandler.handleClientPropertiesUpdatedMessage(data);
break;
case "talkStatusChanged":
this.messageHandler.handleTalkStatusChangedMessage(data);
break;
case "serverPropertiesUpdated":
this.messageHandler.handleServerPropertiesUpdatedMessage(data);
//this.ws.close();
break;
case "connectStatusChanged":
this.messageHandler.handleConnectStatusChangedMessage(data);
break;
case "clientSelfPropertyUpdated":
this.messageHandler.handleClientSelfPropertyUpdatedMessage(data);
break;
case "channels":
this.messageHandler.handleChannelsMessage(data);
break;
default:
console.log(`No handler for event type: ${data.type}`);
break;
}
};
}
}






204 changes: 204 additions & 0 deletions src/handlers/teamspeak/dataHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { IConnection, IChannel, IClient, ITS5DataHandler } from "@interfaces/teamspeak";


/**
* Handles data received from TS5 client (list of connections, channels and clients)
* Updates the states of App.tsx
*/
export class TS5DataHandler implements ITS5DataHandler {
// Local lists of connections, channels and clients
// These lists are used to keep track of the data, independent of the App.tsx state
localConnections: IConnection[];
localChannels: IChannel[];
localClients: IClient[];

// State setters for App.tsx
setConnections: React.Dispatch<React.SetStateAction<IConnection[]>>;
setChannels: React.Dispatch<React.SetStateAction<IChannel[]>>;
setClients: React.Dispatch<React.SetStateAction<IClient[]>>;

constructor(
// State setters for App.tsx
setConnections: React.Dispatch<React.SetStateAction<IConnection[]>>,
setChannels: React.Dispatch<React.SetStateAction<IChannel[]>>,
setClients: React.Dispatch<React.SetStateAction<IClient[]>>
) {
this.setConnections = setConnections;
this.setChannels = setChannels;
this.setClients = setClients;

this.localConnections = [];
this.localChannels = [];
this.localClients = [];
}

// Update App.tsx states
private updateConnectionsState() {
this.setConnections([...this.localConnections]);
}

private updateChannelsState() {
this.setChannels([...this.localChannels]);
}

private updateClientsState() {
this.setClients([...this.localClients]);
}

// Clear all data
clearAll() {
this.localConnections = [];
this.localChannels = [];
this.localClients = [];

this.updateConnectionsState();
this.updateChannelsState();
this.updateClientsState();
}

// Add data to local lists and update states
addConnection(connection: IConnection) {
console.log("Adding connection...", connection)

const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);

if (existingConnection == undefined) {
this.localConnections.push(connection);
this.updateConnectionsState();
console.log("Connection added")
} else {
console.log("Connection already exists")
}
}

addChannel(channel: IChannel) {
console.log("Adding channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);

if (existingChannel == undefined) {
this.localChannels.push(channel);
this.updateChannelsState();
console.log("Channel added")
} else {
console.log("Channel already exists")
}
}

addClient(client: IClient) {
console.log("Adding client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);

if (existingClient == undefined) {
this.localClients.push(client);
this.updateClientsState();
console.log("Client added")
} else {
console.log("Client already exists")
}
}

// Update data in local lists and update states
updateConnection(connection: IConnection) {
console.log("Updating connection...", connection)
const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);

if (existingConnection !== undefined) {
this.localConnections[this.localConnections.indexOf(existingConnection)] = connection;
this.updateConnectionsState();
console.log("Connection updated")
} else {
console.log("Connection does not exist")
}
}

updateChannel(channel: IChannel) {
console.log("Updating channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);

if (existingChannel !== undefined) {
this.localChannels[this.localChannels.indexOf(existingChannel)] = channel;
this.updateChannelsState();
console.log("Channel updated")
} else {
console.log("Channel does not exist")
}
}

updateClient(client: IClient) {
console.log("Updating client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);

if (existingClient !== undefined) {
this.localClients[this.localClients.indexOf(existingClient)] = client;
this.updateClientsState();
console.log("Client updated")
} else {
console.log("Client does not exist")
}
}

// Remove data from local lists and update states
removeConnection(connection: IConnection) {
console.log("Removing connection...", connection)
const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);

if (existingConnection !== undefined) {
this.localConnections.splice(this.localConnections.indexOf(existingConnection), 1);

// Remove all channels and clients associated with the connection
this.localChannels = this.localChannels.filter((localChannel: IChannel) => localChannel.connection.id !== connection.id);
this.localClients = this.localClients.filter((localClient: IClient) => localClient.channel?.connection.id !== connection.id);

this.updateChannelsState();
this.updateClientsState();
this.updateConnectionsState();
console.log("Connection removed")
} else {
console.log("Connection does not exist")
}
}

removeChannel(channel: IChannel) {
console.log("Removing channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);

if (existingChannel !== undefined) {
this.localChannels.splice(this.localChannels.indexOf(existingChannel), 1);

// Remove all clients associated with the channel
this.localClients = this.localClients.filter((localClient: IClient) => localClient.channel?.id !== channel.id);

this.updateClientsState();
this.updateChannelsState();
console.log("Channel removed")
} else {
console.log("Channel does not exist")
}
}

removeClient(client: IClient) {
console.log("Removing client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);

if (existingClient !== undefined) {
this.localClients.splice(this.localClients.indexOf(existingClient), 1);
this.updateClientsState();
console.log("Client removed")
} else {
console.log("Client does not exist")
}
}

// Helper functions
getConnectionById(id: number): IConnection | undefined {
return this.localConnections.find((connection: IConnection) => connection.id === id);
}

getChannelById(id: number, connectionId: number): IChannel | undefined {
return this.localChannels.find((channel: IChannel) => channel.id === id && channel.connection?.id === connectionId);
}

getClientById(id: number, connectionId: number): IClient | undefined {
return this.localClients.find((client: IClient) => client.id === id && client.channel?.connection.id === connectionId);
}
}
Loading

0 comments on commit c6289dd

Please sign in to comment.