chat défilement

This commit is contained in:
Flatlogic Bot 2026-02-15 18:28:21 +00:00
parent 1440c83ccf
commit dfd640b430
2 changed files with 54 additions and 10 deletions

View File

@ -5,11 +5,33 @@ document.addEventListener('DOMContentLoaded', () => {
const messagesList = document.getElementById('messages-list'); const messagesList = document.getElementById('messages-list');
const typingIndicator = document.getElementById('typing-indicator'); const typingIndicator = document.getElementById('typing-indicator');
function scrollToBottom(force = false) {
if (!messagesList) return;
// Smart scroll: only scroll if user is already at the bottom or if forced (e.g. sending a message)
const threshold = 150; // pixels margin
const isAtBottom = messagesList.scrollHeight - messagesList.scrollTop <= messagesList.clientHeight + threshold;
if (force || isAtBottom) {
messagesList.scrollTo({
top: messagesList.scrollHeight,
behavior: 'smooth'
});
// Backup for non-smooth support or rendering delays
setTimeout(() => {
if (force || messagesList.scrollHeight - messagesList.scrollTop <= messagesList.clientHeight + threshold + 200) {
messagesList.scrollTop = messagesList.scrollHeight;
}
}, 100);
}
}
// Emoji list for reactions // Emoji list for reactions
const EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🔥', '✅', '🚀', '❓', '💡', '📌', '💯']; const EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🔥', '✅', '🚀', '❓', '💡', '📌', '💯'];
// Scroll to bottom // Scroll to bottom
messagesList.scrollTop = messagesList.scrollHeight; scrollToBottom(true);
const currentChannel = new URLSearchParams(window.location.search).get('channel_id') || 1; const currentChannel = new URLSearchParams(window.location.search).get('channel_id') || 1;
const currentThread = new URLSearchParams(window.location.search).get('thread_id'); const currentThread = new URLSearchParams(window.location.search).get('thread_id');
@ -45,7 +67,6 @@ document.addEventListener('DOMContentLoaded', () => {
const data = JSON.parse(msg.data); const data = JSON.parse(msg.data);
if (data.channel_id == currentChannel) { if (data.channel_id == currentChannel) {
appendMessage(data); appendMessage(data);
messagesList.scrollTop = messagesList.scrollHeight;
// Desktop Notifications for mentions // Desktop Notifications for mentions
if (data.content.includes(`@${window.currentUsername}`) && data.user_id != window.currentUserId) { if (data.content.includes(`@${window.currentUsername}`) && data.user_id != window.currentUserId) {
@ -174,7 +195,6 @@ document.addEventListener('DOMContentLoaded', () => {
const result = JSON.parse(xhr.responseText); const result = JSON.parse(xhr.responseText);
if (result.success) { if (result.success) {
appendMessage(result.message); appendMessage(result.message);
messagesList.scrollTop = messagesList.scrollHeight;
if (ws && ws.readyState === WebSocket.OPEN) { if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ ws.send(JSON.stringify({
type: 'message', type: 'message',
@ -1417,6 +1437,18 @@ document.addEventListener('DOMContentLoaded', () => {
if (!msg || !msg.id) return; if (!msg || !msg.id) return;
if (document.querySelector(`.message-item[data-id="${msg.id}"]`)) return; if (document.querySelector(`.message-item[data-id="${msg.id}"]`)) return;
// Auto-populate metadata for video platforms if missing
const dmRegexForMeta = /(?:https?:\/\/)?(?:www\.)?(?:dailymotion\.com\/video\/|dai\.ly\/)([a-zA-Z0-9]+)/;
const dmMatchForMeta = msg.content.match(dmRegexForMeta);
if (dmMatchForMeta && !msg.metadata) {
msg.metadata = {
title: 'Dailymotion Video',
url: dmMatchForMeta[0],
image: `https://www.dailymotion.com/thumbnail/video/${dmMatchForMeta[1]}`,
site_name: 'Dailymotion'
};
}
const messagesList = document.getElementById('messages-list'); const messagesList = document.getElementById('messages-list');
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'message-item'; div.className = 'message-item';
@ -1496,11 +1528,11 @@ document.addEventListener('DOMContentLoaded', () => {
let videoHtml = ''; let videoHtml = '';
if (ytMatch && ytMatch[1]) { if (ytMatch && ytMatch[1]) {
videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://www.youtube.com/embed/${ytMatch[1]}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`; videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://www.youtube.com/embed/${ytMatch[1]}?autoplay=0" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`;
} else if (dmMatch && dmMatch[1]) { } else if (dmMatch && dmMatch[1]) {
videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://www.dailymotion.com/embed/video/${dmMatch[1]}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`; videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://www.dailymotion.com/embed/video/${dmMatch[1]}?autoplay=0&queue-enable=0&mute=0" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`;
} else if (vimeoMatch && vimeoMatch[1]) { } else if (vimeoMatch && vimeoMatch[1]) {
videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://player.vimeo.com/video/${vimeoMatch[1]}" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`; videoHtml = `<div class="video-embed mt-2"><iframe width="100%" height="315" src="https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=0" frameborder="0" allow="fullscreen; picture-in-picture" allowfullscreen style="border-radius: 8px; max-width: 560px;"></iframe></div>`;
} }
const authorStyle = msg.role_color ? `color: ${msg.role_color};` : ''; const authorStyle = msg.role_color ? `color: ${msg.role_color};` : '';
@ -1517,7 +1549,7 @@ document.addEventListener('DOMContentLoaded', () => {
${escapeHTML(msg.content).replace(/\n/g, '<br>').replace(mentionRegex, `<span class="mention">@${window.currentUsername}</span>`)} ${escapeHTML(msg.content).replace(/\n/g, '<br>').replace(mentionRegex, `<span class="mention">@${window.currentUsername}</span>`)}
${attachmentHtml} ${attachmentHtml}
${videoHtml} ${videoHtml}
${videoHtml ? '' : embedHtml} ${embedHtml}
</div> </div>
<div class="message-reactions mt-1" data-message-id="${msg.id}"> <div class="message-reactions mt-1" data-message-id="${msg.id}">
<span class="add-reaction-btn" title="Add Reaction">+</span> <span class="add-reaction-btn" title="Add Reaction">+</span>
@ -1526,6 +1558,11 @@ document.addEventListener('DOMContentLoaded', () => {
${actionsHtml} ${actionsHtml}
`; `;
messagesList.appendChild(div); messagesList.appendChild(div);
messagesList.scrollTop = messagesList.scrollHeight; scrollToBottom(isMe);
// Ensure we scroll again when images/videos load
div.querySelectorAll('img, iframe').forEach(el => {
el.addEventListener('load', () => scrollToBottom(isMe));
});
} }
}); });

View File

@ -4,8 +4,15 @@ function fetchOpenGraphData($url) {
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; Bot/1.0)'); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.9',
'Cache-Control: no-cache',
'Pragma: no-cache',
'Upgrade-Insecure-Requests: 1'
]);
$html = curl_exec($ch); $html = curl_exec($ch);
$info = curl_getinfo($ch); $info = curl_getinfo($ch);
curl_close($ch); curl_close($ch);