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 = `
`,
+
%next%`;
+
+shared.initTheme({
+ chatElement : document.body,
+ templates : {
+ message: messageTemplate,
+ messageGroupping: messageTemplate,
sys: `
%message%
`,
- sysMessageUT: `
`
+ sysMessageUT: `
`,
+ lastMsgDate: `
%time{LL}%
`,
+ subject: shared.isMuc?
+ "
"
+ : "
%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)
{