From 60b254216d03774902fbb6d7e3c670f7e3e408a3 Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Tue, 26 Aug 2025 18:13:38 +0200 Subject: [PATCH] some config --- .gitea/compile.yml | 66 +++++++ src-tauri/tauri.conf.json | 1 + src/assets/favicon.ico | Bin 0 -> 38078 bytes src/assets/tauri.svg | 6 - src/frontend.js | 381 ++++++++++++++++++++++++++++++++++++ src/index.html | 392 +------------------------------------- 6 files changed, 451 insertions(+), 395 deletions(-) create mode 100644 .gitea/compile.yml create mode 100644 src/assets/favicon.ico delete mode 100644 src/assets/tauri.svg create mode 100644 src/frontend.js diff --git a/.gitea/compile.yml b/.gitea/compile.yml new file mode 100644 index 0000000..5942a4a --- /dev/null +++ b/.gitea/compile.yml @@ -0,0 +1,66 @@ +name: Build Tauri App + +on: + push: + branches: [ main ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. Checkout repo + - name: Checkout repository + uses: actions/checkout@v3 + + # 2. Install Rust + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + + # 3. Install Linux dependencies + - name: Install Linux dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + libssl-dev \ + libwebkit2gtk-4.1-dev \ + build-essential \ + curl \ + pkg-config \ + squashfs-tools \ + patchelf + + # 4. Build Tauri release bundle (AppImage/DEB/RPM) + - name: Build Tauri release + run: | + cd src-tauri + cargo tauri build --release + + # 5. Upload AppImage as artifact + - name: Upload AppImage + uses: actions/upload-artifact@v3 + with: + name: bytechat-appimage + path: src-tauri/target/release/bundle/appimage/*.AppImage + + # 6. Optionally upload DEB and RPM + - name: Upload DEB + uses: actions/upload-artifact@v3 + with: + name: bytechat-deb + path: src-tauri/target/release/bundle/deb/*.deb + + - name: Upload RPM + uses: actions/upload-artifact@v3 + with: + name: bytechat-rpm + path: src-tauri/target/release/bundle/rpm/*.rpm + diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 9211150..38a42d0 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -24,6 +24,7 @@ "active": true, "targets": "all", "icon": [ + "icons/icon.ico", "icons/icon.png" ] } diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e98367b78d0415a8bc7fea1140b23a56591c9ae5 GIT binary patch literal 38078 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 Z6jZMH8^NX5S~_rEtk - - - - - diff --git a/src/frontend.js b/src/frontend.js new file mode 100644 index 0000000..cf08d47 --- /dev/null +++ b/src/frontend.js @@ -0,0 +1,381 @@ +// 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; \ No newline at end of file diff --git a/src/index.html b/src/index.html index de8678c..e9035b0 100644 --- a/src/index.html +++ b/src/index.html @@ -7,8 +7,10 @@ + +
@@ -84,7 +86,7 @@