From 25441882624fb919803606908d6e318f04bf683f Mon Sep 17 00:00:00 2001 From: mar77i Date: Tue, 19 Nov 2024 15:27:07 +0100 Subject: [PATCH] simplify ws connecting and reconnecting --- chat/static/chat/chat.js | 166 +++++++++++++++++++++++++++++---------- todo.txt | 10 ++- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/chat/static/chat/chat.js b/chat/static/chat/chat.js index a119822..75ae76d 100644 --- a/chat/static/chat/chat.js +++ b/chat/static/chat/chat.js @@ -17,11 +17,15 @@ { "abort": function (msg) { console.log(msg); + messages_manager.stick_to_bottom(); new Message({"text": msg, "user": "xhr_abort"}, null); + messages_manager.bottom_callback(); }, "error": function (msg) { console.log(msg); + messages_manager.stick_to_bottom(); new Message({"text": msg, "user": "xhr_error"}, null); + messages_manager.bottom_callback(); }, "load": function () { var response_data; @@ -85,6 +89,10 @@ messages_footer.children[0].disabled = true; } + function load_msgs_callback(msgs) { + messages_manager.load_msgs_callback(msgs); + } + function set_channel(field_value) { clear_channel(); if (this === window) { @@ -103,10 +111,7 @@ + "?" + this.msg_dest_field + "=" + field_value.toString() ), - function (msgs) { - messages_manager.load_msgs_callback(msgs); - messages_manager.scroll_to_bottom(); - } + load_msgs_callback ); } @@ -156,7 +161,7 @@ this.items_url = "/api/user/"; this.items_classname = "users"; this.items_per_id = {}; - this.items_reload = function () { + this.items_reload = function (callback) { _.clear_children(_.dgEBCN0(this.items_classname).children[0]); _.clear_attributes(this.items_per_id); xhr( @@ -178,6 +183,9 @@ } ); update_channel_header.call(outer); + if (callback) { + callback(); + } } ); }; @@ -221,14 +229,17 @@ _.foreach_arr( this.items_per_id[this.msg_data.channel_id].users, function (user_id) { - var item = private_manager.items_per_id[user_id]; - var li = make_tag_with_data_id("li", item.id); + var li = make_tag_with_data_id("li", user_id); var a = make_callback_a( function () { - private_manager.set_channel(item.id); + private_manager.set_channel(user_id); } ); - a.appendChild(private_manager.item_to_tn(item)); + a.appendChild( + private_manager.item_to_tn( + private_manager.items_per_id[user_id] + ) + ); li.appendChild(a); messages_header.children[1].appendChild(li); } @@ -258,7 +269,7 @@ this.items_url = "/api/channel/"; this.items_classname = "channels"; this.items_per_id = {}; - this.items_reload = function () { + this.items_reload = function (callback) { _.clear_children(_.dgEBCN0(this.items_classname).children[0]); _.clear_attributes(this.items_per_id); xhr( @@ -270,6 +281,9 @@ items_reload_callback.bind(outer) ); update_channel_header.call(outer); + if (callback) { + callback(); + } } ); }; @@ -383,25 +397,28 @@ messages.insertBefore(p, messages.firstElementChild); p.appendChild(button); }; + function no_bottom_callback() { + this.bottom_callback = no_bottom_callback; + } + this.bottom_callback = no_bottom_callback; + this.is_at_the_bottom = function () { + return messages.clientHeight + messages.scrollTop + === messages.scrollHeight; + }; this.load_msgs_callback = function (msgs, next) { + var is_at_the_bottom = this.is_at_the_bottom(); this.previous_button(msgs.previous); + no_bottom_callback(); _.foreach_arr(msgs.result, function (msg) { new Message(msg, next); }); + if (is_at_the_bottom) { + this.scroll_to_bottom(); + } }; - function no_bottom_callback() { - messages_manager.bottom_callback = no_bottom_callback; - } - this.bottom_callback = no_bottom_callback; this.stick_to_bottom = function () { - if ( - messages.clientHeight + messages.scrollTop - === messages.scrollHeight - ) { - messages_manager.bottom_callback = function () { - messages_manager.scroll_to_bottom(); - no_bottom_callback(); - }; + if (this.is_at_the_bottom()) { + this.bottom_callback = messages_manager.scroll_to_bottom; } else { no_bottom_callback(); } @@ -410,6 +427,7 @@ messages.scrollTop = ( messages.scrollHeight - messages.clientHeight ); + no_bottom_callback(); }; } @@ -423,7 +441,6 @@ } }, INSERT: function (data) { - messages_manager.stick_to_bottom(); xhr( "get", current_channel.msg_url + data.obj.id + "/", @@ -437,7 +454,6 @@ if (!messages_manager.by_id(data.obj.id)) { return; } - messages_manager.stick_to_bottom(); xhr( "get", ( @@ -453,17 +469,10 @@ messages_manager.bottom_callback(); }, update_callback: function (data) { - console.log(messages_manager.by_id(data.id)); messages_manager.by_id(data.id).update(data); messages_manager.bottom_callback(); } }; - var ws = new WebSocket( - ws_schemas[window.location.protocol] - + "//" - + window.location.host - + "/" - ); function ws_receive(msg) { var data; try { @@ -486,21 +495,92 @@ private_manager.items_reload(); } if (current_channel && current_channel.match_channel(data)) { + messages_manager.stick_to_bottom(); ws_receive_msg[data.op](data); } } - ws.addEventListener("message", ws_receive); - ws.addEventListener("close", function (event) { - event = event || window.event; - new Message({"text": "Closed", "user": "ws_close"}, null); - // display reconnect button here - }); - ws.addEventListener("error", function (event) { - event = event || window.event; - console.log(event); - new Message({"text": event, "user": "ws_error"}, null); - // display reconnect button here - }); + function connect() { + var ws = new WebSocket( + ws_schemas[window.location.protocol] + + "//" + + window.location.host + + "/" + ); + ws.addEventListener("message", ws_receive); + ws.addEventListener("close", function () { + messages_manager.stick_to_bottom(); + new Message({"text": "Closed", "user": "ws_close"}, null); + messages_manager.bottom_callback(); + toggle_reconnect_button(); + }); + ws.addEventListener("error", function (event) { + event = event || window.event; + console.log(event); + messages_manager.stick_to_bottom(); + new Message({"text": event, "user": "ws_error"}, null); + messages_manager.bottom_callback(); + toggle_reconnect_button(); + }); + return ws; + } + function try_reconnect() { + var messages_footer = _.dgEBCN0("messages_footer"); + var ws; + if (messages_footer.children.length <= 2) { + return; + } + messages_footer.children[2].setAttribute("disabled", ""); + ws = connect(); + ws.addEventListener("open", function () { + var prev_channel; + var prev_id; + if ( + current_channel !== null + && current_channel.hasOwnProperty("msg_data") + && current_channel.hasOwnProperty("msg_dest_field") + ) { + prev_channel = current_channel; + prev_id = current_channel.msg_data[ + current_channel.msg_dest_field + ]; + } else { + prev_channel = window; + prev_id = null; + } + toggle_reconnect_button(); + private_manager.items_reload(function () { + channel_manager.items_reload(function () { + set_channel.call(prev_channel, prev_id); + }); + }); + }); + function reenable_reconnect() { + if (messages_footer.children > 2) { + messages_footer.children[2].removeAttribute("disabled"); + } + } + ws.addEventListener("close", reenable_reconnect); + ws.addEventListener("error", reenable_reconnect); + } + function toggle_reconnect_button() { + var messages_footer = _.dgEBCN0("messages_footer"); + if (messages_footer.children[0].style.display === "none") { + messages_footer.children[0].style.display = ""; + messages_footer.children[1].style.display = ""; + _.clear_after(messages_footer.children[1]); + } else { + messages_footer.children[0].style.display = "none"; + messages_footer.children[1].style.display = "none"; + messages_footer.appendChild(document.createElement("button")); + messages_footer.children[2].appendChild(_.dcTN("Reconnect")); + messages_footer.children[2].style.width = "100%"; + messages_footer.children[2].addEventListener( + "click", + try_reconnect + ); + } + } + connect(); } function send_msg() { diff --git a/todo.txt b/todo.txt index a822541..425e174 100644 --- a/todo.txt +++ b/todo.txt @@ -1,23 +1,25 @@ [ ] display a reconnect button when ws is disconnected - - if reconnect is successful, reload all loaded messages, channel list and user list + - if reconnect is successful, reload channel list and user list and channel messages [ ] show message edit/delete menus - privileged users can edit / delete any message [ ] edit/delete existing messages [ ] new message(s) indicators in left panel [ ] update address bar per channel #channel: / #user: for now? + - update for requested hash [ ] channel management: channel admin -[ ] write email to user +[ ] write email to user with email [ ] ws client infrastructure: - notify-throttle - automatic reconnects? -[ ] server-side throttling, that then needs to be accounted for on the client +[ ] server-side throttling, and add a retry mechanism on the client [ ] tests for pg-trigger→notify websocket - somehow test each trigger statement individually, that is 4 statements * 5 models [ ] ws and chat tests? + [ ] media uploads and view methods: download, image, video/audio player [ ] games: connect 4, battleship... -[ ] moderation: report messages to MANAGERS, including private ones +[ ] moderation: report messages to MANAGERS, including private messages -- 2.47.0