forked from retoor/snek
The condition for preventing auto-scroll on new messages was changed from checking if the user is within the top fifth of the scrollable area to checking if they are beyond the bottom 80% (i.e., scrolled past 83.3%). This effectively inverts the logic so that auto-scroll is suppressed only when the user has scrolled significantly upward, rather than when they are near the top. The change ensures the chat view remains locked to the latest message unless the user deliberately scrolls far up.
153 lines
5.0 KiB
HTML
153 lines
5.0 KiB
HTML
{% extends "app.html" %}
|
|
|
|
{% block main %}
|
|
<section class="chat-area" id="chat">
|
|
<div class="chat-header">
|
|
<h2>{{ channel.label.value }}</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>
|
|
const channelUid = "{{ channel.uid.value }}";
|
|
|
|
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 updateTimes() {
|
|
document.querySelectorAll(".time").forEach((time) => {
|
|
time.innerText = app.timeDescription(time.dataset.created_at);
|
|
});
|
|
}
|
|
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 / 1.2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
let isLoadingExtra = false;
|
|
async function loadExtra() {
|
|
|
|
//const offsetMessage = messagesContainer.querySelector(".chat-messages :nth-child(10)");
|
|
//if(!offsetMessage){
|
|
// return
|
|
//}
|
|
const firstMessage = messagesContainer.querySelector(".message:first-child");
|
|
//if(offsetMessage.dataset.seen){
|
|
// return
|
|
//}
|
|
if(isLoadingExtra){
|
|
return
|
|
}
|
|
if(isScrolledPastHalf()){
|
|
return
|
|
}
|
|
isLoadingExtra = true
|
|
|
|
|
|
|
|
const messages = await app.rpc.getMessages(channelUid, 1, firstMessage.dataset.created_at);
|
|
messages.forEach((message) => {
|
|
firstMessage.insertAdjacentHTML("beforebegin", message.html);
|
|
})
|
|
isLoadingExtra = false;
|
|
}
|
|
messagesContainer.addEventListener("scroll",()=>{
|
|
loadExtra()
|
|
});
|
|
function updateLayout(doScrollDown) {
|
|
const messagesContainer = document.querySelector(".chat-messages");
|
|
//messagesContainer.scrollTop = messagesContainer.scrollHeight + 1000;
|
|
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, 1000);
|
|
|
|
app.addEventListener("channel-message", (data) => {
|
|
if (data.channel_uid !== channelUid) return;
|
|
|
|
if (data.username !== "{{ user.username.value }}") {
|
|
app.playSound(0);
|
|
}
|
|
|
|
const messagesContainer = document.querySelector(".chat-messages");
|
|
const lastMessage = messagesContainer.querySelector(".message:last-child");
|
|
const doScrollDownBecauseLastMessageIsVisible = !lastMessage || isElementVisible(lastMessage);
|
|
|
|
|
|
const message = document.createElement("div");
|
|
message.dataset.color = data.color;
|
|
message.dataset.created_at = data.created_at;
|
|
message.dataset.user_nick = data.user_nick;
|
|
message.dataset.uid = data.uid;
|
|
message.innerHTML = data.html;
|
|
document.querySelector(".chat-messages").appendChild(message.firstChild);
|
|
updateLayout(doScrollDownBecauseLastMessageIsVisible);
|
|
setTimeout(()=>{
|
|
updateLayout(doScrollDownBecauseLastMessageIsVisible)
|
|
},1000)
|
|
});
|
|
|
|
initInputField(document.querySelector("textarea"));
|
|
updateLayout(true);
|
|
</script>
|
|
{% endblock %}
|
|
|
|
|