From e0759b34c27c01d8a6056736ff11e66d796d4773 Mon Sep 17 00:00:00 2001 From: Masl Date: Sun, 18 Aug 2024 20:01:02 +0200 Subject: [PATCH] notifications yes --- client/assets/audio/not.wav | Bin 0 -> 10188 bytes client/index.html | 5 ++ client/js/lists/channelList.js | 1 + client/js/lists/chatList.js | 62 +++++++++++++++-- client/js/notifications.js | 113 +++++++++++++++++++++++++++++++ client/js/settings.js | 68 +++++++------------ client/lib/user/settingsObj.js | 14 ++-- client/lib/user/userMsgSystem.js | 2 +- 8 files changed, 209 insertions(+), 56 deletions(-) create mode 100644 client/assets/audio/not.wav create mode 100644 client/js/notifications.js diff --git a/client/assets/audio/not.wav b/client/assets/audio/not.wav new file mode 100644 index 0000000000000000000000000000000000000000..0547ca87222ad58a6602602251e15161bad0ec04 GIT binary patch literal 10188 zcmeI$`Fj*a8VB&{nLY~>xddck$(luYSV6%+IF&=9xCj^wBtpO-M~DVd5;q|t;NyA( zIm4kG0RjjFLwrc&3P%tKMCHDMsE8tj`;h8ux+nWB8lTGFu+P-z*M4TYzE$1zz8&8$ zE^c2dpKoZdA$`ZDXGGQY`FsLA_m$80NuuEM`|A2er(~q8z%@SKFen9CkcC$uACAHu zsOzumZwoKLD9C|%unu;?3AhY4Lm-I<}8 zp=f`!KPC_p&>>IE6Prj)q<#<&!(j|efGn5}xsV4-VGR_)X4nk};3!nV1vm@Cpj<2$ z?SLJ)=)dT1SJSTMhI7L?6Fd_XA;C_t4_F7R-LMtDhIOz4zJR%q3m-rZWWbv+21dXT z7zi&wFX#$S!!F3MGwh^bQZUcSbC%aEuX!WzMxaI? z-Jln|00ZC^7zQbj3KJj$a$q`q3^RfK-T--kJW?7dwGdl~Y<=^ce5Y}+aj?d!v6?}$ znQXR)e?d!V0gplhhy)e<;24gf!yUK=f55NsBbMG2c`kbYq&C8IVGKvCI%)3Vx3s$GwU3OMx z(m`3OE>-tvd$i4DGuaN?YS~J-Eres&{^c9Ol@czF#E~9a4=tz!l@am?`C8!G{kd+p zwp-23<|bRa!}KtH8RBahOu1Jmmq0y3C^wXHiPY0s@2n>piN+3dhq=sNX3q`H4gF2} zn-rl$DA{VZ%GPrMSwPl2u$pkI2vXS$)htW=Id|cj;X^_JMKKlS;YqluM)B8%o6h>`y*Qm zX`$=lb@8%%SstPeQKx8Av`J(VnN!Pb;+aLbnS}d{a5D&(OFTUYU|m&=Y4S9=kJv}t zHB?-DMPaGgje(oO58MW_+# zYj5K4IK1~0Ta%-t)9p%{j>o0|r+dw^EQLcy< z(HHd>@6UCKxx|dKX15ES#BWl3?SS~gzHbZe-qD( zglkP&6P95^wV}FBUMH^?*NfZSZEl{OXP-0AnX`;p##DW(UPjC4j#_q7&o0XCrd&DY z_E68alpCZEx)nz?4iWTgg_|s%zDi z+Da{wM3M+#Lj?)^P@~nb5?!;vfP28|g`02<*q*oqzr(NaGyDi4HKg8FZYu?Hf&7d3 zi&*R{_QeKcgIQLV^`!Zv*~(~Tu;(id`aypf2=R~riNK!e5s(6-VH~7EI!uIjAQQ46 z2iUoNz&K$1Vg6yp+wpd$lj%GccrGwinkwCpZ^&$7_h5`RMjHhqU>GDq0>r~W=nwrM z4*Eb(VCPC#h=r%&DQF9AAWcnE-%;LCddfZJHR2lab>Ve^^)8|<+DFVICRhRgf>naXR*E5X1&V-bAfrItz*Xz2RX`>cJ|Dqy{95p04jumjjVSHSmh6i&g9a2_tg z6}Sn1f(apLW;e4p!tmhmV2l&vjB-c06NQPwwp4rpl>u4gfpze}*eSL2v~(0v?6O;c4gz17J9$K{k8> zi(oBmg&*MI(8Hlup%|LfG^rWj8{oSHte5Qz2@iA-ItT>L`p)_mKzM``2qzFuAe=xr nfp7xh1i}e~69^{|P9U5>IDv2i;RM17gcArS5KiF#KY{-Ny?kx1 literal 0 HcmV?d00001 diff --git a/client/index.html b/client/index.html index fb1553c..bbdafb3 100644 --- a/client/index.html +++ b/client/index.html @@ -36,6 +36,10 @@

Main Settings




+
+
+ +
@@ -177,6 +181,7 @@

Info and Disclaimers

+ diff --git a/client/js/lists/channelList.js b/client/js/lists/channelList.js index 74aba20..0cb7d46 100644 --- a/client/js/lists/channelList.js +++ b/client/js/lists/channelList.js @@ -5,6 +5,7 @@ let docLastChannelServerId = NoId; async function createChannelEntry(channelId, channelName, serverId, shouldHighlight) { let li = document.createElement("li"); + li.id = `chat-selector-entry-${serverId}-${channelId}`; let span = document.createElement("span"); span.className = "chat-selector-entry"; if (channelId != NoId) diff --git a/client/js/lists/chatList.js b/client/js/lists/chatList.js index f40892f..3d61032 100644 --- a/client/js/lists/chatList.js +++ b/client/js/lists/chatList.js @@ -275,10 +275,14 @@ async function createChatList(serverId, channelId, scrollDown) { async function channelClicked(element, channelId, serverId) { console.log(`Channel ${channelId} clicked`); + try { + if (docLastChannelEntry) + docLastChannelEntry.classList.remove("chat-selector-entry-active"); + element.classList.add("chat-selector-entry-active"); + } catch (e) { + console.warn("ERROR ADDING CLASS: ", e); + } - if (docLastChannelEntry) - docLastChannelEntry.classList.remove("chat-selector-entry-active"); - element.classList.add("chat-selector-entry-active"); docLastChannelEntry = element; docLastChannelId = channelId; await updateChatInfo(serverId, channelId); @@ -332,12 +336,29 @@ async function messageEditedUI(account, chatUserId, messageId, newMessage) } } +async function openChat(serverId, channelId) { + logInfo(`> Opening chat ${serverId} - ${channelId}`); + + docLastServerId = serverId; + docLastChannelId = channelId; + createServerList(docLastServerId); + await createChannelList(docLastServerId, docLastChannelId, true); + await createChatList(docLastServerId, docLastChannelId); + + let channelIdStr = `chat-selector-entry-${serverId}-${channelId}`; + let element = document.getElementById(channelIdStr); + if (element) + element = element.getElementsByTagName("span")[0]; + await channelClicked(element, channelId, serverId); +} + async function messageReceivedUI(account, chatUserId, message) { /* console.log("MESSAGE RECEIVED") console.log(chatUserId); console.log(message);*/ + let shouldScroll = docChatUlDiv.scrollHeight - docChatUlDiv.scrollTop - docChatUlDiv.clientHeight < 140; if (isStrChannelFromGroup(chatUserId)) { @@ -345,6 +366,7 @@ async function messageReceivedUI(account, chatUserId, message) if (groupInfo == null) return; + let username = userGetInfoDisplayUsername(currentUser['mainAccount'], message["from"]); if (docChatLastServerId == groupInfo["groupId"] && docChatLastChannelId == groupInfo["channelId"]) { await userMarkGroupMessagesAsRead(groupInfo["groupId"], groupInfo["channelId"]); @@ -352,28 +374,58 @@ async function messageReceivedUI(account, chatUserId, message) //await createChatList(groupInfo["groupId"], groupInfo["channelId"], true); await refreshChatListArea(groupInfo["groupId"], groupInfo["channelId"]); - createChatEntry(userGetInfoDisplayUsername(currentUser['mainAccount'], message["from"]), message); + createChatEntry(username, message); if (shouldScroll) docChatUlDiv.scrollTop = docChatUlDiv.scrollHeight; } + if (!windowVisible() || + !(docChatLastServerId == groupInfo["groupId"] && docChatLastChannelId == groupInfo["channelId"])) + playNotificationSound(); + + { + if (hasGroupChatInfo(currentUser["mainAccount"], groupInfo["groupId"])) + { + let info = getGroupChatInfo(currentUser['mainAccount'], groupInfo["groupId"]); + let channel = info["channels"].find(x => x["id"] == groupInfo["channelId"]); + + showNotification(`${username} in ${info["groupName"]} - ${channel["name"]}`, message["data"], () => { + openChat(groupInfo["groupId"], groupInfo["channelId"]); + }); + } + else + logError("Group not found") + } + + + if (docLastServerId == groupInfo["groupId"]) await createChannelList(groupInfo["groupId"], docChatLastChannelId, true); } else { + let username = userGetInfoDisplayUsername(currentUser['mainAccount'], message["from"]); if (chatUserId == docChatLastChannelId && docChatLastServerId == DMsId) { await userMarkMessagesAsRead(chatUserId); //await createChatList(DMsId, chatUserId, true); await refreshChatListArea(DMsId, chatUserId); - createChatEntry(userGetInfoDisplayUsername(currentUser['mainAccount'], message["from"]), message); + createChatEntry(username, message); if (shouldScroll) docChatUlDiv.scrollTop = docChatUlDiv.scrollHeight; } + if (!windowVisible() || + !(chatUserId == docChatLastChannelId && docChatLastServerId == DMsId)) + playNotificationSound(); + + showNotification(username, message["data"], () => { + openChat(DMsId, chatUserId); + }); + + if (docLastServerId == DMsId)// && (await userGetMessages(chatUserId)).length == 1) await createChannelList(DMsId, docLastChannelId, true); } diff --git a/client/js/notifications.js b/client/js/notifications.js new file mode 100644 index 0000000..fc4c756 --- /dev/null +++ b/client/js/notifications.js @@ -0,0 +1,113 @@ +let notifications = []; +let notificationsWork = false; +async function checkNotifications() +{ + notificationsWork = false; + //console.log(window.location.protocol); + if (window.location.protocol != "https:" && window.location.protocol != "http:" + && window.location.protocol != "localhost:") + return; + + if (!settingsObj["notification"]["allow-notifications"]) + return; + + if (!("Notification" in window)) + return; + + if (Notification.permission === "granted") + { + notificationsWork = true; + return; + } + + if (Notification.permission !== "denied") + { + if (await Notification.requestPermission()) + notificationsWork = true; + } +} + +const windowHasFocus = function () { + if (document.hasFocus()) return true + let hasFocus = false + + window.addEventListener('focus', function () { + hasFocus = true + }) + window.focus() + + return hasFocus +} + +function clearNotifications() +{ + if (!notificationsWork) + return; + + //console.log(notifications) + for (let not of notifications) { + //console.log(`> Closing notification: ${not.body}`); + not.close(); + } + + notifications = []; +} + +function windowVisible() +{ + return (windowHasFocus() || document.visibilityState == "visible") +} +function canNotify() +{ + if (!settingsObj["notification"]["allow-notifications"]) + return false; + if (!notificationsWork) + return false; + if (windowVisible()) + return false; + + return true; +} + +function showNotification(title, msg, callback) +{ + if (!canNotify()) + return; + + //console.log(`> Showing notification: ${msg}`); + + const notification = new Notification(title, {body: msg, silent: true}); + notifications.push(notification); + + notification.onclick = (ev) => + { + clearNotifications(); + + if (typeof callback == "function") + callback(ev); + window.focus(); + }; +} + +window.addEventListener('focus', () => +{ + clearNotifications(); +}); + +window.addEventListener('visibilitychange', () => +{ + if (document.visibilityState == "visible") + clearNotifications(); +}); + + +let msgSound = new Audio("./assets/audio/not.wav"); +function playNotificationSound() +{ + if (!settingsObj["notification"]["allow-sound"]) + return; + + msgSound.play().then(); +} + +checkNotifications().then(); \ No newline at end of file diff --git a/client/js/settings.js b/client/js/settings.js index e5acc4f..089f77e 100644 --- a/client/js/settings.js +++ b/client/js/settings.js @@ -5,25 +5,29 @@ const settingsMainInputAutoHideChatElement = document.getElementById("settings-m const settingsMainInputAutoShowChatElement = document.getElementById("settings-main-input-auto-show-chat"); const settingsMainInputAllowExternalSourcesGlobalElement = document.getElementById("settings-main-input-allow-external-sources-global"); const settingsMainInputAddPingReply = document.getElementById("settings-main-input-add-ping-reply"); +const settingsMainInputAllowNotifications = document.getElementById("settings-allow-notifications"); +const settingsMainInputAllowNotificationSounds = document.getElementById("settings-allow-notification-sounds"); -function hideSettings() -{ + +function hideSettings() { settingsBgElement.style.display = "none"; } -async function showSettings() -{ +async function showSettings() { settingsMainInputAutoHideChatElement.checked = getSetting(["chat", "auto-hide-chat"]); settingsMainInputAutoShowChatElement.checked = getSetting(["chat", "auto-show-chat"]); settingsMainInputAllowExternalSourcesGlobalElement.checked = getSetting(["chat", "allow-external-sources-global"]); settingsMainInputAddPingReply.checked = getSetting(["chat", "add-ping-reply"]); + settingsMainInputAllowNotifications.checked = getSetting(["notification", "allow-notifications"]); + settingsMainInputAllowNotificationSounds.checked = getSetting(["notification", "allow-sound"]); + + checkNotifications().then(); settingsBgElement.style.display = "block"; } -function setSetting(objPathArr, value) -{ +function setSetting(objPathArr, value) { let temp = settingsObj; for (let key of objPathArr.slice(0, objPathArr.length - 1)) if (temp[key] == undefined) @@ -35,8 +39,7 @@ function setSetting(objPathArr, value) saveSettings(); } -function getSetting(objPathArr) -{ +function getSetting(objPathArr) { let temp = settingsObj; for (let key of objPathArr) if (temp[key] == undefined) @@ -48,19 +51,13 @@ function getSetting(objPathArr) } - - -async function settingsUiClicked() -{ +async function settingsUiClicked() { if (docLastServerId == NoId) return; - if (docLastServerId == DMsId) - { + if (docLastServerId == DMsId) { alert("NO DM SETTINGS YET") - } - else - { + } else { let choice = prompt("1 Add User\n2 Kick User\n3 Leave Group\n4 Add Channel\n5 Remove Channel"); if (choice == null) return; @@ -69,8 +66,7 @@ async function settingsUiClicked() if (!(choice >= 1 && choice <= 5)) return; - if (choice == 1) - { + if (choice == 1) { let userId = prompt("Enter user id:"); if (userId == null) return; @@ -79,8 +75,7 @@ async function settingsUiClicked() return alert("Invalid user id"); let symmKey = await getUserMySymmKey(currentUser["mainAccount"], userId); - if (symmKey == null) - { + if (symmKey == null) { alert("User does not exist!"); return; } @@ -89,14 +84,10 @@ async function settingsUiClicked() let res = await addUserToGroup(currentUser, docLastServerId, userId); if (res !== true && res !== undefined) alert(res); - } - catch (e) - { + } catch (e) { alert(e); } - } - else if (choice == 2) - { + } else if (choice == 2) { let userId = prompt("Enter user id:"); if (userId == null) return; @@ -105,8 +96,7 @@ async function settingsUiClicked() return alert("Invalid user id"); let symmKey = await getUserMySymmKey(currentUser["mainAccount"], userId); - if (symmKey == null) - { + if (symmKey == null) { alert("User does not exist!"); return; } @@ -115,26 +105,18 @@ async function settingsUiClicked() let res = await removeUserFromGroup(currentUser, docLastServerId, userId); if (res !== true && res !== undefined) alert(res); - } - catch (e) - { + } catch (e) { alert(e); } - } - else if (choice == 3) - { + } else if (choice == 3) { try { let res = await leaveGroup(currentUser, docLastServerId); if (res !== true && res !== undefined) alert(res); - } - catch (e) - { + } catch (e) { alert(e); } - } - else if (choice == 4) - { + } else if (choice == 4) { let channelName = prompt("Enter new channel name:"); if (channelName == "" || channelName == undefined) return; @@ -142,9 +124,7 @@ async function settingsUiClicked() let res = await addChannelToGroup(currentUser, docLastServerId, channelName); if (res !== true && res !== undefined) alert(res); - } - else if (choice == 5) - { + } else if (choice == 5) { let channelId = prompt("Enter channel id to delete:"); if (channelId == null) return; diff --git a/client/lib/user/settingsObj.js b/client/lib/user/settingsObj.js index cf1891f..dcceacf 100644 --- a/client/lib/user/settingsObj.js +++ b/client/lib/user/settingsObj.js @@ -1,27 +1,29 @@ let settingsObj = {}; -function tryConformSettings(obj) -{ + +function tryConformSettings(obj) { if (obj == undefined) obj = {}; if (obj["chat"] == undefined) obj["chat"] = {}; if (obj["chat"]["auto-show-chat"] == undefined) obj["chat"]["auto-show-chat"] = true; if (obj["chat"]["auto-hide-chat"] == undefined) obj["chat"]["auto-hide-chat"] = true; if (obj["chat"]["add-ping-reply"] == undefined) obj["chat"]["add-ping-reply"] = true; if (obj["chat"]["allow-external-sources-global"] == undefined) obj["chat"]["allow-external-sources-global"] = false; + if (obj["notification"] == undefined) obj["notification"] = {}; + if (obj["notification"]["allow-notifications"] == undefined) obj["notification"]["allow-notifications"] = true; + if (obj["notification"]["allow-sound"] == undefined) obj["notification"]["allow-sound"] = false; return obj; } -function loadSettings() -{ +function loadSettings() { let obj = _loadObject("main_settings"); if (obj == undefined) obj = {}; settingsObj = tryConformSettings(obj); saveSettings(); } -function saveSettings() -{ + +function saveSettings() { _saveObject("main_settings", settingsObj); } diff --git a/client/lib/user/userMsgSystem.js b/client/lib/user/userMsgSystem.js index bbe1d91..b70e43f 100644 --- a/client/lib/user/userMsgSystem.js +++ b/client/lib/user/userMsgSystem.js @@ -174,7 +174,7 @@ async function addMessageToUser(account, userIdFrom, chatUserId, message, date) } else if (!dontRedirectTypes.includes(type)) { - logWarn(`Message already in user:`, message); + logInfo(`Message already in user:`, message); return; }