diff --git a/src/icons/chevron-double-right.svg b/src/icons/chevron-double-right.svg new file mode 100644 index 0000000..ce46458 --- /dev/null +++ b/src/icons/chevron-double-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/icons/users.svg b/src/icons/users.svg new file mode 100644 index 0000000..aef2f55 --- /dev/null +++ b/src/icons/users.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/index.html b/src/index.html index 2ee56ba..4978617 100644 --- a/src/index.html +++ b/src/index.html @@ -41,6 +41,7 @@


+
- -
-
- - -
-
-
- - - -
-
-
-
- - Recently Used - -
-
-
-
-
- - Favorites - + +
+
+ + +
+
+
+ + + +
+
+
+
+ + Recently Used + +
+
+
+
+
+ + +
-
-
-
-
- - Emojis - +
+
+
+ + Recently Used + +
+
+
+
+
+ + Favorites + +
+
+
+
+
+ + Emojis + +
+
+
-
-
-
+ +
+ +
- + + + +
-
- -
- -
-
+ + -
-
-
- -

Privacy

- +
- +
- +
@@ -454,16 +507,16 @@

Updates

Current Version:

- + - + - + - + - +
@@ -546,18 +601,20 @@

Dangerzone

+ + + +
+ +
+

Hello!

+

Select a conversation or start a new chat to begin messaging.

+
+ + + + + + + + + + + +
+
+
0 members
+
+
+ +
+
+ + +
+
- + + + diff --git a/src/main.js b/src/main.js index 53576fc..e698eec 100644 --- a/src/main.js +++ b/src/main.js @@ -162,9 +162,15 @@ function openEmojiPanel(e) { // Load emoji sections with optimized rendering renderEmojiPanel(); - // Display the picker - use class instead of inline style + // Display the picker picker.classList.add('visible'); + if (isWidescreen) { + picker.classList.add('emoji-picker-widescreen'); + } else { + picker.classList.remove('emoji-picker-widescreen'); + } + // Always use the same fixed position (bottom-up) for both message input and reactions picker.classList.add('emoji-picker-message-type'); @@ -197,9 +203,10 @@ function openEmojiPanel(e) { emojiSearch.focus(); } } else { - // Hide and reset the UI - use class instead of inline style + // Hide and reset the UI emojiSearch.value = ''; picker.classList.remove('visible'); + picker.classList.remove('emoji-picker-widescreen'); picker.style.bottom = ''; // Reset to CSS default strCurrentReactionReference = ''; @@ -208,6 +215,17 @@ function openEmojiPanel(e) { } } +function updateEmojiPickerPosition() { + if (!isWidescreen || !picker.classList.contains('visible')) return; + + // Adjust position based on member panel state + if (memberPanelVisible) { + picker.style.right = '320px'; // 300px panel + 20px margin + } else { + picker.style.right = '20px'; + } +} + /** * Renders the Recently Used emojis immediately, then renders * the All Emojis grid after the last recent emoji image loads. @@ -1057,12 +1075,12 @@ function updateChatHeaderSubtext(chat) { newStatusText = 'Encrypted Notes to Self'; shouldAddGradient = false; } else if (isGroup) { - // Check for typing indicators in groups + // Group chat typing indicators and member count const activeTypers = chat.active_typers || []; const fIsTyping = activeTypers.length > 0; if (fIsTyping) { - // Display typing indicator with Discord-style multi-user support + // Display typing indicator if (activeTypers.length === 1) { const typer = getProfile(activeTypers[0]); const name = typer?.nickname || typer?.name || 'Someone'; @@ -1097,8 +1115,15 @@ function updateChatHeaderSubtext(chat) { } shouldAddGradient = false; } + + // Group name click opens group overview + domChatContact.onclick = () => { + closeChat(); + openGroupOverview(chat); + }; + domChatContact.classList.add('btn'); } else { - // Check for typing indicators in DMs (using chat.active_typers) + // DM chat - check for typing indicators const activeTypers = chat.active_typers || []; const fIsTyping = activeTypers.length > 0; @@ -1129,6 +1154,22 @@ function updateChatHeaderSubtext(chat) { } domChatContact.classList.remove('chat-contact'); domChatContact.classList.add('chat-contact-with-status'); + + const profile = getProfile(chat.id); + + // Proper click handler for profile in widescreen + domChatContact.onclick = () => { + if (isWidescreen && strOpenChat === chat.id) { + console.log('Opening profile in chat area for:', profile?.id); + // In widescreen, show profile in the chat area + showProfileInChatArea(profile); + } else { + // Mobile behavior - open profile tab + closeChat(); + openProfile(profile); + } + }; + domChatContact.classList.add('btn'); } else if (currentHasStatus) { // Hide status: add hidden class, wait for animation, then clear content domChatContactStatus.classList.add('status-hidden'); @@ -2523,6 +2564,7 @@ async function login() { domLogin.classList.remove('fadeout-anim'); domLoginInput.value = ""; domLogin.style.display = 'none'; + updateEncryptedNotesButton(); domLoginEncrypt.style.display = 'none'; // Fade-in the navbar @@ -2642,6 +2684,15 @@ function renderCurrentProfile(cProfile) { /* Start Chat Tab */ // Render our Share npub domShareNpub.textContent = strPubkey; + + // In widescreen, ensure the encrypted notes button is properly positioned + if (isWidescreen && domChatBookmarksBtn) { + domChatBookmarksBtn.style.position = 'absolute'; + domChatBookmarksBtn.style.top = '28px'; + domChatBookmarksBtn.style.right = '23px'; + domChatBookmarksBtn.style.bottom = 'auto'; + domChatBookmarksBtn.style.left = 'auto'; + } } /** @@ -4652,13 +4703,126 @@ async function openChat(contact) { * Open the dialog for starting a new chat */ function openNewChat() { - // Display the UI - domChatNew.style.display = ''; - domChats.style.display = 'none'; - domChat.style.display = 'none'; + if (isWidescreen) { + // Widescreen behavior - show in right panel + navbarSelect('chat-btn'); + + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // Hide hello screen and show new chat in right panel + const helloScreen = document.querySelector('.hello-screen'); + const chatNewElement = document.getElementById('chat-new'); + + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + + // Hide other chat views + document.querySelectorAll('#chat, #create-group').forEach(el => { + el.style.display = 'none'; + el.classList.remove('active'); + }); + + // Show new chat interface + chatNewElement.style.display = ''; + chatNewElement.classList.add('active'); + + // Hide member panel + hideMemberPanel(); + + // Update notes button visibility + updateEncryptedNotesButton(); + + // Focus the input field for better UX + setTimeout(() => { + const newChatInput = document.getElementById('chat-new-input'); + if (newChatInput) { + newChatInput.focus(); + } + }, 100); + + } else { + // Mobile behavior - original implementation + domChatNew.style.display = ''; + domChats.style.display = 'none'; + domChat.style.display = 'none'; + domNavbar.style.display = 'none'; + } +} - // Hide the Navbar - domNavbar.style.display = 'none'; +function closeNewChat() { + if (isWidescreen) { + const chatNewElement = document.getElementById('chat-new'); + chatNewElement.style.display = 'none'; + chatNewElement.classList.remove('active'); + + // Return to hello screen + showHelloScreen(); + } else { + // Mobile behavior + domChatNew.style.display = 'none'; + domNavbar.style.display = ''; + openChatlist(); + } +} + +function setupNewChatBackButton() { + const newChatBackBtn = document.getElementById('chat-new-back-text-btn'); + if (newChatBackBtn) { + // Remove any existing handlers to prevent duplicates + newChatBackBtn.onclick = null; + newChatBackBtn.addEventListener('click', () => { + if (isWidescreen) { + closeNewChat(); + } else { + // Mobile behavior + domChatNew.style.display = 'none'; + domNavbar.style.display = ''; + openChatlist(); + } + }); + } +} + +function setupNewChatInput() { + const newChatInput = document.getElementById('chat-new-input'); + const newChatStartBtn = document.getElementById('chat-new-btn'); + + if (newChatInput && newChatStartBtn) { + // Remove any existing handlers + newChatInput.onkeydown = null; + newChatStartBtn.onclick = null; + + // Unified handler for starting new chat + const startNewChat = () => { + const npub = newChatInput.value.trim(); + if (npub) { + // Clear input + newChatInput.value = ''; + + if (isWidescreen) { + // Close new chat interface and open the actual chat + closeNewChat(); + openChat(npub); + } else { + // Mobile behavior + openChat(npub); + domChatNew.style.display = 'none'; + } + } + }; + + // Set up enter key + newChatInput.onkeydown = async (evt) => { + if ((evt.code === 'Enter' || evt.code === 'NumpadEnter') && !evt.shiftKey) { + evt.preventDefault(); + startNewChat(); + } + }; + + // Set up button click + newChatStartBtn.onclick = startNewChat; + } } /** @@ -5850,11 +6014,21 @@ window.addEventListener("DOMContentLoaded", async () => { popupConfirm(e, '', true, '', 'vector_warning.svg'); } } - domChatBackBtn.onclick = closeChat; + domChatBackBtn.onclick = () => { + if (isWidescreen) { + // In widescreen, close chat and show hello screen + closeChat(); + } else { + // Mobile behavior + closeChat(); + } +}; domChatBookmarksBtn.onclick = () => { openChat(strPubkey); }; domChatNewBackBtn.onclick = closeChat; + // Set up new chat back button handler (widescreen-aware) + setupNewChatBackButton(); // Add scroll event listener for procedural message loading let scrollTimeout; @@ -5865,22 +6039,8 @@ window.addEventListener("DOMContentLoaded", async () => { handleProceduralScroll(); }, 100); }); - domChatNewStartBtn.onclick = () => { - let inputValue = domChatNewInput.value.trim(); - // Parse npub from vectorapp.io profile URL if pasted - const profileUrlMatch = inputValue.match(/https?:\/\/vectorapp\.io\/profile\/(npub1[a-z0-9]{58})/i); - if (profileUrlMatch) { - inputValue = profileUrlMatch[1]; - } - openChat(inputValue); - domChatNewInput.value = ``; - }; - domChatNewInput.onkeydown = async (evt) => { - if ((evt.code === 'Enter' || evt.code === 'NumpadEnter') && !evt.shiftKey) { - evt.preventDefault(); - domChatNewStartBtn.click(); - } - }; + // Set up the new chat input handlers (unified for widescreen/mobile) + setupNewChatInput(); domChatMessageInputCancel.onclick = cancelReply; // Hook up a scroll handler in the chat to display UI elements at certain scroll depths @@ -6218,6 +6378,14 @@ domChatMessageInput.oninput = async () => { }); } }); + + setupEncryptedNotesButton(); + setupProfileBackButton(); + // Setup chat back button to handle encrypted notes return navigation + if (typeof setupChatBackButton === 'function') setupChatBackButton(); + + // Initialize widescreen layout after DOM is loaded + initWidescreenLayout(); }); // Listen for app-wide click interations @@ -6341,6 +6509,16 @@ function adjustSize() { // Re-calculate chat input size on window resize (text may reflow) autoResizeChatInput(); + // New Chat interface in widescreen - ensure proper sizing + if (isWidescreen) { + const chatNewElement = document.getElementById('chat-new'); + if (chatNewElement && chatNewElement.style.display !== 'none') { + chatNewElement.style.height = '100%'; + chatNewElement.style.display = 'flex'; + chatNewElement.style.flexDirection = 'column'; + } + } + // If the chat is open, and they've not significantly scrolled up: auto-scroll down to correct against container resizes softChatScroll(); } @@ -6590,43 +6768,96 @@ function updateCreateGroupValidation(showInline = false) { * Open Create Group tab */ function openCreateGroup() { - // Show panel - domCreateGroup.style.display = ''; - // Hide others - domChats.style.display = 'none'; - domChat.style.display = 'none'; - domNavbar.style.display = 'none'; + if (isWidescreen) { + // Widescreen behavior - show in right panel + navbarSelect('chat-btn'); - // Reset state - arrSelectedGroupMembers = []; - fCreateGroupAttempt = false; - if (domCreateGroupName) domCreateGroupName.value = ''; - if (domCreateGroupFilter) domCreateGroupFilter.value = ''; - if (domCreateGroupStatus) { - domCreateGroupStatus.style.display = 'none'; - domCreateGroupStatus.textContent = ''; - } + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // Hide hello screen and show create group in right panel + const helloScreen = document.querySelector('.hello-screen'); + const createGroupElement = document.getElementById('create-group'); + + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + + // Hide other chat views + document.querySelectorAll('#chat, #chat-new').forEach(el => { + el.style.display = 'none'; + el.classList.remove('active'); + }); + + // Show create group interface + createGroupElement.style.display = ''; + createGroupElement.classList.add('active'); - // Render list - renderCreateGroupList(''); - updateCreateGroupValidation(false); + // Hide member panel + hideMemberPanel(); - // Focus name - domCreateGroupName?.focus(); + // Reset state + arrSelectedGroupMembers = []; + fCreateGroupAttempt = false; + if (domCreateGroupName) domCreateGroupName.value = ''; + if (domCreateGroupFilter) domCreateGroupFilter.value = ''; + if (domCreateGroupStatus) { + domCreateGroupStatus.style.display = 'none'; + domCreateGroupStatus.textContent = ''; + } + + // Render list + renderCreateGroupList(''); + updateCreateGroupValidation(false); + + // Focus name + domCreateGroupName?.focus(); + } else { + // Mobile behavior - original implementation + domCreateGroup.style.display = ''; + //Hide others + domChats.style.display = 'none'; + domChat.style.display = 'none'; + domNavbar.style.display = 'none'; + + // Reset state + arrSelectedGroupMembers = []; + fCreateGroupAttempt = false; + if (domCreateGroupName) domCreateGroupName.value = ''; + if (domCreateGroupFilter) domCreateGroupFilter.value = ''; + if (domCreateGroupStatus) { + domCreateGroupStatus.style.display = 'none'; + domCreateGroupStatus.textContent = ''; + } + + // Render list + renderCreateGroupList(''); + updateCreateGroupValidation(false); + + // Focus name + domCreateGroupName?.focus(); + } } /** * Close Create Group tab and go back to Chat list */ async function closeCreateGroup() { - domCreateGroup.style.display = 'none'; - fCreateGroupAttempt = false; + if (isWidescreen) { + const createGroupElement = document.getElementById('create-group'); + createGroupElement.style.display = 'none'; + createGroupElement.classList.remove('active'); - // Restore navbar to follow the same flow as "Start New Chat" close (see closeChat()) - domNavbar.style.display = ''; - - // Navigate back to chat list - await openChatlist(); + // Return to hello screen + showHelloScreen(); + } else { + // Mobile behavior + domCreateGroup.style.display = 'none'; + // Restore navbar to follow the same flow as "Start New Chat" close (see closeChat()) + domNavbar.style.display = ''; + await openChatlist(); + } + + fCreateGroupAttempt = false; // Adjust layout after UI visibility changes adjustSize(); @@ -6730,3 +6961,1163 @@ Create Group UI wiring } }; })(); + +// Widescreen layout state +let isWidescreen = window.innerWidth >= 1024; +let memberPanelVisible = false; + +// Initialize widescreen layout +function initWidescreenLayout() { + const wasWidescreen = isWidescreen; + isWidescreen = window.innerWidth >= 1024; + + if (isWidescreen) { + document.body.classList.add('widescreen'); + + // IMPORTANT: Always show navbar in widescreen + domNavbar.style.display = ''; + + // Initialize with proper state + if (!strOpenChat) { + navbarSelect('chat-btn'); + showHelloScreen(); + } else { + // If chat is already open, ensure proper layout + setupMemberPanelToggle(); + if (memberPanelVisible) { + loadMemberPanel(); + } + } + + // Update notes button visibility based on login state + const loginForm = document.getElementById('login-form'); + const isLoggedIn = loginForm && loginForm.style.display === 'none'; + if (isLoggedIn) { + updateEncryptedNotesButton(); + } + + // Hide duplicate elements + document.querySelectorAll('.duplicate-fix').forEach(el => el.remove()); + } else { + document.body.classList.remove('widescreen'); + hideMemberPanel(); + + // Ensure mobile view is clean + document.querySelectorAll('.panel-toggle').forEach(toggle => { + toggle.style.display = 'none'; + }); + + // Reset encrypted notes button positioning for mobile AND update visibility + if (domChatBookmarksBtn) { + domChatBookmarksBtn.style.position = 'absolute'; + domChatBookmarksBtn.style.top = '28px'; + domChatBookmarksBtn.style.right = '23px'; + + // Update visibility for mobile view + const loginForm = document.getElementById('login-form'); + const isLoggedIn = loginForm && loginForm.style.display === 'none'; + if (isLoggedIn) { + updateEncryptedNotesButton(); + } + } + } + + adjustSize(); +} + +// Enhanced navbar selection for widescreen +function navbarSelect(strSelectionID = '') { + // Update navbar buttons + for (const navItem of domNavbar.querySelectorAll('div')) { + if (strSelectionID === navItem.id) navItem.classList.remove('navbar-btn-inactive'); + else navItem.classList.add('navbar-btn-inactive'); + } + + // Handle widescreen layout + if (isWidescreen) { + // Hide all left panel content + document.querySelectorAll('.left-panel > *').forEach(el => { + if (el.id && el.id !== 'navbar') { + el.style.display = 'none'; + el.classList.remove('active'); + } + }); + + // Show selected tab in left panel + const tabMap = { + 'profile-btn': 'profile', + 'chat-btn': 'chats', + 'settings-btn': 'settings', + 'invites-btn': 'invites' + }; + + const contentId = tabMap[strSelectionID]; + if (contentId) { + const contentEl = document.getElementById(contentId); + if (contentEl) { + contentEl.style.display = ''; + contentEl.classList.add('active'); + } + } + + // RIGHT PANEL: NEVER close an open chat when switching navbar tabs + // The chat should stay open until user explicitly closes it with back button + const chatElement = document.getElementById('chat'); + const helloScreen = document.querySelector('.hello-screen'); + + if (strOpenChat) { + // ALWAYS show the open chat in right panel, regardless of selected tab + if (chatElement) { + chatElement.style.display = 'flex'; + chatElement.classList.add('active'); + } + if (helloScreen) { + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + } + + // Setup member panel for the open chat (if applicable) + if (strOpenChat !== strPubkey) { + setupMemberPanelToggle(); + if (memberPanelVisible) { + loadMemberPanel(); + } + } + } else { + // No chat open - only show hello screen when specifically on chat tab + if (strSelectionID === 'chat-btn') { + showHelloScreen(); + } else { + // On other tabs with no open chat - hide both + if (chatElement) { + chatElement.style.display = 'none'; + chatElement.classList.remove('active'); + } + if (helloScreen) { + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + } + } + } + } else { + // Mobile behavior - original implementation + // ... (keep existing mobile code) + } +} + +// Show hello screen in right panel +function showHelloScreen() { + if (!isWidescreen || strOpenChat) return; // DON'T show hello screen if chat is open + + const rightPanel = document.querySelector('.right-panel'); + const helloScreen = document.querySelector('.hello-screen'); + const chatElements = document.querySelectorAll('#chat, #chat-new, #create-group'); + + // Hide all chat views + chatElements.forEach(el => { + el.style.display = 'none'; + el.classList.remove('active'); + }); + + // Show hello screen + helloScreen.style.display = 'flex'; + helloScreen.classList.add('active'); + + // Hide member panel + hideMemberPanel(); + + // Show encrypted notes button when no chat is open + updateEncryptedNotesButton(); +} + +// Setup member panel toggle +function setupMemberPanelToggle() { + const chatElement = document.getElementById('chat'); + if (!chatElement) return; + + // Remove existing toggle if any + const existingToggle = chatElement.querySelector('.panel-toggle'); + if (existingToggle) { + existingToggle.remove(); + } + + // Add toggle button for member panel (only for groups and DMs, not encrypted notes) + if (strOpenChat && strOpenChat !== strPubkey) { + const toggleBtn = document.createElement('div'); + toggleBtn.className = 'panel-toggle'; + toggleBtn.innerHTML = ''; + toggleBtn.onclick = toggleMemberPanel; + + chatElement.appendChild(toggleBtn); + updateToggleButton(); + } + // If it's notes, don't add the toggle button at all +} + +// Toggle member panel visibility +function toggleMemberPanel() { + if (!isWidescreen) return; + + memberPanelVisible = !memberPanelVisible; + const memberPanel = document.querySelector('.member-panel'); + + if (memberPanelVisible) { + memberPanel.style.display = 'flex'; + memberPanel.style.flex = '0 0 300px'; + loadMemberPanel(); + + // If profile is open in chat area, adjust layout + if (window.isViewingProfileInChat) { + domProfile.style.flex = '1'; + domProfile.style.minWidth = '400px'; + domProfile.style.width = ''; + } + + } else { + memberPanel.style.display = 'none'; + memberPanel.style.flex = ''; + + // If profile is open in chat area, restore full width + if (window.isViewingProfileInChat) { + domProfile.style.flex = '1'; + domProfile.style.minWidth = '0'; + domProfile.style.width = ''; + } + } + + updateToggleButton(); + updateEmojiPickerPosition(); // Update emoji picker position +} + +// Update toggle button icon +function updateToggleButton() { + const toggleBtn = document.querySelector('.panel-toggle'); + if (!toggleBtn) return; + + const icon = toggleBtn.querySelector('.icon'); + if (memberPanelVisible) { + icon.classList.remove('icon-users'); + icon.classList.add('icon-chevron-double-right'); + } else { + icon.classList.remove('icon-chevron-double-right'); + icon.classList.add('icon-users'); + } +} + +// Hide member panel +function hideMemberPanel() { + memberPanelVisible = false; + const memberPanel = document.querySelector('.member-panel'); + if (memberPanel) { + memberPanel.style.display = 'none'; + } + updateToggleButton(); +} + +// Load members into member panel +function loadMemberPanel() { + if (!isWidescreen || !memberPanelVisible) return; + + const memberList = document.getElementById('member-list'); + const memberCount = document.getElementById('member-count'); + + if (!memberList || !memberCount) return; + + const currentChat = arrChats.find(c => c.id === strOpenChat); + if (!currentChat) return; + + memberList.innerHTML = ''; + + // Handle different chat types + if (currentChat.chat_type === 'MlsGroup') { + // Group chat - show all members including current user + const members = currentChat.participants || []; + const totalMembers = members.length + 1; // +1 for current user + memberCount.textContent = `Members — ${totalMembers}`; + + // Add current user first + const myProfile = arrProfiles.find(p => p.mine); + if (myProfile) { + const myItem = createMemberItem(myProfile, strPubkey); + memberList.appendChild(myItem); + } + + // Add other members + members.forEach(npub => { + const profile = getProfile(npub); + const memberItem = createMemberItem(profile, npub); + memberList.appendChild(memberItem); + }); + } else { + // DM chat - show both users + memberCount.textContent = `Members — 2`; + const myProfile = arrProfiles.find(p => p.mine); + const otherProfile = getProfile(currentChat.id); + + // Add current user + if (myProfile) { + const myItem = createMemberItem(myProfile, strPubkey); + memberList.appendChild(myItem); + } + + // Add other user + if (otherProfile) { + const otherItem = createMemberItem(otherProfile, currentChat.id); + memberList.appendChild(otherItem); + } + } +} + +// Simple member list item +function createMemberItem(profile, npub) { + const memberItem = document.createElement('div'); + memberItem.className = 'member-item'; + + // Avatar + const avatar = document.createElement('div'); + avatar.className = 'member-avatar'; + + if (profile?.avatar) { + const img = document.createElement('img'); + img.src = profile.avatar; + img.onerror = () => { + // If image fails to load, fallback to generated avatar + const fallbackAvatar = pubkeyToAvatar(npub, profile?.nickname || profile?.name, 32); + avatar.innerHTML = ''; + avatar.appendChild(fallbackAvatar); + }; + avatar.appendChild(img); + } else { + // Use generated avatar if no profile avatar + avatar.appendChild(pubkeyToAvatar(npub, profile?.nickname || profile?.name, 32)); + } + + // User name - make sure we have a display name + const name = document.createElement('div'); + name.className = 'member-name'; + + // Get display name with proper fallbacks + let displayName = 'Unknown User'; + if (profile) { + displayName = profile.nickname || profile.name || npub.substring(0, 8) + '...'; + } else { + // If no profile found, try to get from profiles array + const foundProfile = getProfile(npub); + if (foundProfile) { + displayName = foundProfile.nickname || foundProfile.name || npub.substring(0, 8) + '...'; + } else { + displayName = npub.substring(0, 8) + '...'; + } + } + + name.textContent = displayName; + + memberItem.appendChild(avatar); + memberItem.appendChild(name); + + return memberItem; +} + +// Modified openChat function to preserve navbar in widescreen +const originalOpenChat = openChat; +openChat = function(contact) { + // Clear previous notes state when switching between regular chats + if (strOpenChat && strOpenChat !== strPubkey && contact !== strPubkey) { + window.previousStateBeforeNotes = null; + } + // Clear any auto-scroll timer + if (chatOpenAutoScrollTimer) { + clearTimeout(chatOpenAutoScrollTimer); + chatOpenAutoScrollTimer = null; + } + + // Record when the chat was opened + chatOpenTimestamp = Date.now(); + + // After 100ms, stop auto-scrolling on media loads + chatOpenAutoScrollTimer = setTimeout(() => { + chatOpenTimestamp = 0; + chatOpenAutoScrollTimer = null; + }, 100); + + if (isWidescreen) { + // Widescreen behavior + navbarSelect('chat-btn'); + + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // Get the chat + const chat = arrChats.find(c => c.id === contact); + const isGroup = chat?.chat_type === 'MlsGroup'; + const profile = !isGroup ? getProfile(contact) : null; + strOpenChat = contact; + + if (isGroup) { + refreshGroupMemberCount(contact); + } + + // Update notes button (should remain visible in widescreen) + updateEncryptedNotesButton(); + + // Hide hello screen and show chat in right panel + const helloScreen = document.querySelector('.hello-screen'); + const chatElement = document.getElementById('chat'); + + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + chatElement.style.display = 'flex'; + chatElement.classList.add('active'); + + // For encrypted notes, don't show member panel + if (contact === strPubkey) { + hideMemberPanel(); + // Remove the panel toggle button for encrypted notes + const toggleBtn = chatElement.querySelector('.panel-toggle'); + if (toggleBtn) { + toggleBtn.style.display = 'none'; + } + } else { + // Setup member panel for groups and DMs + setupMemberPanelToggle(); + if (memberPanelVisible) { + loadMemberPanel(); + } + } + + // Update chat content + updateChat(chat, (chat?.messages || []).slice(-100), profile, true); + + } else { + // Mobile behavior - use original function + originalOpenChat(contact); + } +}; + +function setupEncryptedNotesButton() { + const notesBtn = document.getElementById('chat-bookmarks-btn'); + if (notesBtn) { + // Remove any existing click handlers to prevent duplicates + notesBtn.onclick = null; + notesBtn.addEventListener('click', handleEncryptedNotesClick, { once: false }); + + // Simple visibility management + updateEncryptedNotesButton(); + } +} + +function handleEncryptedNotesClick() { + // If we're already viewing notes, do nothing + if (strOpenChat === strPubkey) return; + + // Store current state before opening notes + window.previousStateBeforeNotes = { + type: strOpenChat ? 'chat' : 'list', + chatId: strOpenChat, + profileOpen: window.isViewingProfileInChat + }; + + // Open notes + openEncryptedNotes(); +} + +// Simple function to show/hide notes button +function updateEncryptedNotesButton() { + const notesBtn = document.getElementById('chat-bookmarks-btn'); + if (!notesBtn) return; + + // Check if user is logged in by looking at login form visibility + const loginForm = document.getElementById('login-form'); + const isLoggedIn = loginForm && loginForm.style.display === 'none'; + + if (!isLoggedIn) { + // User is not logged in - hide bookmark button completely + notesBtn.style.display = 'none'; + return; + } + + // User is logged in - handle visibility based on view + if (isWidescreen) { + // Widescreen: always show when logged in + notesBtn.style.display = 'flex'; + + // Position it properly in widescreen + notesBtn.style.position = 'absolute'; + notesBtn.style.top = '28px'; + notesBtn.style.right = '23px'; + } else { + // Mobile: only show when in chat list (no chat open) + if (strOpenChat) { + notesBtn.style.display = 'none'; + } else { + notesBtn.style.display = 'flex'; + // Reset positioning for mobile + notesBtn.style.position = ''; + notesBtn.style.top = ''; + notesBtn.style.right = ''; + } + } +} + +function openEncryptedNotes() { + // If we're already in notes, return to previous state + if (strOpenChat === strPubkey && window.previousStateBeforeNotes) { + returnToPreviousState(); + return; + } + + // Store current state before opening notes + if (strOpenChat && strOpenChat !== strPubkey) { + window.previousStateBeforeNotes = { + type: window.isViewingProfileInChat ? 'profile' : 'chat', + chatId: strOpenChat, + profileOpen: window.isViewingProfileInChat + }; + + // If a contact profile is open in chat area, close it properly + if (window.isViewingProfileInChat) { + closeProfileInChatArea(); + } + } else { + window.previousStateBeforeNotes = { + type: 'list', + chatId: null + }; + } + + // Get or create the notes chat + const notesChat = getOrCreateDMChat(strPubkey); + const myProfile = getProfile(strPubkey); + + if (isWidescreen) { + // Widescreen behavior + navbarSelect('chat-btn'); + domNavbar.style.display = ''; + + // Hide hello screen and show chat + const helloScreen = document.querySelector('.hello-screen'); + const chatElement = document.getElementById('chat'); + + helloScreen.style.display = 'none'; + helloScreen.classList.remove('active'); + chatElement.style.display = 'flex'; + chatElement.classList.add('active'); + + // Clear chat area + clearChatArea(); + + // Hide member panel for notes and remove togggle + hideMemberPanel(); + const toggleBtn = chatElement.querySelector('.panel-toggle'); + if (toggleBtn) { + toggleBtn.style.display = 'none'; + } + + // Close any profile view in chat area + if (window.isViewingProfileInChat) { + closeProfileInChatArea(); + } + + // Set current chat to notes + strOpenChat = strPubkey; + + // Update chat with notes content + updateChat(notesChat, notesChat.messages || [], myProfile, true); + + } else { + // Mobile behavior + clearChatArea(); + strOpenChat = strPubkey; + updateChat(notesChat, notesChat.messages || [], myProfile, true); + // Show chat UI, hide others + domChat.style.display = ''; + domChats.style.display = 'none'; + domNavbar.style.display = 'none'; + } + + // Update the back button notification + updateChatBackNotification(); +} + +function clearChatArea() { + // Clear all messages from the chat area + while (domChatMessages.firstChild) { + domChatMessages.removeChild(domChatMessages.firstChild); + } + + // Reset procedural scroll state + resetProceduralScroll(); + + // Clear any existing timestamps or other elements + domChatMessages.innerHTML = ''; + + // Reset any fade elements + if (domChatMessagesFade) { + domChatMessagesFade.style.display = 'none'; + } +} + +function returnToPreviousState() { + if (!window.previousStateBeforeNotes) { + // If no previous state, show appropriate view + if (isWidescreen) { + showHelloScreen(); + } else { + openChatlist(); + } + return; + } + + const previous = window.previousStateBeforeNotes; + + if (previous.type === 'chat' && previous.chatId) { + // Return to the previous chat + openChat(previous.chatId); + } else if (previous.type === 'list') { + // Return to chat list + if (isWidescreen) { + showHelloScreen(); + } else { + openChatlist(); + } + } else { + // Fallback + if (isWidescreen) { + showHelloScreen(); + } else { + openChatlist(); + } + } + + // Clear the previous state + window.previousStateBeforeNotes = null; +} + +// Function to show profile in chat area +function showProfileInChatArea(profile) { + if (!profile || !isWidescreen) return; + + // Store the current chat context before opening profile + window.previousStateBeforeProfile = { + type: 'chat', + chatId: strOpenChat + }; + + // Render the profile for chat area - IMPORTANT: Use the existing function + renderProfileTab(profile); + + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // Hide ALL chat interface elements completely + const chatInterface = document.querySelector('.chat-interface'); + if (chatInterface) { + chatInterface.style.display = 'none'; + chatInterface.style.visibility = 'hidden'; + chatInterface.style.opacity = '0'; + } + + // Also hide individual elements with multiple methods to prevent artifacts + [domChatMessages, domChatMessageBox, domChatContact, domChatContactStatus].forEach(element => { + if (element) { + element.style.display = 'none'; + element.style.visibility = 'hidden'; + element.style.opacity = '0'; + element.style.height = '0'; + element.style.overflow = 'hidden'; + } + }); + + // Get the chat container + const chatContainer = document.getElementById('chat'); + if (!chatContainer) return; + + // Completely clear any chat content that might be visible + chatContainer.style.background = 'var(--bg-color)'; + chatContainer.style.overflow = 'hidden'; + + // Remove any potential borders or outlines from chat container + chatContainer.style.border = 'none'; + chatContainer.style.outline = 'none'; + chatContainer.style.boxShadow = 'none'; + + // Ensure profile is in the correct container + if (!chatContainer.contains(domProfile)) { + console.log('Moving profile to chat container'); + chatContainer.appendChild(domProfile); + } + + // Reset any conflicting styles first + domProfile.style.cssText = ''; + + // Apply chat area specific styles - make profile take full space + domProfile.style.display = 'block'; + domProfile.style.position = 'relative'; + domProfile.style.width = '100%'; + domProfile.style.height = '100%'; + domProfile.style.overflow = 'auto'; + domProfile.style.background = 'var(--bg-color)'; + domProfile.style.zIndex = '100'; + + // Ensure the profile content has proper styling + const profileContent = domProfile.querySelector('.profile-content'); + if (profileContent) { + profileContent.style.padding = '0'; + profileContent.style.margin = '0'; + profileContent.style.height = '100%'; + profileContent.style.overflow = 'auto'; + profileContent.style.background = 'var(--bg-color)'; + } + + // Ensure banner has proper styling + if (domProfileBanner) { + domProfileBanner.style.marginTop = '0'; + domProfileBanner.style.height = 'auto'; + domProfileBanner.style.minHeight = '50px'; + domProfileBanner.style.border = 'none'; + } + + if (domProfileBackBtn) { + if (domProfile.contains(domProfileBackBtn)) { + domProfileBackBtn.remove(); + } + chatContainer.appendChild(domProfileBackBtn); + + // Use fixed positioning with proper calculation + domProfileBackBtn.style.display = 'flex'; + domProfileBackBtn.style.position = 'fixed'; + domProfileBackBtn.style.top = '20px'; + domProfileBackBtn.style.zIndex = '1000'; + domProfileBackBtn.style.borderRadius = '50%'; + domProfileBackBtn.style.width = '40px'; + domProfileBackBtn.style.height = '40px'; + domProfileBackBtn.style.alignItems = 'center'; + domProfileBackBtn.style.justifyContent = 'center'; + domProfileBackBtn.onclick = () => { + closeProfileInChatArea(); + }; + } + + // Hide the member panel toggle when viewing profile + const panelToggle = document.querySelector('.panel-toggle'); + if (panelToggle) { + panelToggle.style.display = 'none'; + } + + // Hide member panel if it was open + const memberPanel = document.querySelector('.member-panel'); + if (memberPanel) { + memberPanel.style.display = 'none'; + } + + // Force a reflow and ensure no visual artifacts remain + setTimeout(() => { + // Double-check that all chat elements are completely hidden + [domChatMessages, domChatMessageBox, domChatContact, domChatContactStatus].forEach(element => { + if (element) { + element.style.display = 'none'; + element.style.visibility = 'hidden'; + } + }); + }, 10); + + // Store that we're viewing profile in chat context + window.isViewingProfileInChat = true; + + // Store the previous state so we can return to it + window.previousStateBeforeNotes = { + type: 'profile', + profile: profile, + chatId: strOpenChat + }; +} + +// Function to close profile and return to chat +function closeProfileInChatArea() { + domProfile.style.display = 'none'; + domProfile.style.cssText = ''; + + // Reset profile content styles + const profileContent = domProfile.querySelector('.profile-content'); + if (profileContent) { + profileContent.style.cssText = ''; + } + + // Reset banner styles + if (domProfileBanner) { + domProfileBanner.style.cssText = ''; + } + + if (domProfileBackBtn) { + const chatContainer = document.getElementById('chat'); + if (chatContainer && chatContainer.contains(domProfileBackBtn)) { + domProfileBackBtn.remove(); + } + domProfile.appendChild(domProfileBackBtn); + + // Reset back button styles + domProfileBackBtn.style.cssText = ''; + domProfileBackBtn.style.display = 'none'; + } + + // Reset chat container styles + const chatContainer = document.getElementById('chat'); + if (chatContainer) { + chatContainer.style.cssText = ''; + } + + // Show ALL chat interface elements properly + const chatInterface = document.querySelector('.chat-interface'); + if (chatInterface) { + chatInterface.style.display = 'block'; + chatInterface.style.visibility = 'visible'; + chatInterface.style.opacity = '1'; + } + + // Show individual elements with proper display values + if (domChatMessages) { + domChatMessages.style.display = 'flex'; + domChatMessages.style.visibility = 'visible'; + domChatMessages.style.opacity = '1'; + domChatMessages.style.height = ''; + domChatMessages.style.overflow = ''; + } + + if (domChatMessageBox) { + domChatMessageBox.style.display = 'flex'; + domChatMessageBox.style.visibility = 'visible'; + domChatMessageBox.style.opacity = '1'; + domChatMessageBox.style.height = ''; + domChatMessageBox.style.overflow = ''; + } + + if (domChatContact) { + domChatContact.style.display = 'flex'; + domChatContact.style.visibility = 'visible'; + domChatContact.style.opacity = '1'; + domChatContact.style.height = ''; + domChatContact.style.overflow = ''; + } + + if (domChatContactStatus) { + domChatContactStatus.style.display = 'block'; + domChatContactStatus.style.visibility = 'visible'; + domChatContactStatus.style.opacity = '1'; + domChatContactStatus.style.height = ''; + domChatContactStatus.style.overflow = ''; + } + + // Show member panel toggle again + const panelToggle = document.querySelector('.panel-toggle'); + if (panelToggle && strOpenChat !== strPubkey) { + panelToggle.style.display = 'flex'; + } + + // Move profile back to main container + const mainContainer = document.getElementById('popup-container'); + if (mainContainer && !mainContainer.contains(domProfile)) { + mainContainer.appendChild(domProfile); + } + + // IMPORTANT: Ensure navbar remains visible + domNavbar.style.display = ''; + + setTimeout(() => { + if (domChatMessages) domChatMessages.style.display = 'flex'; + if (domChatMessageBox) domChatMessageBox.style.display = 'flex'; + if (domChatContact) domChatContact.style.display = 'flex'; + }, 10); + + // Clear the profile viewing state + window.isViewingProfileInChat = false; +} + +// Back button handling for profile view +function setupProfileBackButton() { + if (domProfileBackBtn) { + domProfileBackBtn.onclick = () => { + if (isWidescreen) { + if (window.isViewingProfileInChat) { + // If viewing profile in chat, return to chat + closeProfileInChatArea(); + } else if (strOpenChat === strPubkey && window.previousStateBeforeNotes) { + // If in notes with previous state, return to that state + returnToPreviousState(); + } else if (strOpenChat === strPubkey) { + // If in notes without previous state, go to hello screen + closeChat(); + } else { + openChatlist(); + } + } else { + if (strOpenChat) { + openChat(strOpenChat); + } else { + openChatlist(); + } + } + }; + } +} + +// Fix the chat back button handler to handle notes properly +function setupChatBackButton() { + if (domChatBackBtn) { + // Remove any existing handler + domChatBackBtn.onclick = null; + + domChatBackBtn.onclick = () => { + if (strOpenChat === strPubkey && window.previousStateBeforeNotes) { + // If in notes with previous state, return to that state + returnToPreviousState(); + } else if (strOpenChat === strPubkey) { + // If in notes without previous state, go to appropriate view + if (isWidescreen) { + showHelloScreen(); + } else { + openChatlist(); + } + } else { + // Regular chat - close it + closeChat(); + } + }; + } +} + +// Modified closeChat function for clean transitions +const originalCloseChat = closeChat; +closeChat = async function() { + if (isWidescreen) { + // Widescreen cleanup + if (chatOpenAutoScrollTimer) { + clearTimeout(chatOpenAutoScrollTimer); + chatOpenAutoScrollTimer = null; + } + + // Release memory + while (domChatMessages.firstElementChild) { + const domChild = domChatMessages.firstElementChild; + const domMedias = domChild?.querySelectorAll('img, audio, video'); + for (const domMedia of domMedias) { + if (domMedia instanceof HTMLMediaElement) { + domMedia.pause(); + if (platformFeatures.os === 'android' && domMedia.src.startsWith('blob:')) { + URL.revokeObjectURL(domMedia.src); + } + domMedia.removeAttribute('src'); + domMedia.load(); + } + if (domMedia instanceof HTMLImageElement) { + if (domMedia.src.startsWith('blob:')) { + URL.revokeObjectURL(domMedia.src); + } + domMedia.removeAttribute('src'); + } + } + domChild.remove(); + } + + // Reset state + strOpenChat = ""; + nLastTypingIndicator = 0; + + // Update notes button (should remain visible in widescreen) + updateEncryptedNotesButton(); + + // Cancel any ongoing replies or selections + strCurrentReactionReference = ""; + strCurrentReplyReference = ""; + cancelReply(); + + // Close profile if it's open in chat area + if (window.isViewingProfileInChat) { + closeProfileInChatArea(); + } + + // Show hello screen (conversation area) + showHelloScreen(); + + // Hide member panel + hideMemberPanel(); + + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // Update chat list + renderChatlist(); + + adjustSize(); + } else { + await originalCloseChat(); + } +}; + +// Modified navigation functions for widescreen +function openChatlist() { + if (isWidescreen) { + navbarSelect('chat-btn'); + domProfile.style.display = 'none'; + domProfile.classList.remove('active'); + domSettings.style.display = 'none'; + domSettings.classList.remove('active'); + domInvites.style.display = 'none'; + domInvites.classList.remove('active'); + domChats.style.display = ''; + domChats.classList.add('active'); + + // IMPORTANT: Keep navbar visible in widescreen + domNavbar.style.display = ''; + + // ONLY show hello screen if no chat is currently open + if (!strOpenChat) { + showHelloScreen(); + } + // If strOpenChat exists, the chat will remain visible from the navbarSelect logic + + // Load MLS invites + loadMLSInvites(); + } else { + // Original mobile behavior + navbarSelect('chat-btn'); + domProfile.style.display = 'none'; + domSettings.style.display = 'none'; + domInvites.style.display = 'none'; + + if (domChats.style.display !== '') { + domChats.classList.add('fadein-subtle-anim'); + domChats.addEventListener('animationend', () => domChats.classList.remove('fadein-subtle-anim'), { once: true }); + domChats.style.display = ''; + } + + loadMLSInvites(); + } +} + +function openProfile(cProfile) { + if (isWidescreen) { + // In widescreen, always show profile in the left panel + navbarSelect('profile-btn'); + domChats.style.display = 'none'; + domChats.classList.remove('active'); + domSettings.style.display = 'none'; + domInvites.style.display = 'none'; + + // Render profile in left panel + if (!cProfile) cProfile = arrProfiles.find(a => a.mine); + renderProfileTab(cProfile); + + if (domProfile.style.display !== '') { + domProfile.classList.add('fadein-subtle-anim'); + domProfile.addEventListener('animationend', () => domProfile.classList.remove('fadein-subtle-anim'), { once: true }); + domProfile.style.display = ''; + domProfile.classList.add('active'); + } + + // IMPORTANT: Keep the navbar visible in widescreen + domNavbar.style.display = ''; + + } else { + // Mobile behavior - hide navbar when opening profile + navbarSelect('profile-btn'); + domChats.style.display = 'none'; + domSettings.style.display = 'none'; + domInvites.style.display = 'none'; + domNavbar.style.display = 'none'; // Hide navbar in mobile + + if (!cProfile) cProfile = arrProfiles.find(a => a.mine); + renderProfileTab(cProfile); + + if (domProfile.style.display !== '') { + domProfile.classList.add('fadein-subtle-anim'); + domProfile.addEventListener('animationend', () => domProfile.classList.remove('fadein-subtle-anim'), { once: true }); + domProfile.style.display = ''; + } + } +} + +// Enhanced resize handler with smooth transitions +let resizeTimeout; +window.addEventListener('resize', () => { + const wasWidescreen = isWidescreen; + + // Debounce resize events + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(() => { + document.body.classList.add('layout-transition'); + + initWidescreenLayout(); + + // If layout changed significantly, force a clean re-render + if (wasWidescreen !== isWidescreen) { + if (strOpenChat) { + // Re-open current chat to ensure proper layout + const currentChat = strOpenChat; + strOpenChat = ""; + setTimeout(() => openChat(currentChat), 50); + } + } + + setTimeout(() => { + document.body.classList.remove('layout-transition'); + }, 300); + }, 100); +}); + +// Function to handle login state and UI visibility +function updateUIVisibilityForLoginState(isLoggedIn) { + const loginForm = document.getElementById('login-form'); + const navbar = document.getElementById('navbar'); + const bookmarksBtn = document.getElementById('chat-bookmarks-btn'); + + // Check if login form is currently visible/active + const isLoginActive = loginForm && + !loginForm.classList.contains('fadeout-anim') && + loginForm.style.display !== 'none'; + + if (isLoginActive) { + // Login is active - hide navbar and bookmark + if (navbar) navbar.style.display = 'none'; + if (bookmarksBtn) bookmarksBtn.style.display = 'none'; + } else { + // Login is NOT active - user is logged in + if (isLoggedIn) { + if (window.innerWidth >= 1024) { + // Widescreen - show navbar and bookmarks + if (navbar) navbar.style.display = 'flex'; + if (bookmarksBtn) bookmarksBtn.style.display = 'block'; + } else { + // Mobile - show navbar, hide bookmarks (if that's your design) + if (navbar) navbar.style.display = 'flex'; + if (bookmarksBtn) bookmarksBtn.style.display = 'block'; + } + } else { + // Not logged in and login not active - hide everything + if (navbar) navbar.style.display = 'none'; + if (bookmarksBtn) bookmarksBtn.style.display = 'none'; + } + } +} + +// Call this function when login state changes +document.addEventListener('DOMContentLoaded', function() { + // Initial check - assuming not logged in + updateUIVisibilityForLoginState(false); + + // Check on resize + window.addEventListener('resize', function() { + // Check if we're logged in by looking for the navbar or other logged-in indicators + const isLoggedIn = document.getElementById('navbar').style.display !== 'none' || + document.body.classList.contains('logged-in'); + updateUIVisibilityForLoginState(isLoggedIn); + }); +}); + +// You'll need to call this when login completes +function onLoginSuccess() { + updateUIVisibilityForLoginState(true); +} + +function onLogout() { + updateUIVisibilityForLoginState(false); +} \ No newline at end of file diff --git a/src/styles.css b/src/styles.css index 420c95a..565bdd0 100644 --- a/src/styles.css +++ b/src/styles.css @@ -184,6 +184,14 @@ mask-image: url("./icons/send.svg"); } +.icon-users { + mask-image: url("./icons/users.svg"); +} + +.icon-chevron-double-right { + mask-image: url("./icons/chevron-double-right.svg"); +} + .icon-message { mask-image: url("./icons/message.svg"); } @@ -1046,7 +1054,7 @@ html, body { display: block; max-width: 60%; font-weight: bold; - margin-top: 8px; + margin-top: 12px; margin-bottom: 0; margin-left: auto; margin-right: auto; @@ -1081,6 +1089,7 @@ html, body { width: 60%; margin-left: auto; margin-right: auto; + padding-bottom: 0; margin-top: -1px; transition: opacity 0.25s cubic-bezier(0.22, 1, 0.36, 1), max-height 0.3s cubic-bezier(0.34, 1, 0.64, 1), margin-bottom 0.3s cubic-bezier(0.34, 1, 0.64, 1); max-height: 30px; @@ -1813,7 +1822,7 @@ html, body { .emoji-grid { display: grid; - grid-template-columns: repeat(6, 1fr); + grid-template-columns: repeat(6, 1fr); gap: 4px; margin-top: -12px; } @@ -4133,6 +4142,25 @@ select:disabled:hover { appearance: checkbox; } +.icon-users { + background-color: var(--icon-color-primary) !important; + width: 28px !important; + height: 28px !important; +} + +.icon-chevron-double-right { + background-color: var(--icon-color-primary) !important; + width: 28px !important; + height: 28px !important; +} + +.icon-bookmark { + background-color: var(--icon-color-primary) !important; + margin-top: -10px; + width: 28px !important; + height: 28px !important; +} + /* Image Viewer Modal */ .image-viewer-overlay { position: fixed; @@ -4488,6 +4516,488 @@ hr { margin: 16px 0; } +/* Widescreen Layout */ +@media (min-width: 1024px) { + .container { + flex-direction: row; + height: 100vh; + overflow: hidden; + } + + #login-form { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #060606; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + } + + .login-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + max-width: 400px; + padding: 40px 20px; + } + + /* Left Panel - Navigation and Content */ + .left-panel { + width: 400px; + min-width: 400px; + height: 100vh; + display: flex; + flex-direction: column; + border-right: 1px solid rgba(255, 255, 255, 0.1); + background-color: #060606; + position: relative; + justify-content: flex-start; + overflow-x: hidden; + } + + /* Right Panel*/ + .right-panel { + flex: 1; + height: 100vh; + display: flex; + flex-direction: column; + background-color: #0a0a0a; + position: relative; + overflow: hidden; + } + + /* Widescreen emoji picker positioning */ +.emoji-picker.emoji-picker-widescreen { + left: auto; + right: 20px; + transform: translateX(0) translateY(100%); +} + +.emoji-picker.emoji-picker-widescreen.visible { + transform: translateX(0) translateY(0); +} + +/* Adjust position when member panel is open */ +.member-panel ~ .right-panel .emoji-picker.emoji-picker-widescreen { + right: 320px; +} + + #chat-new { + flex: 1; + display: none; + flex-direction: column; + height: 100%; + background-color: #0a0a0a; + } + + #chat-new.active { + display: flex; + } + + .chat-new-header { + background-color: #161616; + height: 65px; + display: flex; + align-items: center; + padding: 0 15px; + flex-shrink: 0; + } + + .chat-new-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + overflow-y: auto; + } + + .chat-new-center-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + margin-top: -50px; + } + + .chat-new-input-box { + position: relative; + width: 100%; + padding: 5px; + background-color: #0a0a0a; + border-top: 1px solid rgba(255, 255, 255, 0.1); + flex-shrink: 0; + } + + .chat-new-center-content { + position: relative; + bottom: auto; + width: 100%; + } + + .chat-new-input-box { + position: relative; + bottom: auto; + width: 100%; + } + + #create-group { + padding: 0 !important; + margin: 0 !important; + width: 100% !important; + overflow-x: hidden !important; + } + + #create-group .chat-new-center-content { + position: relative; + bottom: auto; + width: 100%; + } + + #create-group .chat-new-input-box { + position: relative; + bottom: auto; + width: 100%; + } + + /* Improve create group form layout */ + #create-group-name { + width: 75%; + margin: 0 auto; + } + + #create-group-filter { + width: 75%; + margin: 0 auto; + } + + #create-group-list { + width: 75%; + margin: 10px auto; + } + + #create-group-create-btn, + #create-group-cancel-btn { + min-width: 120px; + } + + #chats, #profile, #settings, #invites { + flex: 1; + display: none; + overflow-y: auto; + padding-top: 0 !important; + margin-top: 0 !important; + align-content: flex-start; + justify-content: flex-start; + overflow-x: hidden !important; + } + + #chats.active, #profile.active, #settings.active, #invites.active { + display: flex; + flex-direction: column; + } + + /* Hello Screen */ + .hello-screen { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + text-align: center; + padding: 40px; + } + + .hello-title { + font-size: 32px; + margin-bottom: 15px; + color: #59fcb3; + } + + .hello-subtitle { + font-size: 16px; + opacity: 0.7; + line-height: 1.5; + max-width: 400px; + } + + /* Chat in right panel */ + #chat { + flex: 1; + display: none; + flex-direction: column; + padding-top: 0 !important; + } + + #chat.active { + display: flex; + } + + .navbar { + position: absolute; + bottom: 0; + width: 100%; + margin-top: auto; + } + + #profile { + padding: 0 !important; + margin: 0 !important; + width: 100% !important; + overflow-x: hidden !important; + } + + #profile > div:first-child { + padding: 0 !important; + margin: 0 !important; + min-height: auto !important; + height: auto !important; + width: 100% !important; + overflow-x: hidden !important; + } + + #profile-name { + margin-top: 15px !important; + margin-bottom: 5px !important; + width: 100% !important; + } + + #profile-secondary-name { + margin-top: 5px !important; + margin-bottom: 10px !important; + width: 100% !important; + } + + #chats { + padding: 0 !important; + margin: 0 !important; + position: relative; + width: 100% !important; + overflow-x: hidden !important; + } + + .chat-bookmarks-btn { + position: absolute; + top: 10px !important; + right: 15px; + z-index: 10; + margin: 0 !important; + padding-top: 30px; + } + + #chat-list { + padding-top: 0 !important; + margin-top: 0 !important; + padding-left: 15px !important; + padding-right: 15px !important; + width: auto !important; + overflow-x: hidden !important; + } + + #chats > div:nth-child(3) { + margin-top: 0 !important; + margin-bottom: 15px !important; + margin-left: 15px !important; + margin-right: 15px !important; + width: auto !important; + display: flex !important; + justify-content: center !important; + overflow-x: hidden !important; + } + + .new-chat-btn { + width: 50% !important; + margin-left: 5px !important; + margin-right: 5px !important; + overflow: hidden !important; + } + + .sync-line { + margin-top: 0 !important; + margin-bottom: 10px !important; + } + + .chat-new-header, + .chat-new-content, + .chat-new-center-content, + .chat-new-input-box { + position: relative; + bottom: auto; + width: auto; + } + + .chat-new-center-content { + margin-top: 20px; + } + + /* Create Group adjustments */ + #create-group { + flex: 1; + overflow-y: auto; + padding: 0 !important; + margin: 0 !important; + width: 100% !important; + overflow-x: hidden !important; + } + + /* Member Panel */ + .member-panel { + width: 240px; + background: #000000; + border-left: 1px solid #333; + display: flex; + flex-direction: column; + } + + .member-header { + padding: 16px 8px 8px 16px; + font-size: 12px; + font-weight: 600; + color: #8e9297; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 0; + border-bottom: 1px solid #333; + } + + /* Member list styles */ + .member-list { + flex: 1; + overflow-y: auto; + padding: 8px 8px 0 8px; + overflow-x: hidden; + } + + .member-item { + display: flex; + align-items: center; + padding: 8px; + border-radius: 4px; + margin-bottom: 2px; + } + + .member-avatar { + width: 34px; + height: 34px; + border-radius: 50%; + overflow: hidden; + margin-right: 12px; + } + + .member-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .member-name { + font-size: 14px; + color: #ffffff; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .member-count { + margin-top: 20px; + text-align: left; + margin-left: 15px; + } + + /* Panel toggle button */ + .panel-toggle { + position: absolute; + top: 15px; + right: 15px; + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 10; + } + + #profile > div:first-child > div { + margin: 0 !important; + padding: 0 !important; + width: 100% !important; + overflow-x: hidden !important; + } + + #profile-status { + margin-top: 0 !important; + margin-bottom: 10px !important; + width: 100% !important; + } + + .chats { + padding: 0 !important; + margin: 0 !important; + align-items: stretch !important; + width: 100% !important; + overflow-x: hidden !important; + } + + .mls-invites-section { + margin-top: 0 !important; + margin-bottom: 20px !important; + margin-left: 15px !important; + margin-right: 15px !important; + width: auto !important; + overflow-x: hidden !important; + } + + .chatlist-contact { + width: auto !important; + margin-left: 0 !important; + margin-right: 0 !important; + overflow: hidden !important; + } + + .left-panel > * { + max-width: 100% !important; + overflow-x: hidden !important; + } +} + +/* Smooth transitions for layout changes */ +.layout-transition { + transition: all 0.3s ease-in-out; +} + +/* Mobile view (default) */ +@media (max-width: 1023px) { + .left-panel, + .right-panel, + .member-panel { + width: 100% !important; + min-width: auto !important; + } + + .hello-screen { + display: none !important; + } + + .panel-toggle { + display: none !important; + } + + .member-panel { + display: none !important; + } +} + /* Spoilers */ .spoiler-wrapper { display: inline;