Midgard
68f87f4048
Allow 1px difference in isScrolledToBottom to allow for small differences that can occur when the page zoom has been set to e.g. 110%
197 lines
5.6 KiB
JavaScript
197 lines
5.6 KiB
JavaScript
function transformMessageText(message) {
|
|
return message
|
|
.split(/(?<=:|\W|^)(:[a-z0-9_-]+:)(?=:|\W|$)/)
|
|
.map((x, i) => i % 2
|
|
? emoji[x.slice(1, -1)] || x
|
|
: x)
|
|
.join("");
|
|
}
|
|
|
|
|
|
function shouldShowTime(lastShownTime, time) {
|
|
const sim = dateSimilarity(lastShownTime, time);
|
|
if (sim < DATE_SIMILARITY.date) {
|
|
return {date: true, time: true};
|
|
}
|
|
const minutesSinceLastShown = (time - lastShownTime) / 1000 / 60;
|
|
if (minutesSinceLastShown >= 15) {
|
|
return {date: false, time: true};
|
|
}
|
|
return {date: false, time: false};
|
|
}
|
|
|
|
|
|
async function createMessageElement(client, post, lastTime, lastAuthor) {
|
|
let nodes = [];
|
|
|
|
const users = await client.getUsers();
|
|
const isThreadReply = !!post.parent_id;
|
|
|
|
const createAt = new Date(post.create_at);
|
|
let createAtLocalized = createAt;
|
|
let lastTimeLocalized = lastTime;
|
|
try {
|
|
if (client.me.timezone.useAutomaticTimezone !== "true") {
|
|
createAtLocalized = toTimeZone(createAt, client.me.timezone.manualTimezone);
|
|
lastTimeLocalized = toTimeZone(lastTime, client.me.timezone.manualTimezone);
|
|
}
|
|
} catch(e) {}
|
|
const showTime = shouldShowTime(lastTimeLocalized, createAtLocalized);
|
|
if (showTime.time) {
|
|
lastTime = createAt;
|
|
}
|
|
|
|
const postDiv = document.createElement("div");
|
|
postDiv.className = "post";
|
|
if (post.type !== "") {
|
|
postDiv.className += " special";
|
|
}
|
|
|
|
if (showTime.date) {
|
|
const dateElement = document.createElement("div");
|
|
dateElement.className = "date_separator";
|
|
dateElement.innerText = formatYyyyMmDdWeekday(createAtLocalized);
|
|
nodes.push(dateElement);
|
|
}
|
|
|
|
if (showTime.time) {
|
|
postDiv.className += " show_time";
|
|
}
|
|
|
|
const messageDiv = document.createElement("div");
|
|
messageDiv.className = "message";
|
|
messageDiv.innerText = transformMessageText(post.message);
|
|
|
|
const createAtDiv = document.createElement("time");
|
|
createAtDiv.className = "create_at";
|
|
let createAtText = formatHhMm(createAtLocalized);
|
|
createAtDiv.title = createAt.toString();
|
|
createAtDiv.innerText = createAtText;
|
|
createAtDiv.dateTime = createAt.toISOString();
|
|
|
|
const authorName = users[post.user_id] ? users[post.user_id].username : post.user_id;
|
|
const authorDiv = document.createElement("div");
|
|
authorDiv.className = "author";
|
|
authorDiv.innerText = authorName;
|
|
|
|
if (lastAuthor === post.user_id) {
|
|
postDiv.className += " same_author";
|
|
}
|
|
lastAuthor = post.user_id;
|
|
|
|
postDiv.dataset["id"] = post.id;
|
|
postDiv.appendChild(authorDiv);
|
|
postDiv.appendChild(createAtDiv);
|
|
postDiv.appendChild(messageDiv);
|
|
|
|
if (post.metadata && (post.metadata.files || []).length > 0) {
|
|
const attachmentsUl = document.createElement("ul");
|
|
attachmentsUl.className = "attachments";
|
|
for (let file of post.metadata.files || []) {
|
|
const attachmentLi = document.createElement("li");
|
|
attachmentLi.dataset["id"] = file.id;
|
|
|
|
const attachmentA = document.createElement("a");
|
|
client.filePublicLink(file.id).then(link => attachmentA.href = link);
|
|
attachmentA.target = "_blank";
|
|
attachmentA.innerText = file.name;
|
|
|
|
if (file.mini_preview) {
|
|
const attachmentImg = document.createElement("img");
|
|
attachmentImg.src = `data:image/jpeg;base64,${file.mini_preview}`;
|
|
attachmentA.appendChild(attachmentImg);
|
|
}
|
|
|
|
attachmentLi.appendChild(attachmentA);
|
|
attachmentsUl.appendChild(attachmentLi);
|
|
}
|
|
postDiv.appendChild(attachmentsUl);
|
|
}
|
|
|
|
nodes.push(postDiv);
|
|
|
|
return {nodes, lastTime, lastAuthor};
|
|
}
|
|
|
|
|
|
async function populateChannelContents(client, channel, contents) {
|
|
byId("channel_contents").innerHTML = "";
|
|
|
|
let result = {lastAuthor: null, lastTime: null, postDiv: null};
|
|
|
|
let nodes = [];
|
|
for (let id of contents.order) {
|
|
result = await createMessageElement(client, contents.posts[id], result.lastTime, result.lastAuthor);
|
|
nodes.push(...result.nodes);
|
|
}
|
|
|
|
byId("channel_contents").dataset["server"] = client.endpoint;
|
|
byId("channel_contents").dataset["id"] = channel.id;
|
|
byId("channel_contents").dataset["lastTime"] = result.lastTime.getTime();
|
|
byId("channel_contents").dataset["lastAuthor"] = result.lastAuthor;
|
|
byId("channel_contents").append(...nodes);
|
|
scrollToBottom();
|
|
}
|
|
|
|
|
|
async function addMessage(client, post) {
|
|
const shouldScroll = isScrolledToBottom();
|
|
|
|
const result = await createMessageElement(
|
|
client, post,
|
|
new Date(1 * (byId("channel_contents").dataset["lastTime"])),
|
|
byId("channel_contents").dataset["lastAuthor"]
|
|
);
|
|
byId("channel_contents").dataset["lastTime"] = result.lastTime.getTime();
|
|
byId("channel_contents").dataset["lastAuthor"] = result.lastAuthor;
|
|
byId("channel_contents").append(...result.nodes);
|
|
|
|
if (shouldScroll) {
|
|
scrollToBottom();
|
|
}
|
|
}
|
|
|
|
|
|
function scrollToBottom() {
|
|
const el = byId("channel_contents_wrapper");
|
|
el.scrollTop = el.scrollHeight;
|
|
el.className = "";
|
|
}
|
|
|
|
|
|
function updateComposeHeight() {
|
|
byId("compose").style.height = "";
|
|
byId("compose").style.height = (byId("compose").scrollHeight + 1) + "px";
|
|
}
|
|
byId("compose").addEventListener("input", updateComposeHeight);
|
|
|
|
function isScrolledToBottom() {
|
|
const el = byId("channel_contents_wrapper");
|
|
|
|
const scrolledTo = el.clientHeight + el.scrollTop;
|
|
return scrolledTo + 1 >= el.scrollHeight;
|
|
}
|
|
function checkScrolledToBottom() {
|
|
if (isScrolledToBottom()) {
|
|
removeClass(byId("channel_contents_wrapper"), "not-at-bottom");
|
|
} else {
|
|
addClass(byId("channel_contents_wrapper"), "not-at-bottom");
|
|
}
|
|
}
|
|
byId("channel_contents_wrapper").addEventListener("scroll", checkScrolledToBottom);
|
|
|
|
|
|
function currentChannel() {
|
|
return {
|
|
endpoint: byId("channel_contents").dataset["server"],
|
|
channel_id: byId("channel_contents").dataset["id"]
|
|
};
|
|
}
|
|
|
|
|
|
pubsub.subscribe("MESSAGES_NEW", post => {
|
|
const chan = currentChannel();
|
|
if (post.endpoint === chan.endpoint && post.channel_id === chan.channel_id) {
|
|
addMessage(mm_client.getOrCreate(post.endpoint), post);
|
|
}
|
|
});
|