From 7292744a78c179d17caadd4d659caba38cccc4f8 Mon Sep 17 00:00:00 2001 From: Hemna Date: Tue, 5 Sep 2023 14:14:52 -0400 Subject: [PATCH 1/9] 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. --- aprsd/stats.py | 1 - aprsd/web/chat/static/js/send-message.js | 239 +++++++++++++++++------ aprsd/web/chat/templates/index.html | 7 +- 3 files changed, 189 insertions(+), 58 deletions(-) diff --git a/aprsd/stats.py b/aprsd/stats.py index 3c2517e..5c8380a 100644 --- a/aprsd/stats.py +++ b/aprsd/stats.py @@ -246,7 +246,6 @@ class APRSDStats: }, "plugins": plugin_stats, } - LOG.info("APRSD Stats: DONE") return stats def __str__(self): diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index 3888e1f..dff2483 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -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 = ''; + 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 = '
'+msg_html+'
'; + 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 = '
'; msg_html += '
'+time+'
'; msg_html += '
'; msg_html += '
'+from+'
'; - if (ack) { - msg_html += ''; + if (ack_id) { + if (acked) { + msg_html += '
'; + } else { + msg_html += '
'; + } } else { msg_html += ''; } @@ -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", ""); } diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 5e6e3b6..f487860 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -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(); + }); }); @@ -80,6 +85,7 @@
+
@@ -89,7 +95,6 @@
-  
From 29f21a946951b0d42aab6f339da684933c6e95ed Mon Sep 17 00:00:00 2001 From: Hemna Date: Wed, 6 Sep 2023 11:20:59 -0400 Subject: [PATCH 2/9] Updated the webchat UI to look like iMessage --- aprsd/cmds/webchat.py | 2 +- aprsd/web/chat/static/css/tabs.css | 2 +- aprsd/web/chat/static/js/send-message.js | 74 +++++++++++++++--------- aprsd/web/chat/templates/index.html | 7 ++- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index 42d74c0..dd796d9 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -229,7 +229,7 @@ def index(): html_template = "index.html" # For development - # html_template = "mobile.html" + html_template = "index.html" LOG.debug(f"Template {html_template}") diff --git a/aprsd/web/chat/static/css/tabs.css b/aprsd/web/chat/static/css/tabs.css index 755c943..d7ad49c 100644 --- a/aprsd/web/chat/static/css/tabs.css +++ b/aprsd/web/chat/static/css/tabs.css @@ -37,5 +37,5 @@ border: 1px solid #ccc; height: 450px; overflow-y: scroll; - background-color: white; + background-color: #CCCCCC; } diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index dff2483..87d66e5 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -117,13 +117,14 @@ function init_messages() { msg = message_list[callsign][id]; info = time_ack_from_msg(msg); t = info['time']; + d = info['date']; 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); + msg_html = create_message_html(d, t, msg['from'], msg['to'], msg['message'], ack_id, msg, acked); append_message_html(callsign, msg_html, new_callsign); new_callsign = false; } @@ -204,7 +205,7 @@ function append_message_html(callsign, msg_html, new_callsign) { if (new_callsign) { // we have to add a new DIV console.log("new callsign, create msg_div_html "); - msg_div_html = '
'+msg_html+'
'; + msg_div_html = '
'+msg_html+'
'; console.log(msg_div_html); msgsTabs.append(msg_div_html); } else { @@ -218,30 +219,45 @@ function append_message_html(callsign, msg_html, new_callsign) { console.log($(divname).length); if ($(divname).length > 0) { - $(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow"); + $(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "fast"); } $(divname).trigger('click'); } -function create_message_html(time, from, to, message, ack_id, msg, acked=false) { +function create_message_html(date, time, from, to, message, ack_id, msg, acked=false) { div_id = from + "_" + msg.id; - msg_html = '
'; - msg_html += '
'+time+'
'; - msg_html += '
'; - msg_html += '
'+from+'
'; + if (ack_id) { + bubble_class = "bubble alt" + bubble_name_class = "bubble-name alt" + } else { + bubble_class = "bubble" + bubble_name_class = "bubble-name" + } + + date_str = date + " " + time; + + msg_html = '
'; + msg_html += '
'; + msg_html += '

'+from+'  '; + msg_html += ''+date_str+''; if (ack_id) { if (acked) { - msg_html += '

'; + msg_html += 'thumb_up'; } else { - msg_html += '
'; + msg_html += 'thumb_down'; } - } else { - msg_html += ''; } - msg_html += '
>   
'; - msg_html += '
'; - msg_html += '
'+message+'
'; - msg_html += '

'; + msg_html += "

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

'+message+'

'; + msg_html += '
'; + msg_html += "
"; return msg_html } @@ -257,9 +273,10 @@ function flash_message(msg) { function sent_msg(msg) { info = time_ack_from_msg(msg); t = info['time']; + d = info['date']; ack_id = info['ack_id']; - msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, false); + msg_html = create_message_html(d, t, msg['from'], msg['to'], msg['message'], ack_id, msg, false); append_message(msg['to'], msg, msg_html); save_data(); } @@ -283,10 +300,11 @@ function from_msg(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(t, from, false, msg['message'], false, msg, false); + msg_html = create_message_html(d, t, from, false, msg['message'], false, msg, false); append_message(from, msg, msg_html); save_data(); } @@ -324,8 +342,9 @@ function ack_msg(msg) { 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'); + ack_div.html('thumb_up'); + //ack_div.removeClass('thumbs down outline icon'); + //ack_div.addClass('thumbs up outline icon'); } $('.ui.accordion').accordion('refresh'); @@ -338,30 +357,31 @@ function callsign_select(callsign) { } function reset_Tabs() { - tabcontent = document.getElementsByClassName("tabcontent"); + tabcontent = document.getElementsByClassName("speech-wrapper"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; } } - - function openCallsign(evt, callsign) { // This is called when a callsign tab is clicked var i, tabcontent, tablinks; tab_content = tab_content_name(callsign); + console.log("openCallsign " + tab_content); - tabcontent = document.getElementsByClassName("tabcontent"); - console.log(tabcontent); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; + tabs = document.getElementsByClassName("speech-wrapper"); + console.log(tabs); + for (i = 0; i < tabs.length; i++) { + tabs[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", ""); } + + //$(tab_content).style.display = "block"; document.getElementById(tab_content).style.display = "block"; evt.target.className += " active"; callsign_select(callsign); diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index f487860..6e4ffc7 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -15,6 +15,9 @@ + + + @@ -91,10 +94,10 @@
-
+
-
+
From 14e984c9b4143309b595c9e55ad8e9126095f9c4 Mon Sep 17 00:00:00 2001 From: Hemna Date: Fri, 8 Sep 2023 11:19:24 -0400 Subject: [PATCH 3/9] Reworked webchat with new UI This patch reworks the webchat UI to work in both desktop and mobile layouts. Comprimises were made, but there is 1 codebase now between both desktop and mobile. This patch also includes the new imessage/sms chat look. --- aprsd/web/chat/static/css/index.css | 51 +------- aprsd/web/chat/static/js/send-message.js | 150 ++++++++++++----------- aprsd/web/chat/templates/index.html | 76 ++++++------ 3 files changed, 118 insertions(+), 159 deletions(-) diff --git a/aprsd/web/chat/static/css/index.css b/aprsd/web/chat/static/css/index.css index b39acab..3f99928 100644 --- a/aprsd/web/chat/static/css/index.css +++ b/aprsd/web/chat/static/css/index.css @@ -1,6 +1,6 @@ body { background: #eeeeee; - margin: 2em; + margin: 1em; text-align: center; font-family: system-ui, sans-serif; } @@ -11,34 +11,13 @@ footer { height: 10vh; } -.ui.segment { - background: #eeeeee; -} - -ul.list { - list-style-type: disc; -} -ul.list li { - list-style-position: outside; -} - -#left { - margin-right: 2px; - height: 300px; -} -#right { - height: 300px; -} -#center { - height: 300px; -} - #title { font-size: 4em; } #version{ font-size: .5em; } + #uptime, #aprsis { font-size: 1em; } @@ -66,29 +45,3 @@ ul.list li { width: 16px; height: 16px; } - -#msgsTabsDiv .ui.tab { - margin:0px; - padding:0px; - display: block; -} - -#msgsTabsDiv .header, .tiny.text, .content, .break, - .thumbs.down.outline.icon, - .phone.volume.icon - { - display: inline-block; - float: left; - position: relative; -} -#msgsTabsDiv .tiny.text { - width:100px; -} -#msgsTabsDiv .tiny.header { - width:100px; - text-align: left; -} -#msgsTabsDiv .break { - margin: 2px; - text-align: left; -} diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index 87d66e5..f683639 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -105,9 +105,12 @@ function init_messages() { console.log("Adding callsign " + callsign); if (first_callsign === null) { first_callsign = callsign; + active = true; console.log("first_callsign " + first_callsign) + } else { + active = false; } - create_callsign_tab(callsign); + create_callsign_tab(callsign, active); } // and then populate the messages in order for (callsign in message_list) { @@ -131,27 +134,54 @@ function init_messages() { } //Click on the very first tab + /* if (first_callsign !== null) { - click_div = '#'+tab_string(first_callsign); + callsign_tab_id = callsign_tab(first_callsign); var click_timer = setTimeout(function() { - console.log("Click on first tab " + click_div); - $(click_div).click(); + console.log("Click on first tab " + callsign_tab_id); + $(callsign_tab_id).click(); clearTimeout(click_timer); }, 500); } + */ } -function create_callsign_tab(callsign) { +function create_callsign_tab(callsign, active=false) { //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); + var callsignTabs = $("#msgsTabList"); + tab_id = tab_string(callsign); tab_content = tab_content_name(callsign); - divname = content_divname(callsign); + if (active) { + active_str = "active"; + } else { + active_str = ""; + } - item_html = ''; + item_html = ''; console.log(item_html); callsignTabs.append(item_html); + create_callsign_tab_content(callsign, active); +} + +function create_callsign_tab_content(callsign, active=false) { + console.log("create_callsign_tab_content for CALLSIGN=" + callsign) + var callsignTabsContent = $("#msgsTabContent"); + tab_id = tab_string(callsign); + tab_content = tab_content_name(callsign); + wrapper_id = tab_content_speech_wrapper(callsign); + if (active) { + active_str = "show active"; + } else { + active_str = ''; + } + + item_html = '
'; + item_html += '
'; + item_html += '
'; + callsignTabsContent.append(item_html); } function add_callsign(callsign) { @@ -159,7 +189,13 @@ function add_callsign(callsign) { if (callsign in callsign_list) { return false } - create_callsign_tab(callsign); + len = Object.keys(callsign_list).length; + if (len == 0) { + active = true; + } else { + active = false; + } + create_callsign_tab(callsign, active); callsign_list[callsign] = true; return true } @@ -179,9 +215,9 @@ function append_message(callsign, msg, msg_html) { new_callsign = add_callsign(callsign); append_message_html(callsign, msg_html, new_callsign); if (new_callsign) { - //click on the new tab - click_div = '#'+tab_string(callsign); - $(click_div).click(); + //Now click the tab + callsign_tab_id = callsign_tab(callsign); + $(callsign_tab_id).click(); } } @@ -193,50 +229,57 @@ function tab_content_name(callsign) { return tab_string(callsign)+"Content"; } +function tab_content_speech_wrapper(callsign) { + return tab_string(callsign)+"SpeechWrapper"; +} + +function tab_content_speech_wrapper_id(callsign) { + return "#"+tab_content_speech_wrapper(callsign); +} + function content_divname(callsign) { return "#"+tab_content_name(callsign); } +function callsign_tab(callsign) { + return "#"+tab_string(callsign); +} + function append_message_html(callsign, msg_html, new_callsign) { var msgsTabs = $('#msgsTabsDiv'); - console.log("append_message_html " + callsign + " " + msg_html + " " + new_callsign); + console.log("append_message_html " + callsign + " new callsign? " + 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 = '
'+msg_html+'
'; - 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); + tab_content = tab_content_name(callsign); + wrapper_id = tab_content_speech_wrapper_id(callsign); - if ($(divname).length > 0) { - $(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "fast"); + console.log("Appending ("+ msg_html + ") to " + wrapper_id); + $(wrapper_id).append(msg_html); + console.log($(wrapper_id).html()); + + console.log("wrapper_id " + wrapper_id); + console.log($(wrapper_id).children().length); + + if ($(wrapper_id).children().length > 0) { + $(wrapper_id).animate({scrollTop: $(wrapper_id)[0].scrollHeight}, "fast"); } - $(divname).trigger('click'); + } function create_message_html(date, time, from, to, message, ack_id, msg, acked=false) { div_id = from + "_" + msg.id; if (ack_id) { - bubble_class = "bubble alt" - bubble_name_class = "bubble-name alt" + alt = " alt" } else { - bubble_class = "bubble" - bubble_name_class = "bubble-name" + alt = "" } + bubble_class = "bubble" + alt + bubble_name_class = "bubble-name" + alt date_str = date + " " + time; - msg_html = '
'; + msg_html = '
'; + msg_html += '
'; msg_html += '
'; msg_html += '

'+from+'  '; msg_html += ''+date_str+''; @@ -257,7 +300,7 @@ function create_message_html(date, time, from, to, message, ack_id, msg, acked=f msg_html += '

'+message+'

'; msg_html += '
'; - msg_html += "
"; + msg_html += "
"; return msg_html } @@ -355,34 +398,3 @@ function callsign_select(callsign) { var tocall = $("#to_call"); tocall.val(callsign); } - -function reset_Tabs() { - tabcontent = document.getElementsByClassName("speech-wrapper"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } -} - -function openCallsign(evt, callsign) { - // This is called when a callsign tab is clicked - var i, tabcontent, tablinks; - - tab_content = tab_content_name(callsign); - console.log("openCallsign " + tab_content); - - tabs = document.getElementsByClassName("speech-wrapper"); - console.log(tabs); - for (i = 0; i < tabs.length; i++) { - tabs[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", ""); - } - - //$(tab_content).style.display = "block"; - document.getElementById(tab_content).style.display = "block"; - evt.target.className += " active"; - callsign_select(callsign); -} diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 6e4ffc7..27f4da2 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -10,10 +10,8 @@ - - - - + + @@ -36,7 +34,7 @@ console.log(initial_stats); start_update(); init_chat(); - reset_Tabs(); + //reset_Tabs(); console.log("latitude", latitude); console.log("longitude", longitude); @@ -55,49 +53,45 @@ -
-

APRSD WebChat {{ version }}

-
- -
-
- {{ callsign }} - connected to - {{ aprs_connection|safe }} +
+
+
+

APRSD WebChat {{ version }}

+
- -
- NONE +
+
+ {{ callsign }} + connected to + {{ aprs_connection|safe }} + NONE +
-
- -
-

Send Message

-
-
-
- - -
- -
+
+ +
+ +
-
- - +
+ + +
+
+ + +
- - -
-
-
-
-
-
-
+
+
+ +
+
+
From 852760220f10b6bf29ae6f9bacb1ff1908412027 Mon Sep 17 00:00:00 2001 From: Hemna Date: Fri, 8 Sep 2023 12:34:13 -0400 Subject: [PATCH 4/9] Added close X on webchat tabs This patch adds an X on each tab as a way to close the conversation and nuke the local storage for the conversation. --- aprsd/cmds/webchat.py | 16 ++-- aprsd/web/chat/static/js/send-message.js | 104 +++++++++++------------ aprsd/web/chat/templates/index.html | 2 +- 3 files changed, 56 insertions(+), 66 deletions(-) diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index dd796d9..6bcddc5 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -15,7 +15,6 @@ from flask.logging import default_handler from flask_httpauth import HTTPBasicAuth from flask_socketio import Namespace, SocketIO from oslo_config import cfg -from user_agents import parse as ua_parse from werkzeug.security import check_password_hash, generate_password_hash import wrapt @@ -217,20 +216,19 @@ def _get_transport(stats): @auth.login_required @flask_app.route("/") def index(): - ua_str = request.headers.get("User-Agent") + # ua_str = request.headers.get("User-Agent") # this takes about 2 seconds :( - user_agent = ua_parse(ua_str) - LOG.debug(f"Is mobile? {user_agent.is_mobile}") + # user_agent = ua_parse(ua_str) + # LOG.debug(f"Is mobile? {user_agent.is_mobile}") stats = _stats() - if user_agent.is_mobile: - html_template = "mobile.html" - else: - html_template = "index.html" + # if user_agent.is_mobile: + # html_template = "mobile.html" + # else: + # html_template = "index.html" # For development html_template = "index.html" - LOG.debug(f"Template {html_template}") transport, aprs_connection = _get_transport(stats) diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index f683639..73242e4 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -59,6 +59,35 @@ function init_chat() { init_messages(); } +function tab_string(callsign, id=false) { + name = "msgs"+callsign; + if (id) { + return "#"+name; + } else { + return name; + } +} + +function tab_content_name(callsign, id=false) { + return tab_string(callsign, id)+"Content"; +} + +function tab_content_speech_wrapper(callsign, id=false) { + return tab_string(callsign, id)+"SpeechWrapper"; +} + +function tab_content_speech_wrapper_id(callsign) { + return "#"+tab_content_speech_wrapper(callsign); +} + +function content_divname(callsign) { + return "#"+tab_content_name(callsign); +} + +function callsign_tab(callsign) { + return "#"+tab_string(callsign); +} + function message_ts_id(msg) { //Create a 'id' from the message timestamp ts_str = msg["ts"].toString(); @@ -89,7 +118,6 @@ 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 = {}; } @@ -102,11 +130,9 @@ function init_messages() { // 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; active = true; - console.log("first_callsign " + first_callsign) } else { active = false; } @@ -132,23 +158,10 @@ function init_messages() { new_callsign = false; } } - - //Click on the very first tab - /* - if (first_callsign !== null) { - callsign_tab_id = callsign_tab(first_callsign); - var click_timer = setTimeout(function() { - console.log("Click on first tab " + callsign_tab_id); - $(callsign_tab_id).click(); - clearTimeout(click_timer); - }, 500); - } - */ } function create_callsign_tab(callsign, active=false) { //Create the html for the callsign tab and insert it into the DOM - console.log("create_callsign_tab " + callsign) var callsignTabs = $("#msgsTabList"); tab_id = tab_string(callsign); tab_content = tab_content_name(callsign); @@ -159,15 +172,15 @@ function create_callsign_tab(callsign, active=false) { } item_html = ''; - console.log(item_html); + item_html += '' callsignTabs.append(item_html); create_callsign_tab_content(callsign, active); } function create_callsign_tab_content(callsign, active=false) { - console.log("create_callsign_tab_content for CALLSIGN=" + callsign) var callsignTabsContent = $("#msgsTabContent"); tab_id = tab_string(callsign); tab_content = tab_content_name(callsign); @@ -184,6 +197,21 @@ function create_callsign_tab_content(callsign, active=false) { callsignTabsContent.append(item_html); } +function delete_tab(callsign) { + // User asked to delete the tab and the conversation + tab_id = tab_string(callsign, true); + tab_content = tab_content_name(callsign, true); + $(tab_id).remove(); + $(tab_content).remove(); + delete callsign_list[callsign]; + delete message_list[callsign]; + + // Now select the first tab + first_tab = $("#msgsTabList").children().first().children().first(); + $(first_tab).click(); + save_data(); +} + function add_callsign(callsign) { /* Ensure a callsign exists in the left hand nav */ if (callsign in callsign_list) { @@ -201,7 +229,6 @@ function add_callsign(callsign) { } 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(); @@ -221,44 +248,15 @@ function append_message(callsign, msg, msg_html) { } } -function tab_string(callsign) { - return "msgs"+callsign; -} - -function tab_content_name(callsign) { - return tab_string(callsign)+"Content"; -} - -function tab_content_speech_wrapper(callsign) { - return tab_string(callsign)+"SpeechWrapper"; -} - -function tab_content_speech_wrapper_id(callsign) { - return "#"+tab_content_speech_wrapper(callsign); -} - -function content_divname(callsign) { - return "#"+tab_content_name(callsign); -} - -function callsign_tab(callsign) { - return "#"+tab_string(callsign); -} function append_message_html(callsign, msg_html, new_callsign) { var msgsTabs = $('#msgsTabsDiv'); - console.log("append_message_html " + callsign + " new callsign? " + new_callsign); divname_str = tab_content_name(callsign); divname = content_divname(callsign); tab_content = tab_content_name(callsign); wrapper_id = tab_content_speech_wrapper_id(callsign); - console.log("Appending ("+ msg_html + ") to " + wrapper_id); $(wrapper_id).append(msg_html); - console.log($(wrapper_id).html()); - - console.log("wrapper_id " + wrapper_id); - console.log($(wrapper_id).children().length); if ($(wrapper_id).children().length > 0) { $(wrapper_id).animate({scrollTop: $(wrapper_id)[0].scrollHeight}, "fast"); @@ -325,7 +323,6 @@ function sent_msg(msg) { } function from_msg(msg) { - console.log(msg); if (!from_msg_list.hasOwnProperty(msg.from)) { from_msg_list[msg.from] = new Array(); } @@ -355,8 +352,6 @@ function from_msg(msg) { 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); @@ -384,10 +379,7 @@ function ack_msg(msg) { if (msg['ack'] == true) { var ack_div = $('#' + ack_id); - //ack_div.removeClass('thumbs up outline icon'); ack_div.html('thumb_up'); - //ack_div.removeClass('thumbs down outline icon'); - //ack_div.addClass('thumbs up outline icon'); } $('.ui.accordion').accordion('refresh'); diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 27f4da2..62ccfa8 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -80,7 +80,7 @@
- +
From 70ddc44b5c30e0381d5806a8a0c20979bcc0c464 Mon Sep 17 00:00:00 2001 From: Hemna Date: Fri, 8 Sep 2023 14:57:58 -0400 Subject: [PATCH 5/9] Deleted webchat mobile pages removed user-agents package dependency --- aprsd/cmds/webchat.py | 9 - .../web/chat/static/js/send-message-mobile.js | 246 ------------------ aprsd/web/chat/templates/mobile.html | 98 ------- dev-requirements.txt | 38 ++- requirements.in | 2 - requirements.txt | 30 +-- 6 files changed, 32 insertions(+), 391 deletions(-) delete mode 100644 aprsd/web/chat/static/js/send-message-mobile.js delete mode 100644 aprsd/web/chat/templates/mobile.html diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index 6bcddc5..b58ff14 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -216,17 +216,8 @@ def _get_transport(stats): @auth.login_required @flask_app.route("/") def index(): - # ua_str = request.headers.get("User-Agent") - # this takes about 2 seconds :( - # user_agent = ua_parse(ua_str) - # LOG.debug(f"Is mobile? {user_agent.is_mobile}") stats = _stats() - # if user_agent.is_mobile: - # html_template = "mobile.html" - # else: - # html_template = "index.html" - # For development html_template = "index.html" LOG.debug(f"Template {html_template}") diff --git a/aprsd/web/chat/static/js/send-message-mobile.js b/aprsd/web/chat/static/js/send-message-mobile.js deleted file mode 100644 index b8e6c03..0000000 --- a/aprsd/web/chat/static/js/send-message-mobile.js +++ /dev/null @@ -1,246 +0,0 @@ -var cleared = false; -var callsign_list = {}; -var message_list = {}; -var from_msg_list = {}; -const socket = io("/sendmsg"); - -function size_dict(d){c=0; for (i in d) ++c; return c} - -function init_chat() { - socket.on('connect', function () { - console.log("Connected to socketio"); - }); - socket.on('connected', function(msg) { - console.log("Connected!"); - console.log(msg); - }); - - socket.on("sent", function(msg) { - if (cleared == false) { - var msgsdiv = $("#msgsTabsDiv"); - msgsdiv.html('') - cleared = true - } - sent_msg(msg); - }); - - socket.on("ack", function(msg) { - update_msg(msg); - }); - - socket.on("new", function(msg) { - if (cleared == false) { - var msgsdiv = $("#msgsTabsDiv"); - msgsdiv.html('') - cleared = true - } - from_msg(msg); - }); - - $("#sendform").submit(function(event) { - event.preventDefault(); - msg = {'to': $('#to_call').val().toUpperCase(), - 'message': $('#message').val(), - } - socket.emit("send", msg); - $('#message').val(''); - }); - - init_gps(); -} - - -function add_callsign(callsign) { - /* Ensure a callsign exists in the left hand nav */ - dropdown = $('#callsign_dropdown') - - if (callsign in callsign_list) { - console.log(callsign+' already in list.') - return false - } - - var callsignTabs = $("#callsignTabs"); - tab_name = tab_string(callsign); - tab_content = tab_content_name(callsign); - divname = content_divname(callsign); - - item_html = '
'+callsign+'
'; - callsignTabs.append(item_html); - - callsign_list[callsign] = {'name': callsign, 'value': callsign, 'text': callsign} - return true -} - -function append_message(callsign, msg, msg_html) { - console.log('append_message'); - new_callsign = false - if (!message_list.hasOwnProperty(callsign)) { - message_list[callsign] = new Array(); - } - message_list[callsign].push(msg); - - // Find the right div to place the html - new_callsign = add_callsign(callsign); - append_message_html(callsign, msg_html, new_callsign); - if (new_callsign) { - //click on the new tab - click_div = '#'+tab_string(callsign); - console.log("Click on "+click_div); - $(click_div).click(); - } -} - -function tab_string(callsign) { - return "msgs"+callsign; -} - -function tab_content_name(callsign) { - return tab_string(callsign)+"Content"; -} - -function content_divname(callsign) { - return "#"+tab_content_name(callsign); -} - -function append_message_html(callsign, msg_html, new_callsign) { - var msgsTabs = $('#msgsTabsDiv'); - divname_str = tab_content_name(callsign); - divname = content_divname(callsign); - if (new_callsign) { - // we have to add a new DIV - msg_div_html = '
'+msg_html+'
'; - msgsTabs.append(msg_div_html); - } else { - var msgDiv = $(divname); - msgDiv.append(msg_html); - } - - $(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow"); -} - -function create_message_html(time, from, to, message, ack, msg) { - div_id = from + "_" + msg.id; - msg_html = '
'; - msg_html += '
'+time+'
'; - msg_html += '
'; - msg_html += '
'+from+'
'; - if (ack) { - msg_html += ''; - } else { - msg_html += ''; - } - msg_html += '
>   
'; - msg_html += '
'; - msg_html += '
'+message+'
'; - msg_html += '

'; - - return msg_html -} - -function flash_message(msg) { - // Callback function to bring a hidden box back - id = msg.from + "_" + msg.id; - var msgid = $('#'+id); - msgid.effect("pulsate", { times:3 }, 2000); -} - -function sent_msg(msg) { - var msgsdiv = $("#sendMsgsDiv"); - - 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); - append_message(msg['to'], msg, msg_html); -} - -function from_msg(msg) { - var msgsdiv = $("#sendMsgsDiv"); - console.log(msg); - if (!from_msg_list.hasOwnProperty(msg.from)) { - from_msg_list[msg.from] = new Array(); - } - - if (msg.id in from_msg_list[msg.from]) { - // 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 - } - - // 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") - - from = msg['from'] - msg_html = create_message_html(t, from, false, msg['message'], false, msg); - append_message(from, msg, msg_html); -} - -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 - - - - 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'); -} - -function callsign_select(callsign) { - var tocall = $("#to_call"); - tocall.val(callsign); -} - -function reset_Tabs() { - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } -} - -function openCallsign(evt, callsign) { - var i, tabcontent, tablinks; - - tab_content = tab_content_name(callsign); - - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } - tablinks = document.getElementsByClassName("tablinks"); - for (i = 0; i < tablinks.length; i++) { - tablinks[i].className = tablinks[i].className.replace(" active", ""); - } - document.getElementById(tab_content).style.display = "block"; - evt.target.className += " active"; - callsign_select(callsign); -} diff --git a/aprsd/web/chat/templates/mobile.html b/aprsd/web/chat/templates/mobile.html deleted file mode 100644 index 091c315..0000000 --- a/aprsd/web/chat/templates/mobile.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

APRSD WebChat {{ version }}

-
- -
-
- {{ callsign }} - connected to - {{ aprs_connection|safe }} -
- -
- NONE -
-
- -
-

Send Message

-
-
-
Callsign
- - -
-
- - - -
-
- - -
- -
-
- -
- -
-   -
-
- -
- PyPI version - -
- - diff --git a/dev-requirements.txt b/dev-requirements.txt index 8d55f81..0330cf3 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,34 +1,33 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --annotation-style=line dev-requirements.in # -add-trailing-comma==3.0.1 # via gray +add-trailing-comma==3.1.0 # via gray alabaster==0.7.13 # via sphinx attrs==23.1.0 # via jsonschema, referencing autoflake==1.5.3 # via gray babel==2.12.1 # via sphinx black==23.7.0 # via gray -build==0.10.0 # via pip-tools +build==1.0.3 # via pip-tools cachetools==5.3.1 # via tox certifi==2023.7.22 # via requests cfgv==3.4.0 # via pre-commit chardet==5.2.0 # via tox charset-normalizer==3.2.0 # via requests -click==8.1.6 # via black, pip-tools +click==8.1.7 # via black, pip-tools colorama==0.4.6 # via tox commonmark==0.9.1 # via rich configargparse==1.7 # via gray -coverage[toml]==7.3.0 # via pytest-cov +coverage[toml]==7.3.1 # via pytest-cov distlib==0.3.7 # via virtualenv docutils==0.20.1 # via sphinx -exceptiongroup==1.1.3 # via pytest -filelock==3.12.2 # via tox, virtualenv +filelock==3.12.3 # via tox, virtualenv fixit==0.1.4 # via gray flake8==6.1.0 # via -r dev-requirements.in, fixit, pep8-naming gray==0.13.0 # via -r dev-requirements.in -identify==2.5.26 # via pre-commit +identify==2.5.27 # via pre-commit idna==3.4 # via requests imagesize==1.4.1 # via sphinx importlib-resources==6.0.1 # via fixit @@ -40,7 +39,7 @@ jsonschema-specifications==2023.7.1 # via jsonschema libcst==1.0.1 # via fixit markupsafe==2.1.3 # via jinja2 mccabe==0.7.0 # via flake8 -mypy==1.5.0 # via -r dev-requirements.in +mypy==1.5.1 # via -r dev-requirements.in mypy-extensions==1.0.0 # via black, mypy, typing-inspect nodeenv==1.8.0 # via pre-commit packaging==23.1 # via black, build, pyproject-api, pytest, sphinx, tox @@ -48,40 +47,39 @@ pathspec==0.11.2 # via black pep8-naming==0.13.3 # via -r dev-requirements.in pip-tools==7.3.0 # via -r dev-requirements.in platformdirs==3.10.0 # via black, tox, virtualenv -pluggy==1.2.0 # via pytest, tox -pre-commit==3.3.3 # via -r dev-requirements.in +pluggy==1.3.0 # via pytest, tox +pre-commit==3.4.0 # via -r dev-requirements.in pycodestyle==2.11.0 # via flake8 pyflakes==3.1.0 # via autoflake, flake8 pygments==2.16.1 # via rich, sphinx -pyproject-api==1.5.3 # via tox +pyproject-api==1.6.1 # via tox pyproject-hooks==1.0.0 # via build -pytest==7.4.0 # via -r dev-requirements.in, pytest-cov +pytest==7.4.2 # via -r dev-requirements.in, pytest-cov pytest-cov==4.1.0 # via -r dev-requirements.in pyupgrade==3.10.1 # via gray pyyaml==6.0.1 # via fixit, libcst, pre-commit referencing==0.30.2 # via jsonschema, jsonschema-specifications requests==2.31.0 # via sphinx rich==12.6.0 # via gray -rpds-py==0.9.2 # via jsonschema, referencing +rpds-py==0.10.2 # via jsonschema, referencing snowballstemmer==2.2.0 # via sphinx -sphinx==7.1.2 # via -r dev-requirements.in, sphinxcontrib-applehelp, sphinxcontrib-devhelp, sphinxcontrib-htmlhelp, sphinxcontrib-qthelp, sphinxcontrib-serializinghtml +sphinx==7.2.5 # via -r dev-requirements.in, sphinxcontrib-applehelp, sphinxcontrib-devhelp, sphinxcontrib-htmlhelp, sphinxcontrib-qthelp, sphinxcontrib-serializinghtml sphinxcontrib-applehelp==1.0.7 # via sphinx sphinxcontrib-devhelp==1.0.5 # via sphinx sphinxcontrib-htmlhelp==2.0.4 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.6 # via sphinx -sphinxcontrib-serializinghtml==1.1.8 # via sphinx +sphinxcontrib-serializinghtml==1.1.9 # via sphinx tokenize-rt==5.2.0 # via add-trailing-comma, pyupgrade toml==0.10.2 # via autoflake -tomli==2.0.1 # via black, build, coverage, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox -tox==4.8.0 # via -r dev-requirements.in +tox==4.11.2 # via -r dev-requirements.in typing-extensions==4.7.1 # via libcst, mypy, typing-inspect typing-inspect==0.9.0 # via libcst unify==0.5 # via gray untokenize==0.1.1 # via unify urllib3==2.0.4 # via requests -virtualenv==20.24.3 # via pre-commit, tox -wheel==0.41.1 # via pip-tools +virtualenv==20.24.5 # via pre-commit, tox +wheel==0.41.2 # via pip-tools # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/requirements.in b/requirements.in index 08f0b25..bcbe328 100644 --- a/requirements.in +++ b/requirements.in @@ -27,8 +27,6 @@ wrapt # kiss3 uses attrs kiss3 attrs -# for mobile checking -user-agents dataclasses dacite2 oslo.config diff --git a/requirements.txt b/requirements.txt index 3823144..de23c8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --annotation-style=line requirements.in @@ -13,7 +13,7 @@ bitarray==2.8.1 # via ax253, kiss3 blinker==1.6.2 # via flask certifi==2023.7.22 # via requests charset-normalizer==3.2.0 # via requests -click==8.1.6 # via -r requirements.in, click-completion, click-params, flask +click==8.1.7 # via -r requirements.in, click-completion, click-params, flask click-completion==0.5.2 # via -r requirements.in click-params==0.4.1 # via -r requirements.in commonmark==0.9.1 # via rich @@ -23,12 +23,12 @@ debtcollector==2.5.0 # via oslo-config decorator==5.1.1 # via validators dnspython==2.4.2 # via eventlet eventlet==0.33.3 # via -r requirements.in -flask==2.3.2 # via -r requirements.in, flask-httpauth, flask-socketio +flask==2.3.3 # via -r requirements.in, flask-httpauth, flask-socketio flask-httpauth==4.8.0 # via -r requirements.in -flask-socketio==5.3.5 # via -r requirements.in +flask-socketio==5.3.6 # via -r requirements.in geographiclib==2.0 # via geopy -geopy==2.3.0 # via -r requirements.in -gevent==23.7.0 # via -r requirements.in +geopy==2.4.0 # via -r requirements.in +gevent==23.9.0.post1 # via -r requirements.in greenlet==2.0.2 # via eventlet, gevent idna==3.4 # via requests imapclient==2.3.1 # via -r requirements.in @@ -38,33 +38,31 @@ jinja2==3.1.2 # via click-completion, flask kiss3==8.0.0 # via -r requirements.in markupsafe==2.1.3 # via jinja2, werkzeug netaddr==0.8.0 # via oslo-config -oslo-config==9.1.1 # via -r requirements.in -oslo-i18n==6.0.0 # via oslo-config +oslo-config==9.2.0 # via -r requirements.in +oslo-i18n==6.1.0 # via oslo-config pbr==5.11.1 # via -r requirements.in, oslo-i18n, stevedore -pluggy==1.2.0 # via -r requirements.in +pluggy==1.3.0 # via -r requirements.in plumbum==1.8.2 # via rpyc pygments==2.16.1 # via rich pyserial==3.5 # via pyserial-asyncio pyserial-asyncio==0.6 # via kiss3 -python-engineio==4.5.1 # via python-socketio -python-socketio==5.8.0 # via -r requirements.in, flask-socketio -pytz==2023.3 # via -r requirements.in +python-engineio==4.7.0 # via python-socketio +python-socketio==5.9.0 # via -r requirements.in, flask-socketio +pytz==2023.3.post1 # via -r requirements.in pyyaml==6.0.1 # via -r requirements.in, oslo-config requests==2.31.0 # via -r requirements.in, oslo-config, update-checker rfc3986==2.0.0 # via oslo-config rich==12.6.0 # via -r requirements.in rpyc==5.3.1 # via -r requirements.in rush==2021.4.0 # via -r requirements.in -shellingham==1.5.0.post1 # via -r requirements.in, click-completion +shellingham==1.5.3 # via -r requirements.in, click-completion six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient -soupsieve==2.4.1 # via beautifulsoup4 +soupsieve==2.5 # via beautifulsoup4 stevedore==5.1.0 # via oslo-config tabulate==0.9.0 # via -r requirements.in thesmuggler==1.0.1 # via -r requirements.in -ua-parser==0.18.0 # via user-agents update-checker==0.18.0 # via -r requirements.in urllib3==2.0.4 # via requests -user-agents==2.2.0 # via -r requirements.in validators==0.20.0 # via click-params werkzeug==2.3.7 # via -r requirements.in, flask wrapt==1.15.0 # via -r requirements.in, debtcollector From ba6b410795808e737c7dd035812c674ce8f9755f Mon Sep 17 00:00:00 2001 From: Hemna Date: Sun, 10 Sep 2023 11:12:38 -0400 Subject: [PATCH 6/9] Update index.html to use chat.css --- aprsd/web/chat/static/css/chat.css | 93 +++++++++++++++++++++++++++++ aprsd/web/chat/templates/index.html | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 aprsd/web/chat/static/css/chat.css diff --git a/aprsd/web/chat/static/css/chat.css b/aprsd/web/chat/static/css/chat.css new file mode 100644 index 0000000..d32cc65 --- /dev/null +++ b/aprsd/web/chat/static/css/chat.css @@ -0,0 +1,93 @@ +.speech-wrapper { + padding: 5px 30px; + border: 1px solid #ccc; + height: 450px; + overflow-y: scroll; + overflow-x: none; + background-color: #CCCCCC; +} + +.bubble-row { + display: flex; + width: 100%; + justify-content: flex-start; + &.alt { + justify-content: flex-end; + } +} + +.bubble { + /*width: 350px; */ + height: auto; + display: block; + background: #f5f5f5; + border-radius: 4px; + box-shadow: 2px 8px 5px #555; + position: relative; + margin: 0 0 15px; + &.alt { + margin: 0 0 15px; + } +} + +.bubble-text { + padding: 5px 5px 0px 8px; +} + +.bubble-name { + width: 280px; + font-weight: 600; + font-size: 12px; + margin: 0 0 4px; + color: #3498db; + display: flex; + align-items: center; + .material-symbols-rounded { + margin-left: auto; + font-weight: normal; + color: #808080; + } + &.alt { + color: #2ecc71; + } +} + +.bubble-timestamp { + margin-right: auto; + font-size: 11px; + text-transform: uppercase; + color: #bbb +} + +.bubble-message { + font-size: 16px; + margin: 0px; + padding: 0px 5px 5px 0px; + color: #2b2b2b; +} + +.bubble-arrow { + position: absolute; + width: 0; + bottom:30px; + left: -16px; + height: 0px; + &.alt{ + right: -2px; + bottom: 30px; + left: auto; + } +} +.bubble-arrow:after { + content: ""; + position: absolute; + border: 0 solid transparent; + border-top: 9px solid #f5f5f5; + border-radius: 0 20px 0; + width: 15px; + height: 30px; + transform: rotate(145deg); +} +.bubble-arrow.alt:after { + transform: rotate(45deg) scaleY(-1); +} diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 62ccfa8..c255d42 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -15,7 +15,7 @@ - + From b4e02c760ee1eac155406b0939ad058fdf0e2449 Mon Sep 17 00:00:00 2001 From: Hemna Date: Sun, 10 Sep 2023 12:13:05 -0400 Subject: [PATCH 7/9] Center the webchat input form This patch centers the input form for the webchat page over the center of the page. --- aprsd/web/chat/static/css/chat.css | 4 ++++ aprsd/web/chat/templates/index.html | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/aprsd/web/chat/static/css/chat.css b/aprsd/web/chat/static/css/chat.css index d32cc65..270d682 100644 --- a/aprsd/web/chat/static/css/chat.css +++ b/aprsd/web/chat/static/css/chat.css @@ -1,3 +1,7 @@ +input[type=search]::-webkit-search-cancel-button { + -webkit-appearance: searchfield-cancel-button; +} + .speech-wrapper { padding: 5px 30px; border: 1px solid #ccc; diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index c255d42..b56f647 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -68,10 +68,10 @@
-
+
- +
From 8a90d5480ae379a3af225a4f73d452c8d5ae402e Mon Sep 17 00:00:00 2001 From: Hemna Date: Tue, 12 Sep 2023 15:44:34 -0400 Subject: [PATCH 8/9] webchat: set to_call to value of tab when selected This patch will set the to_call form field to the callsign of the tab when the tab is activated in the UI. NOTE: still need to populate it when clicking on the already active tab. --- aprsd/web/chat/static/js/send-message.js | 2 +- aprsd/web/chat/templates/index.html | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index 73242e4..8cd3ac0 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -172,7 +172,7 @@ function create_callsign_tab(callsign, active=false) { } item_html = '' diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index b56f647..3f92c2f 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -49,6 +49,15 @@ localStorage.clear(); }); }); + + // When a tab is clicked, populate the to_call form field. + $(document).on('shown.bs.tab', 'button[data-bs-toggle="tab"]', function (e) { + var tab = $(e.target); + var callsign = tab.attr("callsign"); + var to_call = $('#to_call'); + console.log("Tab clicked on " + callsign); + to_call.val(callsign); + }); From 1400e3e71144d4dc8a48874301b90960485a3768 Mon Sep 17 00:00:00 2001 From: Hemna Date: Tue, 12 Sep 2023 16:37:06 -0400 Subject: [PATCH 9/9] webchat: got active tab onclick working This patch adds the ability to click on the already existing active tab and have it populate the to_call input box. --- aprsd/web/chat/static/js/send-message.js | 2 +- aprsd/web/chat/templates/index.html | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index 8cd3ac0..447aa8c 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -172,7 +172,7 @@ function create_callsign_tab(callsign, active=false) { } item_html = '' diff --git a/aprsd/web/chat/templates/index.html b/aprsd/web/chat/templates/index.html index 3f92c2f..ce815f9 100644 --- a/aprsd/web/chat/templates/index.html +++ b/aprsd/web/chat/templates/index.html @@ -48,16 +48,16 @@ console.log('Wipe local storage'); localStorage.clear(); }); + + // When a tab is clicked, populate the to_call form field. + $(document).on('shown.bs.tab', 'button[data-bs-toggle="tab"]', function (e) { + var tab = $(e.target); + var callsign = tab.attr("callsign"); + var to_call = $('#to_call'); + to_call.val(callsign); + }); }); - // When a tab is clicked, populate the to_call form field. - $(document).on('shown.bs.tab', 'button[data-bs-toggle="tab"]', function (e) { - var tab = $(e.target); - var callsign = tab.attr("callsign"); - var to_call = $('#to_call'); - console.log("Tab clicked on " + callsign); - to_call.val(callsign); - });