From 9635893934e1a6f29bafde003bcde911a0a63fb9 Mon Sep 17 00:00:00 2001 From: Hemna Date: Thu, 21 Sep 2023 16:29:15 -0400 Subject: [PATCH] Webchat: Added tab notifications and raw packet This patch adds an auto mouseover hover popover for displaying the raw APRS packet. This patch also adds the notification counter for an unselected tab. --- aprsd/cmds/webchat.py | 29 ++------ aprsd/web/chat/static/css/chat.css | 16 ++++ aprsd/web/chat/static/js/send-message.js | 94 +++++++++++++++--------- aprsd/web/chat/templates/index.html | 7 +- 4 files changed, 88 insertions(+), 58 deletions(-) diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index b58ff14..14bf6a1 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -23,7 +23,7 @@ from aprsd import cli_helper, client, conf, packets, stats, threads, utils from aprsd.log import rich as aprsd_logging from aprsd.main import cli from aprsd.threads import rx, tx -from aprsd.utils import objectstore, trace +from aprsd.utils import trace CONF = cfg.CONF @@ -57,7 +57,9 @@ def signal_handler(sig, frame): signal.signal(signal.SIGTERM, sys.exit(0)) -class SentMessages(objectstore.ObjectStoreMixin): +#class SentMessages(objectstore.ObjectStoreMixin): +class SentMessages: + _instance = None lock = threading.Lock() @@ -74,25 +76,7 @@ class SentMessages(objectstore.ObjectStoreMixin): @wrapt.synchronized(lock) def add(self, msg): - self.data[msg.msgNo] = self.create(msg.msgNo) - self.data[msg.msgNo]["from"] = msg.from_call - self.data[msg.msgNo]["to"] = msg.to_call - self.data[msg.msgNo]["message"] = msg.message_text.rstrip("\n") - self.data[msg.msgNo]["raw"] = msg.message_text.rstrip("\n") - - def create(self, id): - return { - "id": id, - "ts": time.time(), - "ack": False, - "from": None, - "to": None, - "raw": None, - "message": None, - "status": None, - "last_update": None, - "reply": None, - } + self.data[msg.msgNo] = msg.__dict__ @wrapt.synchronized(lock) def __len__(self): @@ -174,7 +158,7 @@ class WebChatProcessPacketThread(rx.APRSDProcessPacketThread): "reply": None, } self.socketio.emit( - "new", msg, + "new", packet.__dict__, namespace="/sendmsg", ) @@ -317,6 +301,7 @@ class SendMessageNamespace(Namespace): to_call=data["to"].upper(), message_text=data["message"], ) + pkt.prepare() self.msg = pkt msgs = SentMessages() msgs.add(pkt) diff --git a/aprsd/web/chat/static/css/chat.css b/aprsd/web/chat/static/css/chat.css index 2cbaf15..c8a837c 100644 --- a/aprsd/web/chat/static/css/chat.css +++ b/aprsd/web/chat/static/css/chat.css @@ -93,3 +93,19 @@ input[type=search]::-webkit-search-cancel-button { .bubble-arrow.alt:after { transform: rotate(45deg) scaleY(-1); } + +.popover { + max-width: 400px; +} +.popover-header { + font-size: 8pt; + max-width: 400px; + padding: 5px; + background-color: #ee; +} + +.popover-body { + white-space: pre-line; + max-width: 400px; + padding: 5px; +} diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index 3f0a604..a8bbd9d 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -2,6 +2,7 @@ var cleared = false; var callsign_list = {}; var message_list = {}; var from_msg_list = {}; +var selected_tab_callsign = null; const socket = io("/sendmsg"); MSG_TYPE_TX = "tx"; @@ -30,6 +31,8 @@ function init_chat() { }); socket.on("sent", function(msg) { + console.log("SENT: "); + console.log(msg); if (cleared === false) { console.log("CLEARING #msgsTabsDiv"); var msgsdiv = $("#msgsTabsDiv"); @@ -42,6 +45,8 @@ function init_chat() { socket.on("ack", function(msg) { msg["type"] = MSG_TYPE_ACK; + console.log("ACK MESSAGE") + console.log(msg) ack_msg(msg); }); @@ -51,6 +56,8 @@ function init_chat() { msgsdiv.html('') cleared = true; } + console.log("NEW MESSAGE") + console.log(msg) msg["type"] = MSG_TYPE_RX; from_msg(msg); }); @@ -92,6 +99,11 @@ function tab_li_string(callsign, id=false) { return tab_string(callsign,id)+"Li"; } +function tab_notification_id(callsign, id=false) { + // The ID of the span that contains the notification count + return tab_string(callsign, id)+"notify"; +} + function tab_content_name(callsign, id=false) { return tab_string(callsign, id)+"Content"; } @@ -114,7 +126,7 @@ function callsign_tab(callsign) { function message_ts_id(msg) { //Create a 'id' from the message timestamp - ts_str = msg["ts"].toString(); + ts_str = msg["timestamp"].toString(); ts = ts_str.split(".")[0]*1000; id = ts_str.split('.')[0]; return {'timestamp': ts, 'id': id}; @@ -148,8 +160,8 @@ function init_messages() { if (message_list == null) { message_list = {}; } - console.log(callsign_list); - console.log(message_list); + //console.log(callsign_list); + //console.log(message_list); // Now loop through each callsign and add the tabs first_callsign = null; @@ -177,7 +189,8 @@ function init_messages() { ack_id = info['ack_id']; acked = msg['ack']; } - msg_html = create_message_html(d, t, msg['from'], msg['to'], msg['message'], ack_id, msg, acked); + msg_html = create_message_html(d, t, msg['from_call'], msg['to_call'], + msg['message_text'], ack_id, msg, acked); append_message_html(callsign, msg_html, new_callsign); new_callsign = false; } @@ -204,20 +217,17 @@ function scroll_main_content(callsign=false) { c_scroll_height = c_div.prop('scrollHeight'); //console.log("callsign height " + c_height + " scrollHeight " + c_scroll_height); if (c_height === undefined) { - console.log("c_height is undefined"); return false; } if (c_height > clientHeight) { wc.animate({ scrollTop: c_scroll_height }, 500); } else { - console.log("scroll to 0 " + callsign) wc.animate({ scrollTop: 0 }, 500); } } else { if (scrollHeight > clientHeight) { wc.animate({ scrollTop: wc.prop('scrollHeight') }, 500); } else { - console.log("scroll to 0 " + callsign) wc.animate({ scrollTop: 0 }, 500); } } @@ -228,6 +238,7 @@ function create_callsign_tab(callsign, active=false) { var callsignTabs = $("#msgsTabList"); tab_id = tab_string(callsign); tab_id_li = tab_li_string(callsign); + tab_notify_id = tab_notification_id(callsign); tab_content = tab_content_name(callsign); if (active) { active_str = "active"; @@ -236,8 +247,10 @@ function create_callsign_tab(callsign, active=false) { } item_html = '' callsignTabs.append(item_html); @@ -303,6 +316,15 @@ function append_message(callsign, msg, msg_html) { ts_id = message_ts_id(msg); id = ts_id['id'] message_list[callsign][id] = msg; + if (selected_tab_callsign != callsign) { + // We need to update the notification for the tab + tab_notify_id = tab_notification_id(callsign, true); + // get the current count of notifications + count = parseInt($(tab_notify_id).text()); + count += 1; + $(tab_notify_id).text(count); + $(tab_notify_id).removeClass('visually-hidden'); + } // Find the right div to place the html new_callsign = add_callsign(callsign); @@ -310,8 +332,8 @@ function append_message(callsign, msg, msg_html) { if (new_callsign) { //Now click the tab callsign_tab_id = callsign_tab(callsign); - $(callsign_tab_id).click(); - callsign_select(callsign); + //$(callsign_tab_id).click(); + //callsign_select(callsign); } } @@ -338,35 +360,42 @@ function create_message_html(date, time, from, to, message, ack_id, msg, acked=f alt = "" } - bubble_class = "bubble" + alt + bubble_class = "bubble" + alt + " text-nowrap" bubble_name_class = "bubble-name" + alt date_str = date + " " + time; + sane_date_str = date_str.replace(/ /g,"").replaceAll("/","").replaceAll(":",""); msg_html = '
'; - msg_html += '
'; + msg_html += '
'; msg_html += '
'; msg_html += '

'+from+'  '; msg_html += ''+date_str+''; if (ack_id) { if (acked) { - msg_html += 'thumb_up'; + msg_html += 'thumb_up'; } else { - msg_html += 'thumb_down'; + msg_html += 'thumb_down'; } } msg_html += "

"; bubble_msg_class = "bubble-message" if (ack_id) { bubble_arrow_class = "bubble-arrow alt" + popover_placement = "left" } else { bubble_arrow_class = "bubble-arrow" + popover_placement = "right" } msg_html += '

'+message+'

'; msg_html += '
'; msg_html += "
"; - return msg_html + popover_html = '\n' + + return msg_html+popover_html } function flash_message(msg) { @@ -383,35 +412,34 @@ function sent_msg(msg) { d = info['date']; ack_id = info['ack_id']; - msg_html = create_message_html(d, t, msg['from'], msg['to'], msg['message'], ack_id, msg, false); - append_message(msg['to'], msg, msg_html); + msg_html = create_message_html(d, t, msg['from_call'], msg['to_call'], msg['message_text'], ack_id, msg, false); + append_message(msg['to_call'], msg, msg_html); save_data(); - scroll_main_content(msg['from']); + scroll_main_content(msg['from_call']); } function from_msg(msg) { if (!from_msg_list.hasOwnProperty(msg.from)) { - from_msg_list[msg.from] = new Array(); + from_msg_list[msg.from_call] = new Array(); } - if (msg.id in from_msg_list[msg.from]) { + if (msg.id in from_msg_list[msg.from_call]) { // We already have this message console.log("We already have this message " + msg); // Do some flashy thing? flash_message(msg); return false } else { - console.log("Adding message " + msg.id + " to " + msg.from); - from_msg_list[msg.from][msg.id] = msg + console.log("Adding message " + msg.msgNo + " to " + msg.from_call); + from_msg_list[msg.from_call][msg.msgNo] = msg } - info = time_ack_from_msg(msg); t = info['time']; d = info['date']; ack_id = info['ack_id']; - from = msg['from'] - msg_html = create_message_html(d, t, from, false, msg['message'], false, msg, false); + from = msg['from_call'] + msg_html = create_message_html(d, t, from, false, msg['message_text'], false, msg, false); append_message(from, msg, msg_html); save_data(); scroll_main_content(from); @@ -419,14 +447,11 @@ function from_msg(msg) { function ack_msg(msg) { // Acknowledge a message - console.log("ack_msg "); - // 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']; + callsign = msg['to_call']; // Ensure the message_list has this callsign if (!message_list.hasOwnProperty(callsign)) { console.log("No message_list for " + callsign); @@ -456,8 +481,11 @@ function ack_msg(msg) { } function callsign_select(callsign) { - console.log("callsign_select " + callsign); - var tocall = $("#to_call"); - tocall.val(callsign); - scroll_main_content(callsign); + var tocall = $("#to_call"); + tocall.val(callsign); + scroll_main_content(callsign); + selected_tab_callsign = callsign; + tab_notify_id = tab_notification_id(callsign, true); + $(tab_notify_id).addClass('visually-hidden'); + $(tab_notify_id).text(0); } diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 0569e7f..0578570 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -2,7 +2,7 @@ - + @@ -36,8 +36,8 @@ var latitude = parseFloat('{{ latitude|safe }}'); var longitude = parseFloat('{{ longitude|safe }}'); - var memory_chart = null - var message_chart = null + var memory_chart = null; + var message_chart = null; $(document).ready(function() { console.log(initial_stats); @@ -64,6 +64,7 @@ var callsign = tab.attr("callsign"); var to_call = $('#to_call'); to_call.val(callsign); + selected_tab_callsign = callsign; }); });