2023-09-06 13:36:54 +00:00
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
2023-10-04 17:54:50 +00:00
//const split_char = "\0;\1;";
2023-09-06 13:36:54 +00:00
// 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
// TODO: Remove for one pager, also remove function!
//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 = `
< span class = "position-absolute top-0 start-85 translate-middle p-2 bg-danger border border-light rounded-circle" >
< span class = "visually-hidden" > New file selected < / s p a n >
< / s p a n >
` ;
} ) ;
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 = `
< i class = "bi bi-filetype-${file[" extension "]}" style = "font-size: 1.8rem" > < / i >
` ;
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 = `
< div class = "m-auto mt-1 p-0 w-25 rounded bg-secondary bg-gradient" id = "msg-${uuid}" >
< p class = "text-white mb-0 text-break" style = "font-size: 0.7rem;" > < i class = "m-1 bi bi-pencil" > < /i><i id="msg-${uuid}-icon" class="m-1 bi bi-wifi-1"></i > $ { dxcallsign } is typing ... . < / p >
< / d i v >
` ;
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 = `
< div class = "card-header border-0 bg-transparent text-end p-0 mb-0 hover-overlay" >
< img class = "w-100 rounded-2" src = " data : image / png ; base64 , $ { FD . atob (
obj . _attachments [ filename ] [ "data" ] ,
) } " >
< p class = "text-right mb-0 p-1" style = "text-align: right; font-size : 1rem" >
< span class = "p-1" style = "text-align: right; font-size : 0.8rem" > $ { filename } < / s p a n >
< span class = "p-1" style = "text-align: right; font-size : 0.8rem" > $ { filesize } < / s p a n >
< i class = "bi bi-filetype-${filetype}" style = "font-size: 2rem;" > < / i >
< / p >
< / d i v >
< hr class = "m-0 p-0" >
` ;
} else {
var fileheader = `
< div class = "card-header border-0 bg-transparent text-end p-0 mb-0 hover-overlay" >
< p class = "text-right mb-0 p-1" style = "text-align: right; font-size : 1rem" >
< span class = "p-1" style = "text-align: right; font-size : 0.8rem" > $ { filename } < / s p a n >
< span class = "p-1" style = "text-align: right; font-size : 0.8rem" > $ { filesize } < / s p a n >
< i class = "bi bi-filetype-${filetype}" style = "font-size: 2rem;" > < / i >
< / p >
< / d i v >
< hr class = "m-0 p-0" >
` ;
}
var controlarea _transmit = `
< div class = "ms-auto" id = "msg-${obj._id}-control-area" >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-arrow-repeat link-secondary" id = "retransmit-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-download link-secondary" id = "save-file-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = "del-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< / d i v >
` ;
var controlarea _receive = `
< div class = "me-auto" id = "msg-${obj._id}-control-area" >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-download link-secondary" id = "save-file-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = "del-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< / d i v >
` ;
} else {
var filename = "" ;
var fileheader = "" ;
var filetype = "text/plain" ;
var controlarea _transmit = `
< div class = "ms-auto" id = "msg-${obj._id}-control-area" >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-arrow-repeat link-secondary" id = "retransmit-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = "del-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< / d i v >
` ;
var controlarea _receive = `
< div class = "float-start" id = "msg-${obj._id}-control-area" >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = "del-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< / d i v >
` ;
}
} 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 =
'<img id="user-image-' +
dxcallsign +
'" class="p-1 rounded-circle" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGVvcGxlLWZpbGwiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTcgMTRzLTEgMC0xLTEgMS00IDUtNCA1IDMgNSA0LTEgMS0xIDFIN1ptNC02YTMgMyAwIDEgMCAwLTYgMyAzIDAgMCAwIDAgNlptLTUuNzg0IDZBMi4yMzggMi4yMzggMCAwIDEgNSAxM2MwLTEuMzU1LjY4LTIuNzUgMS45MzYtMy43MkE2LjMyNSA2LjMyNSAwIDAgMCA1IDljLTQgMC01IDMtNSA0czEgMSAxIDFoNC4yMTZaTTQuNSA4YTIuNSAyLjUgMCAxIDAgMC01IDIuNSAyLjUgMCAwIDAgMCA1WiIvPgo8L3N2Zz4="></img>' ;
} else {
var user _image =
'<img id="user-image-' +
dxcallsign +
'" class="p-1 rounded-circle" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGVyc29uLWNpcmNsZSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNMTEgNmEzIDMgMCAxIDEtNiAwIDMgMyAwIDAgMSA2IDB6Ii8+CiAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMCA4YTggOCAwIDEgMSAxNiAwQTggOCAwIDAgMSAwIDh6bTgtN2E3IDcgMCAwIDAtNS40NjggMTEuMzdDMy4yNDIgMTEuMjI2IDQuODA1IDEwIDggMTBzNC43NTcgMS4yMjUgNS40NjggMi4zN0E3IDcgMCAwIDAgOCAxeiIvPgo8L3N2Zz4="></img>' ;
getSetUserInformation ( dxcallsign ) ;
getSetUserSharedFolder ( dxcallsign ) ;
}
var new _callsign = `
< a class = "list-group-item list-group-item-action rounded-4 rounded-top rounded-bottom border-1 mb-2 ${callsign_selected}" id = "chat-${dxcallsign}-list" data - bs - toggle = "list" href = "#chat-${dxcallsign}" role = "tab" aria - controls = "chat-${dxcallsign}" >
< div class = "d-flex w-100 justify-content-between" >
< div class = "rounded-circle p-0" >
$ { user _image }
< / d i v >
< span style = "font-size:1.2rem;" > < strong id = "chat-${dxcallsign}-list-displaydxcall" > $ { dxcallsign } < / s t r o n g > < / s p a n >
< span class = "badge bg-secondary text-white p-1 h-100" id = "chat-${dxcallsign}-list-dxgrid" > < small > $ { dxgrid } < / s m a l l > < / s p a n >
< span style = "font-size:0.8rem;" id = "chat-${dxcallsign}-list-time" > $ { timestampHours } < / s p a n >
< span class = "position-absolute m-2 bottom-0 end-0" style = "font-size:0.8rem;" id = "chat-${dxcallsign}-list-shortmsg" > $ { shortmsg } < / s p a n >
< / d i v >
< / a >
` ;
document
. getElementById ( "list-tab-chat" )
. insertAdjacentHTML ( "beforeend" , new _callsign ) ;
var message _area = `
< div class = "tab-pane fade ${callsign_selected}" id = "chat-${dxcallsign}" role = "tabpanel" aria - labelledby = "chat-${dxcallsign}-list" > < / d i v >
` ;
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 = `
< div class = "m-auto mt-1 p-0 w-50 rounded bg-secondary bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-white mb-0 text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-arrow-left-right" > < / i > s n r : $ { o b j . s n r } - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
if ( obj . type == "ping-ack" ) {
var new _message = `
< div class = "m-auto mt-1 p-0 w-50 rounded bg-secondary bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-white mb-0 text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-check-lg" > < / i > P i n g a c k d x / m i n e s n r : $ { o b j . s n r } - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
if ( obj . type == "beacon" ) {
var new _message = `
< div class = "p-0 rounded m-auto mt-1 w-50 bg-info bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-white text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-broadcast" > < / i > s n r : $ { o b j . s n r } - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
if ( obj . type == "request" ) {
var new _message = `
< div class = "p-0 rounded m-auto mt-1 w-50 bg-warning bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-white text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-info" > < / i > $ { o b j . m s g } - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
if ( obj . type == "response" ) {
var new _message = `
< div class = "p-0 rounded m-auto mt-1 w-50 bg-warning bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-white text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-info" > < / i > R e s p o n s e - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
if ( obj . type == "newchat" ) {
var new _message = `
< div class = "p-0 rounded m-auto mt-1 w-50 bg-light bg-gradient" id = "msg-${obj._id}" >
< p class = "text-small text-dark text-break" style = "font-size: 0.7rem;" > < i class = "m-3 bi bi-file-earmark-plus" > < / i > n e w c h a t o p e n e d - $ { t i m e s t a m p } < / p >
< / d i v >
` ;
}
// CHECK FOR NEW LINE AND REPLACE WITH <br>
var message _html = obj . msg . replaceAll ( /\n/g , "<br>" ) ;
if ( obj . type == "received" ) {
if ( obj . new == 1 ) {
showOsPopUp ( "Message received from " + obj . dxcallsign , obj . msg ) ;
}
2023-10-22 22:23:23 +00:00
// 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 = '<i class="bi bi-shield-fill-check"></i>' ;
} else {
var hmac _signed = '<i class="bi bi-shield-x"></i>' ;
}
2023-09-06 13:36:54 +00:00
var new _message = `
< div class = "d-flex align-items-center" style = "margin-left: auto;" > <!-- max - width : 75 % ; -- >
< div class = "mt-3 rounded-3 mb-0" style = "max-width: 75%;" id = "msg-${obj._id}" >
< div class = "card border-light bg-body-tertiary" id = "msg-${obj._id}" >
$ { fileheader }
< div class = "card-body rounded-3 p-0" >
< p class = "card-text p-2 mb-0 text-break text-wrap" > $ { message _html } < / p >
< p class = "text-right mb-0 p-1 text-white" style = "text-align: left; font-size : 0.9rem" >
< span class = "badge bg-body-tertiary text-muted" > $ { timestamp } < / s p a n >
< / p >
2023-10-22 22:23:23 +00:00
< span id = "msg-${obj._id}-hmac-badge" class = "position-absolute top-0 start-100 translate-middle badge rounded-1 bg-secondary border border-white" >
2023-09-06 13:36:54 +00:00
2023-10-22 22:23:23 +00:00
< span id = "msg-${obj._id}-hmac-signed" class = "" > $ { hmac _signed } < / s p a n >
2023-09-06 13:36:54 +00:00
< / s p a n >
< / d i v >
< / d i v >
< / d i v >
$ { controlarea _receive }
< / d i v >
` ;
}
if ( obj . type == "broadcast_received" ) {
console . log ( obj ) ;
var new _message = `
< div class = "d-flex align-items-center" style = "margin-left: auto;" > <!-- max - width : 75 % ; -- >
< div class = "mt-3 rounded-3 mb-0" style = "max-width: 75%;" id = "msg-${obj._id}" >
<!-- < p class = "font-monospace text-small mb-0 text-muted text-break" > $ { timestamp } < / p > - - >
< div class = "card border-light bg-body-tertiary" id = "msg-${obj._id}" >
< div class = "card-body rounded-3 p-0" >
< p class = "card-text p-2 mb-0 text-break text-wrap" > $ { message _html } < / p >
< p class = "text-right mb-0 p-1 text-white" style = "text-align: left; font-size : 0.9rem" >
< span class = "badge bg-body-tertiary text-muted" > $ { timestamp } < / s p a n >
< / p >
< span id = "msg-${obj._id}-dxcallsign-badge" class = "position-absolute top-0 start-100 translate-middle badge rounded-1 bg-secondary border border-white" >
< span id = "msg-${obj._id}-attempts" class = "" > $ { obj . broadcast _sender } < / s p a n >
< span class = "visually-hidden" > dxcallsign < / s p a n >
< / s p a n >
< / d i v >
< / d i v >
< / d i v >
< div class = "me-auto" id = "msg-${obj._id}-control-area" >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = "del-msg-${obj._id}" style = "font-size: 1.2rem;" > < / i > < / b u t t o n >
< / d i v >
< / d i v >
` ;
}
if ( obj . type == "broadcast_transmit" ) {
var new _message = `
< div class = "d-flex align-items-center" >
< div class = "ms-auto" id = "msg-${obj._id}-control-area" >
<!-- < button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-arrow-repeat link-secondary" id = " retransmit - msg - $ {
obj . _id
} " style=" font - size : 1.2 rem ; " > < / i > < / b u t t o n > - - >
< button class = "btn bg-transparent p-1 m-1" > < i class = "bi bi-trash link-secondary" id = " del - msg - $ {
obj . _id
} " style=" font - size : 1.2 rem ; " > < / i > < / b u t t o n >
< / d i v >
< div class = "rounded-3 mt-3 mb-0 me-2" style = "max-width: 75%;" >
< div class = "card border-primary bg-primary" id = "msg-${obj._id}" >
< div class = "card-body rounded-3 p-0 text-right bg-primary" >
< p class = "card-text p-1 mb-0 text-white text-break text-wrap" > $ { message _html } < / p >
< p class = "text-right mb-0 p-1 text-white" style = "text-align: right; font-size : 0.9rem" >
< span class = "text-light" style = "font-size: 0.7rem;" > $ { timestamp } - < / s p a n >
< span class = "text-white" id = " msg - $ {
obj . _id
} - status " style=" font - size : 0.8 rem ; " > $ { get _icon _for _state (
obj . status ,
) } < / s p a n >
< / p >
< span id = " msg - $ {
obj . _id
} - attempts - badge " class=" position - absolute top - 0 start - 100 translate - middle badge rounded - 1 bg - primary border border - white " >
< span id = " msg - $ {
obj . _id
} - attempts " class=" " > $ { attempt } / $ { max _retry _attempts } < / s p a n >
< span class = "visually-hidden" > retries < / s p a n >
< / s p a n >
< / d i v >
< / d i v >
< / d i v >
` ;
}
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 = `
< div class = "d-flex align-items-center" >
$ { controlarea _transmit }
< div class = "rounded-3 mt-3 mb-0 me-2" style = "max-width: 75%;" >
< div class = "card border-primary bg-primary" id = "msg-${obj._id}" >
$ { fileheader }
< div class = "card-body rounded-3 p-0 text-right bg-primary" >
< p class = "card-text p-1 mb-0 text-white text-break text-wrap" > $ { message _html } < / p >
< p class = "text-right mb-0 p-1 text-white" style = "text-align: right; font-size : 0.9rem" >
< span class = "text-light" style = "font-size: 0.7rem;" > $ { timestamp } - < / s p a n >
< span class = "text-white" id = " msg - $ {
obj . _id
} - status " style=" font - size : 0.8 rem ; " > $ { get _icon _for _state (
obj . status ,
) } < / s p a n >
< / p >
< span id = " msg - $ {
obj . _id
} - attempts - badge " class=" position - absolute top - 0 start - 100 translate - middle badge rounded - 1 bg - primary border border - white " >
< span id = " msg - $ {
obj . _id
} - attempts " class=" " > $ { attempt } / $ { max _retry _attempts } < / s p a n >
< span class = "visually-hidden" > retries < / s p a n >
< / s p a n >
< div class = "progress p-0 m-0 rounded-0 rounded-bottom bg-secondary" style = "height: 10px;" >
< div class = "progress-bar progress-bar-striped ${progressbar_bg} p-0 m-0 rounded-0 force-gpu" id = " msg - $ {
obj . _id
} - progress " role=" progressbar " style=" width : $ {
obj . percent
} % ; " aria-valuenow=" $ {
obj . percent
} " aria-valuemin=" 0 " aria-valuemax=" 100 " > < / d i v >
< p class = "justify-content-center d-flex position-absolute m-0 p-0 w-100 text-white" style = "font-size: xx-small" id = " msg - $ {
obj . _id
} - progress - information " > $ { percent _value } - $ {
obj . bytesperminute
} Bpm < / p >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
` ;
}
// 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 = '<i class="bi bi-check" style="font-size:1rem;"></i>' ;
} else if ( state == "transmitting" ) {
//var status_icon = '<i class="bi bi-arrow-left-right" style="font-size:0.8rem;"></i>';
var status _icon = `
< i class = "spinner-border ms-auto" style = "width: 0.8rem; height: 0.8rem;" role = "status" aria - hidden = "true" > < / i >
` ;
} else if ( state == "failed" ) {
var status _icon =
'<i class="bi bi-exclamation-circle" style="font-size:1rem;"></i>' ;
} else if ( state == "transmitted" ) {
var status _icon = '<i class="bi bi-check-all" style="font-size:1rem;"></i>' ;
} else {
var status _icon = '<i class="bi bi-question" style="font-size:1rem;"></i>' ;
}
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<r.length;t++)n=n>>>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 +=
'<i class="bi bi-file-earmark-arrow-down" style="font-size: 1.8rem;cursor: pointer"></i> ' +
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 = ` <i class="bi ${ type } " style="font-size: 1.8rem"></i> ` ;
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 = "<small>" + data . user _info _gridsquare + "</small>" ;
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 = "<small>no grid</small>" ;
// 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 =
'<span class="spinner-grow spinner-grow-sm force-gpu" role="status" aria-hidden="true">' ;
}
setTimeout ( ( ) => {
btn . innerHTML = curText ;
btn . disabled = false ;
} , timems ) ;
}
ipcRenderer . on ( "update-config" , ( event , data ) => {
config = data ;
} ) ;
ipcRenderer . on ( "action-update-unread-messages" , ( event ) => {
checkForNewMessages ( ) . then ( function ( count ) {
ipcRenderer . send ( "request-update-unread-messages-main" , count ) ;
} ) ;
} ) ;
ipcRenderer . on ( "action-clean-db" , ( event ) => {
dbClean ( ) ;
} ) ;
// https://stackoverflow.com/a/18650828
function formatBytes ( bytes , decimals = 2 ) {
if ( ! + bytes ) return "0 Bytes" ;
const k = 1024 ;
const dm = decimals < 0 ? 0 : decimals ;
const sizes = [ "Bytes" , "KB" , "MB" , "GB" , "TB" , "PB" , "EB" , "ZB" , "YB" ] ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
return ` ${ parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( dm ) ) } ${ sizes [ i ] } ` ;
}
function sendFileReq ( dxcall , file ) {
//console.log(file," clicked");
ipcRenderer . send ( "run-tnc-command" , {
command : "requestSharedFile" ,
dxcallsign : dxcall ,
file : file ,
} ) ;
}
function changeGuiDesign ( design ) {
console . log ( design ) ;
if (
design != "default" &&
design != "default_light" &&
design != "default_dark" &&
design != "default_auto"
) {
var theme _path =
"../node_modules/bootswatch/dist/" + design + "/bootstrap.min.css" ;
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
} else if ( design == "default" || design == "default_light" ) {
var theme _path = "../node_modules/bootstrap/dist/css/bootstrap.min.css" ;
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
document . documentElement . setAttribute ( "data-bs-theme" , "light" ) ;
} else if ( design == "default_dark" ) {
var theme _path = "../node_modules/bootstrap/dist/css/bootstrap.min.css" ;
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
document . querySelector ( "html" ) . setAttribute ( "data-bs-theme" , "dark" ) ;
} else if ( design == "default_auto" ) {
var theme _path = "../node_modules/bootstrap/dist/css/bootstrap.min.css" ;
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
// https://stackoverflow.com/a/57795495
// check if dark mode or light mode used in OS
if (
window . matchMedia &&
window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches
) {
// dark mode
document . documentElement . setAttribute ( "data-bs-theme" , "dark" ) ;
} else {
document . documentElement . setAttribute ( "data-bs-theme" , "light" ) ;
}
// also register event listener for automatic change
window
. matchMedia ( "(prefers-color-scheme: dark)" )
. addEventListener ( "change" , ( event ) => {
let newColorScheme = event . matches ? "dark" : "light" ;
if ( newColorScheme == "dark" ) {
document . documentElement . setAttribute ( "data-bs-theme" , "dark" ) ;
} else {
document . documentElement . setAttribute ( "data-bs-theme" , "light" ) ;
}
} ) ;
} else {
var theme _path = "../node_modules/bootstrap/dist/css/bootstrap.min.css" ;
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
document . documentElement . setAttribute ( "data-bs-theme" , "light" ) ;
}
//update path to css file
document . getElementById ( "bootstrap_theme" ) . href = escape ( theme _path ) ;
}
function checkForWaitingMessages ( dxcall ) {
db . find ( {
selector : {
dxcallsign : dxcall ,
type : "transmit" ,
status : "failed" ,
//attempt: { $lt: parseInt(config.max_retry_attempts) }
} ,
} )
. then ( function ( result ) {
console . log (
"Found " + result . docs . length + " messages waiting for " + dxcall ,
) ;
// handle result
if ( result . docs . length > 0 ) {
// only want to process the first available item object, then return
// this ensures, we are only sending one message at once
if ( typeof result . docs [ 0 ] . attempt == "undefined" ) {
db . upsert ( result . docs [ 0 ] . _id , function ( doc ) {
if ( ! doc . attempt ) {
doc . attempt = 1 ;
}
doc . attempt ++ ;
return doc ;
} ) ;
console . log ( "old message found - adding attempt field" ) ;
result . docs [ 0 ] . attempt = 1 ;
}
if ( result . docs [ 0 ] . attempt < config . max _retry _attempts ) {
console . log ( "RESENDING MESSAGE TRIGGERED BY BEACON OR PING" ) ;
console . log ( result . docs [ 0 ] ) ;
document
. getElementById ( "retransmit-msg-" + result . docs [ 0 ] . _id )
. click ( ) ;
} else {
console . log ( "max retries reached...can't auto repeat" ) ;
document
. getElementById ( "msg-" + result . docs [ 0 ] . _id + "-attempts-badge" )
. classList . remove ( "bg-primary" ) ;
document
. getElementById ( "msg-" + result . docs [ 0 ] . _id + "-attempts-badge" )
. classList . add ( "bg-danger" ) ;
}
return ;
} else {
//console.log("nope");
}
} )
. catch ( function ( err ) {
console . log ( err ) ;
} ) ;
}
async function checkForNewMessages ( ) {
var newmsgs ;
await db
. find ( {
selector : {
new : { $eq : 1 } ,
} ,
limit : 1 ,
} )
. then ( function ( result ) {
if ( result . docs . length > 0 ) newmsgs = true ;
else newmsgs = false ;
} )
. catch ( function ( err ) {
console . log ( err ) ;
} ) ;
return newmsgs ;
}
function clearUnreadMessages ( dxcall ) {
//console.log(dxcall);
//Selector of dxcall and new $eq: 1 isn't working, don't know why
//For now parse all messages of callsign to clear new flag
db . find ( {
//{
selector :
//$and:[
{ dxcallsign : dxcall } , //, {new: { $gte: 1}}
//]
// }
} )
. then ( function ( result ) {
//console.log(result);
//console.log ("New messages count to clear for " + dxcall + ": " + result.docs.length)
result . docs . forEach ( function ( item ) {
if ( item . new == 1 ) {
db . upsert ( item . _id , function ( doc ) {
doc . new = 0 ;
//console.log("Clearing new on _id " + item._id);
return doc ;
} ) ;
}
} ) ;
} )
. catch ( function ( err ) {
console . log ( err ) ;
} ) ;
}
//Have the operating system show a notification popup
function showOsPopUp ( title , message ) {
if ( config . enable _sys _notification == 0 ) return ;
const NOTIFICATION _TITLE = title ;
const NOTIFICATION _BODY = message ;
new Notification ( NOTIFICATION _TITLE , { body : NOTIFICATION _BODY } ) ;
}
//Function to clean old beacons and optimize database
async function dbClean ( ) {
//Only keep the most x latest days of beacons
let beaconKeep = 7 ;
let itemCount = 0 ;
let timestampPurge = Math . floor (
( Date . now ( ) - beaconKeep * 24 * 60 * 60 * 1000 ) / 1000 ,
) ;
if (
confirm (
"Delete beacons and pings older than " +
beaconKeep +
" days and compact database?" ,
)
) {
} else {
ipcRenderer . send ( "request-update-dbclean-spinner" ) ;
return ;
}
//Items to purge from database
var purgeFilter = [
{ type : "beacon" } ,
{ type : "ping-ack" } ,
{ type : "ping" } ,
] ;
await db
. find ( {
selector : {
$and : [ { timestamp : { $lt : timestampPurge } } , { $or : purgeFilter } ] ,
} ,
} )
. then ( async function ( result ) {
//console.log("Purging " + result.docs.length + " beacons received before " + timestampPurge);
itemCount = result . docs . length ;
result . docs . forEach ( async function ( item ) {
await db
. get ( item . _id )
. then ( async function ( doc ) {
await db . remove ( doc ) ;
} )
. catch ( function ( err ) {
console . log ( err ) ;
} ) ;
} ) ;
} )
. catch ( function ( err ) {
console . log ( err ) ;
} ) ;
//Compact database
await db . compact ( ) ;
window . alert (
"Database maintenance is complete. " +
itemCount +
" items removed from database. It's recommended you now restart the GUI." ,
) ;
ipcRenderer . send ( "request-update-dbclean-spinner" ) ;
}