Restore previous conversations in webchat

This patch saves the webchat conversations messages in the browser's
local storage.  When the user comes back to the page, the
conversations are restored.
This commit is contained in:
Hemna 2023-09-05 14:14:52 -04:00
parent 619b1b708e
commit 7292744a78
3 changed files with 189 additions and 58 deletions

View File

@ -246,7 +246,6 @@ class APRSDStats:
},
"plugins": plugin_stats,
}
LOG.info("APRSD Stats: DONE")
return stats
def __str__(self):

View File

@ -4,6 +4,10 @@ var message_list = {};
var from_msg_list = {};
const socket = io("/sendmsg");
MSG_TYPE_TX = "tx";
MSG_TYPE_RX = "rx";
MSG_TYPE_ACK = "ack";
function size_dict(d){c=0; for (i in d) ++c; return c}
function init_chat() {
@ -16,24 +20,28 @@ function init_chat() {
});
socket.on("sent", function(msg) {
if (cleared == false) {
if (cleared === false) {
console.log("CLEARING #msgsTabsDiv");
var msgsdiv = $("#msgsTabsDiv");
msgsdiv.html('')
cleared = true
msgsdiv.html('');
cleared = true;
}
msg["type"] = MSG_TYPE_TX;
sent_msg(msg);
});
socket.on("ack", function(msg) {
update_msg(msg);
msg["type"] = MSG_TYPE_ACK;
ack_msg(msg);
});
socket.on("new", function(msg) {
if (cleared == false) {
if (cleared === false) {
var msgsdiv = $("#msgsTabsDiv");
msgsdiv.html('')
cleared = true
cleared = true;
}
msg["type"] = MSG_TYPE_RX;
from_msg(msg);
});
@ -47,32 +55,124 @@ function init_chat() {
});
init_gps();
// Try and load any existing chat threads from last time
init_messages();
}
function add_callsign(callsign) {
/* Ensure a callsign exists in the left hand nav */
function message_ts_id(msg) {
//Create a 'id' from the message timestamp
ts_str = msg["ts"].toString();
ts = ts_str.split(".")[0]*1000;
id = ts_str.split('.')[0];
return {'timestamp': ts, 'id': id};
}
if (callsign in callsign_list) {
return false
}
function time_ack_from_msg(msg) {
// Return the time and ack_id from a message
ts_id = message_ts_id(msg);
ts = ts_id['timestamp'];
id = ts_id['id'];
ack_id = "ack_" + id
var d = new Date(ts).toLocaleDateString("en-US")
var t = new Date(ts).toLocaleTimeString("en-US")
return {'time': t, 'date': d, 'ack_id': ack_id};
}
function save_data() {
// Save the relevant data to local storage
localStorage.setItem('callsign_list', JSON.stringify(callsign_list));
localStorage.setItem('message_list', JSON.stringify(message_list));
}
function init_messages() {
// This tries to load any previous conversations from local storage
callsign_list = JSON.parse(localStorage.getItem('callsign_list'));
message_list = JSON.parse(localStorage.getItem('message_list'));
console.log("init_messages");
if (callsign_list == null) {
callsign_list = {};
}
if (message_list == null) {
message_list = {};
}
console.log(callsign_list);
console.log(message_list);
// Now loop through each callsign and add the tabs
first_callsign = null;
for (callsign in callsign_list) {
console.log("Adding callsign " + callsign);
if (first_callsign === null) {
first_callsign = callsign;
console.log("first_callsign " + first_callsign)
}
create_callsign_tab(callsign);
}
// and then populate the messages in order
for (callsign in message_list) {
new_callsign = true;
cleared = true;
for (id in message_list[callsign]) {
msg = message_list[callsign][id];
info = time_ack_from_msg(msg);
t = info['time'];
ack_id = false;
acked = false;
if (msg['type'] == MSG_TYPE_TX) {
ack_id = info['ack_id'];
acked = msg['ack'];
}
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, acked);
append_message_html(callsign, msg_html, new_callsign);
new_callsign = false;
}
}
//Click on the very first tab
if (first_callsign !== null) {
click_div = '#'+tab_string(first_callsign);
var click_timer = setTimeout(function() {
console.log("Click on first tab " + click_div);
$(click_div).click();
clearTimeout(click_timer);
}, 500);
}
}
function create_callsign_tab(callsign) {
//Create the html for the callsign tab and insert it into the DOM
console.log("create_callsign_tab " + callsign)
var callsignTabs = $("#callsignTabs");
tab_name = tab_string(callsign);
tab_content = tab_content_name(callsign);
divname = content_divname(callsign);
item_html = '<div class="tablinks" id="'+tab_name+'" onclick="openCallsign(event, \''+callsign+'\');">'+callsign+'</div>';
console.log(item_html);
callsignTabs.append(item_html);
}
function add_callsign(callsign) {
/* Ensure a callsign exists in the left hand nav */
if (callsign in callsign_list) {
return false
}
create_callsign_tab(callsign);
callsign_list[callsign] = true;
return true
}
function append_message(callsign, msg, msg_html) {
console.log("append_message " + callsign + " " + msg + " " + msg_html);
new_callsign = false
if (!message_list.hasOwnProperty(callsign)) {
message_list[callsign] = new Array();
//message_list[callsign] = new Array();
message_list[callsign] = {};
}
message_list[callsign].push(msg);
ts_id = message_ts_id(msg);
id = ts_id['id']
message_list[callsign][id] = msg;
// Find the right div to place the html
new_callsign = add_callsign(callsign);
@ -98,29 +198,43 @@ function content_divname(callsign) {
function append_message_html(callsign, msg_html, new_callsign) {
var msgsTabs = $('#msgsTabsDiv');
console.log("append_message_html " + callsign + " " + msg_html + " " + new_callsign);
divname_str = tab_content_name(callsign);
divname = content_divname(callsign);
if (new_callsign) {
// we have to add a new DIV
console.log("new callsign, create msg_div_html ");
msg_div_html = '<div class="tabcontent" id="'+divname_str+'" style="height:450px;">'+msg_html+'</div>';
console.log(msg_div_html);
msgsTabs.append(msg_div_html);
} else {
var msgDiv = $(divname);
console.log("Appending ("+ msg_html + ") to " + divname);
console.log(msgDiv);
msgDiv.append(msg_html);
console.log(msgDiv);
}
console.log("divname " + divname);
console.log($(divname).length);
$(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow");
if ($(divname).length > 0) {
$(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow");
}
$(divname).trigger('click');
}
function create_message_html(time, from, to, message, ack, msg) {
function create_message_html(time, from, to, message, ack_id, msg, acked=false) {
div_id = from + "_" + msg.id;
msg_html = '<div class="item" id="'+div_id+'">';
msg_html += '<div class="tiny text">'+time+'</div>';
msg_html += '<div class="middle aligned content">';
msg_html += '<div class="tiny red header">'+from+'</div>';
if (ack) {
msg_html += '<i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i>';
if (ack_id) {
if (acked) {
msg_html += '<div class="middle aligned content"><i class="thumbs up outline icon" id="' + ack_id + '" data-content="Message ACKed"></i></div>';
} else {
msg_html += '<div class="middle aligned content"><i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i></div>';
}
} else {
msg_html += '<i class="phone volume icon" data-content="Recieved Message"></i>';
}
@ -139,23 +253,18 @@ function flash_message(msg) {
msgid.effect("pulsate", { times:3 }, 2000);
}
function sent_msg(msg) {
var msgsdiv = $("#sendMsgsDiv");
info = time_ack_from_msg(msg);
t = info['time'];
ack_id = info['ack_id'];
ts_str = msg["ts"].toString();
ts = ts_str.split(".")[0]*1000;
id = ts_str.split('.')[0]
ack_id = "ack_" + id
var d = new Date(ts).toLocaleDateString("en-US")
var t = new Date(ts).toLocaleTimeString("en-US")
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg);
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, false);
append_message(msg['to'], msg, msg_html);
save_data();
}
function from_msg(msg) {
var msgsdiv = $("#sendMsgsDiv");
console.log(msg);
if (!from_msg_list.hasOwnProperty(msg.from)) {
from_msg_list[msg.from] = new Array();
@ -172,42 +281,55 @@ function from_msg(msg) {
from_msg_list[msg.from][msg.id] = msg
}
// We have an existing entry
ts_str = msg["ts"].toString();
ts = ts_str.split(".")[0]*1000;
id = ts_str.split('.')[0]
ack_id = "ack_" + id
var d = new Date(ts).toLocaleDateString("en-US")
var t = new Date(ts).toLocaleTimeString("en-US")
info = time_ack_from_msg(msg);
t = info['time'];
ack_id = info['ack_id'];
from = msg['from']
msg_html = create_message_html(t, from, false, msg['message'], false, msg);
msg_html = create_message_html(t, from, false, msg['message'], false, msg, false);
append_message(from, msg, msg_html);
save_data();
}
function update_msg(msg) {
var msgsdiv = $("#sendMsgsDiv");
// We have an existing entry
ts_str = msg["ts"].toString();
id = ts_str.split('.')[0]
pretty_id = "pretty_" + id
loader_id = "loader_" + id
ack_id = "ack_" + id
span_id = "span_" + id
function ack_msg(msg) {
// Acknowledge a message
console.log("ack_msg ");
console.log(msg);
console.log(message_list);
// We have an existing entry
ts_id = message_ts_id(msg);
console.log(ts_id)
id = ts_id['id'];
//Mark the message as acked
callsign = msg['to'];
// Ensure the message_list has this callsign
if (!message_list.hasOwnProperty(callsign)) {
console.log("No message_list for " + callsign);
return false
}
// Ensure the message_list has this id
if (!message_list[callsign].hasOwnProperty(id)) {
console.log("No message_list for " + callsign + " " + id);
return false
}
console.log("Marking message as acked " + callsign + " " + id)
if (message_list[callsign][id]['ack'] == true) {
console.log("Message already acked");
return false;
}
message_list[callsign][id]['ack'] = true;
ack_id = "ack_" + id
if (msg['ack'] == true) {
var ack_div = $('#' + ack_id);
//ack_div.removeClass('thumbs up outline icon');
ack_div.removeClass('thumbs down outline icon');
ack_div.addClass('thumbs up outline icon');
}
if (msg['ack'] == true) {
var loader_div = $('#' + loader_id);
var ack_div = $('#' + ack_id);
loader_div.removeClass('ui active inline loader');
loader_div.addClass('ui disabled loader');
ack_div.removeClass('thumbs up outline icon');
ack_div.addClass('thumbs up outline icon');
}
$('.ui.accordion').accordion('refresh');
$('.ui.accordion').accordion('refresh');
save_data();
}
function callsign_select(callsign) {
@ -222,16 +344,21 @@ function reset_Tabs() {
}
}
function openCallsign(evt, callsign) {
// This is called when a callsign tab is clicked
var i, tabcontent, tablinks;
tab_content = tab_content_name(callsign);
tabcontent = document.getElementsByClassName("tabcontent");
console.log(tabcontent);
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
console.log(tablinks);
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}

View File

@ -42,6 +42,11 @@
// Have to disable the beacon button.
$('#send_beacon').prop('disabled', true);
}
$("#wipe_local").click(function() {
console.log('Wipe local storage');
localStorage.clear();
});
});
</script>
</head>
@ -80,6 +85,7 @@
</div>
<input type="submit" name="submit" class="ui button" id="send_msg" value="Send" />
<button type="button" class="ui button" id="send_beacon" value="Send GPS Beacon">Send GPS Beacon</button>
<button type="button" class="ui button" id="wipe_local" value="wipe local storage">Wipe LocalStorage</button>
</form>
</div>
</div>
@ -89,7 +95,6 @@
<div class="tab" id="callsignTabs"></div>
</div>
<div class="ten wide column ui raised segment" id="msgsTabsDiv" style="height:450px;padding:0px;">
&nbsp;
</div>
</div>