diff --git a/src/templates/chat.html b/src/templates/chat.html
index 556470f..f323017 100644
--- a/src/templates/chat.html
+++ b/src/templates/chat.html
@@ -148,15 +148,31 @@
if (diff > 150) { // Keyboard is likely open
if (messagesWrapper) {
- messagesWrapper.style.paddingBottom = '100px';
- // Scroll to bottom when keyboard opens
+ messagesWrapper.style.paddingBottom = '120px'; // More padding for mobile keyboard
+ // Auto-scroll when keyboard opens, with multiple attempts for reliability
+ userScrolled = false; // Reset scroll state when keyboard opens
setTimeout(() => {
- messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
- }, 100);
+ scrollToBottom(false);
+ }, 200);
+ // iOS often needs multiple scroll attempts
+ if (isIOS) {
+ setTimeout(() => {
+ messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
+ }, 400);
+ setTimeout(() => {
+ messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
+ }, 600);
+ }
}
} else {
if (messagesWrapper) {
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);
}
- // Focus handling for textarea
+ // Mobile-optimized focus handling for textarea
document.addEventListener('DOMContentLoaded', function() {
const messageInput = document.getElementById('messageInput');
if (messageInput) {
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() {
- 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) {
const messagesWrapper = document.getElementById('messagesWrapper');
if (messagesWrapper) {
- if (smooth) {
- messagesWrapper.scrollTo({
- top: messagesWrapper.scrollHeight,
- behavior: 'smooth'
- });
- } else {
+ // On mobile, especially iOS, direct scrollTop assignment is more reliable
+ if (isMobile) {
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');
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;
}
- // Store the last scroll position to detect if user manually scrolled
+ // Mobile scroll state management
let lastScrollTop = 0;
let userScrolled = false;
+ let scrollTimeout;
+ let touchStartY = 0;
+ let isScrolling = false;
- // Detect user scroll
+ // Detect user scroll with mobile optimizations
document.addEventListener('DOMContentLoaded', function() {
const messagesWrapper = document.getElementById('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() {
- const currentScrollTop = messagesWrapper.scrollTop;
- const maxScroll = messagesWrapper.scrollHeight - messagesWrapper.clientHeight;
-
- // If user scrolled up manually, don't auto-scroll for new messages
- if (currentScrollTop < lastScrollTop && currentScrollTop < maxScroll - 50) {
- userScrolled = true;
- }
-
- // If user scrolled to near bottom, resume auto-scrolling
- if (isNearBottom()) {
- userScrolled = false;
- }
-
- lastScrollTop = currentScrollTop;
- });
+ clearTimeout(scrollTimeout);
+ scrollTimeout = setTimeout(() => {
+ const currentScrollTop = messagesWrapper.scrollTop;
+ const maxScroll = messagesWrapper.scrollHeight - messagesWrapper.clientHeight;
+
+ // More lenient detection for mobile
+ const upScrollThreshold = isMobile ? 30 : 50;
+
+ // If user scrolled up manually, don't auto-scroll for new messages
+ if (currentScrollTop < lastScrollTop && currentScrollTop < maxScroll - upScrollThreshold) {
+ userScrolled = true;
+ }
+
+ // 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
- // You'll need to call this function whenever a new message is added
+ // Mobile-optimized message adding function
function addMessageWithAutoScroll(messageElement) {
const messagesContainer = document.getElementById('messagesContainer');
if (messagesContainer && messageElement) {
@@ -242,23 +334,34 @@
// Only auto-scroll if user hasn't manually scrolled up
if (!userScrolled) {
- // Small delay to ensure DOM is updated
+ // Longer delay for mobile to ensure proper rendering
+ const delay = isMobile ? 200 : 50;
setTimeout(() => {
- scrollToBottom(true);
- }, 50);
+ scrollToBottom(false); // Always instant scroll on mobile
+ }, delay);
}
}
}
- // Function to force scroll to bottom (useful for when joining a room)
+ // Force scroll with mobile optimizations
function forceScrollToBottom() {
userScrolled = false;
+ const delay = isMobile ? 300 : 100;
setTimeout(() => {
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() {
const messagesContainer = document.getElementById('messagesContainer');
if (messagesContainer) {
@@ -273,9 +376,10 @@
);
if (hasNewMessage && !userScrolled) {
+ const delay = isMobile ? 250 : 50;
setTimeout(() => {
- scrollToBottom(true);
- }, 50);
+ scrollToBottom(false); // Instant scroll for reliability
+ }, delay);
}
}
});