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, "plugins": plugin_stats,
} }
LOG.info("APRSD Stats: DONE")
return stats return stats
def __str__(self): def __str__(self):

View File

@ -4,6 +4,10 @@ var message_list = {};
var from_msg_list = {}; var from_msg_list = {};
const socket = io("/sendmsg"); 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 size_dict(d){c=0; for (i in d) ++c; return c}
function init_chat() { function init_chat() {
@ -16,24 +20,28 @@ function init_chat() {
}); });
socket.on("sent", function(msg) { socket.on("sent", function(msg) {
if (cleared == false) { if (cleared === false) {
console.log("CLEARING #msgsTabsDiv");
var msgsdiv = $("#msgsTabsDiv"); var msgsdiv = $("#msgsTabsDiv");
msgsdiv.html('') msgsdiv.html('');
cleared = true cleared = true;
} }
msg["type"] = MSG_TYPE_TX;
sent_msg(msg); sent_msg(msg);
}); });
socket.on("ack", function(msg) { socket.on("ack", function(msg) {
update_msg(msg); msg["type"] = MSG_TYPE_ACK;
ack_msg(msg);
}); });
socket.on("new", function(msg) { socket.on("new", function(msg) {
if (cleared == false) { if (cleared === false) {
var msgsdiv = $("#msgsTabsDiv"); var msgsdiv = $("#msgsTabsDiv");
msgsdiv.html('') msgsdiv.html('')
cleared = true cleared = true;
} }
msg["type"] = MSG_TYPE_RX;
from_msg(msg); from_msg(msg);
}); });
@ -47,32 +55,124 @@ function init_chat() {
}); });
init_gps(); init_gps();
// Try and load any existing chat threads from last time
init_messages();
} }
function add_callsign(callsign) { function message_ts_id(msg) {
/* Ensure a callsign exists in the left hand nav */ //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) { function time_ack_from_msg(msg) {
return false // 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"); var callsignTabs = $("#callsignTabs");
tab_name = tab_string(callsign); tab_name = tab_string(callsign);
tab_content = tab_content_name(callsign); tab_content = tab_content_name(callsign);
divname = content_divname(callsign); divname = content_divname(callsign);
item_html = '<div class="tablinks" id="'+tab_name+'" onclick="openCallsign(event, \''+callsign+'\');">'+callsign+'</div>'; item_html = '<div class="tablinks" id="'+tab_name+'" onclick="openCallsign(event, \''+callsign+'\');">'+callsign+'</div>';
console.log(item_html);
callsignTabs.append(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; callsign_list[callsign] = true;
return true return true
} }
function append_message(callsign, msg, msg_html) { function append_message(callsign, msg, msg_html) {
console.log("append_message " + callsign + " " + msg + " " + msg_html);
new_callsign = false new_callsign = false
if (!message_list.hasOwnProperty(callsign)) { 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 // Find the right div to place the html
new_callsign = add_callsign(callsign); new_callsign = add_callsign(callsign);
@ -98,29 +198,43 @@ function content_divname(callsign) {
function append_message_html(callsign, msg_html, new_callsign) { function append_message_html(callsign, msg_html, new_callsign) {
var msgsTabs = $('#msgsTabsDiv'); var msgsTabs = $('#msgsTabsDiv');
console.log("append_message_html " + callsign + " " + msg_html + " " + new_callsign);
divname_str = tab_content_name(callsign); divname_str = tab_content_name(callsign);
divname = content_divname(callsign); divname = content_divname(callsign);
if (new_callsign) { if (new_callsign) {
// we have to add a new DIV // 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>'; 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); msgsTabs.append(msg_div_html);
} else { } else {
var msgDiv = $(divname); var msgDiv = $(divname);
console.log("Appending ("+ msg_html + ") to " + divname);
console.log(msgDiv);
msgDiv.append(msg_html); 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'); $(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; div_id = from + "_" + msg.id;
msg_html = '<div class="item" id="'+div_id+'">'; msg_html = '<div class="item" id="'+div_id+'">';
msg_html += '<div class="tiny text">'+time+'</div>'; msg_html += '<div class="tiny text">'+time+'</div>';
msg_html += '<div class="middle aligned content">'; msg_html += '<div class="middle aligned content">';
msg_html += '<div class="tiny red header">'+from+'</div>'; msg_html += '<div class="tiny red header">'+from+'</div>';
if (ack) { if (ack_id) {
msg_html += '<i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i>'; 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 { } else {
msg_html += '<i class="phone volume icon" data-content="Recieved Message"></i>'; 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); msgid.effect("pulsate", { times:3 }, 2000);
} }
function sent_msg(msg) { 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(); msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, false);
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);
append_message(msg['to'], msg, msg_html); append_message(msg['to'], msg, msg_html);
save_data();
} }
function from_msg(msg) { function from_msg(msg) {
var msgsdiv = $("#sendMsgsDiv");
console.log(msg); console.log(msg);
if (!from_msg_list.hasOwnProperty(msg.from)) { if (!from_msg_list.hasOwnProperty(msg.from)) {
from_msg_list[msg.from] = new Array(); from_msg_list[msg.from] = new Array();
@ -172,42 +281,55 @@ function from_msg(msg) {
from_msg_list[msg.from][msg.id] = msg from_msg_list[msg.from][msg.id] = msg
} }
// We have an existing entry info = time_ack_from_msg(msg);
ts_str = msg["ts"].toString(); t = info['time'];
ts = ts_str.split(".")[0]*1000; ack_id = info['ack_id'];
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")
from = msg['from'] 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); append_message(from, msg, msg_html);
save_data();
} }
function update_msg(msg) { function ack_msg(msg) {
var msgsdiv = $("#sendMsgsDiv"); // Acknowledge a message
// We have an existing entry console.log("ack_msg ");
ts_str = msg["ts"].toString(); console.log(msg);
id = ts_str.split('.')[0] console.log(message_list);
pretty_id = "pretty_" + id
loader_id = "loader_" + id
ack_id = "ack_" + id
span_id = "span_" + id
// 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) { $('.ui.accordion').accordion('refresh');
var loader_div = $('#' + loader_id); save_data();
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');
} }
function callsign_select(callsign) { function callsign_select(callsign) {
@ -222,16 +344,21 @@ function reset_Tabs() {
} }
} }
function openCallsign(evt, callsign) { function openCallsign(evt, callsign) {
// This is called when a callsign tab is clicked
var i, tabcontent, tablinks; var i, tabcontent, tablinks;
tab_content = tab_content_name(callsign); tab_content = tab_content_name(callsign);
tabcontent = document.getElementsByClassName("tabcontent"); tabcontent = document.getElementsByClassName("tabcontent");
console.log(tabcontent);
for (i = 0; i < tabcontent.length; i++) { for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none"; tabcontent[i].style.display = "none";
} }
tablinks = document.getElementsByClassName("tablinks"); tablinks = document.getElementsByClassName("tablinks");
console.log(tablinks);
for (i = 0; i < tablinks.length; i++) { for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", ""); tablinks[i].className = tablinks[i].className.replace(" active", "");
} }

View File

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