Add sliding and sliding animations on mobile.

This commit is contained in:
天クマ 2026-07-17 11:08:49 -03:00
commit aa42223fc9
4 changed files with 153 additions and 23 deletions

View file

@ -21,7 +21,7 @@
}, 100);
});
</script>
<div class="app-container">
<div class="app-container no-active-chat">
<!-- Sidebar -->
<aside class="sidebar">
<header class="sidebar-header">
@ -83,7 +83,7 @@
<div class="active-chat-container hidden" id="active-chat-container">
<header class="chat-header">
<div class="active-contact-info">
<!-- <button class="icon-btn back-btn" id="back-to-sidebar"><i data-lucide="arrow-left"></i></button> -->
<!-- <button class="icon-btn back-btn mif-arrow-left mif-2x" id="back-to-sidebar" title="Back to chats"></button> -->
<div class="avatar active-avatar" id="active-chat-avatar">C</div>
<div>
<h3 id="active-chat-name">Contact Name</h3>

102
js/app.js
View file

@ -27,6 +27,15 @@ function loadChats() {
elements.chatsLoader.classList.remove('hidden');
fetchChats().then(() => {
ui.renderChatList(getChats(), activeChatState, selectChat);
const hash = window.location.hash;
if (hash && hash.startsWith('#chat-')) {
const chatId = hash.replace('#chat-', '');
const chat = getChats().find(c => c.id === chatId);
if (chat) {
selectChat(chat, true, false);
}
}
});
} catch (error) {
console.error('Failed to load chats:', error);
@ -44,11 +53,67 @@ function loadChats() {
}
}
let isScrollingProgrammatically = false;
let scrollTimeout = null;
function scrollToChat(smooth = true) {
isScrollingProgrammatically = true;
elements.appContainer.scrollTo({
left: elements.appContainer.clientWidth,
behavior: smooth ? 'smooth' : 'auto'
});
setTimeout(() => { isScrollingProgrammatically = false; }, smooth ? 400 : 50);
}
function scrollToList(smooth = true) {
isScrollingProgrammatically = true;
elements.appContainer.scrollTo({
left: 0,
behavior: smooth ? 'smooth' : 'auto'
});
setTimeout(() => { isScrollingProgrammatically = false; }, smooth ? 400 : 50);
}
function setupEventListeners() {
if (!window.location.hash) {
window.location.hash = '';
}
window.addEventListener('hashchange', () => {
const hash = window.location.hash;
if (hash && hash.startsWith('#chat-')) {
const chatId = hash.replace('#chat-', '');
const chat = getChats().find(c => c.id === chatId);
if (chat) {
selectChat(chat, true);
}
} else {
closeActiveChat(true);
}
});
elements.appContainer.addEventListener('scroll', () => {
if (window.innerWidth > 768) return;
if (isScrollingProgrammatically) return;
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
const scrollLeft = elements.appContainer.scrollLeft;
const width = elements.appContainer.clientWidth;
if (scrollLeft < width * 0.2) {
// User swiped back to the list view
if (activeChatState) {
closeActiveChat(false);
}
}
}, 100);
});
document.addEventListener('keydown', (e) => {
if (e.code == "Escape") {
e.preventDefault();
ui.toggleChatState();
closeActiveChat(false);
}
});
@ -94,8 +159,7 @@ function setupEventListeners() {
})
elements.backToSidebarBtn.addEventListener('click', () => {
elements.sidebar.classList.remove('hidden');
elements.activeChatContainer.classList.add('hidden');
closeActiveChat(false);
});
elements.settingsIconBtn.addEventListener('click', openSettings);
@ -155,7 +219,7 @@ async function handleIncomingMessage(msg) {
}
}
async function selectChat(chat) {
async function selectChat(chat, isPopState = false, smoothScroll = true) {
activeChatState = chat;
chat.unreadCount = 0;
@ -179,8 +243,14 @@ async function selectChat(chat) {
</span>
</div>`;
elements.appContainer.classList.remove('no-active-chat');
if (window.innerWidth <= 768) {
elements.sidebar.classList.add('hidden');
scrollToChat(smoothScroll);
}
if (!isPopState && window.location.hash !== `#chat-${chat.id}`) {
window.location.hash = `chat-${chat.id}`;
}
try {
@ -193,6 +263,28 @@ async function selectChat(chat) {
}
}
async function closeActiveChat(isPopState = false) {
activeChatState = null;
if (window.innerWidth <= 768) {
scrollToList();
setTimeout(() => {
if (!activeChatState) {
elements.appContainer.classList.add('no-active-chat');
ui.toggleChatState(false);
}
}, 350);
} else {
ui.toggleChatState(false);
}
if (!isPopState) {
if (window.location.hash.startsWith('#chat-')) {
history.back();
}
}
}
async function sendMessage() {
const text = elements.messageInput.value.trim();
if (!text || !activeChatState) return;

View file

@ -19,6 +19,7 @@ export const elements = {
messageInput: document.getElementById('message-input'),
backToSidebarBtn: document.querySelector('.chat-header'),
sidebar: document.querySelector('.sidebar'),
appContainer: document.querySelector('.app-container'),
settingsModal: document.getElementById('settings-modal'),
settingsIconBtn: document.querySelector('.header-actions button[title="Settings"]'),
cancelSettingsBtn: document.getElementById('cancel-settings'),

View file

@ -29,11 +29,21 @@
user-select: none;
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
height: 100dvh;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
}
body {
background-color: var(--bg-main);
color: var(--text-primary);
height: 100vh;
overflow: hidden;
background-image:
radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.12) 0px, transparent 50%),
radial-gradient(at 100% 100%, rgba(168, 85, 247, 0.12) 0px, transparent 50%);
@ -70,10 +80,11 @@ input:focus {
.app-container {
display: flex;
width: 100vw;
height: 100vh;
width: 100%;
height: 100%;
overflow: hidden;
backdrop-filter: var(--glass-blur);
position: relative;
}
.app-name {
@ -727,33 +738,59 @@ input:focus {
display: none !important;
}
/* Responsive design */
@media (max-width: 768px) {
.app-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
/* Hide scrollbars */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.app-container::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.sidebar {
width: 100%;
position: absolute;
left: 0;
top: 0;
flex: 0 0 100%;
position: relative;
left: auto;
top: auto;
height: 100%;
z-index: 10;
}
.sidebar.hidden {
transform: translateX(-100%);
display: flex !important;
z-index: 1;
scroll-snap-align: start;
scroll-snap-stop: always;
}
.chat-area {
width: 100%;
flex: 0 0 100%;
position: relative;
left: auto;
top: auto;
height: 100%;
z-index: 1;
scroll-snap-align: start;
scroll-snap-stop: always;
}
/* Disable scrolling to active chat if none is selected */
/* .app-container.no-active-chat .chat-area {
display: none !important;
} */
.back-btn {
display: flex;
margin-right: 8px;
margin-right: 12px;
}
.message-group {