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.
This commit is contained in:
Azareal 2018-09-08 22:47:37 +10:00
parent e22a96f53e
commit 3822fe12d2
3 changed files with 99 additions and 94 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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 = "<div class='alertItem'>You don't have any alerts</div>";
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 = "<div class='alertItem'>"+msg+"</div>";
}
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 = "<div class='alertItem'>"+data.errmsg+"</div>";
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 = "<div class='alertItem'>You don't have any alerts</div>";
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 = "<div class='alertItem'>"+errtxt+"</div>";
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]) {