sliding-sync/client/render.js
2022-03-07 17:29:36 +00:00

237 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This file contains code to map data structures to actual HTML elements.
* It is mostly functional and boring and it does not include any sliding sync specific data.
* In other words, if you want to learn about sliding sync, this isn't the file to look at.
*/
const membershipChangeText = (ev) => {
const prevContent = (ev.unsigned || {}).prev_content || {};
const prevMembership = prevContent.membership || "leave";
const nowMembership = ev.content.membership;
if (nowMembership != prevMembership) {
switch (nowMembership) {
case "join":
return ev.state_key + " joined the room";
case "leave":
return ev.state_key + " left the room";
case "ban":
return ev.sender + " banned " + ev.state_key + " from the room";
case "invite":
return ev.sender + " invited " + ev.state_key + " to the room";
case "knock":
return ev.state_key + " knocked on the room";
}
}
if (nowMembership == prevMembership && nowMembership == "join") {
// display name or avatar change
if (prevContent.displayname !== ev.content.displayname) {
return (
ev.state_key + " set their name to " + ev.content.displayname
);
}
if (prevContent.avatar_url !== ev.content.avatar_url) {
return ev.state_key + " changed their profile picture";
}
}
return ev.type + " event";
};
const textForEvent = (ev) => {
let body = "";
switch (ev.type) {
case "m.room.message":
body = ev.content.body;
break;
case "m.room.member":
body = membershipChangeText(ev);
break;
case "m.reaction":
body = "reacted with " + (ev.content["m.relates_to"] || {}).key;
break;
default:
body = ev.type + " event";
break;
}
return body;
};
const randomName = (i, long) => {
if (i % 17 === 0) {
return long
? "Ever have that feeling where youre not sure if youre awake or dreaming?"
: "There is no spoon";
} else if (i % 13 === 0) {
return long
? "Choice is an illusion created between those with power and those without."
: "Get Up Trinity";
} else if (i % 11 === 0) {
return long
? "Thats how it is with people. Nobody cares how it works as long as it works."
: "I know kung fu";
} else if (i % 7 === 0) {
return long
? "The body cannot live without the mind."
: "Free your mind";
} else if (i % 5 === 0) {
return long
? "Perhaps we are asking the wrong questions…"
: "Agent Smith";
} else if (i % 3 === 0) {
return long
? "You've been living in a dream world, Neo."
: "Mr Anderson";
} else {
return long ? "Mr. Wizard, get me the hell out of here! " : "Morpheus";
}
};
const zeroPad = (n) => {
if (n < 10) {
return "0" + n;
}
return n;
};
const formatTimestamp = (originServerTs) => {
const d = new Date(originServerTs);
return (
d.toDateString() +
" " +
zeroPad(d.getHours()) +
":" +
zeroPad(d.getMinutes()) +
":" +
zeroPad(d.getSeconds())
);
};
const mxcToUrl = (syncv2ServerUrl, mxc) => {
const path = mxc.substr("mxc://".length);
if (!path) {
return;
}
return `${syncv2ServerUrl}/_matrix/media/r0/thumbnail/${path}?width=64&height=64&method=crop`;
};
export const renderRoomHeader = (room, syncv2ServerUrl) => {
document.getElementById("selectedroomname").textContent =
room.name || room.room_id;
if (room.avatar) {
document.getElementById("selectedroomavatar").src =
mxcToUrl(syncv2ServerUrl, room.avatar) || "/client/placeholder.svg";
} else {
document.getElementById("selectedroomavatar").src =
"/client/placeholder.svg";
}
if (room.topic) {
document.getElementById("selectedroomtopic").textContent = room.topic;
} else {
document.getElementById("selectedroomtopic").textContent = "";
}
};
export const renderEvent = (eventIdKey, ev) => {
const template = document.getElementById("messagetemplate");
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template#avoiding_documentfragment_pitfall
const msgCell = template.content.firstElementChild.cloneNode(true);
msgCell.setAttribute("id", eventIdKey);
msgCell.getElementsByClassName("msgsender")[0].textContent = ev.sender;
msgCell.getElementsByClassName("msgtimestamp")[0].textContent =
formatTimestamp(ev.origin_server_ts);
let body = textForEvent(ev);
msgCell.getElementsByClassName("msgcontent")[0].textContent = body;
return msgCell;
};
/**
* Render a room cell for the room list.
* @param {Element} roomCell The DOM element to put the details into. The cell must be already initialised with `roomCellTemplate`.
* @param {object} room The room data model, which can be null to indicate a placeholder.
*/
export const renderRoomCell = (
roomCell,
room,
index,
isHighlighted,
syncv2ServerUrl
) => {
// if this child is a placeholder and it was previously a placeholder then do nothing.
if (!room && roomCell.getAttribute("x-placeholder") === "yep") {
return;
}
const roomNameSpan = roomCell.getElementsByClassName("roomname")[0];
const roomContentSpan = roomCell.getElementsByClassName("roomcontent")[0];
const roomSenderSpan = roomCell.getElementsByClassName("roomsender")[0];
const roomTimestampSpan =
roomCell.getElementsByClassName("roomtimestamp")[0];
const unreadCountSpan = roomCell.getElementsByClassName("unreadcount")[0];
// remove previous unread counts
unreadCountSpan.textContent = "";
unreadCountSpan.classList.remove("unreadcountnotify");
unreadCountSpan.classList.remove("unreadcounthighlight");
if (!room) {
// make a placeholder
roomNameSpan.textContent = randomName(index, false);
roomNameSpan.style = "background: #e0e0e0; color: #e0e0e0;";
roomContentSpan.textContent = randomName(index, true);
roomContentSpan.style = "background: #e0e0e0; color: #e0e0e0;";
roomSenderSpan.textContent = "";
roomTimestampSpan.textContent = "";
roomCell.getElementsByClassName("roomavatar")[0].src =
"/client/placeholder.svg";
roomCell.style = "";
roomCell.setAttribute("x-placeholder", "yep");
return;
}
roomCell.removeAttribute("x-placeholder"); // in case this was previously a placeholder
roomCell.style = "";
roomNameSpan.textContent = room.name || room.room_id;
roomNameSpan.style = "";
roomContentSpan.style = "";
if (room.avatar) {
roomCell.getElementsByClassName("roomavatar")[0].src =
mxcToUrl(syncv2ServerUrl, room.avatar) || "/client/placeholder.svg";
} else {
roomCell.getElementsByClassName("roomavatar")[0].src =
"/client/placeholder.svg";
}
if (isHighlighted) {
roomCell.style = "background: #d7d7f7";
}
if (room.highlight_count > 0) {
// use the notification count instead to avoid counts dropping down. This matches ele-web
unreadCountSpan.textContent = room.notification_count + "";
unreadCountSpan.classList.add("unreadcounthighlight");
} else if (room.notification_count > 0) {
unreadCountSpan.textContent = room.notification_count + "";
unreadCountSpan.classList.add("unreadcountnotify");
} else {
unreadCountSpan.textContent = "";
}
if (room.obsolete) {
roomContentSpan.textContent = "";
roomSenderSpan.textContent = room.obsolete;
} else if (room.timeline && room.timeline.length > 0) {
const mostRecentEvent = room.timeline[room.timeline.length - 1];
roomSenderSpan.textContent = mostRecentEvent.sender;
roomTimestampSpan.textContent = formatTimestamp(
mostRecentEvent.origin_server_ts
);
const body = textForEvent(mostRecentEvent);
if (mostRecentEvent.type === "m.room.member") {
roomContentSpan.textContent = "";
roomSenderSpan.textContent = body;
} else {
roomContentSpan.textContent = body;
}
} else {
roomContentSpan.textContent = "";
}
};