From 3822fe12d2d9f2f241dd3816e983b6b36e10c15a Mon Sep 17 00:00:00 2001 From: Azareal Date: Sat, 8 Sep 2018 22:47:37 +1000 Subject: [PATCH] Refactored the client-side alert logic. Exported WsHubImpl's PushMessage method. Fixed a bug in WSUser's WriteAll and WriteToPage methods where they wouldn't skip over the nil sockets. --- common/ws_hub.go | 2 +- common/ws_user.go | 6 ++ public/global.js | 185 +++++++++++++++++++++++----------------------- 3 files changed, 99 insertions(+), 94 deletions(-) diff --git a/common/ws_hub.go b/common/ws_hub.go index f7fb9d90..498686b8 100644 --- a/common/ws_hub.go +++ b/common/ws_hub.go @@ -322,7 +322,7 @@ func (hub *WsHubImpl) RemoveConn(wsUser *WSUser, conn *websocket.Conn) { wsUser.Unlock() } -func (hub *WsHubImpl) pushMessage(targetUser int, msg string) error { +func (hub *WsHubImpl) PushMessage(targetUser int, msg string) error { wsUser, err := hub.getUser(targetUser) if err != nil { return err diff --git a/common/ws_user.go b/common/ws_user.go index d73e3742..50f2c325 100644 --- a/common/ws_user.go +++ b/common/ws_user.go @@ -23,6 +23,9 @@ type WSUserSocket struct { func (wsUser *WSUser) WriteAll(msg string) error { msgbytes := []byte(msg) for _, socket := range wsUser.Sockets { + if socket == nil { + continue + } w, err := socket.conn.NextWriter(websocket.TextMessage) if err != nil { return err @@ -41,6 +44,9 @@ func (wsUser *WSUser) WriteToPage(msg string, page string) error { func (wsUser *WSUser) WriteToPageBytes(msg []byte, page string) error { var success bool for _, socket := range wsUser.Sockets { + if socket == nil { + continue + } if socket.Page != page { continue } diff --git a/public/global.js b/public/global.js index c3cae288..5e58d5b6 100644 --- a/public/global.js +++ b/public/global.js @@ -1,5 +1,6 @@ 'use strict'; var formVars = {}; +var alertMapping = {}; var alertList = []; var alertCount = 0; var moreTopicCount = 0; @@ -26,7 +27,6 @@ function postLink(event) { event.preventDefault(); let formAction = $(event.target).closest('a').attr("href"); - //console.log("Form Action: " + formAction); $.ajax({ url: formAction, type: "POST", dataType: "json", error: ajaxError, data: {js: "1"} }); } @@ -37,60 +37,88 @@ function bindToAlerts() { }); } -var alertsInitted = false; -// TODO: Add the ability for users to dismiss alerts -function loadAlerts(menuAlerts) -{ - if(!alertsInitted) return; +function addAlert(msg, notice = false) { + var mmsg = msg.msg; + if("sub" in msg) { + for(var i = 0; i < msg.sub.length; i++) { + mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]); + } + } - var alertListNode = menuAlerts.getElementsByClassName("alertList")[0]; - var alertCounterNode = menuAlerts.getElementsByClassName("alert_counter")[0]; + let aItem = Template_alert({ + ASID: msg.asid, + Path: msg.path, + Avatar: msg.avatar || "", + Message: mmsg + }) + + alertMapping[msg.asid] = aItem; + alertList.push(msg.asid); + if(alertList.length > 8) alertList.shift(); + + if(notice) { + // TODO: Add some sort of notification queue to avoid flooding the end-user with notices? + // TODO: Use the site name instead of "Something Happened" + if(Notification.permission === "granted") { + var n = new Notification("Something Happened",{ + body: mmsg, + icon: msg.avatar, + }); + setTimeout(n.close.bind(n), 8000); + } + } + + runInitHook("after_add_alert"); +} + +function updateAlertList(menuAlerts) { + let alertListNode = menuAlerts.getElementsByClassName("alertList")[0]; + let alertCounterNode = menuAlerts.getElementsByClassName("alert_counter")[0]; alertCounterNode.textContent = "0"; + + let outList = ""; + let j = 0; + for(var i = 0; i < alertList.length && j < 8; i++) { + outList += alertMapping[alertList[i]]; + j++; + } + + if(outList == "") outList = "
You don't have any alerts
"; + alertListNode.innerHTML = outList; + + if(alertCount != 0) { + alertCounterNode.textContent = alertCount; + menuAlerts.classList.add("has_alerts"); + } else { + menuAlerts.classList.remove("has_alerts"); + } + + bindToAlerts(); + runInitHook("after_update_alert_list"); +} + +function setAlertError(menuAlerts,msg) { + let alertListNode = menuAlerts.getElementsByClassName("alertList")[0]; + alertListNode.innerHTML = "
"+msg+"
"; +} + +var alertsInitted = false; +function loadAlerts(menuAlerts) { + if(!alertsInitted) return; $.ajax({ type: 'get', dataType: 'json', url:'/api/?action=get&module=alerts', success: (data) => { if("errmsg" in data) { - alertListNode.innerHTML = "
"+data.errmsg+"
"; + setAlertError(menuAlerts,data.errmsg) return; } - - var alist = ""; for(var i in data.msgs) { - var msg = data.msgs[i]; - var mmsg = msg.msg; - if("sub" in msg) { - for(var i = 0; i < msg.sub.length; i++) { - mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]); - //console.log("Sub #" + i + ":",msg.sub[i]); - } - } - - let aItem = Template_alert({ - ASID: msg.asid || 0, - Path: msg.path, - Avatar: msg.avatar || "", - Message: mmsg - }) - alist += aItem; - alertList.push(aItem); - //console.log(msg); - //console.log(mmsg); - } - - if(alist == "") alist = "
You don't have any alerts
"; - alertListNode.innerHTML = alist; - - if(data.msgCount != 0 && data.msgCount != undefined) { - alertCounterNode.textContent = data.msgCount; - menuAlerts.classList.add("has_alerts"); - } else { - menuAlerts.classList.remove("has_alerts"); + addAlert(data.msgs[i]); } alertCount = data.msgCount; - - bindToAlerts(); + updateAlertList(menuAlerts) }, error: (magic,theStatus,error) => { let errtxt @@ -104,7 +132,7 @@ function loadAlerts(menuAlerts) console.log(err); } console.log("error", error); - alertListNode.innerHTML = "
"+errtxt+"
"; + setAlertError(menuAlerts,errtxt); } }); } @@ -129,46 +157,13 @@ function SplitN(data,ch,n) { } function wsAlertEvent(data) { - var msg = data.msg; - if("sub" in data) { - for(var i = 0; i < data.sub.length; i++) { - msg = msg.replace("\{"+i+"\}", data.sub[i]); - } - } - - let aItem = Template_alert({ - ASID: data.asid || 0, - Path: data.path, - Avatar: data.avatar || "", - Message: msg - }) - alertList.push(aItem); - if(alertList.length > 8) alertList.shift(); - //console.log("post alertList",alertList); - alertCount++; + addAlert(data, true); var alist = ""; - for (var i = 0; i < alertList.length; i++) alist += alertList[i]; - - //console.log(alist); + for (var i = 0; i < alertList.length; i++) alist += alertMapping[alertList[i]]; // TODO: Add support for other alert feeds like PM Alerts var generalAlerts = document.getElementById("general_alerts"); - var alertListNode = generalAlerts.getElementsByClassName("alertList")[0]; - var alertCounterNode = generalAlerts.getElementsByClassName("alert_counter")[0]; - alertListNode.innerHTML = alist; - alertCounterNode.textContent = alertCount; - - // TODO: Add some sort of notification queue to avoid flooding the end-user with notices? - // TODO: Use the site name instead of "Something Happened" - if(Notification.permission === "granted") { - var n = new Notification("Something Happened",{ - body: msg, - icon: data.avatar, - }); - setTimeout(n.close.bind(n), 8000); - } - - bindToAlerts(); + updateAlertList(generalAlerts, alist); } function runWebSockets() { @@ -209,6 +204,20 @@ function runWebSockets() { if ("msg" in data) { // TODO: Fix the data race where the alert template hasn't been loaded yet wsAlertEvent(data); + } else if("event" in data) { + if(data.event == "dismiss-alert"){ + Object.keys(alertBuffer).forEach((key) => { + if(key==data.asid) { + alertCount--; + for(var i = 0; i < alertList.length;i++) { + if(alertList[i]==key) { + alertList.splice(i); + } + } + delete alertMapping[key]; + } + }); + } } else if("Topics" in data) { console.log("topic in data"); console.log("data:", data); @@ -300,17 +309,17 @@ function mainInit(){ $(".add_like").click(function(event) { event.preventDefault(); - let likeButton = this; let target = this.closest("a").getAttribute("href"); console.log("target: ", target); - likeButton.classList.remove("add_like"); - likeButton.classList.add("remove_like"); - let controls = likeButton.closest(".controls"); + this.classList.remove("add_like"); + this.classList.add("remove_like"); + let controls = this.closest(".controls"); let hadLikes = controls.classList.contains("has_likes"); if(!hadLikes) controls.classList.add("has_likes"); let likeCountNode = controls.getElementsByClassName("like_count")[0]; console.log("likeCountNode",likeCountNode); likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) + 1; + let likeButton = this; $.ajax({ url: target, @@ -356,10 +365,6 @@ function mainInit(){ $(".show_on_edit").hide(); let formAction = this.form.getAttribute("action"); - //console.log("New Topic Name: ", topicNameInput); - //console.log("New Topic Status: ", topicStatusInput); - //console.log("New Topic Content: ", topicContentInput); - //console.log("Form Action: ", formAction); $.ajax({ url: formAction, type: "POST", @@ -393,7 +398,6 @@ function mainInit(){ block.html(newContent); var formAction = $(this).closest('a').attr("href"); - //console.log("Form Action:",formAction); $.ajax({ url: formAction, type: "POST", error: ajaxError, dataType: "json", data: { isJs: "1", edit_item: newContent } }); }); @@ -413,7 +417,6 @@ function mainInit(){ block.html(newContent); let formAction = $(this).closest('a').attr("href"); - //console.log("Form Action:", formAction); $.ajax({ url: formAction + "?session=" + me.User.Session, type: "POST", @@ -430,7 +433,6 @@ function mainInit(){ if($(this).find("input").length !== 0) return; //console.log("clicked .edit_fields"); var blockParent = $(this).closest('.editable_parent'); - //console.log(blockParent); blockParent.find('.hide_on_edit').addClass("edit_opened"); blockParent.find('.show_on_edit').addClass("edit_opened"); blockParent.find('.editable_block').show(); @@ -443,9 +445,6 @@ function mainInit(){ else var it = ['No','Yes']; var itLen = it.length; var out = ""; - //console.log("Field Name:",fieldName); - //console.log("Field Type:",fieldType); - //console.log("Field Value:",fieldValue); for (var i = 0; i < itLen; i++) { var sel = ""; if(fieldValue == i || fieldValue == it[i]) {