Skip to content

Commit

Permalink
bubble theme: work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Ri0n committed Jun 26, 2024
1 parent 3500d46 commit 11a1178
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 104 deletions.
27 changes: 21 additions & 6 deletions themes/chatview/psi/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ function psiThemeAdapter(chat) {
this.template_name = template_name;
},

TemplateEscapeUriVar : function(name) {
this.name = name; // cdata member name
},

Template : function(raw) {
var splitted = raw.split(/(%[\w]+(?:\{[^\{]+\})?%)/), i;
this.parts = [];
Expand All @@ -102,6 +106,7 @@ function psiThemeAdapter(chat) {
switch (m[1]) {
case "time": this.parts.push(new shared.TemplateTimeVar(m[1], m[2])); break;
case "template": this.parts.push(new shared.TemplateTemplateVar(m[2])); break;
case "escapeURI": this.parts.push(new shared.TemplateEscapeUriVar(m[2])); break;
default: this.parts.push(new shared.TemplateVar(m[1], m[2])); break;
}
} else {
Expand All @@ -114,12 +119,14 @@ function psiThemeAdapter(chat) {
if (typeof(scroll) == 'boolean') {
shared.scroller.atBottom = scroll;
}
var el;
if (nextEl) {
chat.util.siblingHtml(nextEl, html);
el = chat.util.siblingHtml(nextEl, html);
} else {
chat.util.appendHtml(shared.chatElement, html, shared.isMuc? shared.cdata.sender : "");
el = chat.util.appendHtml(shared.chatElement, html, shared.isMuc? shared.cdata.sender : "");
}
shared.scroller.invalidate();
return el;
},

stopGroupping : function() {
Expand All @@ -141,6 +148,7 @@ function psiThemeAdapter(chat) {
shared.groupping = config.groupping || shared.groupping;
proxy = config.proxy;
shared.varHandlers = config.varHandlers || {};
shared.msgElPostProcess = config.postProcess;
for (var tname in config.templates) {
if (config.templates[tname]) {
t[tname] = new shared.Template(config.templates[tname]);
Expand Down Expand Up @@ -203,8 +211,9 @@ function psiThemeAdapter(chat) {

shared.TemplateVar.prototype = {
toString : function() {
if (shared.varHandlers[this.name]) {
return shared.varHandlers[this.name]();
const varHandler = shared.varHandlers[this.name];
if (varHandler) {
return varHandler();
}
var d = shared.cdata[this.name];
if (this.name == "sender") { //may not be html
Expand Down Expand Up @@ -249,11 +258,14 @@ function psiThemeAdapter(chat) {
return "" + tt;
}

shared.TemplateEscapeUriVar.prototype.toString = function() {
return encodeURIComponent(shared.cdata[this.name]);
}

shared.Template.prototype.toString = function() {
return this.parts.join("");
}


chat.adapter.receiveObject = function(data) {
shared.cdata = data;
if (!inited) {
Expand Down Expand Up @@ -317,8 +329,11 @@ function psiThemeAdapter(chat) {
break;
}
if (template) {
shared.appendHtml(template.toString(), data.local?true:null, data.nextOfGroup?
var el = shared.appendHtml(template.toString(), data.local?true:null, data.nextOfGroup?
shared.prevGrouppingData.nextEl:null); //force scroll on local messages
if (shared.msgElPostProcess) {
shared.msgElPostProcess(el);
}
shared.stopGroupping();// safe clean up previous data
if (shared.cdata.nextEl) { //convert to DOM
shared.cdata.nextEl = document.getElementById(shared.cdata.nextEl);
Expand Down
170 changes: 74 additions & 96 deletions themes/chatview/psi/bubble/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,81 @@

shared.initTheme({
chatElement : document.body,
templates : {},
templates : {
message: `<div class="%msgClasses%" id="%id%" data-nick="%escapeURI{sender}%">
<img class='avatar' src='%avatarurl%'/>
<div class="msgheader">
<span class="nick">%sender%</span>
%nickControl%
</div>
<span class="time" title="%time{LL}%">%time%</span>
%quoteTxt%
<span class="msgtext">%message%</span>
</div>`,
sys: `<div class="sysmsg" title="%time{LL}%">%message%</div>`,
sysMessageUT: `<div class="sysmsg" title="%time{LL}%">%message%<div class="usertext">%usertext%</div></div>`
},
dateFormat : "HH:mm:ss",
proxy : function() { //optional
//shared.chat.console(shared.cdata);
render(shared.cdata);
return false;
if (shared.cdata.type == "reactions") {
render_reactions(shared.cdata);
return false;
}
},
varHandlers : {
msgClasses: function() {
let classes = ["msg"];
if (prevMessage["type"] == "message" && prevMessage["sender"] == shard.cdata.sender) {
classes.push("grnext")
}
if (shared.cdata.local) {
classes.push("mymsg");
}
return classes.join(" ");
},
nickControl: function() {
return shared.cdata.local? "": `<div class="reply">Reply</div>`;
},
quoteTxt: function() {
if (!shared.cdata.reply) {
return "";
}
const quoteMsg = document.getElementById(shared.cdata.reply);
if (quoteMsg) {
const quoteNick = util.escapeHtml(decodeURIComponent(quoteMsg.getAttribute("data-nick")));
const quoteText = quoteMsg.getElementsByClassName("msgtext")[0].innerHTML;
return `<blockquote><div>${quoteNick}</div>${quoteText}</blockquote>`;
}
return "";
}
},
postProcess: function(el) {
let shared_timer = {}
el.addEventListener("mouseleave", function () {
if (shared_timer.timer) { // if we were going to show it
clearTimeout(shared_timer.timer);
shared_timer.timer = null;
} else {
const rs = el.getElementsByClassName("like_button")[0];
rs.parentNode.removeChild(rs);
}
});
el.addEventListener("mouseenter", function () {
shared_timer.timer = setTimeout(function () {
let selector = el.appendChild(document.createElement("div"));
selector.classList.add("like_button");
selector.textContent = "❤️";
setTimeout(() => { selector.classList.add('noopacity'); }, 0);
shared_timer.timer = null;
selector.addEventListener("click", () => show_reactions_selector(selector, document.documentElement));
}, 500);
});
if (shared.cdata.reply) {
const bq = el.getElementsByTagName("blockquote")[0];
if (bq) {
bq.addEventListener("click", () => { quoteMsg.scrollIntoView({ "behavior": "smooth", "block": "center" }) });
}
}
}
});

Expand Down Expand Up @@ -171,70 +240,6 @@
});
}

function render_message(event) {
let classes = ["msg"];
if (prevMessage["type"] == "message" && prevMessage["sender"] == event["sender"]) {
classes.push("grnext")
}
let nickControl = "";
if (event.local) {
classes.push("mymsg");
} else {
nickControl = `<div class="reply">Reply</div>`;
}
classes = classes.join(" ");
//const avatar = new Option(avatars[event["sender"]]).innerHTML;
const nickHtml = new Option(event["sender"]).innerHTML;
const nickAttr = encodeURIComponent(event["sender"]);
let quoteMsg = null;
let quoteTxt = "";
if (event.reply) {
quoteMsg = document.getElementById(event.reply);
if (quoteMsg) {
const quoteNick = decodeURIComponent(quoteMsg.getAttribute("data-nick"));
const quoteText = quoteMsg.getElementsByClassName("msgtext")[0].innerHTML;
quoteTxt = `<blockquote><div>${quoteNick}</div>${quoteText}</blockquote>`;
}
}
const time = (new Date()).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
let el = fromHTML(`<div class="${classes}" id="${event.id}" data-nick="${nickAttr}">
<img class='avatar' src='%avatarurl%'/>
<div class="msgheader">
<span class="nick">${nickHtml}</span>
${nickControl}
</div>
<span class="time">${time}</span>
${quoteTxt}
<span class="msgtext">${event["message"]}</span>
</div>`);
document.body.appendChild(el);
let shared_timer = {}
el.addEventListener("mouseleave", function () {
if (shared_timer.timer) { // if we were going to show it
clearTimeout(shared_timer.timer);
shared_timer.timer = null;
} else {
const rs = el.getElementsByClassName("like_button")[0];
rs.parentNode.removeChild(rs);
}
});
el.addEventListener("mouseenter", function () {
shared_timer.timer = setTimeout(function () {
let selector = el.appendChild(document.createElement("div"));
selector.classList.add("like_button");
selector.textContent = "❤️";
setTimeout(() => { selector.classList.add('noopacity'); }, 0);
shared_timer.timer = null;
selector.addEventListener("click", () => show_reactions_selector(selector, document.documentElement));
}, 500);
});
if (event.reply) {
const bq = el.getElementsByTagName("blockquote")[0];
if (bq) {
bq.addEventListener("click", () => { quoteMsg.scrollIntoView({ "behavior": "smooth", "block": "center" }) });
}
}
}

function render_reactions(event) {
const msg = document.getElementById(event.target);
Expand Down Expand Up @@ -267,34 +272,6 @@
}
}

function render_system(event) {
let text = new Option(event["message"]).innerHTML;
if (event["usertext"]) {
let usertext = event["usertext"].replaceAll("\n", "<br/>");
document.body.appendChild(fromHTML(`<div class="sysmsg">${text}<div class="usertext">${usertext}</div></div>`));
} else {
document.body.appendChild(fromHTML(`<div class="sysmsg">${text}</div>`));
}
}

function render(event) {
if (event["type"] == "message") {
switch (event["mtype"]) {
case "message": render_message(event); break;
case "system": render_system(event); break;
}
if (event.mtype === "message" || event.mtype === "system") {
prevMessage = {
"type": event["type"],
"sender": event["sender"],
};
}
document.documentElement.scrollTo({ "top": document.documentElement.scrollHeight, "behavior": "smooth" })
} else if (event["type"] == "reactions") {
render_reactions(event);
}
}

setup_reactions_selector();
setup_context_menu();

Expand Down Expand Up @@ -450,6 +427,7 @@
top: 0;
left: 0;
position: absolute;
max-width: 2.3rem;
}

.mymsg .avatar {
Expand Down
8 changes: 6 additions & 2 deletions themes/chatview/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,16 @@ function initPsiTheme() {

appendHtml : function(dest, html, sender) {
chat.util.prepareContents(html, sender);
var firstAdded = htmlSource.firstChild;
while (htmlSource.firstChild) dest.appendChild(htmlSource.firstChild);
return firstAdded;
},

siblingHtml : function(dest, html, sourceUser) {
chat.util.prepareContents(html, sourceUser);
var firstAdded = htmlSource.firstChild;
while (htmlSource.firstChild) dest.parentNode.insertBefore(htmlSource.firstChild, dest);
return firstAdded;
},

ensureDeleted : function(id) {
Expand Down Expand Up @@ -465,8 +469,8 @@ function initPsiTheme() {
// if we have an id then this is a replacable message.
// next weird logic is as follows:
// - wrapping to some element may break elements flow
// - using well know elements may break styles
// - setting just starting mark is useless (we will never find correct end)
// - using well known elements may break styles
// - setting just starting mark is useless (we will never find the correct end)
var uniqId;
if (isMuc) {
var u = usersMap[nick];
Expand Down

0 comments on commit 11a1178

Please sign in to comment.