diff --git a/themes/chatview/psi/bubble/index.html b/themes/chatview/psi/bubble/index.html index bd3c9453a..b6b7abf3c 100644 --- a/themes/chatview/psi/bubble/index.html +++ b/themes/chatview/psi/bubble/index.html @@ -13,34 +13,14 @@ var util = shared.chat.util; var themeStyle = document.getElementById("themeStyle").sheet; var cssBody = util.findStyleSheet(themeStyle, "body").style; - -available_reactions = [ - "😂", - "🤣", - "🔥", - "👍", - "😭", - "🙏", - "❤️", - "😘", - "🥰", - "😍", - "😊", -] - -var prevMessage = { - "type": undefined, - "sender": undefined, -}; +const reactionsSelector = new shared.chat.ReactionsSelector(); +const chatMenu = new shared.chat.ContextMenu(); var applyPsiSettings = function() { util.getFont(function(cssFont){util.updateObject(cssBody, cssFont)}); } -shared.initTheme({ - chatElement : document.body, - templates : { - message: `
+const messageTemplate = `
%sender% @@ -49,11 +29,21 @@ %time% %quoteTxt% %message% -
`, +
%next%`; + +shared.initTheme({ + chatElement : document.body, + templates : { + message: messageTemplate, + messageGroupping: messageTemplate, sys: `
%message%
`, - sysMessageUT: `
%message%
%usertext%
` + sysMessageUT: `
%message%
%usertext%
`, + lastMsgDate: `
%time{LL}%
`, + subject: shared.isMuc? + "
%message%
%usertext%
" + : "
%usertext%
", }, - dateFormat : "HH:mm:ss", + dateFormat : "HH:mm", proxy : function() { //optional if (shared.cdata.type == "reactions") { render_reactions(shared.cdata); @@ -63,7 +53,7 @@ varHandlers : { msgClasses: function() { let classes = ["msg"]; - if (prevMessage["type"] == "message" && prevMessage["sender"] == shard.cdata.sender) { + if (shared.cdata.nextOfGroup) { classes.push("grnext") } if (shared.cdata.local) { @@ -105,7 +95,7 @@ selector.textContent = "❤️"; setTimeout(() => { selector.classList.add('noopacity'); }, 0); shared_timer.timer = null; - selector.addEventListener("click", () => show_reactions_selector(selector, document.documentElement)); + selector.addEventListener("click", () => reactionsSelector.show(selector, document.documentElement)); }, 500); }); if (shared.cdata.reply) { @@ -117,62 +107,13 @@ } }); -function fromHTML(html, trim = true) { - // Process the HTML string. - html = trim ? html.trim() : html; - if (!html) return null; - - // Then set up a new template element. - const template = document.createElement('template'); - template.innerHTML = html; - const result = template.content.children; - - // Then return either an HTMLElement or HTMLCollection, - // based on whether the input HTML had one or more roots. - if (result.length === 1) return result[0]; - return result; -} - -function setup_reactions_selector() { - const rs = document.body.appendChild(document.createElement("div")); - rs.classList.add("reactions_selector"); - rs.id = "reactions_selector"; - available_reactions.forEach(emoji => { - const em = rs.appendChild(document.createElement("em")); - em.textContent = emoji; - }); - - rs.addEventListener("click", function (event) { - if (event.target.localName == "em") { - event.target.parentNode.style.display = "none"; - } - event.stopPropagation(); - }); - rs.addEventListener("mouseleave", function (event) { - rs.style.display = "none"; - event.stopPropagation(); - }); -} -function show_reactions_selector(nearEl, scrollEl) { - const rs = document.getElementById("reactions_selector"); - const nbr = nearEl.getBoundingClientRect(); - rs.style.top = (nbr.top + scrollEl.scrollTop) + "px"; - rs.style.display = "flex"; - const selectorRect = rs.getBoundingClientRect(); - const scrollRect = scrollEl.getBoundingClientRect(); - if (nbr.left + selectorRect.width > scrollRect.right) { - rs.style.left = (nbr.right - selectorRect.width) + "px"; - } else { - rs.style.left = nbr.left + "px"; - } -} function setup_context_menu() { - document.addEventListener("contextmenu", function (event) { - const nick = event.target.getAttribute("data-nick"); + chatMenu.addItemProvider((event) => { + const isNick = event.target.className == "nick"; let msgNode; - if (!nick) { + if (!isNick) { msgNode = event.target; while (msgNode) { if (msgNode.classList && msgNode.classList.contains("msg")) { @@ -181,62 +122,25 @@ msgNode = msgNode.parentNode; } } - if (nick || msgNode) { - event.stopPropagation(); - event.preventDefault(); - + if (isNick || msgNode) { let items; - if (nick) { + if (isNick) { items = [ - { "id": "info", "text": "Info" }, - { "id": "private_chat", "text": "Open Chat" }, - { "id": "kick", "text": "Kick" } + { text: "Info", action: ()=>{} }, + { text: "Open Chat", action: ()=>{} }, + { text: "Kick", action: ()=>{} } ] } else { items = [ - { "id": "delete", "text": "Delete" }, - { "id": "reply", "text": "Reply" }, - { "id": "forward", "text": "Forward" }, - { "id": "copy", "text": "Copy" } + { text: "Delete", action: ()=>{} }, + { text: "Reply", action: ()=>{} }, + { text: "Forward", action: ()=>{} }, + { text: "Copy", action: ()=>{} } ] } - - show_context_menu(event.x, event.y + document.documentElement.scrollTop, items).then((id) => { - console.log(id + " selected"); - }).catch(() => { }); - return false; + return items; } - }); -} - -function show_context_menu(x, y, items) { - if (window.activeMenu) { - window.activeMenu.destroyMenu(); - } - const menu = document.body.appendChild(document.createElement("div")); - menu.classList.add("context_menu"); - return new Promise((resolve, reject) => { - for (let i = 0; i < items.length; i++) { - const item = menu.appendChild(document.createElement("div")); - item.textContent = items[i].text; - item.menuItemId = items[i].id; - item.addEventListener("click", (event) => { - resolve(event.target.menuItemId); - event.stopPropagation(); - menu.destroyMenu(); - }, { "once": true }); - } - menu.destroyMenu = () => { - document.body.removeEventListener("click", menu.destroyMenu); - menu.parentNode.removeChild(menu); - window.activeMenu = undefined; - reject(); - }; - document.body.addEventListener("click", menu.destroyMenu, { "once": true }); - - menu.style.top = y + "px"; - menu.style.left = x + "px"; - window.activeMenu = menu; + return []; }); } @@ -272,7 +176,6 @@ } } -setup_reactions_selector(); setup_context_menu(); applyPsiSettings(); @@ -326,7 +229,7 @@ margin-left: 3rem; padding: .5rem; position: relative; - background-color: white; + background-color: #f0f0f0; border-radius: 0 .6rem .6rem .6rem; box-shadow: 0px 0px 0.5rem black; clip-path: inset(-0.5rem -5rem -0.5rem -5rem); @@ -335,6 +238,10 @@ flex-wrap: wrap; } + .msg:hover { + background-color: #ffffff; + } + .msg::before { width: 2rem; height: 2rem; @@ -359,6 +266,10 @@ padding-top: .5rem; } + .mymsg:hover { + background-color: #ffe; + } + .mymsg::before { left: inherit; right: 0; @@ -422,16 +333,18 @@ } .avatar { - margin-left: -3rem; + margin-left: -2.8rem; font-size: 2.3rem; top: 0; left: 0; position: absolute; max-width: 2.3rem; + z-index: -1; + border-radius: .5rem; } .mymsg .avatar { - margin-right: -3rem; + margin-right: -2.8rem; left: inherit; right: 0; } diff --git a/themes/chatview/util.js b/themes/chatview/util.js index bdec16de4..138d9a677 100644 --- a/themes/chatview/util.js +++ b/themes/chatview/util.js @@ -685,6 +685,132 @@ function initPsiTheme() { o.cancel = stopAnimation; // stops any current in-progress autoscroll }, + ReactionsSelector : function() { + var available_reactions = [ + "😂", + "🤣", + "🔥", + "👍", + "😭", + "🙏", + "❤️", + "😘", + "🥰", + "😍", + "😊", + ] + + const rs = document.createElement("div"); + rs.style.display = "none"; + rs.classList.add("reactions_selector"); + available_reactions.forEach(emoji => { + const em = rs.appendChild(document.createElement("em")); + em.textContent = emoji; + }); + document.body.appendChild(rs); + + rs.addEventListener("click", function (event) { + if (event.target.localName == "em") { + event.target.parentNode.style.display = "none"; + } + event.stopPropagation(); + }); + rs.addEventListener("mouseleave", function (event) { + rs.style.display = "none"; + event.stopPropagation(); + }); + + this.show = function(nearEl, scrollEl) { + const nbr = nearEl.getBoundingClientRect(); + rs.style.top = (nbr.top + scrollEl.scrollTop) + "px"; + rs.style.display = "flex"; + const selectorRect = rs.getBoundingClientRect(); + const scrollRect = scrollEl.getBoundingClientRect(); + if (nbr.left + selectorRect.width > scrollRect.right) { + rs.style.left = (nbr.right - selectorRect.width) + "px"; + } else { + rs.style.left = nbr.left + "px"; + } + } + this.hide = function() { + rs.style.display = "none"; + } + }, + + ContextMenu : function() { + this.items = []; + this.providers = []; + + this.addItem = function(text, action) { + this.items.push({text: text, action: action}); + }; + this.addItemProvider = function(itemProvider) { + this.providers.push(itemProvider); + }; + this.show = function(x, y, items) { + if (window.activeMenu) { + window.activeMenu.destroyMenu(); + } + const menu = document.body.appendChild(document.createElement("div")); + menu.classList.add("context_menu"); + return new Promise((resolve, reject) => { + for (let i = 0; i < items.length; i++) { + const item = menu.appendChild(document.createElement("div")); + item.textContent = items[i].text; + const action = items[i].action; + item.addEventListener("click", (event) => { + event.stopPropagation(); + menu.destroyMenu(); + try { + if (action instanceof Function) { + action(); + } + } finally { + resolve(action); + } + }, { "once": true }); + } + menu.destroyMenu = () => { + document.body.removeEventListener("click", menu.destroyMenu); + menu.parentNode.removeChild(menu); + window.activeMenu = undefined; + reject(); + }; + document.body.addEventListener("click", menu.destroyMenu, { "once": true }); + + menu.style.top = y + "px"; + menu.style.left = x + "px"; + window.activeMenu = menu; + }); + }; + + var menu = this; + document.addEventListener("contextmenu", function (event) { + var all_items = menu.items.slice(); + try { + for (let i = 0; i < menu.providers.length; i++) { + all_items = all_items.concat(menu.providers[i](event)); + } + } catch(e) { + chat.console(e+""); + } + if (!all_items.length) { + return true; + } + + event.stopPropagation(); + event.preventDefault(); + + var totalScrollY = 0; + var el = event.target; + while (el && el.scrollTop !== undefined) { + totalScrollY += el.scrollTop; + el = el.parentNode; + } + menu.show(event.x, event.y + 0/*totalScrollY*/, all_items).catch(()=>{}); + }); + }, + DateTimeFormatter : function(formatStr) { function convertToTr35(format) {