|
{% extends "app.html" %}
|
|
|
|
{% block main %}
|
|
<section class="chat-area" id="chat">
|
|
<div class="chat-header">
|
|
<h2>{{ name }}</h2>
|
|
</div>
|
|
<div class="chat-messages">
|
|
{% for message in messages %}
|
|
{% autoescape false %}
|
|
{{ message.html }}
|
|
{% endautoescape %}
|
|
{% endfor %}
|
|
</div>
|
|
<chat-window style="display:none" class="chat-area"></chat-window>
|
|
<div class="chat-input">
|
|
<textarea placeholder="Type a message..." rows="2"></textarea>
|
|
<upload-button channel="{{ channel.uid.value }}"></upload-button>
|
|
</div>
|
|
</section>
|
|
|
|
<script type="module">
|
|
import { app } from "/app.js";
|
|
|
|
const channelUid = "{{ channel.uid.value }}";
|
|
|
|
function getInputField(){
|
|
return document.querySelector("textarea")
|
|
}
|
|
|
|
function initInputField(textBox) {
|
|
textBox.addEventListener('change', (e) => {
|
|
e.preventDefault();
|
|
this.dispatchEvent(new CustomEvent('change', { detail: e.target.value, bubbles: true }));
|
|
});
|
|
|
|
textBox.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
const message = e.target.value.trim();
|
|
if (message) {
|
|
app.rpc.sendMessage(channelUid, message);
|
|
e.target.value = '';
|
|
}
|
|
}
|
|
});
|
|
textBox.focus();
|
|
}
|
|
|
|
function replyMessage(message) {
|
|
const field = getInputField()
|
|
field.value = "```markdown\n> " + (message || '') + "\n```\n";
|
|
field.focus();
|
|
}
|
|
|
|
function updateTimes() {
|
|
document.querySelectorAll(".time").forEach((container) => {
|
|
const messageDiv = container.closest('.message');
|
|
const userNick = messageDiv.dataset.user_nick;
|
|
const text = messageDiv.querySelector(".text").innerText;
|
|
const time = document.createElement("span");
|
|
time.innerText = app.timeDescription(container.dataset.created_at);
|
|
|
|
container.replaceChildren(time);
|
|
const reply = document.createElement("a");
|
|
reply.innerText = " reply";
|
|
reply.href = "#reply";
|
|
container.appendChild(reply);
|
|
reply.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
replyMessage(text);
|
|
})
|
|
});
|
|
}
|
|
|
|
function isElementVisible(element) {
|
|
const rect = element.getBoundingClientRect();
|
|
return (
|
|
rect.top >= 0 &&
|
|
rect.left >= 0 &&
|
|
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
|
);
|
|
}
|
|
|
|
const messagesContainer = document.querySelector(".chat-messages");
|
|
|
|
function isScrolledPastHalf() {
|
|
let scrollTop = messagesContainer.scrollTop;
|
|
let scrollableHeight = messagesContainer.scrollHeight - messagesContainer.clientHeight;
|
|
|
|
if (scrollTop < scrollableHeight / 2) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
let isLoadingExtra = false;
|
|
|
|
async function loadExtra() {
|
|
const firstMessage = messagesContainer.querySelector(".message:first-child");
|
|
if (isLoadingExtra) {
|
|
return;
|
|
}
|
|
if (!isScrolledPastHalf()) {
|
|
return;
|
|
}
|
|
|
|
isLoadingExtra = true;
|
|
|
|
const messages = await app.rpc.getMessages(channelUid, 0, firstMessage.dataset.created_at);
|
|
|
|
messages.forEach((message) => {
|
|
firstMessage.insertAdjacentHTML("beforebegin", message.html);
|
|
})
|
|
updateLayout(false);
|
|
|
|
isLoadingExtra = false;
|
|
}
|
|
|
|
messagesContainer.addEventListener("scroll", () => {
|
|
loadExtra();
|
|
});
|
|
|
|
let lastMessage
|
|
|
|
function updateLayout(doScrollDown) {
|
|
const messagesContainer = document.querySelector(".chat-messages");
|
|
updateTimes();
|
|
let previousUser = null;
|
|
document.querySelectorAll(".message").forEach((message) => {
|
|
if (previousUser !== message.dataset.user_uid) {
|
|
message.classList.add("switch-user");
|
|
previousUser = message.dataset.user_uid;
|
|
} else {
|
|
message.classList.remove("switch-user");
|
|
}
|
|
});
|
|
lastMessage = messagesContainer.querySelector(".message:last-child");
|
|
if (doScrollDown) {
|
|
lastMessage?.scrollIntoView({ inline: "nearest" });
|
|
}
|
|
}
|
|
|
|
setInterval(updateTimes, 30000);
|
|
|
|
function isMentionToMe(message){
|
|
const mentionText = '@{{ user.username.value }}';
|
|
return message.toLowerCase().includes(mentionText);
|
|
}
|
|
function extractMentions(message) {
|
|
return [...new Set(message.match(/@\w+/g) || [])];
|
|
}
|
|
function isMentionForSomeoneElse(message){
|
|
const mentions = extractMentions(message);
|
|
const mentionText = '@{{ user.username.value }}';
|
|
return mentions.length > 0 && mentions.indexOf(mentionText) == -1;
|
|
}
|
|
|
|
app.addEventListener("channel-message", (data) => {
|
|
if (data.channel_uid !== channelUid) {
|
|
if(!isMentionForSomeoneElse(data.message)){
|
|
channelSidebar.notify(data);
|
|
app.playSound("messageOtherChannel");
|
|
}
|
|
|
|
return;
|
|
}
|
|
if (data.username !== "{{ user.username.value }}") {
|
|
if(isMentionToMe(data.message)){
|
|
app.playSound("mention");
|
|
}else if (!isMentionForSomeoneElse(data.message)){
|
|
app.playSound("message");
|
|
}
|
|
}
|
|
|
|
const messagesContainer = document.querySelector(".chat-messages");
|
|
lastMessage = messagesContainer.querySelector(".message:last-child");
|
|
const doScrollDownBecauseLastMessageIsVisible = !lastMessage || isElementVisible(lastMessage);
|
|
|
|
const message = document.createElement("div");
|
|
message.innerHTML = data.html;
|
|
document.querySelector(".chat-messages").appendChild(message.firstChild);
|
|
updateLayout(doScrollDownBecauseLastMessageIsVisible);
|
|
setTimeout(() => {
|
|
updateLayout(doScrollDownBecauseLastMessageIsVisible)
|
|
}, 1000);
|
|
});
|
|
|
|
initInputField(getInputField());
|
|
updateLayout(true);
|
|
</script>
|
|
{% endblock %}
|