some config
This commit is contained in:
392
src/index.html
392
src/index.html
@@ -7,8 +7,10 @@
|
||||
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
||||
<meta http-equiv="X-Frame-Options" content="DENY">
|
||||
<link rel="stylesheet" href="./stylesheet.css">
|
||||
<link rel="icon" type="image/png" href="./assets/favicon.ico">
|
||||
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@4.7.2/dist/socket.io.min.js"></script>
|
||||
<script src="./main.js"></script>
|
||||
<script src="./frontend.js"></script>
|
||||
</head>
|
||||
<body style="overflow-y: auto;">
|
||||
<div class="chat-container">
|
||||
@@ -84,7 +86,7 @@
|
||||
<textarea
|
||||
class="message-input"
|
||||
id="messageInput"
|
||||
placeholder="Message ByteChat…"
|
||||
placeholder="Send a Message…"
|
||||
rows="1"
|
||||
disabled
|
||||
maxlength="4000"
|
||||
@@ -102,393 +104,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="security-indicator" id="securityIndicator" style="display: none; bottom: calc(0.75rem + 80px); right: 15px; padding: 0.4rem 0.8rem; font-size: clamp(0.7rem, 3vw, 0.8rem); z-index: 999;">
|
||||
🔒 End-to-End Encrypted
|
||||
</div>-->
|
||||
|
||||
<script>
|
||||
// Mobile keyboard handling
|
||||
let initialViewportHeight = window.innerHeight;
|
||||
|
||||
function adjustForKeyboard() {
|
||||
const currentHeight = window.visualViewport ? window.visualViewport.height : window.innerHeight;
|
||||
const diff = initialViewportHeight - currentHeight;
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
|
||||
if (diff > 150) { // Keyboard is likely open
|
||||
keyboardOpen = true;
|
||||
if (messagesWrapper) {
|
||||
messagesWrapper.style.paddingBottom = '140px'; // Even more padding for mobile keyboard
|
||||
// Reset scroll state and aggressively scroll to bottom
|
||||
userScrolled = false;
|
||||
|
||||
// Multiple scroll attempts with increasing delays
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 250);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 400);
|
||||
|
||||
// iOS often needs even more attempts
|
||||
if (isIOS) {
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 200;
|
||||
}, 600);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 200;
|
||||
}, 800);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 200;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keyboardOpen = false;
|
||||
if (messagesWrapper) {
|
||||
messagesWrapper.style.paddingBottom = '80px';
|
||||
// Scroll to bottom when keyboard closes too
|
||||
setTimeout(() => {
|
||||
if (!userScrolled) {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 100;
|
||||
}
|
||||
}, 200);
|
||||
setTimeout(() => {
|
||||
if (!userScrolled) {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 100;
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for viewport changes (keyboard open/close)
|
||||
if (window.visualViewport) {
|
||||
window.visualViewport.addEventListener('resize', adjustForKeyboard);
|
||||
} else {
|
||||
window.addEventListener('resize', adjustForKeyboard);
|
||||
}
|
||||
|
||||
// Mobile-optimized focus handling for textarea
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
if (messageInput) {
|
||||
messageInput.addEventListener('focus', function() {
|
||||
// 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 + 200;
|
||||
}
|
||||
}, 800);
|
||||
// Additional scroll attempt for stubborn mobile browsers
|
||||
setTimeout(() => {
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (messagesWrapper) {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 250;
|
||||
}
|
||||
}, 1200);
|
||||
}
|
||||
});
|
||||
|
||||
messageInput.addEventListener('blur', function() {
|
||||
setTimeout(adjustForKeyboard, 200);
|
||||
setTimeout(adjustForKeyboard, 400);
|
||||
});
|
||||
|
||||
// Handle input events for better mobile experience
|
||||
messageInput.addEventListener('input', function() {
|
||||
if (isMobile && !userScrolled) {
|
||||
setTimeout(() => {
|
||||
scrollToBottom(false);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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);
|
||||
let keyboardOpen = false;
|
||||
|
||||
function scrollToBottom(smooth = true) {
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (messagesWrapper) {
|
||||
// On mobile, especially iOS, direct scrollTop assignment is more reliable
|
||||
if (isMobile) {
|
||||
// Different scroll behavior based on keyboard state
|
||||
const extraPadding = keyboardOpen ? 200 : 100;
|
||||
const targetScroll = messagesWrapper.scrollHeight + extraPadding;
|
||||
messagesWrapper.scrollTop = targetScroll;
|
||||
|
||||
// Multiple aggressive scroll attempts for mobile reliability
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + extraPadding;
|
||||
}, 50);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + extraPadding;
|
||||
}, 150);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + extraPadding;
|
||||
}, 300);
|
||||
|
||||
// Extra attempts when just viewing (no keyboard)
|
||||
if (!keyboardOpen) {
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 750);
|
||||
}
|
||||
|
||||
// Final iOS-specific attempt
|
||||
if (isIOS) {
|
||||
const finalPadding = keyboardOpen ? 250 : 200;
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + finalPadding;
|
||||
}, keyboardOpen ? 500 : 1000);
|
||||
}
|
||||
} else {
|
||||
if (smooth) {
|
||||
messagesWrapper.scrollTo({
|
||||
top: messagesWrapper.scrollHeight,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if user is near bottom of messages
|
||||
function isNearBottom() {
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (!messagesWrapper) return true;
|
||||
|
||||
// Larger threshold for mobile to account for touch scrolling momentum
|
||||
const threshold = isMobile ? 150 : 100;
|
||||
return messagesWrapper.scrollHeight - messagesWrapper.scrollTop - messagesWrapper.clientHeight < threshold;
|
||||
}
|
||||
|
||||
// Mobile scroll state management
|
||||
let lastScrollTop = 0;
|
||||
let userScrolled = false;
|
||||
let scrollTimeout;
|
||||
let touchStartY = 0;
|
||||
let isScrolling = false;
|
||||
|
||||
// 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() {
|
||||
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 });
|
||||
}
|
||||
});
|
||||
|
||||
// Mobile-optimized message adding function
|
||||
function addMessageWithAutoScroll(messageElement) {
|
||||
const messagesContainer = document.getElementById('messagesContainer');
|
||||
if (messagesContainer && messageElement) {
|
||||
messagesContainer.appendChild(messageElement);
|
||||
|
||||
// Only auto-scroll if user hasn't manually scrolled up
|
||||
if (!userScrolled) {
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (messagesWrapper) {
|
||||
// Immediate first scroll
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 100;
|
||||
|
||||
// Progressive scroll attempts with different padding
|
||||
setTimeout(() => {
|
||||
const padding = keyboardOpen ? 200 : 150;
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + padding;
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
const padding = keyboardOpen ? 200 : 150;
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + padding;
|
||||
}, 250);
|
||||
|
||||
// Extra attempts when just viewing (no keyboard)
|
||||
if (!keyboardOpen && isMobile) {
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 200;
|
||||
}, 400);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 250;
|
||||
}, 600);
|
||||
// Final attempt for stubborn browsers
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 300;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// iOS-specific final attempt
|
||||
if (isIOS) {
|
||||
const finalPadding = keyboardOpen ? 250 : 300;
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + finalPadding;
|
||||
}, keyboardOpen ? 600 : 1200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force scroll with mobile optimizations
|
||||
function forceScrollToBottom() {
|
||||
userScrolled = false;
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (messagesWrapper) {
|
||||
// Immediate aggressive scroll
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 150;
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 200;
|
||||
}, 300);
|
||||
|
||||
// Extra attempts for iOS
|
||||
if (isIOS) {
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 250;
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 250;
|
||||
}, 700);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile-optimized mutation observer
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const messagesContainer = document.getElementById('messagesContainer');
|
||||
if (messagesContainer) {
|
||||
// Create a MutationObserver to watch for new messages
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
||||
// Check if any added nodes are message elements
|
||||
const hasNewMessage = Array.from(mutation.addedNodes).some(node =>
|
||||
node.nodeType === Node.ELEMENT_NODE &&
|
||||
(node.classList.contains('message-group') || node.querySelector('.message-group'))
|
||||
);
|
||||
|
||||
if (hasNewMessage && !userScrolled) {
|
||||
const messagesWrapper = document.getElementById('messagesWrapper');
|
||||
if (messagesWrapper) {
|
||||
// Immediate scroll
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 100;
|
||||
|
||||
setTimeout(() => {
|
||||
const padding = keyboardOpen ? 200 : 150;
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + padding;
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
const padding = keyboardOpen ? 250 : 200;
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + padding;
|
||||
}, 300);
|
||||
|
||||
// Extra scrolling when just viewing (no keyboard)
|
||||
if (!keyboardOpen && isMobile) {
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 250;
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 300;
|
||||
}, 800);
|
||||
// Final aggressive attempt for viewing mode
|
||||
setTimeout(() => {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight + 350;
|
||||
}, 1200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing
|
||||
observer.observe(messagesContainer, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Expose functions globally so your existing JavaScript can use them
|
||||
window.ByteChat = window.ByteChat || {};
|
||||
window.ByteChat.scrollToBottom = scrollToBottom;
|
||||
window.ByteChat.forceScrollToBottom = forceScrollToBottom;
|
||||
window.ByteChat.addMessageWithAutoScroll = addMessageWithAutoScroll;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user