Update src/templates/chat.html
This commit is contained in:
@@ -148,15 +148,31 @@
|
|||||||
|
|
||||||
if (diff > 150) { // Keyboard is likely open
|
if (diff > 150) { // Keyboard is likely open
|
||||||
if (messagesWrapper) {
|
if (messagesWrapper) {
|
||||||
messagesWrapper.style.paddingBottom = '100px';
|
messagesWrapper.style.paddingBottom = '120px'; // More padding for mobile keyboard
|
||||||
// Scroll to bottom when keyboard opens
|
// Auto-scroll when keyboard opens, with multiple attempts for reliability
|
||||||
|
userScrolled = false; // Reset scroll state when keyboard opens
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
scrollToBottom(false);
|
||||||
}, 100);
|
}, 200);
|
||||||
|
// iOS often needs multiple scroll attempts
|
||||||
|
if (isIOS) {
|
||||||
|
setTimeout(() => {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}, 400);
|
||||||
|
setTimeout(() => {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (messagesWrapper) {
|
if (messagesWrapper) {
|
||||||
messagesWrapper.style.paddingBottom = '80px';
|
messagesWrapper.style.paddingBottom = '80px';
|
||||||
|
// Scroll to bottom when keyboard closes too
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!userScrolled) {
|
||||||
|
scrollToBottom(false);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,31 +184,66 @@
|
|||||||
window.addEventListener('resize', adjustForKeyboard);
|
window.addEventListener('resize', adjustForKeyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus handling for textarea
|
// Mobile-optimized focus handling for textarea
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const messageInput = document.getElementById('messageInput');
|
const messageInput = document.getElementById('messageInput');
|
||||||
if (messageInput) {
|
if (messageInput) {
|
||||||
messageInput.addEventListener('focus', function() {
|
messageInput.addEventListener('focus', function() {
|
||||||
setTimeout(adjustForKeyboard, 300);
|
// Reset scroll state when focusing input
|
||||||
|
userScrolled = false;
|
||||||
|
// Multiple timeout attempts for different mobile browsers
|
||||||
|
setTimeout(adjustForKeyboard, 200);
|
||||||
|
setTimeout(adjustForKeyboard, 400);
|
||||||
|
if (isMobile) {
|
||||||
|
setTimeout(adjustForKeyboard, 600);
|
||||||
|
setTimeout(() => {
|
||||||
|
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||||
|
if (messagesWrapper) {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
messageInput.addEventListener('blur', function() {
|
messageInput.addEventListener('blur', function() {
|
||||||
setTimeout(adjustForKeyboard, 300);
|
setTimeout(adjustForKeyboard, 200);
|
||||||
|
setTimeout(adjustForKeyboard, 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle input events for better mobile experience
|
||||||
|
messageInput.addEventListener('input', function() {
|
||||||
|
if (isMobile && !userScrolled) {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToBottom(false);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-scroll functionality for new messages
|
// Mobile-optimized auto-scroll functionality
|
||||||
|
let isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768;
|
||||||
|
let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||||
|
|
||||||
function scrollToBottom(smooth = true) {
|
function scrollToBottom(smooth = true) {
|
||||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||||
if (messagesWrapper) {
|
if (messagesWrapper) {
|
||||||
if (smooth) {
|
// On mobile, especially iOS, direct scrollTop assignment is more reliable
|
||||||
messagesWrapper.scrollTo({
|
if (isMobile) {
|
||||||
top: messagesWrapper.scrollHeight,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
// Double-check scroll position after a short delay for mobile
|
||||||
|
setTimeout(() => {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
if (smooth) {
|
||||||
|
messagesWrapper.scrollTo({
|
||||||
|
top: messagesWrapper.scrollHeight,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,39 +253,80 @@
|
|||||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||||
if (!messagesWrapper) return true;
|
if (!messagesWrapper) return true;
|
||||||
|
|
||||||
const threshold = 100; // pixels from bottom
|
// Larger threshold for mobile to account for touch scrolling momentum
|
||||||
|
const threshold = isMobile ? 150 : 100;
|
||||||
return messagesWrapper.scrollHeight - messagesWrapper.scrollTop - messagesWrapper.clientHeight < threshold;
|
return messagesWrapper.scrollHeight - messagesWrapper.scrollTop - messagesWrapper.clientHeight < threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the last scroll position to detect if user manually scrolled
|
// Mobile scroll state management
|
||||||
let lastScrollTop = 0;
|
let lastScrollTop = 0;
|
||||||
let userScrolled = false;
|
let userScrolled = false;
|
||||||
|
let scrollTimeout;
|
||||||
|
let touchStartY = 0;
|
||||||
|
let isScrolling = false;
|
||||||
|
|
||||||
// Detect user scroll
|
// Detect user scroll with mobile optimizations
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||||
if (messagesWrapper) {
|
if (messagesWrapper) {
|
||||||
|
|
||||||
|
// Handle touch events for better mobile detection
|
||||||
|
messagesWrapper.addEventListener('touchstart', function(e) {
|
||||||
|
touchStartY = e.touches[0].clientY;
|
||||||
|
isScrolling = false;
|
||||||
|
clearTimeout(scrollTimeout);
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
messagesWrapper.addEventListener('touchmove', function(e) {
|
||||||
|
if (!isScrolling) {
|
||||||
|
isScrolling = true;
|
||||||
|
const touchY = e.touches[0].clientY;
|
||||||
|
const deltaY = touchStartY - touchY;
|
||||||
|
|
||||||
|
// If user is swiping up (scrolling up), mark as user scrolled
|
||||||
|
if (deltaY < -10 && !isNearBottom()) {
|
||||||
|
userScrolled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
messagesWrapper.addEventListener('touchend', function() {
|
||||||
|
// Check scroll position after touch ends with delay for momentum scrolling
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isNearBottom()) {
|
||||||
|
userScrolled = false;
|
||||||
|
}
|
||||||
|
isScrolling = false;
|
||||||
|
}, 300);
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Regular scroll event with debouncing for mobile
|
||||||
messagesWrapper.addEventListener('scroll', function() {
|
messagesWrapper.addEventListener('scroll', function() {
|
||||||
const currentScrollTop = messagesWrapper.scrollTop;
|
clearTimeout(scrollTimeout);
|
||||||
const maxScroll = messagesWrapper.scrollHeight - messagesWrapper.clientHeight;
|
scrollTimeout = setTimeout(() => {
|
||||||
|
const currentScrollTop = messagesWrapper.scrollTop;
|
||||||
// If user scrolled up manually, don't auto-scroll for new messages
|
const maxScroll = messagesWrapper.scrollHeight - messagesWrapper.clientHeight;
|
||||||
if (currentScrollTop < lastScrollTop && currentScrollTop < maxScroll - 50) {
|
|
||||||
userScrolled = true;
|
// More lenient detection for mobile
|
||||||
}
|
const upScrollThreshold = isMobile ? 30 : 50;
|
||||||
|
|
||||||
// If user scrolled to near bottom, resume auto-scrolling
|
// If user scrolled up manually, don't auto-scroll for new messages
|
||||||
if (isNearBottom()) {
|
if (currentScrollTop < lastScrollTop && currentScrollTop < maxScroll - upScrollThreshold) {
|
||||||
userScrolled = false;
|
userScrolled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastScrollTop = currentScrollTop;
|
// If user scrolled to near bottom, resume auto-scrolling
|
||||||
});
|
if (isNearBottom()) {
|
||||||
|
userScrolled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollTop = currentScrollTop;
|
||||||
|
}, isMobile ? 150 : 50); // Longer debounce for mobile
|
||||||
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Override the message adding function to include auto-scroll
|
// Mobile-optimized message adding function
|
||||||
// You'll need to call this function whenever a new message is added
|
|
||||||
function addMessageWithAutoScroll(messageElement) {
|
function addMessageWithAutoScroll(messageElement) {
|
||||||
const messagesContainer = document.getElementById('messagesContainer');
|
const messagesContainer = document.getElementById('messagesContainer');
|
||||||
if (messagesContainer && messageElement) {
|
if (messagesContainer && messageElement) {
|
||||||
@@ -242,23 +334,34 @@
|
|||||||
|
|
||||||
// Only auto-scroll if user hasn't manually scrolled up
|
// Only auto-scroll if user hasn't manually scrolled up
|
||||||
if (!userScrolled) {
|
if (!userScrolled) {
|
||||||
// Small delay to ensure DOM is updated
|
// Longer delay for mobile to ensure proper rendering
|
||||||
|
const delay = isMobile ? 200 : 50;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToBottom(true);
|
scrollToBottom(false); // Always instant scroll on mobile
|
||||||
}, 50);
|
}, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to force scroll to bottom (useful for when joining a room)
|
// Force scroll with mobile optimizations
|
||||||
function forceScrollToBottom() {
|
function forceScrollToBottom() {
|
||||||
userScrolled = false;
|
userScrolled = false;
|
||||||
|
const delay = isMobile ? 300 : 100;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToBottom(false);
|
scrollToBottom(false);
|
||||||
}, 100);
|
// iOS double-check
|
||||||
|
if (isIOS) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||||
|
if (messagesWrapper) {
|
||||||
|
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observer to watch for new messages being added to the DOM
|
// Mobile-optimized mutation observer
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const messagesContainer = document.getElementById('messagesContainer');
|
const messagesContainer = document.getElementById('messagesContainer');
|
||||||
if (messagesContainer) {
|
if (messagesContainer) {
|
||||||
@@ -273,9 +376,10 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (hasNewMessage && !userScrolled) {
|
if (hasNewMessage && !userScrolled) {
|
||||||
|
const delay = isMobile ? 250 : 50;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToBottom(true);
|
scrollToBottom(false); // Instant scroll for reliability
|
||||||
}, 50);
|
}, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user