diff --git a/freechat-client/main.js b/freechat-client/main.js index 41f1712..3a77b5d 100644 --- a/freechat-client/main.js +++ b/freechat-client/main.js @@ -2,6 +2,8 @@ const { ipcMain } = require('electron') const { app, BrowserWindow } = require('electron') const { ArgumentParser } = require('argparse') const path = require('path') +const URL = require('url') +const WebSocket = require('ws') const { Config, from_cli_parser } = require('./src/config.js') const { Cache } = require('./src/cache') @@ -18,6 +20,8 @@ const arguments = (() => { let win let config = from_cli_parser(arguments) let cache = new Cache() +let wsclient = null + // NOTE: this line is only here for demonstration as the current make script exposes this var // so that we can test against self signed certificates but still use ssl @@ -72,3 +76,54 @@ ipcMain.handle('config-update', async function(event, updated_config) { await config.write_disk() }) + +ipcMain.handle('open-socket', async function(event, kwargs ) { + const server_conf = kwargs['conf'] + const conn_url = kwargs['url'] + + const cache_target = server_conf['wsurl'] + + // shutdown the websocket incase there is already one running + if(wsclient != null) { + console.log("Live websocket found, closing for replacement") + wsclient.close() + wsclient = null + } + + wsclient = new WebSocket(conn_url) + + // Setting some listeners for the websocket + wsclient.on('open', function() { + cache.set_active(cache_target) + }) + wsclient.on('close', function(code, reason) { + console.log("Websocket close event: ",code, reason) + }) + wsclient.on('error', function(error) { + console.log('Websocket runtime error: ', error) + }) + wsclient.on('message', function(message) { + try { + cache.new_message(cache_target, message) + } catch (err) { + console.log(err) + } + }) +}) + +ipcMain.handle('cache-new-server', function(event, kwargs) { + const serv_config = kwargs['cfg'] + const channels = kwargs['channels'] + const messages = kwargs['messages'] + // optional flag which tells the cache to set this as the active server + // NOTE: Must also be the wsurl + const active_target = kwargs['active_target'] + + cache.add_server(active_target, serv_config, channels, messages) + if(active_target) { + if(active_target != serv_config['wsurl']) { + throw 'Active target must be the same as the target\'s web-socket URL' + } + cache.set_active(active_target) + } +}) diff --git a/freechat-client/src/cache.ts b/freechat-client/src/cache.ts index 7409cb1..41bab01 100644 --- a/freechat-client/src/cache.ts +++ b/freechat-client/src/cache.ts @@ -1,14 +1,18 @@ +import * as EventEmitter from 'events'; // unused for now but maybe useful later import { ServerConfig, Message, Channel } from './types'; +import { Event } from './events'; + class ServerCache { channels: Array - // Channel id's are used to key into the list of messages for that given channel - messages: Map> + // Channel-id => Array + messages: Map> config: ServerConfig constructor(config: ServerConfig, channels: Array, messages: Array) { this.config = config this.channels = channels + this.messages = new Map() for(const message of messages) { const ref = this.messages.get(message.cid) @@ -18,45 +22,45 @@ class ServerCache { ref.push(message) } } + console.log('initial message cache: ', this.messages) } push_message(message: Message) { let ref = this.messages.get(message.cid) if(ref) { - ref.push(message) + ref.push(message) // NOTE: this one here? + console.log('pushed') } else { this.messages.set(message.cid, [message]) + console.log('set') } + console.log('message cache state: ', this.messages.get(message.cid)) + console.log('message: ', message) } } -export class Cache { +export class Cache extends EventEmitter { // hostname -> Cache - servers: Map + private servers: Map + public active_host: String constructor() { + super() this.servers = new Map() + this.active_host = null } - add_server(hostname: String, servercfg: ServerConfig, channels: Array, messages: Array) { + add_server(url: String, servercfg: ServerConfig, channels: Array, messages: Array) { let new_entry = new ServerCache(servercfg, channels, messages) - this.servers.set(servercfg.url, new_entry) - } - - add_message(hostname: String, message: Message) { - let ref = this.servers.get(hostname) - if(!ref) { - throw `No server config found for ${hostname}` - } - - ref.push_message(message) + this.servers.set(url, new_entry) + console.log(this.servers) } update_channels(hostname: String, channels: Array) { this.servers.get(hostname).channels = channels } - last_id(hostname: String, channel_id: Number) : Number|null { + last_id(hostname: String, channel_id: BigInt) : BigInt|null { try { let ref = this.servers.get(hostname).messages.get(channel_id) return ref[ref.length - 1].cid @@ -64,5 +68,24 @@ export class Cache { return null } } + + new_message(origin: String, message: string): void { + // we assume at this point that we have the hostname contained smoewhere + let ref = this.servers.get(origin) + if(ref) { + let event = new Event(message) + ref.push_message(event.message) + } else { + console.error(`No server with ${origin} as hostname`) + console.error('Active host: ', this.active_host) + console.error('Servers: ', this.servers) + } + } + + set_active(url: String) { + this.active_host = url + } } + + diff --git a/freechat-client/src/events.ts b/freechat-client/src/events.ts new file mode 100644 index 0000000..a5278d1 --- /dev/null +++ b/freechat-client/src/events.ts @@ -0,0 +1,63 @@ +import * as JSONBig from 'json-bigint'; +import { Channel, Message } from './types'; + +export class Event { + public type: string + public message: Message|null + // NOTE: these two fields may benefit from have a more legible property wrapping + // them, cost would be pretty minimal(probably but idrk) + public d_channel: Channel|null + public c_channel: Channel|null + + + + constructor(raw: string) { + const parsed = JSONBig.parse(raw) + this.type = parsed['type'] + + switch(this.type) { + case 'new-message' : { + this.message = this._message(parsed[this.type]) + this.d_channel = null + this.c_channel = null + break + } + case 'delete-channel' : { + this.d_channel = null + // todo + break + } + case 'create-channel' : { + this.c_channel = null + // todo + break + } + default: { + this.message = null + this.d_channel = null + this.c_channel = null + } + } + } + + // Attempts to setup a new Message object in the message field + private _message(obj: Object): Message|null { + // NOTE: this takes data from the network directly so its important + // to keep in mind the "user-friendly" field names + let uid = obj['author_id'] + let cid = obj['channel_id'] + let content = obj['content'] + let content_type = obj['content_type'] + let mid = obj['id'] + let uname = obj['name'] + let time = obj['time'] + + return new Message(mid, time, content, content_type, uid, uname, cid) + } + + // Sets up the inner channel data + private _channel(obj: Object) : Channel|null { + // todo: still require some testing to verify event data is appropriate + return null + } +}