diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4660034a..039946ab 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,13 +16,11 @@ updates: # Maintain dependencies for npm - package-ecosystem: "npm" - directory: "/gui_vue" + directory: "/gui" schedule: interval: "daily" target-branch: "ls-gui-single-pager" - - # Maintain dependencies for pip - package-ecosystem: "pip" directory: "/" diff --git a/.gitignore b/.gitignore index 77eda681..ce0407d4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,9 @@ coverage.sh coverage.xml #ignore node_modules -/gui_vue/node_modules/ +/gui/node_modules/ -#Ignore gui_vue build items -/gui_vue/dist -/gui_vue/release -/gui_vue/dist-electron \ No newline at end of file +#Ignore gui build items +/gui/dist +/gui/release +/gui/dist-electron \ No newline at end of file diff --git a/gui_vue/.eslintrc.json b/gui/.eslintrc.json similarity index 100% rename from gui_vue/.eslintrc.json rename to gui/.eslintrc.json diff --git a/gui/.gitignore b/gui/.gitignore deleted file mode 100644 index 67045665..00000000 --- a/gui/.gitignore +++ /dev/null @@ -1,104 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port diff --git a/gui_vue/README.md b/gui/README.md similarity index 100% rename from gui_vue/README.md rename to gui/README.md diff --git a/gui/daemon.js b/gui/daemon.js deleted file mode 100644 index 779b25f9..00000000 --- a/gui/daemon.js +++ /dev/null @@ -1,347 +0,0 @@ -var net = require("net"); -const path = require("path"); -const { ipcRenderer } = require("electron"); -const log = require("electron-log"); -const daemonLog = log.scope("daemon"); - -// https://stackoverflow.com/a/26227660 -var appDataFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Application Support" - : process.env.HOME + "/.config"); -var configFolder = path.join(appDataFolder, "FreeDATA"); -var configPath = path.join(configFolder, "config.json"); -const config = require(configPath); - -var daemon = new net.Socket(); -var socketchunk = ""; // Current message, per connection. - -// global to keep track of daemon connection error emissions -var daemonShowConnectStateError = 1; - -// global for storing ip information -var daemon_port = config.daemon_port; -var daemon_host = config.daemon_host; - -setTimeout(connectDAEMON, 500); - -function connectDAEMON() { - if (daemonShowConnectStateError == 1) { - daemonLog.info("connecting to daemon"); - } - - //clear message buffer after reconnecting or initial connection - socketchunk = ""; - - daemon.connect(config.daemon_port, config.daemon_host); - - //client.setTimeout(5000); -} - -daemon.on("connect", function (err) { - daemonLog.info("daemon connection established"); - let Data = { - daemon_connection: daemon.readyState, - }; - ipcRenderer.send("request-update-daemon-connection", Data); - - daemonShowConnectStateError = 1; -}); - -daemon.on("error", function (err) { - if (daemonShowConnectStateError == 1) { - daemonLog.error("daemon connection error"); - daemonLog.info("Make sure the daemon is started."); - daemonLog.info('Run "python daemon.py" in the tnc directory.'); - - daemonShowConnectStateError = 0; - } - setTimeout(connectDAEMON, 500); - daemon.destroy(); - let Data = { - daemon_connection: daemon.readyState, - }; - ipcRenderer.send("request-update-daemon-connection", Data); -}); - -/* -client.on('close', function(data) { - console.log(' TNC connection closed'); - setTimeout(connectTNC, 2000) - let Data = { - daemon_connection: daemon.readyState, - }; - ipcRenderer.send('request-update-daemon-connection', Data); -}); -*/ - -daemon.on("end", function (data) { - daemonLog.warn("daemon connection ended"); - daemon.destroy(); - setTimeout(connectDAEMON, 500); - let Data = { - daemon_connection: daemon.readyState, - }; - ipcRenderer.send("request-update-daemon-connection", Data); -}); - -//exports.writeCommand = function(command){ -writeDaemonCommand = function (command) { - // we use the writingCommand function to update our TCPIP state because we are calling this function a lot - // if socket opened, we are able to run commands - if (daemon.readyState == "open") { - //uiMain.setDAEMONconnection('open') - daemon.write(command + "\n"); - } - - if (daemon.readyState == "closed") { - //uiMain.setDAEMONconnection('closed') - } - - if (daemon.readyState == "opening") { - //uiMain.setDAEMONconnection('opening') - } - - let Data = { - daemon_connection: daemon.readyState, - }; - ipcRenderer.send("request-update-daemon-connection", Data); -}; - -// "https://stackoverflow.com/questions/9070700/nodejs-net-createserver-large-amount-of-data-coming-in" - -daemon.on("data", function (socketdata) { - /* - inspired by: - stackoverflow.com questions 9070700 nodejs-net-createserver-large-amount-of-data-coming-in - */ - - socketdata = socketdata.toString("utf8"); // convert data to string - socketchunk += socketdata; // append data to buffer so we can stick long data together - - // check if we received begin and end of json data - if (socketchunk.startsWith('{"') && socketchunk.endsWith('"}\n')) { - var data = ""; - - // split data into chunks if we received multiple commands - socketchunk = socketchunk.split("\n"); - data = JSON.parse(socketchunk[0]); - - // search for empty entries in socketchunk and remove them - for (i = 0; i < socketchunk.length; i++) { - if (socketchunk[i] === "") { - socketchunk.splice(i, 1); - } - } - - //iterate through socketchunks array to execute multiple commands in row - for (i = 0; i < socketchunk.length; i++) { - //check if data is not empty - if (socketchunk[i].length > 0) { - //try to parse JSON - try { - data = JSON.parse(socketchunk[i]); - } catch (e) { - console.log(e); // "SyntaxError - daemonLog.error(e); - daemonLog.debug(socketchunk[i]); - socketchunk = ""; - } - } - - if (data["command"] == "daemon_state") { - let Data = { - input_devices: data["input_devices"], - output_devices: data["output_devices"], - python_version: data["python_version"], - hamlib_version: data["hamlib_version"], - serial_devices: data["serial_devices"], - tnc_running_state: data["daemon_state"][0]["status"], - ram_usage: data["ram"], - cpu_usage: data["cpu"], - version: data["version"], - }; - ipcRenderer.send("request-update-daemon-state", Data); - } - - if (data["command"] == "test_hamlib") { - let Data = { - hamlib_result: data["result"], - }; - ipcRenderer.send("request-update-hamlib-test", Data); - } - } - - //finally delete message buffer - socketchunk = ""; - } -}); - -function hexToBytes(hex) { - for (var bytes = [], c = 0; c < hex.length; c += 2) - bytes.push(parseInt(hex.substr(c, 2), 16)); - return bytes; -} - -exports.getDaemonState = function () { - //function getDaemonState(){ - command = '{"type" : "get", "command" : "daemon_state"}'; - writeDaemonCommand(command); -}; - -// START TNC -// ` `== multi line string - -exports.startTNC = function ( - mycall, - mygrid, - rx_audio, - tx_audio, - radiocontrol, - devicename, - deviceport, - pttprotocol, - pttport, - serialspeed, - data_bits, - stop_bits, - handshake, - rigctld_ip, - rigctld_port, - enable_fft, - enable_scatter, - low_bandwidth_mode, - tuning_range_fmin, - tuning_range_fmax, - enable_fsk, - tx_audio_level, - respond_to_cq, - rx_buffer_size, - enable_explorer, - explorer_stats, - auto_tune, - tx_delay, - tci_ip, - tci_port, - enable_mesh, -) { - var json_command = JSON.stringify({ - type: "set", - command: "start_tnc", - parameter: [ - { - mycall: mycall, - mygrid: mygrid, - rx_audio: rx_audio, - tx_audio: tx_audio, - radiocontrol: radiocontrol, - devicename: devicename, - deviceport: deviceport, - pttprotocol: pttprotocol, - pttport: pttport, - serialspeed: serialspeed, - data_bits: data_bits, - stop_bits: stop_bits, - handshake: handshake, - rigctld_port: rigctld_port, - rigctld_ip: rigctld_ip, - enable_scatter: enable_scatter, - enable_fft: enable_fft, - enable_fsk: enable_fsk, - low_bandwidth_mode: low_bandwidth_mode, - tuning_range_fmin: tuning_range_fmin, - tuning_range_fmax: tuning_range_fmax, - tx_audio_level: tx_audio_level, - respond_to_cq: respond_to_cq, - rx_buffer_size: rx_buffer_size, - enable_explorer: enable_explorer, - enable_stats: explorer_stats, - enable_auto_tune: auto_tune, - tx_delay: tx_delay, - tci_ip: tci_ip, - tci_port: tci_port, - enable_mesh: enable_mesh, - }, - ], - }); - - daemonLog.debug(json_command); - writeDaemonCommand(json_command); -}; - -// STOP TNC -exports.stopTNC = function () { - command = '{"type" : "set", "command": "stop_tnc" , "parameter": "---" }'; - writeDaemonCommand(command); -}; - -// TEST HAMLIB -exports.testHamlib = function ( - radiocontrol, - devicename, - deviceport, - serialspeed, - pttprotocol, - pttport, - data_bits, - stop_bits, - handshake, - rigctld_ip, - rigctld_port, -) { - var json_command = JSON.stringify({ - type: "get", - command: "test_hamlib", - parameter: [ - { - radiocontrol: radiocontrol, - devicename: devicename, - deviceport: deviceport, - pttprotocol: pttprotocol, - pttport: pttport, - serialspeed: serialspeed, - data_bits: data_bits, - stop_bits: stop_bits, - handshake: handshake, - rigctld_port: rigctld_port, - rigctld_ip: rigctld_ip, - }, - ], - }); - daemonLog.debug(json_command); - writeDaemonCommand(json_command); -}; - -//Save myCall -exports.saveMyCall = function (callsign) { - command = - '{"type" : "set", "command": "mycallsign" , "parameter": "' + - callsign + - '"}'; - writeDaemonCommand(command); -}; - -// Save myGrid -exports.saveMyGrid = function (grid) { - command = - '{"type" : "set", "command": "mygrid" , "parameter": "' + grid + '"}'; - writeDaemonCommand(command); -}; - -ipcRenderer.on("action-update-daemon-ip", (event, arg) => { - daemon.destroy(); - let Data = { - busy_state: "-", - arq_state: "-", - //channel_state: "-", - frequency: "-", - mode: "-", - bandwidth: "-", - dbfs_level: 0, - }; - ipcRenderer.send("request-update-tnc-state", Data); - daemon_port = arg.port; - daemon_host = arg.adress; - connectDAEMON(); -}); diff --git a/gui_vue/electron-builder.json5 b/gui/electron-builder.json5 similarity index 100% rename from gui_vue/electron-builder.json5 rename to gui/electron-builder.json5 diff --git a/gui_vue/electron/electron-env.d.ts b/gui/electron/electron-env.d.ts similarity index 100% rename from gui_vue/electron/electron-env.d.ts rename to gui/electron/electron-env.d.ts diff --git a/gui_vue/electron/main/index.ts b/gui/electron/main/index.ts similarity index 100% rename from gui_vue/electron/main/index.ts rename to gui/electron/main/index.ts diff --git a/gui_vue/electron/preload/index.ts b/gui/electron/preload/index.ts similarity index 100% rename from gui_vue/electron/preload/index.ts rename to gui/electron/preload/index.ts diff --git a/gui/freedata.js b/gui/freedata.js deleted file mode 100644 index 6b02a8d2..00000000 --- a/gui/freedata.js +++ /dev/null @@ -1,37 +0,0 @@ -const fs = require("fs"); -const { ipcRenderer } = require("electron"); - -/** - * Save config and update config setting globally - * @param {string} config - config data - * @param {string} configPath - */ -exports.saveConfig = function (config, configPath) { - fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); - ipcRenderer.send("set-config-global", config); -}; - -/** - * Binary to ASCII replacement - * @param {string} data in normal/usual utf-8 format - * @returns base64 encoded string - */ -exports.btoa_FD = function (data) { - return Buffer.from(data, "utf-8").toString("base64"); -}; -/** - * ASCII to Binary replacement - * @param {string} data in base64 encoding - * @returns utf-8 normal/usual string - */ -exports.atob_FD = function (data) { - return Buffer.from(data, "base64").toString("utf-8"); -}; -/** - * UTF8 to ASCII btoa - * @param {string} data in base64 encoding - * @returns base64 bota compatible data for use in browser - */ -exports.atob = function (data) { - return window.btoa(Buffer.from(data, "base64").toString("utf8")); -}; diff --git a/gui_vue/index.html b/gui/index.html similarity index 100% rename from gui_vue/index.html rename to gui/index.html diff --git a/gui/main.js b/gui/main.js deleted file mode 100644 index 4ca90f81..00000000 --- a/gui/main.js +++ /dev/null @@ -1,998 +0,0 @@ -const { app, BrowserWindow, ipcMain, dialog, shell } = require("electron"); -const https = require("https"); -const { autoUpdater } = require("electron-updater"); -const path = require("path"); -const fs = require("fs"); -const os = require("os"); -const spawn = require("child_process").spawn; - -const log = require("electron-log"); -const mainLog = log.scope("main"); -const daemonProcessLog = log.scope("freedata-daemon"); -const mime = require("mime"); -const net = require("net"); -const FD = require("./freedata"); - -//Useful for debugging event emitter memory leaks -//require('events').EventEmitter.defaultMaxListeners = 10; -//process.traceProcessWarnings=true; - -const sysInfo = log.scope("system information"); -sysInfo.info("SYSTEM INFORMATION ----------------------------- "); -sysInfo.info("APP VERSION : " + app.getVersion()); -sysInfo.info("PLATFORM : " + os.platform()); -sysInfo.info("ARCHITECTURE: " + os.arch()); -sysInfo.info("FREE MEMORY: " + os.freemem()); -sysInfo.info("TOTAL MEMORY: " + os.totalmem()); -sysInfo.info("LOAD AVG : " + os.loadavg()); -sysInfo.info("RELEASE : " + os.release()); -sysInfo.info("TYPE : " + os.type()); -sysInfo.info("VERSION : " + os.version()); -sysInfo.info("UPTIME : " + os.uptime()); - -app.setName("FreeDATA"); - -var appDataFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Application Support" - : process.env.HOME + "/.config"); -var configFolder = path.join(appDataFolder, "FreeDATA"); -var configPath = path.join(configFolder, "config.json"); - -// create config folder if not exists -if (!fs.existsSync(configFolder)) { - fs.mkdirSync(configFolder); -} - -// create config file if not exists with defaults -const configDefaultSettings = - '{\ - "tnc_host": "127.0.0.1",\ - "tnc_port": "3000",\ - "daemon_host": "127.0.0.1",\ - "daemon_port": "3001",\ - "mycall": "AA0AA-0",\ - "mygrid": "JN40aa",\ - "radiocontrol" : "disabled",\ - "hamlib_deviceid": "RIG_MODEL_DUMMY_NOVFO",\ - "hamlib_deviceport": "ignore",\ - "hamlib_stop_bits": "ignore",\ - "hamlib_data_bits": "ignore",\ - "hamlib_handshake": "ignore",\ - "hamlib_serialspeed": "ignore",\ - "hamlib_dtrstate": "ignore",\ - "hamlib_pttprotocol": "ignore",\ - "hamlib_ptt_port": "ignore",\ - "hamlib_dcd": "ignore",\ - "hamlbib_serialspeed_ptt": "9600",\ - "hamlib_rigctld_port" : "4532",\ - "hamlib_rigctld_ip" : "127.0.0.1",\ - "hamlib_rigctld_path" : "",\ - "hamlib_rigctld_server_port" : "4532",\ - "hamlib_rigctld_custom_args": "",\ - "tci_port" : "50001",\ - "tci_ip" : "127.0.0.1",\ - "spectrum": "waterfall",\ - "tnclocation": "localhost",\ - "enable_scatter" : "False",\ - "enable_fft" : "False",\ - "enable_fsk" : "False",\ - "low_bandwidth_mode" : "False",\ - "theme" : "default",\ - "screen_height" : 430,\ - "screen_width" : 1050,\ - "update_channel" : "latest",\ - "beacon_interval" : 300,\ - "received_files_folder" : "None",\ - "tuning_range_fmin" : "-50.0",\ - "tuning_range_fmax" : "50.0",\ - "respond_to_cq" : "True",\ - "rx_buffer_size" : "16", \ - "enable_explorer" : "False", \ - "wftheme": 2, \ - "high_graphics" : "True",\ - "explorer_stats" : "False", \ - "auto_tune" : "False", \ - "enable_is_writing" : "True", \ - "shared_folder_path" : ".", \ - "enable_request_profile" : "True", \ - "enable_request_shared_folder" : "False", \ - "max_retry_attempts" : 5, \ - "enable_auto_retry" : "False", \ - "tx_delay" : 0, \ - "auto_start": 0, \ - "enable_sys_notification": 1, \ - "enable_mesh_features": "False" \ - }'; - -if (!fs.existsSync(configPath)) { - fs.writeFileSync(configPath, configDefaultSettings); -} - -// load settings -var config = require(configPath); - -//config validation -// check running config against default config. -// if parameter not exists, add it to running config to prevent errors -sysInfo.info("CONFIG VALIDATION ----------------------------- "); - -var parsedConfig = JSON.parse(configDefaultSettings); -for (key in parsedConfig) { - if (config.hasOwnProperty(key)) { - sysInfo.info("FOUND SETTTING [" + key + "]: " + config[key]); - } else { - sysInfo.error("MISSING SETTTING [" + key + "] : " + parsedConfig[key]); - config[key] = parsedConfig[key]; - fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); - } -} -sysInfo.info("------------------------------------------ "); -/* -var chatDB = path.join(configFolder, 'chatDB.json') -// create chat database file if not exists -const configContentChatDB = ` -{ "chatDB" : [{ - "id" : "00000000", - "timestamp" : 1234566, - "mycall" : "AA0AA", - "dxcall" : "AB0AB", - "dxgrid" : "JN1200", - "message" : "hallowelt" -}] -} -`; -if (!fs.existsSync(chatDB)) { - fs.writeFileSync(chatDB, configContentChatDB); -} -*/ - -/* -// Creates receivedFiles folder if not exists -// https://stackoverflow.com/a/26227660 -var appDataFolder = process.env.HOME -var applicationFolder = path.join(appDataFolder, "FreeDATA"); -var receivedFilesFolder = path.join(applicationFolder, "receivedFiles"); - -// https://stackoverflow.com/a/13544465 -fs.mkdir(receivedFilesFolder, { - recursive: true -}, function(err) { - console.log(err); -}); - -*/ - -let win = null; -let logViewer = null; -var daemonProcess = null; - -// create a splash screen -function createSplashScreen() { - splashScreen = new BrowserWindow({ - height: 250, - width: 250, - transparent: true, - frame: false, - alwaysOnTop: true, - }); - splashScreen.loadFile("src/splash.html"); - splashScreen.center(); -} - -function createWindow() { - win = new BrowserWindow({ - width: config.screen_width, - height: config.screen_height, - show: false, - autoHideMenuBar: true, - icon: "src/img/icon.png", - webPreferences: { - //preload: path.join(__dirname, 'preload-main.js'), - backgroundThrottle: false, - preload: require.resolve("./preload-main.js"), - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: false, - sandbox: false, - //https://stackoverflow.com/questions/53390798/opening-new-window-electron/53393655 - //https://github.com/electron/remote - }, - }); - // hide menu bar - win.setMenuBarVisibility(false); - - //open dev tools - /*win.webContents.openDevTools({ - mode: 'undocked', - activate: true, - }) - */ - win.loadFile("src/index.html"); - - - // Emitted when the window is closed. - win.on("closed", function () { - console.log("closing all windows....."); - close_all(); - }); - - win.once("ready-to-show", () => { - log.transports.file.level = "debug"; - autoUpdater.logger = log.scope("updater"); - - autoUpdater.channel = config.update_channel; - - autoUpdater.autoInstallOnAppQuit = false; - autoUpdater.autoDownload = true; - autoUpdater.checkForUpdatesAndNotify(); - //autoUpdater.quitAndInstall(); - }); - - -} - -app.whenReady().then(() => { - // show splash screen - createSplashScreen(); - - // create main window - createWindow(); - - // wait some time, then close splash screen and show main windows - setTimeout(function () { - splashScreen.close(); - win.show(); - }, 3000); - - //Generate daemon binary path - var daemonPath = ""; - switch (os.platform().toLowerCase()) { - case "darwin": - case "linux": - daemonPath = path.join(process.resourcesPath, "tnc", "freedata-daemon"); - - break; - case "win32": - case "win64": - daemonPath = path.join( - process.resourcesPath, - "tnc", - "freedata-daemon.exe", - ); - break; - default: - console.log("Unhandled OS Platform: ", os.platform()); - break; - } - - //Start daemon binary if it exists - if (fs.existsSync(daemonPath)) { - mainLog.info("Starting freedata-daemon binary"); - daemonProcess = spawn(daemonPath, [], { - cwd: path.join(daemonPath, ".."), - }); - // return process messages - daemonProcess.on("error", (err) => { - daemonProcessLog.error(`error when starting daemon: ${err}`); - }); - daemonProcess.on("message", (data) => { - daemonProcessLog.info(`${data}`); - }); - daemonProcess.stdout.on("data", (data) => { - daemonProcessLog.info(`${data}`); - }); - daemonProcess.stderr.on("data", (data) => { - daemonProcessLog.info(`${data}`); - let arg = { - entry: `${data}`, - }; - // send info to log only if log screen available - // it seems an error occurs when updating - if (logViewer !== null && logViewer !== "") { - try { - //logViewer.webContents.send("action-update-log", arg); - } catch (e) { - // empty for keeping error stuff silent - // this is important to avoid error messages if we are going to close the app while - // an logging information will be pushed to the logger - } - } - }); - daemonProcess.on("close", (code) => { - daemonProcessLog.warn(`daemonProcess exited with code ${code}`); - }); - } else { - daemonProcess = null; - daemonPath = null; - mainLog.info("Daemon binary doesn't exist--normal for dev environments."); - } - win.send("action-set-app-version", app.getVersion()); -}); - -app.on("activate", () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } -}); - -app.on("window-all-closed", () => { - close_all(); -}); - -// IPC HANDLER -//Update configuration globally -ipcMain.on("set-config-global", (event, data) => { - config = data; - win.webContents.send("update-config", config); - -}); - -//Show/update task bar/button progressbar -ipcMain.on("request-show-electron-progressbar", (event, data) => { - win.setProgressBar(data / 100); -}); - - -ipcMain.on("request-update-dbclean-spinner", () => { - //Turn off dbclean spinner - win.webContents.send("action-update-dbclean-spinner"); -}); - -// UPDATE TNC CONNECTION -ipcMain.on("request-update-tnc-ip", (event, data) => { - win.webContents.send("action-update-tnc-ip", data); -}); - -// UPDATE DAEMON CONNECTION -ipcMain.on("request-update-daemon-ip", (event, data) => { - win.webContents.send("action-update-daemon-ip", data); -}); - -ipcMain.on("request-update-tnc-state", (event, arg) => { - win.webContents.send("action-update-tnc-state", arg); - //meshViewer.send("action-update-mesh-table", arg); - //data.webContents.send('action-update-tnc-state', arg); -}); - -/* -ipcMain.on('request-update-data-state', (event, arg) => { - //win.webContents.send('action-update-data-state', arg); - //data.webContents.send('action-update-data-state', arg); -}); - -ipcMain.on('request-update-heard-stations', (event, arg) => { - win.webContents.send('action-update-heard-stations', arg); -}); -*/ -ipcMain.on("request-update-daemon-state", (event, arg) => { - win.webContents.send("action-update-daemon-state", arg); -}); - -ipcMain.on("request-update-hamlib-test", (event, arg) => { - win.webContents.send("action-update-hamlib-test", arg); -}); - -ipcMain.on("request-update-tnc-connection", (event, arg) => { - win.webContents.send("action-update-tnc-connection", arg); -}); - -ipcMain.on("request-update-daemon-connection", (event, arg) => { - win.webContents.send("action-update-daemon-connection", arg); -}); - -ipcMain.on("run-tnc-command", (event, arg) => { - win.webContents.send("run-tnc-command", arg); -}); - -ipcMain.on("tnc-fec-iswriting", (event, arg) => { - win.webContents.send("run-tnc-command-fec-iswriting"); -}); - -ipcMain.on("request-update-rx-buffer", (event, arg) => { - win.webContents.send("action-update-rx-buffer", arg); -}); - -/* -ipcMain.on('request-update-rx-msg-buffer', (event, arg) => { - win.webContents.send('action-update-rx-msg-buffer', arg); -}); -*/ -ipcMain.on("request-new-msg-received", (event, arg) => { - win.webContents.send("action-new-msg-received", arg); -}); -ipcMain.on("request-update-transmission-status", (event, arg) => { - //chat.webContents.send("action-update-transmission-status", arg); - win.webContents.send("action-update-transmission-status", arg); -}); - -ipcMain.on("request-update-reception-status", (event, arg) => { - win.webContents.send("action-update-reception-status", arg); - //chat.webContents.send("action-update-reception-status", arg); -}); - -//Called by main to query chat if there are new messages -ipcMain.on("request-update-unread-messages", () => { - //mainLog.info("Got request to check if chat has new messages") - win.webContents.send("action-update-unread-messages"); -}); -//Called by chat to notify main if there are new messages -ipcMain.on("request-update-unread-messages-main", (event, arg) => { - win.webContents.send("action-update-unread-messages-main", arg); - //mainLog.info("Received reply from chat and ?new messages = " +arg); -}); - -//Called by main to notify chat we should clean the DB -ipcMain.on("request-clean-db", () => { - win.webContents.send("action-clean-db"); -}); - - -//file selector -ipcMain.on("get-file-path", (event, data) => { - dialog - .showOpenDialog({ - defaultPath: path.join(__dirname, "../"), - buttonLabel: "Select File", - properties: ["openFile"], - }) - .then((filePaths) => { - if (filePaths.canceled == false) { - win.webContents.send(data.action, { path: filePaths }); - } - }); -}); - -//folder selector -ipcMain.on("get-folder-path", (event, data) => { - dialog - .showOpenDialog({ - defaultPath: path.join(__dirname, "../"), - buttonLabel: "Select folder", - properties: ["openDirectory"], - }) - .then((folderPaths) => { - if (folderPaths.canceled == false) { - win.webContents.send(data.action, { path: folderPaths }); - //win.webContents.send(data.action, { path: filePaths }); - } - }); -}); - -//open folder -ipcMain.on("open-folder", (event, data) => { - shell.showItemInFolder(data.path); -}); - -//select file -ipcMain.on("select-file", (event, data) => { - dialog - .showOpenDialog({ - defaultPath: path.join(__dirname, "../"), - buttonLabel: "Select file", - properties: ["openFile"], - }) - .then((filepath) => { - console.log(filepath.filePaths[0]); - - try { - //fs.readFile(filepath.filePaths[0], 'utf8', function (err, data) { - //Has to be binary - fs.readFile(filepath.filePaths[0], "binary", function (err, data) { - console.log(data.length); - - console.log(data); - - var filename = path.basename(filepath.filePaths[0]); - var mimeType = mime.getType(filename); - console.log(mimeType); - if (mimeType == "" || mimeType == null) { - mimeType = "plain/text"; - } - - win.webContents.send("return-selected-files", { - data: data, - mime: mimeType, - filename: filename, - }); - - }); - } catch (err) { - console.log(err); - } - }); -}); - -//select image file -ipcMain.on("select-user-image", (event, data) => { - dialog - .showOpenDialog({ - defaultPath: path.join(__dirname, "../"), - buttonLabel: "Select file", - properties: ["openFile"], - }) - .then((filepath) => { - console.log(filepath.filePaths[0]); - - try { - // read data as base64 which makes conversion to blob easier - fs.readFile(filepath.filePaths[0], "base64", function (err, data) { - var filename = path.basename(filepath.filePaths[0]); - var mimeType = mime.getType(filename); - - if (mimeType == "" || mimeType == null) { - mimeType = "plain/text"; - } - - win.webContents.send("return-select-user-image", { - data: data, - mime: mimeType, - filename: filename, - }); - - }); - } catch (err) { - console.log(err); - } - }); -}); - -// read files in folder - use case "shared folder" -ipcMain.on("read-files-in-folder", (event, data) => { - let fileList = []; - if (config["enable_request_shared_folder"].toLowerCase() == "false") { - //mainLog.info("Shared file folder is disable, not populating fileList"); - - win.webContents.send("return-shared-folder-files", { - files: fileList, - }); - - return; - } - let folder = data.folder; - let files = fs.readdirSync(folder); - console.log(folder); - console.log(files); - files.forEach((file) => { - try { - let filePath = folder + "/" + file; - if (fs.lstatSync(filePath).isFile()) { - let fileSizeInBytes = fs.statSync(filePath).size; - let extension = path.extname(filePath); - fileList.push({ - name: file, - extension: extension.substring(1), - size: fileSizeInBytes, - }); - } - } catch (err) { - console.log(err); - } - }); - - win.webContents.send("return-shared-folder-files", { - files: fileList, - }); - -}); - -//save file to folder -ipcMain.on("save-file-to-folder", (event, data) => { - console.log(data.file); - - dialog.showSaveDialog({ defaultPath: data.filename }).then((filepath) => { - console.log(filepath.filePath); - console.log(data.file); - - try { - let arraybuffer = Buffer.from(data.file, "base64").toString("utf-8"); - console.log(arraybuffer); - //Has to be binary - fs.writeFile( - filepath.filePath, - arraybuffer, - "binary", - function (err, data) {}, - ); - } catch (err) { - console.log(err); - } - }); -}); - -//tnc messages START -------------------------------------- - -// FEC iswriting received -ipcMain.on("request-show-fec-toast-iswriting", (event, data) => { - win.webContents.send("action-show-fec-toast-iswriting", data); - win.webContents.send("action-show-feciswriting", data); -}); - -// CQ TRANSMITTING -ipcMain.on("request-show-cq-toast-transmitting", (event, data) => { - win.webContents.send("action-show-cq-toast-transmitting", data); -}); - -// CQ RECEIVED -ipcMain.on("request-show-cq-toast-received", (event, data) => { - win.webContents.send("action-show-cq-toast-received", data); -}); - -// QRV TRANSMITTING -ipcMain.on("request-show-qrv-toast-transmitting", (event, data) => { - win.webContents.send("action-show-qrv-toast-transmitting", data); -}); - -// QRV RECEIVED -ipcMain.on("request-show-qrv-toast-received", (event, data) => { - win.webContents.send("action-show-qrv-toast-received", data); -}); - -// BEACON TRANSMITTING -ipcMain.on("request-show-beacon-toast-transmitting", (event, data) => { - win.webContents.send("action-show-beacon-toast-transmitting", data); -}); - -// BEACON RECEIVED -ipcMain.on("request-show-beacon-toast-received", (event, data) => { - win.webContents.send("action-show-beacon-toast-received", data); -}); - -// PING TRANSMITTING -ipcMain.on("request-show-ping-toast-transmitting", (event, data) => { - win.webContents.send("action-show-ping-toast-transmitting", data); -}); - -// PING RECEIVED -ipcMain.on("request-show-ping-toast-received", (event, data) => { - win.webContents.send("action-show-ping-toast-received", data); -}); - -// PING RECEIVED ACK -ipcMain.on("request-show-ping-toast-received-ack", (event, data) => { - win.webContents.send("action-show-ping-toast-received-ack", data); -}); - -// ARQ DATA CHANNEL OPENING -ipcMain.on("request-show-arq-toast-datachannel-opening", (event, data) => { - win.webContents.send("action-show-arq-toast-datachannel-opening", data); -}); - -// ARQ DATA CHANNEL WAITING -ipcMain.on("request-show-arq-toast-datachannel-waiting", (event, data) => { - win.webContents.send("action-show-arq-toast-datachannel-waiting", data); -}); - -// ARQ DATA CHANNEL OPEN -ipcMain.on("request-show-arq-toast-datachannel-opened", (event, data) => { - win.webContents.send("action-show-arq-toast-datachannel-opened", data); -}); - -// ARQ DATA RECEIVED OPENER -ipcMain.on( - "request-show-arq-toast-datachannel-received-opener", - (event, data) => { - win.webContents.send( - "action-show-arq-toast-datachannel-received-opener", - data, - ); - }, -); - -// ARQ TRANSMISSION FAILED -ipcMain.on("request-show-arq-toast-transmission-failed", (event, data) => { - win.webContents.send("action-show-arq-toast-transmission-failed", data); -}); - -// ARQ TRANSMISSION FAILED -ipcMain.on("request-show-arq-toast-transmission-failed-ver", (event, data) => { - win.webContents.send("action-show-arq-toast-transmission-failed-ver", data); -}); - -// ARQ TRANSMISSION RECEIVING -ipcMain.on("request-show-arq-toast-transmission-receiving", (event, data) => { - win.webContents.send("action-show-arq-toast-transmission-receiving", data); -}); - -// ARQ TRANSMISSION RECEIVED -ipcMain.on("request-show-arq-toast-transmission-received", (event, data) => { - win.webContents.send("action-show-arq-toast-transmission-received", data); -}); - -// ARQ TRANSMISSION TRANSMITTING -ipcMain.on( - "request-show-arq-toast-transmission-transmitting", - (event, data) => { - win.webContents.send( - "action-show-arq-toast-transmission-transmitting", - data, - ); - }, -); - -// ARQ TRANSMISSION TRANSMITTED -ipcMain.on("request-show-arq-toast-transmission-transmitted", (event, data) => { - win.webContents.send("action-show-arq-toast-transmission-transmitted", data); -}); - -// ARQ SESSION CONNECTING -ipcMain.on("request-show-arq-toast-session-connecting", (event, data) => { - win.webContents.send("action-show-arq-toast-session-connecting", data); -}); - -// ARQ SESSION WAITING -ipcMain.on("request-show-arq-toast-session-waiting", (event, data) => { - win.webContents.send("action-show-arq-toast-session-waiting", data); -}); - -// ARQ SESSION CONNECTED -ipcMain.on("request-show-arq-toast-session-connected", (event, data) => { - win.webContents.send("action-show-arq-toast-session-connected", data); -}); - -// ARQ SESSION CLOSE -ipcMain.on("request-show-arq-toast-session-close", (event, data) => { - win.webContents.send("action-show-arq-toast-session-close", data); -}); - -// ARQ SESSION FAILED -ipcMain.on("request-show-arq-toast-session-failed", (event, data) => { - win.webContents.send("action-show-arq-toast-session-failed", data); -}); - -//tnc messages END -------------------------------------- - -//restart and install udpate -ipcMain.on("request-restart-and-install", (event, data) => { - close_sub_processes(); - autoUpdater.quitAndInstall(); -}); - -// LISTENER FOR UPDATER EVENTS -autoUpdater.on("update-available", (info) => { - mainLog.info("update available"); - - let arg = { - status: "update-available", - info: info, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("update-not-available", (info) => { - mainLog.info("update not available"); - let arg = { - status: "update-not-available", - info: info, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("update-downloaded", (info) => { - mainLog.info("update downloaded"); - let arg = { - status: "update-downloaded", - info: info, - }; - win.webContents.send("action-updater", arg); - // we need to call this at this point. - // if an update is available and we are force closing the app - // the entire screen crashes... - //mainLog.info('quit application and install update'); - //autoUpdater.quitAndInstall(); -}); - -autoUpdater.on("checking-for-update", () => { - mainLog.info("checking for update"); - let arg = { - status: "checking-for-update", - version: app.getVersion(), - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("download-progress", (progress) => { - let arg = { - status: "download-progress", - progress: progress, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("error", (error) => { - mainLog.info("update error"); - let arg = { - status: "error", - progress: error, - }; - win.webContents.send("action-updater", arg); - mainLog.error("AUTO UPDATER : " + error); -}); - -function close_sub_processes() { - mainLog.warn("closing sub processes"); - - // closing the tnc binary if not closed when closing application and also our daemon which has been started by the gui - try { - if (daemonProcess != null) { - daemonProcess.kill(); - } - } catch (e) { - mainLog.error(e); - } - - mainLog.warn("closing tnc and daemon"); - try { - if (os.platform() == "win32" || os.platform() == "win64") { - spawn("Taskkill", ["/IM", "freedata-tnc.exe", "/F"]); - spawn("Taskkill", ["/IM", "freedata-daemon.exe", "/F"]); - } - - if (os.platform() == "linux") { - spawn("pkill", ["-9", "freedata-tnc"]); - spawn("pkill", ["-9", "freedata-daemon"]); - } - - if (os.platform() == "darwin") { - spawn("pkill", ["-9", "freedata-tnc"]); - spawn("pkill", ["-9", "freedata-daemon"]); - } - } catch (e) { - mainLog.error(e); - } -} - -function close_all() { - // function for closing the application with closing all used processes - - close_sub_processes(); - - mainLog.warn("quitting app"); - - win.destroy(); - - - app.quit(); -} - -// RUN RIGCTLD -ipcMain.on("request-start-rigctld", (event, data) => { - try { - let rigctld_proc = spawn(data.path, data.parameters, { - windowsVerbatimArguments: true, - }); - - rigctld_proc.on("exit", function (code) { - console.log("rigctld process exited with code " + code); - - // if rigctld crashes, error code is -2 - // then we are going to restart rigctld - // this "fixes" a problem with latest rigctld on raspberry pi - //if (code == -2){ - // setTimeout(ipcRenderer.send('request-start-rigctld', data), 500); - //} - //let rigctld_proc = spawn(data.path, data.parameters); - }); - } catch (e) { - console.log(e); - } - - /* - const rigctld = exec(data.path, data.parameters); - rigctld.stdout.on("data", data => { - console.log(`stdout: ${data}`); - }); - */ -}); - -// STOP RIGCTLD -ipcMain.on("request-stop-rigctld", (event, data) => { - mainLog.warn("closing rigctld"); - try { - if (os.platform() == "win32" || os.platform() == "win64") { - spawn("Taskkill", ["/IM", "rigctld.exe", "/F"]); - } - - if (os.platform() == "linux") { - spawn("pkill", ["-9", "rigctld"]); - } - - if (os.platform() == "darwin") { - spawn("pkill", ["-9", "rigctld"]); - } - } catch (e) { - mainLog.error(e); - } -}); - -// CHECK RIGCTLD CONNECTION -// create new socket so we are not reopening every time a new one -var rigctld_connection = new net.Socket(); -var rigctld_connection_state = false; -var rigctld_events_wired = false; - -ipcMain.on("request-check-rigctld", (event, data) => { - try { - let Data = { - state: "unknown", - }; - - if (!rigctld_connection_state) { - rigctld_connection = new net.Socket(); - rigctld_events_wired = false; - rigctld_connection.connect(data.port, data.ip); - } - - // Check if we have created a new socket object and attach listeners if not already created - if (typeof rigctld_connection != "undefined" && !rigctld_events_wired) { - rigctld_connection.on("connect", function () { - rigctld_events_wired = true; - mainLog.info("Starting rigctld event listeners"); - rigctld_connection_state = true; - Data["state"] = "Connected"; - Data["active"] = true; - if (win !== null && win !== "" && typeof win != "undefined") { - // try catch for being sure we have a clean app close - try { - win.webContents.send("action-check-rigctld", Data); - } catch (e) { - console.log(e); - } - } - }); - - rigctld_connection.on("error", function () { - rigctld_connection_state = false; - Data["state"] = "Not Connected"; - Data["active"] = false; - if (win !== null && win !== "" && typeof win != "undefined") { - // try catch for being sure we have a clean app close - try { - win.webContents.send("action-check-rigctld", Data); - } catch (e) { - console.log(e); - } - } - }); - - rigctld_connection.on("end", function () { - rigctld_connection_state = false; - }); - } - } catch (e) { - console.log(e); - } -}); - -function downloadJsonUrlToFile(url, callsignPath) { - // https://nodejs.org/api/https.html#httpsgetoptions-callback - - https - .get(url, (res) => { - //console.log("statusCode:", res.statusCode); - //console.log("headers:", res.headers); - - res.on("data", (d) => { - //console.log(d); - let json = JSON.parse(d); - fs.writeFileSync(callsignPath, JSON.stringify(json, null, 2)); - sysInfo.info( - "Download " + url + " return statuscode: " + res.statusCode, - ); - }); - }) - .on("error", (e) => { - console.error(e); - }); -} -function downloadCallsignReverseLookupData() { - sysInfo.info("Downloading callsigns.json"); - var callsignPath = path.join(configFolder, "callsigns.json"); - downloadJsonUrlToFile( - "https://api.freedata.app/callsign_lookup.php", - callsignPath, - ); -} - -downloadCallsignReverseLookupData(); diff --git a/gui/package.json b/gui/package.json index 45cd7b48..4deaaf3c 100644 --- a/gui/package.json +++ b/gui/package.json @@ -1,15 +1,17 @@ { "name": "FreeDATA", - "version": "0.10.2-alpha.1", "description": "FreeDATA ", - "main": "main.js", + "private": true, + "version": "0.11.0-alpha", + "main": "dist-electron/main/index.js", "scripts": { - "start": "electron .", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.0.0" + "start": "git pull && npm i && vite", + "dev": "vite", + "check" : "vue-tsc --noEmit", + "build": "vue-tsc --noEmit && vite build && electron-builder", + "preview": "vite preview", + "lint": "eslint --ext .js,.vue src", + "lint-fix": "eslint --ext .js,.vue --fix src" }, "repository": { "type": "git", @@ -28,9 +30,8 @@ }, "homepage": "https://freedata.app", "dependencies": { - "@electron/asar": "^3.2.4", - "@electron/osx-sign": "^1.0.4", - "@popperjs/core": "^2.11.8", + "@electron/notarize": "^2.1.0", + "@vueuse/electron": "^10.4.1", "blob-util": "^2.0.2", "bootstrap": "^5.3.1", "bootstrap-icons": "^1.10.5", @@ -38,73 +39,44 @@ "browser-image-compression": "^2.0.2", "chart.js": "^4.3.3", "chartjs-plugin-annotation": "^3.0.1", + "electron-builder-notarize": "^1.5.1", "electron-log": "^4.4.8", - "electron-updater": "^6.1.1", + "electron-updater": "^6.1.4", "emoji-picker-element": "^1.18.3", "emoji-picker-element-data": "^1.4.0", - "express-pouchdb": "^4.2.0", + "file-saver": "^2.0.5", "mime": "^3.0.0", + "pinia": "^2.1.6", "pouchdb": "^8.0.1", "pouchdb-browser": "^8.0.1", - "pouchdb-express-router": "^0.0.11", "pouchdb-find": "^8.0.1", - "pouchdb-replication": "^8.0.1", "pouchdb-upsert": "^2.2.0", "qth-locator": "^2.1.0", - "utf8": "^3.0.0", - "uuid": "^9.0.0" + "sass": "^1.66.1", + "socket.io": "^4.7.2", + "uuid": "^9.0.0", + "vue": "^3.3.4", + "vue-chartjs": "^5.2.0", + "vuemoji-picker": "^0.2.0" }, "devDependencies": { - "@electron/notarize": "^2.1.0", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@vitejs/plugin-vue": "^4.4.0", "electron": "^27.0.0", "electron-builder": "^24.6.3", - "electron-builder-notarize": "^1.5.1" - }, - "build": { - "productName": "FreeDATA", - "appId": "app.freedata", - "afterSign": "electron-builder-notarize", - "npmRebuild": "false", - "directories": { - "buildResources": "build", - "output": "dist" - }, - "mac": { - "target": [ - "default" - ], - "icon": "build/icon.png", - "hardenedRuntime": true, - "entitlements": "build/entitlements.plist", - "entitlementsInherit": "build/entitlements.plist", - "gatekeeperAssess": false - }, - "win": { - "icon": "build/icon.png", - "target": [ - "nsis" - ] - }, - "linux": { - "icon": "build/icon.png", - "target": [ - "AppImage" - ], - "category": "Development" - }, - "publish": { - "provider": "github", - "releaseType": "release" - }, - "extraResources": [ - { - "from": "../tnc/dist/tnc/", - "to": "tnc", - "filter": [ - "**/*", - "!**/.git" - ] - } - ] + "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-standard-with-typescript": "^39.1.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-n": "^16.1.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-vue": "^9.17.0", + "typescript": "^5.2.2", + "vite": "^4.3.2", + "vite-plugin-electron": "^0.14.0", + "vite-plugin-electron-renderer": "^0.14.5", + "vue": "^3.3.4", + "vue-tsc": "^1.4.2" } } diff --git a/gui/preload-chat.js b/gui/preload-chat.js deleted file mode 100644 index 4299bd86..00000000 --- a/gui/preload-chat.js +++ /dev/null @@ -1,2967 +0,0 @@ -const path = require("path"); -const { ipcRenderer } = require("electron"); -const { v4: uuidv4 } = require("uuid"); -const imageCompression = require("browser-image-compression"); -const blobUtil = require("blob-util"); -const FD = require("./freedata"); -const fs = require("fs"); - -// https://stackoverflow.com/a/26227660 -var appDataFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Application Support" - : process.env.HOME + "/.config"); -var configFolder = path.join(appDataFolder, "FreeDATA"); -var configPath = path.join(configFolder, "config.json"); -var config = require(configPath); -// set date format -const dateFormat = new Intl.DateTimeFormat(navigator.language, { - timeStyle: "long", - dateStyle: "short", -}); -// set date format information -const dateFormatShort = new Intl.DateTimeFormat(navigator.language, { - year: "numeric", - month: "numeric", - day: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - hour12: false, -}); - -const dateFormatHours = new Intl.DateTimeFormat(navigator.language, { - hour: "numeric", - minute: "numeric", - hour12: false, -}); -// split character -const split_char = "\0;\1;"; -// global for our selected file we want to transmit -// ----------------- some chat globals -var filetype = ""; -var file = ""; -var filename = ""; -var callsign_counter = 0; -var selected_callsign = ""; -var lastIsWritingBroadcast = new Date().getTime(); -var defaultUserIcon = - "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGVyc29uLWJvdW5kaW5nLWJveCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNMS41IDFhLjUuNSAwIDAgMC0uNS41djNhLjUuNSAwIDAgMS0xIDB2LTNBMS41IDEuNSAwIDAgMSAxLjUgMGgzYS41LjUgMCAwIDEgMCAxaC0zek0xMSAuNWEuNS41IDAgMCAxIC41LS41aDNBMS41IDEuNSAwIDAgMSAxNiAxLjV2M2EuNS41IDAgMCAxLTEgMHYtM2EuNS41IDAgMCAwLS41LS41aC0zYS41LjUgMCAwIDEtLjUtLjV6TS41IDExYS41LjUgMCAwIDEgLjUuNXYzYS41LjUgMCAwIDAgLjUuNWgzYS41LjUgMCAwIDEgMCAxaC0zQTEuNSAxLjUgMCAwIDEgMCAxNC41di0zYS41LjUgMCAwIDEgLjUtLjV6bTE1IDBhLjUuNSAwIDAgMSAuNS41djNhMS41IDEuNSAwIDAgMS0xLjUgMS41aC0zYS41LjUgMCAwIDEgMC0xaDNhLjUuNSAwIDAgMCAuNS0uNXYtM2EuNS41IDAgMCAxIC41LS41eiIvPgogIDxwYXRoIGQ9Ik0zIDE0cy0xIDAtMS0xIDEtNCA2LTQgNiAzIDYgNC0xIDEtMSAxSDN6bTgtOWEzIDMgMCAxIDEtNiAwIDMgMyAwIDAgMSA2IDB6Ii8+Cjwvc3ZnPg=="; -var defaultGroupIcon = - "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGVvcGxlLWZpbGwiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTcgMTRzLTEgMC0xLTEgMS00IDUtNCA1IDMgNSA0LTEgMS0xIDFIN1ptNC02YTMgMyAwIDEgMCAwLTYgMyAzIDAgMCAwIDAgNlptLTUuNzg0IDZBMi4yMzggMi4yMzggMCAwIDEgNSAxM2MwLTEuMzU1LjY4LTIuNzUgMS45MzYtMy43MkE2LjMyNSA2LjMyNSAwIDAgMCA1IDljLTQgMC01IDMtNSA0czEgMSAxIDFoNC4yMTZaTTQuNSA4YTIuNSAyLjUgMCAxIDAgMC01IDIuNSAyLjUgMCAwIDAgMCA1WiIvPgo8L3N2Zz4="; - -// ----------------------------------- -// Initially fill sharedFolderFileList -//TODO: Make this automatically ever N seconds -var sharedFolderFileList = ""; -ipcRenderer.send("read-files-in-folder", { - folder: config.shared_folder_path, -}); - -var chatDB = path.join(configFolder, "chatDB"); -var userDB = path.join(configFolder, "userDB"); -// ---- MessageDB -try { - var PouchDB = require("pouchdb"); -} catch (err) { - console.log(err); - - /* - This is a fix for raspberryPi where we get an error when loading pouchdb because of - leveldown package isnt running on ARM devices. - pouchdb-browser does not depend on leveldb and seems to be working. - */ - console.log("using pouchdb-browser fallback"); - var PouchDB = require("pouchdb-browser"); -} - -PouchDB.plugin(require("pouchdb-find")); -//PouchDB.plugin(require('pouchdb-replication')); -PouchDB.plugin(require("pouchdb-upsert")); - -var db = new PouchDB(chatDB); -var users = new PouchDB(userDB); - -/* -------- CREATE DATABASE INDEXES */ -createChatIndex(); -createUserIndex(); - -// REMOTE SYNC ATTEMPTS - -//var remoteDB = new PouchDB('http://172.20.10.4:5984/chatDB') - -/* - -// we need express packages for running pouchdb sync "express-pouchdb" -var express = require('express'); -var app = express(); -app.use('/', require('express-pouchdb')(PouchDB)); -app.listen(5984); -var db = new PouchDB(chatDB); - - -app.use('/chatDB', require('pouchdb-express-router')(PouchDB)); -app.listen(5984); - - - -db.sync('http://172.20.10.4:5984/jojo', { -//var sync = PouchDB.sync('chatDB', 'http://172.20.10.4:5984/chatDB', { - live: true, - retry: false -}).on('change', function (change) { - // yo, something changed! - console.log(change) -}).on('paused', function (err) { - // replication was paused, usually because of a lost connection - console.log(err) -}).on('active', function (info) { - // replication was resumed - console.log(info) -}).on('error', function (err) { - // totally unhandled error (shouldn't happen) - console.log(err) -}).on('denied', function (err) { - // a document failed to replicate (e.g. due to permissions) - console.log(err) -}).on('complete', function (info) { - // handle complete; - console.log(info) -}); -*/ - -var dxcallsigns = new Set(); - -//Set default chat filter -var chatFilter = [ - { type: "newchat" }, - { type: "received" }, - { type: "transmit" }, - { type: "ping-ack" }, - { type: "broadcast_received" }, - { type: "broadcast_transmit" }, - - //{ type: "request" }, - //{ type: "response" }, -]; - -// WINDOW LISTENER -window.addEventListener("DOMContentLoaded", () => { - updateAllChat(false); - // theme selector - changeGuiDesign(config.theme); - - const userInfoFields = [ - "user_info_image", - "user_info_callsign", - "user_info_gridsquare", - "user_info_name", - "user_info_age", - "user_info_location", - "user_info_radio", - "user_info_antenna", - "user_info_email", - "user_info_website", - "user_info_comments", - ]; - users - .find({ - selector: { - user_info_callsign: config.mycall, - }, - }) - .then(function (result) { - console.log(result); - if (typeof result.docs[0] !== "undefined") { - // handle result - userInfoFields.forEach(function (elem) { - if (elem !== "user_info_image") { - document.getElementById(elem).value = result.docs[0][elem]; - } else { - document.getElementById(elem).src = result.docs[0][elem]; - } - }); - } else { - console.log( - config.mycall + " not found in user db - creating new entry", - ); - // add initial entry for own callsign and grid - let obj = new Object(); - obj.user_info_callsign = config.mycall; - obj.user_info_gridsquare = config.mygrid; - addUserToDatabaseIfNotExists(obj); - - document.getElementById("user_info_callsign").value = config.mycall; - document.getElementById("user_info_gridsquare").value = config.mygrid; - } - }) - .catch(function (err) { - console.log(err); - }); - - //save user info - document.getElementById("userInfoSave").addEventListener("click", () => { - let obj = new Object(); - userInfoFields.forEach(function (subelem) { - if (subelem !== "user_info_image") { - obj[subelem] = document.getElementById(subelem).value; - } else { - obj[subelem] = document.getElementById(subelem).src; - } - }); - addUserToDatabaseIfNotExists(obj); - }); - - //Add event listener for filter apply button - document.getElementById("btnFilter").addEventListener("click", () => { - chatFilter.length = 0; - if (document.getElementById("chkMessage").checked == true) { - chatFilter = [{ type: "newchat" }]; - chatFilter.push( - { type: "received" }, - { type: "transmit" }, - { type: "broadcast_received" }, - { type: "broadcast_transmit" }, - ); - } - if (document.getElementById("chkPing").checked == true) - chatFilter.push({ type: "ping" }); - if (document.getElementById("chkPingAck").checked == true) - chatFilter.push({ type: "ping-ack" }); - if (document.getElementById("chkBeacon").checked == true) - chatFilter.push({ type: "beacon" }); - if (document.getElementById("chkRequest").checked == true) - chatFilter.push({ type: "request" }); - if (document.getElementById("chkResponse").checked == true) - chatFilter.push({ type: "response" }); - if (document.getElementById("chkNewMessage").checked == true) - chatFilter.push({ new: 1 }); - updateAllChat(true); - }); - - document - .querySelector("emoji-picker") - .addEventListener("emoji-click", (event) => { - var msg = document.getElementById("chatModuleMessage"); - //Convert to utf-8--so we can just use utf-8 everywhere - msg.setRangeText(event.detail.emoji.unicode.toString("utf-8")); - //console.log(event.detail); - //msg.focus(); - }); - document.getElementById("emojipickerbutton").addEventListener("click", () => { - var element = document.getElementById("emojipickercontainer"); - console.log(element.style.display); - if (element.style.display === "none") { - element.style.display = "block"; - } else { - element.style.display = "none"; - } - }); - - document - .getElementById("delete_selected_chat") - .addEventListener("click", () => { - db.find({ - selector: { - dxcallsign: selected_callsign, - }, - }) - .then(function (result) { - // handle result - if (typeof result !== "undefined") { - result.docs.forEach(function (item) { - console.log(item); - db.get(item._id) - .then(function (doc) { - db.remove(doc) - .then(function (doc) { - updateAllChat(true); - return true; - }) - .catch(function (err) { - console.log(err); - }); - }) - .catch(function (err) { - console.log(err); - }); - }); - } - }) - .catch(function (err) { - console.log(err); - }); - }); - document.getElementById("selectFilesButton").addEventListener("click", () => { - //document.getElementById('selectFiles').click(); - ipcRenderer.send("select-file", { - title: "Title", - }); - }); - - document.getElementById("requestUserInfo").addEventListener("click", () => { - ipcRenderer.send("run-tnc-command", { - command: "requestUserInfo", - dxcallsign: selected_callsign, - }); - - pauseButton(document.getElementById("requestUserInfo"), 60000); - }); - - document.getElementById("ping").addEventListener("click", () => { - ipcRenderer.send("run-tnc-command", { - command: "ping", - dxcallsign: selected_callsign, - }); - }); - - document.addEventListener("keyup", function (event) { - // Number 13 == Enter - if ( - event.keyCode === 13 && - !event.shiftKey && - document.activeElement.id == "chatModuleMessage" - ) { - // Cancel the default action, if needed - event.preventDefault(); - // Trigger the button element with a click - document.getElementById("sendMessage").click(); - } - }); - - // ADJUST TEXTAREA SIZE - document.getElementById("chatModuleMessage").addEventListener("input", () => { - var textarea = document.getElementById("chatModuleMessage"); - var text = textarea.value; - - if (document.getElementById("expand_textarea").checked) { - var lines = 6; - } else { - var lines = text.split("\n").length; - - if (lines >= 6) { - lines = 6; - } - } - var message_container_height_offset = 180 + 20 * lines; - var message_container_height = `calc(100% - ${message_container_height_offset}px)`; - document.getElementById("message-container").style.height = - message_container_height; - textarea.rows = lines; - - console.log(textarea.value); - if (lastIsWritingBroadcast < new Date().getTime() - 5 * 2000) { - //console.log("Sending FECIsWriting"); - console.log(config.enable_is_writing); - if (config.enable_is_writing == "True") { - ipcRenderer.send("tnc-fec-iswriting"); - } - lastIsWritingBroadcast = new Date().getTime(); - } - }); - - document.getElementById("expand_textarea").addEventListener("click", () => { - var textarea = document.getElementById("chatModuleMessage"); - - if (document.getElementById("expand_textarea").checked) { - var lines = 6; - document.getElementById("expand_textarea_button").className = - "bi bi-chevron-compact-down"; - } else { - var lines = 1; - document.getElementById("expand_textarea_button").className = - "bi bi-chevron-compact-up"; - } - - var message_container_height_offset = 180 + 20 * lines; - //var message_container_height_offset = 90 + (23*lines); - var message_container_height = `calc(100% - ${message_container_height_offset}px)`; - document.getElementById("message-container").style.height = - message_container_height; - textarea.rows = lines; - console.log(textarea.rows); - }); - - // NEW CHAT - - document - .getElementById("createNewChatButton") - .addEventListener("click", () => { - var dxcallsign = document.getElementById("chatModuleNewDxCall").value; - var uuid = uuidv4(); - db.post({ - _id: uuid, - timestamp: Math.floor(Date.now() / 1000), - dxcallsign: dxcallsign.toUpperCase(), - dxgrid: "---", - msg: "null", - checksum: "null", - type: "newchat", - status: "null", - uuid: uuid, - }) - .then(function (response) { - // handle response - console.log("new database entry"); - console.log(response); - }) - .catch(function (err) { - console.log(err); - }); - update_chat_obj_by_uuid(uuid); - }); - - // open file selector for user image - document.getElementById("userImageSelector").addEventListener("click", () => { - ipcRenderer.send("select-user-image", { - title: "Title", - }); - }); - - // open file selector for shared folder - document - .getElementById("sharedFolderButton") - .addEventListener("click", () => { - ipcRenderer.send("read-files-in-folder", { - folder: config.shared_folder_path, - }); - }); - - document - .getElementById("openSharedFilesFolder") - .addEventListener("click", () => { - ipcRenderer.send("open-folder", { - path: config.shared_folder_path, - }); - }); - - document - .getElementById("requestSharedFolderList") - .addEventListener("click", () => { - ipcRenderer.send("run-tnc-command", { - command: "requestSharedFolderList", - dxcallsign: selected_callsign, - }); - - pauseButton(document.getElementById("requestSharedFolderList"), 60000); - }); - - // SEND MSG - document.getElementById("sendMessage").addEventListener("click", () => { - document.getElementById("emojipickercontainer").style.display = "none"; - - var dxcallsign = selected_callsign.toUpperCase(); - var textarea = document.getElementById("chatModuleMessage"); - var chatmessage = textarea.value; - //Remove non-printable chars from begining and end of string--should save us a byte here and there - chatmessage = chatmessage.toString().trim(); - // reset textarea size - var message_container_height_offset = 200; - var message_container_height = `calc(100% - ${message_container_height_offset}px)`; - document.getElementById("message-container").style.height = - message_container_height; - textarea.rows = 1; - document.getElementById("expand_textarea_button").className = - "bi bi-chevron-compact-up"; - document.getElementById("expand_textarea").checked = false; - - //console.log(file); - //console.log(filename); - //console.log(filetype); - if (filetype == "") { - filetype = "plain/text"; - } - var timestamp = Math.floor(Date.now() / 1000); - - var uuid = uuidv4(); - let uuidlast = uuid.lastIndexOf("-"); - uuidlast += 1; - if (uuidlast > 0) { - uuid = uuid.substring(uuidlast); - } - - // check if broadcast - if (dxcallsign.startsWith("BC-")) { - //let broadcastChannelId = dxcallsign.split("BC-")[1]; - //broadcastChannelIdCRC = crc32(broadcastChannelId) - // .toString(16) - // .toUpperCase(); - //dxcallsignWithID = "BC-" + broadcastChannelIdCRC; - var tnc_command = "broadcast"; - var message_type = "broadcast_transmit"; - - // slice uuid for reducing overhead - uuid = uuid.slice(-4); - - let Data = { - command: tnc_command, - broadcastChannel: dxcallsign, - data: chatmessage, - uuid: uuid, - }; - ipcRenderer.send("run-tnc-command", Data); - } else { - var message_type = "transmit"; - var file_checksum = crc32(file).toString(16).toUpperCase(); - var tnc_command = "msg"; - var data_with_attachment = - timestamp + - split_char + - chatmessage + - split_char + - filename + - split_char + - filetype + - split_char + - file; - - document.getElementById("selectFilesButton").innerHTML = ``; - - console.log(data_with_attachment); - let Data = { - command: tnc_command, - dxcallsign: dxcallsign, - mode: 255, - frames: 5, - data: data_with_attachment, - checksum: file_checksum, - uuid: uuid, - }; - ipcRenderer.send("run-tnc-command", Data); - } - - db.post({ - _id: uuid, - timestamp: timestamp, - dxcallsign: dxcallsign, - dxgrid: "null", - msg: chatmessage, - checksum: file_checksum, - type: message_type, - status: "transmit", - attempt: 1, - uuid: uuid, - _attachments: { - [filename]: { - content_type: filetype, - //data: btoa(file) - data: FD.btoa_FD(file), - }, - }, - }) - .then(function (response) { - // handle response - console.log("new database entry"); - console.log(response); - }) - .catch(function (err) { - console.log(err); - }); - update_chat_obj_by_uuid(uuid); - - // clear input - document.getElementById("chatModuleMessage").value = ""; - - // after adding file data to our attachment variable, delete it from global - filetype = ""; - file = ""; - filename = ""; - }); - // cleanup after transmission - filetype = ""; - file = ""; - filename = ""; -}); - -ipcRenderer.on("return-selected-files", (event, arg) => { - filetype = arg.mime; - console.log(filetype); - - file = arg.data; - filename = arg.filename; - document.getElementById("selectFilesButton").innerHTML = ` - - New file selected - - `; -}); - -ipcRenderer.on("return-shared-folder-files", (event, arg) => { - console.log(arg); - sharedFolderFileList = arg.files; - - var tbl = document.getElementById("sharedFolderTable"); - if (tbl == undefined) return; - tbl.innerHTML = ""; - let counter = 0; - arg.files.forEach((file) => { - //console.log(file["name"]); - var row = document.createElement("tr"); - - let id = document.createElement("td"); - let idText = document.createElement("span"); - idText.innerText = counter += 1; - id.appendChild(idText); - row.appendChild(id); - - let filename = document.createElement("td"); - let filenameText = document.createElement("span"); - filenameText.innerText = file["name"]; - filename.appendChild(filenameText); - row.appendChild(filename); - - let filetype = document.createElement("td"); - let filetypeText = document.createElement("span"); - filetypeText.innerHTML = ` - - `; - filetype.appendChild(filetypeText); - row.appendChild(filetype); - - let filesize = document.createElement("td"); - let filesizeText = document.createElement("span"); - filesizeText.innerText = formatBytes(file["size"], 2); - filesize.appendChild(filesizeText); - row.appendChild(filesize); - - tbl.appendChild(row); - }); -}); - -ipcRenderer.on("return-select-user-image", (event, arg) => { - let imageFiletype = arg.mime; - let imageFile = arg.data; - - imageFile = blobUtil.base64StringToBlob(imageFile, imageFiletype); - - var options = { - maxSizeMB: 0.01, - maxWidthOrHeight: 125, - useWebWorker: false, - }; - - imageCompression(imageFile, options) - .then(function (compressedFile) { - console.log( - "compressedFile instanceof Blob", - compressedFile instanceof Blob, - ); // true - console.log( - `compressedFile size ${compressedFile.size / 1024 / 1024} MB`, - ); // smaller than maxSizeMB - - console.log(compressedFile.size); - - blobUtil - .blobToBase64String(compressedFile) - .then(function (base64String) { - // update image - - document.getElementById("user_info_image").src = - "data:" + imageFiletype + ";base64," + base64String; - }) - .catch(function (err) { - document.getElementById("user_info_image").src = "img/icon.png"; - }); - }) - .catch(function (error) { - console.log(error.message); - }); -}); - - - - -ipcRenderer.on("action-update-transmission-status", (event, arg) => { - var data = arg["data"][0]; - - if (data.status == "opening") return; - if (typeof data.uuid === undefined) return; - - //console.log(data.status); - if (data.uuid !== "no-uuid") { - db.get(data.uuid, { - attachments: true, - }) - .then(function (doc) { - return db.put({ - _id: doc.uuid.toString(), - _rev: doc._rev, - timestamp: doc.timestamp, - dxcallsign: doc.dxcallsign, - dxgrid: doc.dxgrid, - msg: doc.msg, - checksum: doc.checksum, - type: "transmit", - status: data.status, - percent: data.percent, - bytesperminute: data.bytesperminute, - uuid: doc.uuid, - _attachments: doc._attachments, - }); - }) - .then(function (response) { - update_chat_obj_by_uuid(data.uuid); - }) - .catch(function (err) { - console.log(err); - console.log(data); - }); - } -}); - -//Render is typing message in correct chat window -ipcRenderer.on("action-show-feciswriting", (event, arg) => { - //console.log("In action-show-feciswriting"); - //console.log(arg); - let uuid = uuidv4.toString(); - let dxcallsign = arg["data"][0]["dxcallsign"]; - var new_message = ` -
-

${dxcallsign} is typing....

- -
- `; - var id = "chat-" + dxcallsign; - let chatwin = document.getElementById(id); - if (chatwin == undefined) { - //console.log("Element not found!!!!! :("); - return; - } - chatwin.insertAdjacentHTML("beforeend", new_message); - scrollMessagesToBottom(); - let animIcon = document.getElementById("msg-" + uuid + "-icon"); - //Remove notification after about 4.5 seconds hopefully enough time before a second notification can come in - setTimeout(function () { - animIcon.classList = "m-1 bi bi-wifi-2"; - }, 1000); - setTimeout(function () { - animIcon.classList = "m-1 bi bi-wifi"; - }, 2000); - setTimeout(function () { - animIcon.classList = "m-1 bi bi-wifi-2"; - }, 3000); - setTimeout(function () { - animIcon.classList = "m-1 bi bi-wifi-1"; - }, 4000); - setTimeout(() => { - let feciw = document.getElementById("msg-" + uuid); - feciw.remove(); - }, 4500); -}); - -ipcRenderer.on("action-new-msg-received", (event, arg) => { - console.log(arg.data); - - var new_msg = arg.data; - new_msg.forEach(function (item) { - let obj = new Object(); - - //handle broadcast - if (item.fec == "broadcast") { - console.log("BROADCAST RECEIVED"); - console.log(item); - var transmitting_station = item.dxcallsign; - var encoded_data = FD.atob_FD(item.data); - var splitted_data = encoded_data.split(split_char); - console.log(splitted_data); - console.log(transmitting_station); - // add callsign to message: - var message = splitted_data[3]; - console.log(message); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = splitted_data[1]; - obj.dxgrid = "null"; - obj.uuid = splitted_data[2]; - obj.broadcast_sender = transmitting_station; - obj.command = "msg"; - obj.checksum = "null"; - obj.msg = message; - obj.status = "received"; - obj.snr = item.snr; - obj.type = "broadcast_received"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.new = 1; - console.log(obj); - add_obj_to_database(obj); - update_chat_obj_by_uuid(obj.uuid); - - db.find({ - selector: { - dxcallsign: obj.dxcallsign, - }, - }).then(function (result) { - // handle result - console.log(result); - }); - - //handle ping - } else if (item.ping == "received") { - obj.timestamp = parseInt(item.timestamp); - obj.dxcallsign = item.dxcallsign; - obj.dxgrid = item.dxgrid; - obj.uuid = item.uuid; - obj.command = "ping"; - obj.checksum = "null"; - obj.msg = "null"; - obj.status = item.status; - obj.hmac_signed = item.hmac_signed; - obj.snr = item.snr; - obj.type = "ping"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.new = 0; - - add_obj_to_database(obj); - update_chat_obj_by_uuid(obj.uuid); - // check for messages which failed and try to transmit them - if (config.enable_auto_retry.toUpperCase() == "TRUE") { - checkForWaitingMessages(obj.dxcallsign); - } - - // handle ping-ack - } else if (item.ping == "acknowledge") { - obj.timestamp = parseInt(item.timestamp); - obj.dxcallsign = item.dxcallsign; - obj.dxgrid = item.dxgrid; - obj.uuid = item.uuid; - obj.command = "ping-ack"; - obj.checksum = "null"; - obj.msg = "null"; - obj.status = item.status; - obj.snr = item.dxsnr + "/" + item.snr; - obj.type = "ping-ack"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.new = 0; - add_obj_to_database(obj); - update_chat_obj_by_uuid(obj.uuid); - - // handle beacon - } else if (item.beacon == "received") { - obj.timestamp = parseInt(item.timestamp); - obj.dxcallsign = item.dxcallsign; - obj.dxgrid = item.dxgrid; - obj.uuid = item.uuid; - obj.command = "beacon"; - obj.checksum = "null"; - obj.msg = "null"; - obj.status = item.status; - obj.snr = item.snr; - obj.type = "beacon"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.new = 0; - add_obj_to_database(obj); - update_chat_obj_by_uuid(obj.uuid); - // check for messages which failed and try to transmit them - if (config.enable_auto_retry.toUpperCase() == "TRUE") { - checkForWaitingMessages(obj.dxcallsign); - } - // handle ARQ transmission - } else if (item.arq == "transmission" && item.status == "received") { - //var encoded_data = atob(item.data); - //var encoded_data = Buffer.from(item.data,'base64').toString('utf-8'); - var encoded_data = FD.atob_FD(item.data); - var splitted_data = encoded_data.split(split_char); - - console.log(splitted_data); - - if (splitted_data[1] == "msg") { - obj.timestamp = parseInt(splitted_data[4]); - obj.dxcallsign = item.dxcallsign; - obj.dxgrid = item.dxgrid; - obj.command = splitted_data[1]; - obj.checksum = splitted_data[2]; - obj.uuid = splitted_data[3]; - obj.msg = splitted_data[5]; - obj.status = "null"; - obj.snr = "null"; - obj.type = "received"; - obj.filename = splitted_data[6]; - obj.filetype = splitted_data[7]; - //obj.file = btoa(splitted_data[8]); - obj.file = FD.btoa_FD(splitted_data[8]); - obj.hmac_signed = item.hmac_signed; - obj.new = 1; - } else if (splitted_data[1] == "req" && splitted_data[2] == "0") { - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "request"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Request for station info"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - if (config.enable_request_profile == "True") { - sendUserData(item.dxcallsign); - } - } else if (splitted_data[1] == "req" && splitted_data[2] == "1") { - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "request"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Request for shared folder list"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - if (config.enable_request_shared_folder == "True") { - sendSharedFolderList(item.dxcallsign); - } - } else if ( - splitted_data[1] == "req" && - splitted_data[2].substring(0, 1) == "2" - ) { - let name = splitted_data[2].substring(1); - //console.log("In handle req for shared folder file"); - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "request"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Request for shared file " + name; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - if (config.enable_request_shared_folder == "True") { - sendSharedFolderFile(item.dxcallsign, name); - } - } else if (splitted_data[1] == "res-0") { - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "response"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Response for station info"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - console.log(splitted_data); - let userData = new Object(); - userData.user_info_image = splitted_data[2]; - userData.user_info_callsign = splitted_data[3]; - userData.user_info_gridsquare = splitted_data[4]; - userData.user_info_name = splitted_data[5]; - userData.user_info_age = splitted_data[6]; - userData.user_info_location = splitted_data[7]; - userData.user_info_radio = splitted_data[8]; - userData.user_info_antenna = splitted_data[9]; - userData.user_info_email = splitted_data[10]; - userData.user_info_website = splitted_data[11]; - userData.user_info_comments = splitted_data[12]; - - addUserToDatabaseIfNotExists(userData); - getSetUserInformation(splitted_data[3]); - } else if (splitted_data[1] == "res-1") { - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "response"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Response for shared file list"; - obj.filename = "null"; - obj.filetype = "null"; - obj.file = "null"; - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - console.log(splitted_data); - - let userData = new Object(); - - userData.user_info_callsign = obj.dxcallsign; - let filelist = JSON.parse(splitted_data[3]); - console.log(filelist); - userData.user_shared_folder = filelist; - addFileListToUserDatabaseIfNotExists(userData); - getSetUserSharedFolder(obj.dxcallsign); - - //getSetUserInformation(selected_callsign); - } else if (splitted_data[1] == "res-2") { - console.log("In received response-2"); - let sharedFileInfo = splitted_data[2].split("/", 2); - - obj.uuid = uuidv4().toString(); - obj.timestamp = Math.floor(Date.now() / 1000); - obj.dxcallsign = item.dxcallsign; - obj.command = splitted_data[1]; - obj.type = "received"; - obj.status = "received"; - obj.snr = "null"; - obj.msg = "Response for shared file download"; - obj.filename = sharedFileInfo[0]; - obj.filetype = "application/octet-stream"; - obj.file = FD.btoa_FD(sharedFileInfo[1]); - obj.hmac_signed = item.hmac_signed; - obj.new = 0; - } else { - console.log("no rule matched for handling received data!"); - } - - add_obj_to_database(obj); - update_chat_obj_by_uuid(obj.uuid); - } - }); - //window.location = window.location; -}); - -// Update chat list -update_chat = function (obj) { - var dxcallsign = obj.dxcallsign; - var timestamp = dateFormat.format(obj.timestamp * 1000); - //var timestampShort = dateFormatShort.format(obj.timestamp * 1000); - var timestampHours = dateFormatHours.format(obj.timestamp * 1000); - - var dxgrid = obj.dxgrid; - - // check if obj.attempt exists - if (typeof obj.attempt == "undefined") { - db.upsert(obj._id, function (doc) { - if (!doc.attempt) { - doc.attempt = 1; - } - return doc; - }); - var attempt = 1; - obj.attempt = attempt; - } else { - var attempt = obj.attempt; - } - - // add percent and bytes per minute if not existing - //console.log(obj.percent) - if (typeof obj.percent == "undefined") { - obj.percent = 0; - obj.bytesperminute = 0; - } - - // check if wrong status message - if ( - obj.status == "transmitting" && - obj.type == "transmit" && - obj.percent < 100 - ) { - var TimeDifference = new Date().getTime() / 1000 - obj.timestamp; - if (TimeDifference > 21600) { - //Six hours - console.log( - "Resetting message to failed state since in transmit status for over 6 hours:", - ); - console.log(obj); - db.upsert(obj._id, function (doc) { - doc.status = "failed"; - return doc; - }); - obj.status = "failed"; - } - } - // check if in transmitting status and @ 100% - if ( - obj.status == "transmitting" && - obj.type == "transmit" && - obj.percent == 100 - ) { - var TimeDifference = new Date().getTime() / 1000 - obj.timestamp; - if (TimeDifference > 21600) { - //Six hours - console.log( - "Resetting message to transmitted since in transmit state and at 100%:", - ); - console.log(obj); - db.upsert(obj._id, function (doc) { - doc.status = "transmitted"; - return doc; - }); - obj.status = "transmitted"; - } - } - if (typeof obj.new == "undefined") { - obj.new = 0; - } - - if (typeof config.max_retry_attempts == "undefined") { - var max_retry_attempts = 3; - } else { - var max_retry_attempts = parseInt(config.max_retry_attempts); - } - //console.log(obj.msg); - // define shortmessage - if (obj.msg == "null" || obj.msg == "NULL") { - var shortmsg = obj.type; - } else { - var shortmsg = obj.msg; - var maxlength = 30; - var shortmsg = - shortmsg.length > maxlength - ? shortmsg.substring(0, maxlength - 3) + "..." - : shortmsg; - } - try { - //console.log(Object.keys(obj._attachments)[0].length) - if ( - typeof obj._attachments !== "undefined" && - Object.keys(obj._attachments)[0].length > 0 - ) { - //var filename = obj._attachments; - var filename = Object.keys(obj._attachments)[0]; - var filetype = filename.split(".")[1]; - var filesize = obj._attachments[filename]["length"] + " Bytes"; - if (filesize == "undefined Bytes") { - // get filesize of new submitted data - // not that nice.... - // we really should avoid converting back from base64 for performance reasons... - //var filesize = Math.ceil(atob(obj._attachments[filename]["data"]).length) + "Bytes"; - var filesize = - Math.ceil(FD.atob_FD(obj._attachments[filename]["data"]).length) + - " Bytes"; - } - - // check if image, then display it - if (filetype == "image/png" || filetype == "png") { - var fileheader = ` -
- -

- ${filename} - ${filesize} - -

-
-
- `; - } else { - var fileheader = ` -
-

- ${filename} - ${filesize} - -

-
-
- `; - } - - var controlarea_transmit = ` -
- - - -
- - `; - - var controlarea_receive = ` - -
- - -
- - `; - } else { - var filename = ""; - var fileheader = ""; - var filetype = "text/plain"; - var controlarea_transmit = ` -
- - -
- `; - var controlarea_receive = ` -
- -
- `; - } - } catch (err) { - console.log("error with database parsing..."); - console.log(err); - } - // CALLSIGN LIST - if (!document.getElementById("chat-" + dxcallsign + "-list")) { - // increment callsign counter - callsign_counter++; - dxcallsigns.add(dxcallsign); - if ( - (callsign_counter == 1 && selected_callsign == "") || - selected_callsign == dxcallsign - ) { - var callsign_selected = "active show"; - //document.getElementById('chatModuleDxCall').value = dxcallsign; - selected_callsign = dxcallsign; - } - - if (dxcallsign.startsWith("BC-")) { - var user_image = - ''; - } else { - var user_image = - ''; - - getSetUserInformation(dxcallsign); - getSetUserSharedFolder(dxcallsign); - } - - var new_callsign = ` - - -
-
- ${user_image} - -
- - ${dxcallsign} - ${dxgrid} - ${timestampHours} - ${shortmsg} -
- -
- - `; - - document - .getElementById("list-tab-chat") - .insertAdjacentHTML("beforeend", new_callsign); - var message_area = ` -
- `; - document - .getElementById("nav-tabContent-Chat") - .insertAdjacentHTML("beforeend", message_area); - - // finally get and set user information to first selected item - getSetUserInformation(selected_callsign); - getSetUserSharedFolder(selected_callsign); - - // create eventlistener for listening on clicking on a callsign - document - .getElementById("chat-" + dxcallsign + "-list") - .addEventListener("click", function () { - //document.getElementById('chatModuleDxCall').value = dxcallsign; - selected_callsign = dxcallsign; - //Reset unread messages and new message indicator - let clear = selected_callsign; - clearUnreadMessages(clear); - document.getElementById( - `chat-${selected_callsign}-list-displaydxcall`, - ).textContent = selected_callsign; - document - .getElementById(`chat-${selected_callsign}-list`) - .classList.remove("list-group-item-warning"); - setTimeout(scrollMessagesToBottom, 200); - - //get user information - getSetUserInformation(selected_callsign); - getSetUserSharedFolder(selected_callsign); - if (selected_callsign.startsWith("BC-")) { - document - .getElementById("chatModuleMessage") - .setAttribute("maxlength", 16); - //console.log("Setting max message size to 16") - } else { - document - .getElementById("chatModuleMessage") - .setAttribute("maxlength", 524288); - //console.log("Setting max message size to big#") - } - }); - - // if callsign entry already exists - update - } else { - // gridsquare - update only on receive - if (obj.type !== "transmit") { - document.getElementById("chat-" + dxcallsign + "-list-dxgrid").innerHTML = - dxgrid; - } - // time - document.getElementById("chat-" + dxcallsign + "-list-time").innerHTML = - timestampHours; - // short message - document.getElementById("chat-" + dxcallsign + "-list-shortmsg").innerHTML = - shortmsg; - if (obj.new == 1) { - document.getElementById( - `chat-${obj.dxcallsign}-list-displaydxcall`, - ).textContent = "*" + obj.dxcallsign; - document - .getElementById(`chat-${dxcallsign}-list`) - .classList.add("list-group-item-warning"); - } - } - // APPEND MESSAGES TO CALLSIGN - - if (!document.getElementById("msg-" + obj._id)) { - if (obj.type == "ping") { - //if (obj.new == 1) - //{ - // showOsPopUp("Ping from " + obj.dxcallsign,"You've been ping'd!"); - //} - var new_message = ` -
-

snr: ${obj.snr} - ${timestamp}

-
- `; - } - if (obj.type == "ping-ack") { - var new_message = ` -
-

Ping ack dx/mine snr: ${obj.snr} - ${timestamp}

-
- `; - } - if (obj.type == "beacon") { - var new_message = ` -
-

snr: ${obj.snr} - ${timestamp}

-
- `; - } - if (obj.type == "request") { - var new_message = ` -
-

${obj.msg} - ${timestamp}

-
- `; - } - if (obj.type == "response") { - var new_message = ` -
-

Response - ${timestamp}

-
- `; - } - if (obj.type == "newchat") { - var new_message = ` -
-

new chat opened - ${timestamp}

-
- `; - } - - // CHECK FOR NEW LINE AND REPLACE WITH
- var message_html = obj.msg.replaceAll(/\n/g, "
"); - - if (obj.type == "received") { - if (obj.new == 1) { - showOsPopUp("Message received from " + obj.dxcallsign, obj.msg); - } - - // check if message is signed or not for adjusting icon - if ( - typeof obj.hmac_signed !== "undefined" && - obj.hmac_signed !== "False" - ) { - console.log(hmac_signed); - var hmac_signed = ''; - } else { - var hmac_signed = ''; - } - - var new_message = ` -
- -
-
- ${fileheader} - -
-

${message_html}

-

- ${timestamp} - -

- - - - - ${hmac_signed} - - -
-
-
- ${controlarea_receive} -
- `; - } - - if (obj.type == "broadcast_received") { - console.log(obj); - var new_message = ` -
-
- -
-
-

${message_html}

-

- ${timestamp} - -

- - - - ${obj.broadcast_sender} - dxcallsign - -
-
-
-
- -
-
- `; - } - if (obj.type == "broadcast_transmit") { - var new_message = ` -
-
- - -
-
-
-
-

${message_html}

-

- ${timestamp} - - ${get_icon_for_state( - obj.status, - )} -

- - - ${attempt}/${max_retry_attempts} - retries - - -
-
-
- `; - } - - if (obj.type == "transmit") { - //console.log(obj); - //console.log('msg-' + obj._id + '-status') - - if (obj.status == "failed") { - var progressbar_bg = "bg-danger"; - var percent_value = "TRANSMISSION FAILED"; - //Set to 100 so progressbar background populates - obj.percent = 100; - } else if (obj.status == "transmitted") { - var progressbar_bg = "bg-success"; - var percent_value = "TRANSMITTED"; - } else { - var progressbar_bg = "bg-primary"; - var percent_value = obj.percent + " %"; - } - - //Sneak in low graphics mode if so enabled for progress bars - if (config.high_graphics.toString().toUpperCase() != "TRUE") { - progressbar_bg += " disable-effects"; - //console.log("Low graphics enabled for chat module"); - } - - var new_message = ` -
- ${controlarea_transmit} -
-
- ${fileheader} -
-

${message_html}

-

- ${timestamp} - - ${get_icon_for_state( - obj.status, - )} -

- - - ${attempt}/${max_retry_attempts} - retries - -
-
-

${percent_value} - ${ - obj.bytesperminute - } Bpm

-
-
-
-
-
- `; - } - // CHECK CHECK CHECK --> This could be done better - var id = "chat-" + obj.dxcallsign; - document.getElementById(id).insertAdjacentHTML("beforeend", new_message); - - /* UPDATE EXISTING ELEMENTS */ - } else if (document.getElementById("msg-" + obj._id)) { - //console.log("element already exists......"); - //console.log(obj); - - // console.log(obj.status) - // console.log(obj.attempt) - - if ( - obj.status != "broadcast_transmit" || - obj.status != "broadcast_received" - ) { - document.getElementById("msg-" + obj._id + "-status").innerHTML = - get_icon_for_state(obj.status); - - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("aria-valuenow", obj.percent); - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("style", "width:" + obj.percent + "%;"); - document.getElementById( - "msg-" + obj._id + "-progress-information", - ).innerHTML = obj.percent + "% - " + obj.bytesperminute + " Bpm"; - - document.getElementById("msg-" + obj._id + "-attempts").innerHTML = - obj.attempt + "/" + max_retry_attempts; - } - - if (obj.status == "transmit") { - document.getElementById("msg-" + obj._id + "-status").innerHTML = - get_icon_for_state(obj.status); - - if (typeof obj.percent !== "undefined") { - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("aria-valuenow", obj.percent); - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("style", "width:" + obj.percent + "%;"); - document.getElementById( - "msg-" + obj._id + "-progress-information", - ).innerHTML = obj.percent + "% - " + obj.bytesperminute + " Bpm"; - } else { - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("aria-valuenow", 0); - document - .getElementById("msg-" + obj._id + "-progress") - .setAttribute("style", "width:0%;"); - document.getElementById( - "msg-" + obj._id + "-progress-information", - ).innerHTML = "0% - 0 Bpm"; - } - - document.getElementById("msg-" + obj._id + "-attempts").innerHTML = - obj.attempt + "/" + max_retry_attempts; - } - - if (obj.status == "transmitted") { - //document.getElementById('msg-' + obj._id + '-progress').classList.remove("progress-bar-striped"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.remove("progress-bar-animated"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.remove("bg-danger"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.add("bg-success"); - - document.getElementById("msg-" + obj._id + "-progress").innerHTML = ""; - document.getElementById( - "msg-" + obj._id + "-progress-information", - ).innerHTML = "TRANSMITTED - " + obj.bytesperminute + " Bpm"; - } else if ( - obj.status != "broadcast_transmit" || - obj.status != "broadcast_received" - ) { - document - .getElementById("msg-" + obj._id + "-progress") - .classList.add("progress-bar-striped"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.add("progress-bar-animated"); - } - - if (obj.status == "failed") { - //document.getElementById('msg-' + obj._id + '-progress').classList.remove("progress-bar-striped"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.remove("progress-bar-animated"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.remove("bg-primary"); - document - .getElementById("msg-" + obj._id + "-progress") - .classList.add("bg-danger"); - - console.log( - document.getElementById("msg-" + obj._id + "-progress").classList, - ); - - document.getElementById( - "msg-" + obj._id + "-progress-information", - ).innerHTML = "TRANSMISSION FAILED - " + obj.bytesperminute + " Bpm"; - } - - //document.getElementById(id).className = message_class; - } - - //Delete message event listener - if ( - document.getElementById("del-msg-" + obj._id) && - !document - .getElementById("del-msg-" + obj._id) - .hasAttribute("listenerOnClick") - ) { - // set Attribute to determine if we already created an EventListener for this element - document - .getElementById("del-msg-" + obj._id) - .setAttribute("listenerOnClick", "true"); - document - .getElementById("del-msg-" + obj._id) - .addEventListener("click", () => { - db.get(obj._id, { - attachments: true, - }) - .then(function (doc) { - db.remove(doc._id, doc._rev, function (err) { - if (err) console.log("Error removing item " + err); - }); - }) - .catch(function (err) { - console.log(err); - }); - - document.getElementById("msg-" + obj._id).remove(); - document.getElementById("msg-" + obj._id + "-control-area").remove(); - console.log("Removed message " + obj._id.toString()); - - // stop transmission if deleted message is still in progress - if (obj.status == "transmitting") { - let Data = { - command: "stop_transmission", - }; - ipcRenderer.send("run-tnc-command", Data); - } - }); - //scrollMessagesToBottom(); - } - - // CREATE SAVE TO FOLDER EVENT LISTENER - if ( - document.getElementById("save-file-msg-" + obj._id) && - !document - .getElementById("save-file-msg-" + obj._id) - .hasAttribute("listenerOnClick") - ) { - // set Attribute to determine if we already created an EventListener for this element - document - .getElementById("save-file-msg-" + obj._id) - .setAttribute("listenerOnClick", "true"); - - document - .getElementById("save-file-msg-" + obj._id) - .addEventListener("click", () => { - saveFileToFolder(obj._id); - }); - } - // CREATE RESEND MSG EVENT LISTENER - - // check if element exists and if we already created NOT created an event listener - if ( - document.getElementById("retransmit-msg-" + obj._id) && - !document - .getElementById("retransmit-msg-" + obj._id) - .hasAttribute("listenerOnClick") - ) { - // set Attribute to determine if we already created an EventListener for this element - document - .getElementById("retransmit-msg-" + obj._id) - .setAttribute("listenerOnClick", "true"); - document - .getElementById("retransmit-msg-" + obj._id) - .addEventListener("click", () => { - // increment attempt - db.upsert(obj._id, function (doc) { - if (!doc.attempt) { - doc.attempt = 1; - } - doc.attempt++; - return doc; - }) - .then(function (res) { - // success, res is {rev: '1-xxx', updated: true, id: 'myDocId'} - console.log(res); - update_chat_obj_by_uuid(obj.uuid); - }) - .catch(function (err) { - // error - console.log(err); - }); - - db.get(obj._id, { - attachments: true, - }) - .then(function (doc) { - // handle doc - console.log(doc); - - var filename = Object.keys(obj._attachments)[0]; - var filetype = filename.content_type; - - console.log(filename); - console.log(filetype); - var file = obj._attachments[filename].data; - console.log(file); - console.log(Object.keys(obj._attachments)[0].data); - - //var file = atob(obj._attachments[filename]["data"]) - db.getAttachment(obj._id, filename).then(function (data) { - console.log(data); - //Rewrote this part to use buffers to ensure encoding is corect -- n1qm - var binaryString = FD.atob_FD(data); - - console.log(binaryString); - var data_with_attachment = - doc.timestamp + - split_char + - doc.msg + - split_char + - filename + - split_char + - filetype + - split_char + - binaryString; - let Data = { - command: "msg", - dxcallsign: doc.dxcallsign, - mode: 255, - frames: 5, - data: data_with_attachment, - checksum: doc.checksum, - uuid: doc.uuid, - }; - console.log(Data); - ipcRenderer.send("run-tnc-command", Data); - }); - /* - // convert blob data to binary string - blobUtil.blobToBinaryString(data).then(function (binaryString) { - console.log(binaryString) - }).catch(function (err) { - // error - console.log(err); - binaryString = blobUtil.arrayBufferToBinaryString(data); - - }).then(function(){ - - console.log(binaryString) - console.log(binaryString.length) - - var data_with_attachment = doc.timestamp + split_char + utf8.encode(doc.msg) + split_char + filename + split_char + filetype + split_char + binaryString; - let Data = { - command: "msg", - dxcallsign: doc.dxcallsign, - mode: 255, - frames: 1, - data: data_with_attachment, - checksum: doc.checksum, - uuid: doc.uuid - }; - console.log(Data) - ipcRenderer.send('run-tnc-command', Data); - - }); - }); - */ - }) - .catch(function (err) { - console.log(err); - }); - }); - } - //window.location = window.location - - // scroll to bottom on new message - scrollMessagesToBottom(); -}; - -function saveFileToFolder(id) { - db.get(id, { - attachments: true, - }) - .then(function (obj) { - console.log(obj); - console.log(Object.keys(obj._attachments)[0].content_type); - var filename = Object.keys(obj._attachments)[0]; - var filetype = filename.content_type; - var file = filename.data; - console.log(file); - console.log(filename.data); - db.getAttachment(id, filename) - .then(function (data) { - // handle result - console.log(data.length); - //data = new Blob([data.buffer], { type: 'image/png' } /* (1) */) - console.log(data); - // we need to encode data because of error "an object could not be cloned" - let Data = { - file: data, - filename: filename, - filetype: filetype, - }; - console.log(Data); - ipcRenderer.send("save-file-to-folder", Data); - }) - .catch(function (err) { - console.log(err); - return false; - }); - }) - .catch(function (err) { - console.log(err); - }); -} - -// function for setting an ICON to the corresponding state -function get_icon_for_state(state) { - if (state == "transmit") { - var status_icon = ''; - } else if (state == "transmitting") { - //var status_icon = ''; - var status_icon = ` - - `; - } else if (state == "failed") { - var status_icon = - ''; - } else if (state == "transmitted") { - var status_icon = ''; - } else { - var status_icon = ''; - } - return status_icon; -} - -update_chat_obj_by_uuid = function (uuid) { - db.get(uuid, { - attachments: true, - }) - .then(function (doc) { - update_chat(doc); - //return doc - }) - .catch(function (err) { - console.log(err); - }); -}; - -add_obj_to_database = function (obj) { - console.log(obj); - db.put({ - _id: obj.uuid, - timestamp: parseInt(obj.timestamp), - broadcast_sender: obj.broadcast_sender, - uuid: obj.uuid, - dxcallsign: obj.dxcallsign, - dxgrid: obj.dxgrid, - msg: obj.msg, - checksum: obj.checksum, - type: obj.type, - command: obj.command, - status: obj.status, - snr: obj.snr, - attempt: obj.attempt, - hmac_signed: obj.hmac_signed, - new: obj.new, - _attachments: { - [obj.filename]: { - content_type: obj.filetype, - data: obj.file, - }, - }, - }) - .then(function (response) { - console.log("new database entry"); - console.log(response); - }) - .catch(function (err) { - console.log("already exists"); - console.log(err); - console.log(obj); - db.upsert(obj.uuid, function (doc) { - doc = obj; - return doc; - }) - .then(function (response) { - console.log("upsert"); - console.log(response); - }) - .catch(function (err) { - console.log(err); - }); - }); -}; -/* users database functions */ -addUserToDatabaseIfNotExists = function (obj) { - /* - "user_info_callsign", - "user_info_gridsquare", - "user_info_name", - "user_info_age", - "user_info_location", - "user_info_radio", - "user_info_antenna", - "user_info_email", - "user_info_website", - "user_info_comments", -*/ - console.log(obj); - users - .find({ - selector: { - user_info_callsign: obj.user_info_callsign, - }, - }) - .then(function (result) { - // handle result - console.log(result); - if (result.docs.length > 0) { - users - .put({ - _id: result.docs[0]._id, - _rev: result.docs[0]._rev, - user_info_callsign: obj.user_info_callsign, - user_info_gridsquare: obj.user_info_gridsquare, - user_info_name: obj.user_info_name, - user_info_age: obj.user_info_age, - user_info_location: obj.user_info_location, - user_info_radio: obj.user_info_radio, - user_info_antenna: obj.user_info_antenna, - user_info_email: obj.user_info_email, - user_info_website: obj.user_info_website, - user_info_comments: obj.user_info_comments, - user_info_image: obj.user_info_image, - }) - .then(function (response) { - console.log("UPDATED USER"); - console.log(response); - console.log(obj); - }) - .catch(function (err) { - console.log(err); - }); - } else { - users - .post({ - user_info_callsign: obj.user_info_callsign, - user_info_gridsquare: obj.user_info_gridsquare, - user_info_name: obj.user_info_name, - user_info_age: obj.user_info_age, - user_info_location: obj.user_info_location, - user_info_radio: obj.user_info_radio, - user_info_antenna: obj.user_info_antenna, - user_info_email: obj.user_info_email, - user_info_website: obj.user_info_website, - user_info_comments: obj.user_info_comments, - }) - .then(function (response) { - console.log("NEW USER ADDED"); - }) - .catch(function (err) { - console.log(err); - }); - } - }) - .catch(function (err) { - console.log(err); - }); -}; - -addFileListToUserDatabaseIfNotExists = function (obj) { - console.log(obj); - users - .find({ - selector: { - user_info_callsign: obj.user_info_callsign, - }, - }) - .then(function (result) { - // handle result - if (result.docs.length > 0) { - users - .put({ - _id: result.docs[0]._id, - _rev: result.docs[0]._rev, - user_shared_folder: obj.user_shared_folder, - user_info_callsign: result.docs[0].user_info_callsign, - user_info_gridsquare: result.docs[0].user_info_gridsquare, - user_info_name: result.docs[0].user_info_name, - user_info_age: result.docs[0].user_info_age, - user_info_location: result.docs[0].user_info_location, - user_info_radio: result.docs[0].user_info_radio, - user_info_antenna: result.docs[0].user_info_antenna, - user_info_email: result.docs[0].user_info_email, - user_info_website: result.docs[0].user_info_website, - user_info_comments: result.docs[0].user_info_comments, - }) - .then(function (response) { - console.log("File List: UPDATED USER"); - console.log(response); - console.log(obj); - getSetUserSharedFolder(obj.user_info_callsign); - }) - .catch(function (err) { - console.log(err); - }); - } else { - users - .post({ - user_info_callsign: obj.user_info_callsign, - user_shared_folder: obj.user_shared_folder, - }) - .then(function (response) { - console.log("File List: NEW USER ADDED"); - getSetUserSharedFolder(obj.user_info_callsign); - }) - .catch(function (err) { - console.log(err); - }); - } - }) - .catch(function (err) { - console.log(err); - }); -}; - -// Scroll to bottom of message-container -function scrollMessagesToBottom() { - var messageBody = document.getElementById("message-container"); - messageBody.scrollTop = messageBody.scrollHeight - messageBody.clientHeight; -} - -// CRC CHECKSUMS -// https://stackoverflow.com/a/50579690 -// crc32 calculation -//console.log(crc32('abc')); -//var crc32=function(r){for(var a,o=[],c=0;c<256;c++){a=c;for(var f=0;f<8;f++)a=1&a?3988292384^a>>>1:a>>>1;o[c]=a}for(var n=-1,t=0;t>>8^o[255&(n^r.charCodeAt(t))];return(-1^n)>>>0}; -//console.log(crc32('abc').toString(16).toUpperCase()); // hex - -var makeCRCTable = function () { - var c; - var crcTable = []; - for (var n = 0; n < 256; n++) { - c = n; - for (var k = 0; k < 8; k++) { - c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; - } - crcTable[n] = c; - } - return crcTable; -}; - -var crc32 = function (str) { - var crcTable = window.crcTable || (window.crcTable = makeCRCTable()); - var crc = 0 ^ -1; - - for (var i = 0; i < str.length; i++) { - crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xff]; - } - - return (crc ^ -1) >>> 0; -}; - -function returnObjFromCallsign(database, callsign) { - return new Promise((resolve, reject) => { - users - .find({ - selector: { - user_info_callsign: callsign, - }, - }) - .then(function (result) { - //return new Promise((resolve, reject) => { - if (typeof result.docs[0] !== "undefined") { - resolve(result.docs[0]); - } else { - reject("Promise rejected"); - } - - /* - if (typeof result.docs[0] !== "undefined") { - return result.docs[0]; - - - - - } else { - return false; - } - - */ - }) - .catch(function (err) { - console.log(err); - }); - }); -} - -function createChatIndex() { - db.createIndex({ - index: { - fields: [ - "timestamp", - "uuid", - "dxcallsign", - "dxgrid", - "msg", - "checksum", - "type", - "command", - "status", - "percent", - "attempt", - "hmac_signed", - "bytesperminute", - "_attachments", - "new", - ], - }, - }) - .then(function (result) { - // handle result - console.log(result); - }) - .catch(function (err) { - console.log(err); - }); -} - -function createUserIndex() { - users - .createIndex({ - index: { - fields: [ - "timestamp", - "user_info_callsign", - "user_info_gridsquare", - "user_info_name", - "user_info_age", - "user_info_location", - "user_info_radio", - "user_info_antenna", - "user_info_email", - "user_info_website", - "user_info_comments", - "user_info_image", - ], - }, - }) - .then(function (result) { - // handle result - console.log(result); - return true; - }) - .catch(function (err) { - console.log(err); - return false; - }); -} - -async function updateAllChat(clear) { - if (clear == true) { - filetype = ""; - file = ""; - filename = ""; - callsign_counter = 0; - //selected_callsign = ""; - dxcallsigns.clear(); - document.getElementById("list-tab-chat").innerHTML = ""; - document.getElementById("nav-tabContent-Chat").innerHTML = ""; - //document.getElementById("list-tab-chat").childNodes.remove(); - //document.getElementById("nav-tab-content").childrenNodes.remove(); - } - //Ensure we create an index before running db.find - //We can't rely on the default index existing before we get here...... :'( - await db - .createIndex({ - index: { - fields: [{ dxcallsign: "asc" }, { timestamp: "asc" }], - }, - }) - .then(async function (result) { - // handle result - await db - .find({ - selector: { - $and: [ - { dxcallsign: { $exists: true } }, - { timestamp: { $exists: true } }, - { $or: chatFilter }, - ], - //$or: chatFilter - }, - sort: [{ dxcallsign: "asc" }, { timestamp: "asc" }], - }) - .then(async function (result) { - console.log(result); - // handle result async - //document.getElementById("blurOverlay").classList.add("bg-primary"); - console.log(result); - if (typeof result !== "undefined") { - for (const item of result.docs) { - //await otherwise history will not be in chronological order - await db - .get(item._id, { - attachments: true, - }) - .then(function (item_with_attachments) { - update_chat(item_with_attachments); - }); - } - } - }) - .catch(function (err) { - console.log(err); - }); - }) - .catch(function (err) { - console.log(err); - }); - if (clear == true && dxcallsigns.has(selected_callsign) == false) { - //Selected call sign is not visible, reset to first call sign - let tmp = dxcallsigns.entries().next().value[0]; - selected_callsign = tmp; - document - .getElementById("chat-" + tmp + "-list") - .classList.add("active", "show"); - document.getElementById("chat-" + tmp).classList.add("active", "show"); - scrollMessagesToBottom(); - } -} - -function getSetUserSharedFolder(selected_callsign) { - // TODO: This is a dirty hotfix for avoiding, this function is canceld too fast. - //Should be fixed - //console.log("get set user information:" + selected_callsign); - - if ( - selected_callsign == "" || - selected_callsign == null || - typeof selected_callsign == "undefined" - ) { - console.log("return triggered"); - return; - } - - // disable button if broadcast - if (selected_callsign.startsWith("BC-")) { - document.getElementById("sharedFolderDXButton").disabled = true; - } else { - document.getElementById("sharedFolderDXButton").disabled = false; - } - - returnObjFromCallsign(users, selected_callsign) - .then(function (data) { - console.log(data); - - if (typeof data.user_shared_folder !== "undefined") { - console.log(data.user_shared_folder); - // shared folder table - var icons = [ - "aac", - "ai", - "bmp", - "cs", - "css", - "csv", - "doc", - "docx", - "exe", - "gif", - "heic", - "html", - "java", - "jpg", - "js", - "json", - "jsx", - "key", - "m4p", - "md", - "mdx", - "mov", - "mp3", - "mp4", - "otf", - "pdf", - "php", - "png", - "ppt", - "pptx", - "psd", - "py", - "raw", - "rb", - "sass", - "scss", - "sh", - "sql", - "svg", - "tiff", - "tsx", - "ttf", - "txt", - "wav", - "woff", - "xls", - "xlsx", - "xml", - "yml", - ]; - var tbl = document.getElementById("sharedFolderTableDX"); - tbl.innerHTML = ""; - let counter = 0; - data.user_shared_folder.forEach((file) => { - var row = document.createElement("tr"); - - let dxcall = selected_callsign; - let name = file["name"]; - let type = file["extension"]; - - if (icons.indexOf(type) == -1) { - type = "bi-file-earmark"; - } else { - type = "bi-filetype-" + type; - } - - let id = document.createElement("td"); - let idText = document.createElement("span"); - counter += 1; - idText.innerHTML += - ' ' + - counter; - id.appendChild(idText); - row.appendChild(id); - - let filename = document.createElement("td"); - let filenameText = document.createElement("span"); - filenameText.innerText = file["name"]; - filename.appendChild(filenameText); - row.appendChild(filename); - - let filetype = document.createElement("td"); - let filetypeText = document.createElement("span"); - filetypeText.innerHTML = ``; - filetype.appendChild(filetypeText); - row.appendChild(filetype); - - let filesize = document.createElement("td"); - let filesizeText = document.createElement("span"); - filesizeText.innerText = formatBytes(file["size"], 2); - filesize.appendChild(filesizeText); - row.appendChild(filesize); - id.addEventListener("click", function () { - //console.log(name," clicked"); - sendFileReq(dxcall, name); - }); - tbl.appendChild(row); - }); - } else { - document.getElementById("sharedFolderTableDX").innerHTML = "no data"; - } - }) - .catch(function (err) { - console.log(err); - document.getElementById("sharedFolderTableDX").innerHTML = "no data"; - }); -} - -function getSetUserInformation(selected_callsign) { - //Get user information - //console.log("get set user information:" + selected_callsign); - - if ( - selected_callsign == "" || - selected_callsign == null || - typeof selected_callsign == "undefined" - ) { - console.log("return triggered"); - return; - } - - // disable button if broadcast - if (selected_callsign.startsWith("BC-")) { - document.getElementById("userModalDXButton").disabled = true; - document.getElementById("ping").disabled = true; - } else { - document.getElementById("userModalDXButton").disabled = false; - document.getElementById("ping").disabled = false; - } - - document.getElementById("dx_user_info_callsign").innerHTML = - selected_callsign; - - returnObjFromCallsign(users, selected_callsign) - .then(function (data) { - console.log(data); - - // image - if (typeof data.user_info_image !== "undefined") { - try { - console.log("try checking for image if base64 data"); - // determine if we have a base64 encoded image - console.log(data.user_info_image); - console.log(data.user_info_image.split(";base64,")[1]); - // split data string by "base64" for separating image type from base64 string - atob(data.user_info_image.split(";base64,")[1]); - - if (selected_callsign.startsWith("BC-")) { - data.user_info_image = defaultGroupIcon; - } - - document.getElementById("dx_user_info_image").src = - data.user_info_image; - document.getElementById("user-image-" + selected_callsign).src = - data.user_info_image; - } catch (e) { - console.log(e); - console.log("corrupted image data"); - - if (selected_callsign.startsWith("BC-")) { - var userIcon = defaultGroupIcon; - } else { - var userIcon = defaultUserIcon; - } - - document.getElementById("user-image-" + selected_callsign).src = - userIcon; - document.getElementById("dx_user_info_image").src = userIcon; - } - } else { - // throw error and use placeholder data - // throw new Error("Data not available or corrupted"); - document.getElementById("dx_user_info_image").src = defaultUserIcon; - document.getElementById("user-image-" + selected_callsign).src = - defaultUserIcon; - } - - // Callsign list elements - document.getElementById( - "chat-" + selected_callsign + "-list-dxgrid", - ).innerHTML = "" + data.user_info_gridsquare + ""; - document.getElementById("user-image-" + selected_callsign).className = - "p-1 rounded-circle"; - document.getElementById("user-image-" + selected_callsign).style = - "width: 60px"; - - // DX Station tab - document.getElementById("dx_user_info_name").innerHTML = - data.user_info_name; - document.getElementById("dx_user_info_age").innerHTML = - data.user_info_age; - document.getElementById("dx_user_info_gridsquare").innerHTML = - data.user_info_gridsquare; - document.getElementById("dx_user_info_location").innerHTML = - data.user_info_location; - document.getElementById("dx_user_info_email").innerHTML = - data.user_info_email; - document.getElementById("dx_user_info_website").innerHTML = - data.user_info_website; - document.getElementById("dx_user_info_radio").innerHTML = - data.user_info_radio; - document.getElementById("dx_user_info_antenna").innerHTML = - data.user_info_antenna; - document.getElementById("dx_user_info_comments").innerHTML = - data.user_info_comments; - - document.getElementById("dx_user_info_gridsquare").className = ""; - document.getElementById("dx_user_info_name").className = - "badge bg-secondary"; - document.getElementById("dx_user_info_age").className = - "badge bg-secondary"; - document.getElementById("dx_user_info_gridsquare").className = ""; - document.getElementById("dx_user_info_location").className = ""; - document.getElementById("dx_user_info_email").className = ""; - document.getElementById("dx_user_info_website").className = ""; - document.getElementById("dx_user_info_radio").className = ""; - document.getElementById("dx_user_info_antenna").className = ""; - document.getElementById("dx_user_info_comments").className = ""; - }) - .catch(function (err) { - console.log("writing user info to modal failed"); - console.log(err); - - if (selected_callsign.startsWith("BC-")) { - var userIcon = defaultGroupIcon; - } else { - var userIcon = defaultUserIcon; - } - - // Callsign list elements - document.getElementById("user-image-" + selected_callsign).src = userIcon; - document.getElementById("user-image-" + selected_callsign).className = - "p-1 rounded-circle w-100"; - document.getElementById("user-image-" + selected_callsign).style = - "height:60px"; - document.getElementById( - "chat-" + selected_callsign + "-list-dxgrid", - ).innerHTML = "no grid"; - - // DX Station tab - - document.getElementById("dx_user_info_image").src = defaultUserIcon; - document.getElementById("dx_user_info_gridsquare").className = - "placeholder col-4"; - document.getElementById("dx_user_info_name").className = - "placeholder col-4"; - document.getElementById("dx_user_info_age").className = - "placeholder col-2"; - document.getElementById("dx_user_info_gridsquare").className = - "placeholder col-3"; - document.getElementById("dx_user_info_location").className = - "placeholder col-3"; - document.getElementById("dx_user_info_email").className = - "placeholder col-7"; - document.getElementById("dx_user_info_website").className = - "placeholder col-7"; - document.getElementById("dx_user_info_radio").className = - "placeholder col-4"; - document.getElementById("dx_user_info_antenna").className = - "placeholder col-4"; - document.getElementById("dx_user_info_comments").className = - "placeholder col-7"; - }); -} - -function sendSharedFolderList(dxcallsign) { - ipcRenderer.send("read-files-in-folder", { - folder: config.shared_folder_path, - }); - - console.log(sharedFolderFileList); - let fileListWithCallsign = ""; - fileListWithCallsign += dxcallsign; - fileListWithCallsign += split_char; - fileListWithCallsign += JSON.stringify(sharedFolderFileList); - - console.log(fileListWithCallsign); - - ipcRenderer.send("run-tnc-command", { - command: "responseSharedFolderList", - dxcallsign: dxcallsign, - folderFileList: fileListWithCallsign, - }); -} - -function sendSharedFolderFile(dxcallsign, filename) { - let filePath = path.join(config.shared_folder_path, filename); - console.log("In function sendSharedFolderFile ", filePath); - - //Make sure nothing sneaky is going on - if (!filePath.startsWith(config.shared_folder_path)) { - console.error("File is outside of shared folder path!"); - return; - } - - if (!fs.existsSync(filePath)) { - console.warn("File doesn't seem to exist"); - return; - } - - //Read file's data - let fileData = null; - try { - //Has to be binary - let data = fs.readFileSync(filePath); - fileData = data.toString("utf-8"); - } catch (err) { - console.log(err); - return; - } - - ipcRenderer.send("run-tnc-command", { - command: "responseSharedFile", - dxcallsign: dxcallsign, - file: filename, - filedata: fileData, - }); -} - -function sendUserData(dxcallsign) { - const userInfoFields = [ - "user_info_image", - "user_info_callsign", - "user_info_gridsquare", - "user_info_name", - "user_info_age", - "user_info_location", - "user_info_radio", - "user_info_antenna", - "user_info_email", - "user_info_website", - "user_info_comments", - ]; - - let info = ""; - userInfoFields.forEach(function (subelem) { - if (subelem !== "user_info_image") { - info += document.getElementById(subelem).value; - info += split_char; - } else { - info += document.getElementById(subelem).src; - info += split_char; - } - }); - - console.log(info); - - ipcRenderer.send("run-tnc-command", { - command: "responseUserInfo", - dxcallsign: dxcallsign, - userinfo: info, - }); -} - -//Temporarily disable a button with timeout -function pauseButton(btn, timems) { - btn.disabled = true; - var curText = btn.innerHTML; - if (config.high_graphics.toUpperCase() == "TRUE") { - btn.innerHTML = - '