Add sliding and sliding animations on mobile.
This commit is contained in:
parent
00d22f7769
commit
aa42223fc9
4 changed files with 153 additions and 23 deletions
|
|
@ -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
102
js/app.js
|
|
@ -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;
|
||||
|
|
|
|||
1
js/ui.js
1
js/ui.js
|
|
@ -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'),
|
||||
|
|
|
|||
69
style.css
69
style.css
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue