From 730f356e14a6e445b7f7d69af6a65dbe35a21df6 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Sat, 30 Nov 2024 00:40:10 -0500 Subject: [PATCH] Save connection settings as cookies, add a reset connection button to sidebar --- app.js | 137 ++++++++++++++++++++++++++------------- index.html | 185 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 233 insertions(+), 89 deletions(-) diff --git a/app.js b/app.js index e1756c9..fe80a88 100644 --- a/app.js +++ b/app.js @@ -48,34 +48,88 @@ function waitForPeerResponse(expectedMessageFragment, timeout = 900000) { }); } +// Utility functions for managing cookies +function setCookie(name, value, days = 365) { + const date = new Date(); + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); + const expires = `expires=${date.toUTCString()}`; + document.cookie = `${name}=${encodeURIComponent(value)};${expires};path=/`; +} + +function getCookie(name) { + const cookies = document.cookie.split('; '); + for (let i = 0; i < cookies.length; i++) { + const [key, value] = cookies[i].split('='); + if (key === name) return decodeURIComponent(value); + } + return null; +} + +function deleteCookie(name) { + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; +} + +// Load connections from cookies +function loadConnections() { + const savedConnections = getCookie('connections'); + return savedConnections ? JSON.parse(savedConnections) : {}; +} + +// Save connections to cookies +function saveConnections() { + setCookie('connections', JSON.stringify(connections)); +} + +// Add Reset Connections Button +// Toggle Reset Connections Button Visibility +function toggleResetButtonVisibility() { + const resetConnectionsBtn = document.querySelector('#sidebar .btn-danger'); + if (!resetConnectionsBtn) return; + + // Show or hide the button based on active connections + resetConnectionsBtn.style.display = Object.keys(connections).length > 0 ? 'block' : 'none'; +} + +// Add Reset Connections Button +const resetConnectionsBtn = document.createElement('button'); +resetConnectionsBtn.textContent = 'Reset Connections'; +resetConnectionsBtn.className = 'btn btn-danger w-100 mt-2'; +resetConnectionsBtn.addEventListener('click', () => { + console.log('[INFO] Resetting connections and clearing cookies.'); + Object.keys(connections).forEach((topicId) => { + disconnectConnection(topicId); + }); + deleteCookie('connections'); + resetConnectionsView(); + showWelcomePage(); + toggleResetButtonVisibility(); // Ensure button visibility is updated +}); +document.getElementById('sidebar').appendChild(resetConnectionsBtn); + // Initialize the app console.log('[INFO] Client app initialized'); +// Load connections from cookies and restore them document.addEventListener('DOMContentLoaded', () => { - console.log('[INFO] Initializing Client App'); + const savedConnections = loadConnections(); + console.log('[INFO] Restoring saved connections:', savedConnections); - const connectionTitle = document.getElementById('connection-title'); - if (!connectionTitle) { - console.error('[ERROR] Connection title element is missing! Creating dynamically...'); - const content = document.getElementById('content'); - const titleElement = document.createElement('h1'); - titleElement.id = 'connection-title'; - titleElement.textContent = '󠀠'; - titleElement.classList.add('hidden'); // Initially hidden - content.insertBefore(titleElement, content.firstChild); - } + // Restore saved connections + Object.keys(savedConnections).forEach((topicId) => { + const topicHex = savedConnections[topicId].topicHex; + addConnection(topicHex); // Add connections using the saved topicHex + }); - if (Object.keys(connections).length === 0) { - showWelcomePage(); - } else { + if (Object.keys(connections).length > 0) { hideWelcomePage(); + } else { + showWelcomePage(); } - assertVisibility(); // Ensure visibility is correct after initialization + assertVisibility(); // Ensure visibility reflects the restored connections }); - // Show Status Indicator // Modify showStatusIndicator to recreate it dynamically function showStatusIndicator(message = 'Processing...') { @@ -137,6 +191,10 @@ collapseSidebarBtn.addEventListener('click', () => { sidebar.classList.toggle('collapsed'); const btn = collapseSidebarBtn; btn.innerHTML = sidebar.classList.contains('collapsed') ? '>' : '<'; + + // Toggle Reset Connections Button Visibility + const resetConnectionsBtn = document.querySelector('#sidebar .btn-danger'); + resetConnectionsBtn.style.display = sidebar.classList.contains('collapsed') ? 'none' : 'block'; }); function handlePeerData(data, topicId, peer) { @@ -188,20 +246,18 @@ addConnectionForm.addEventListener('submit', (e) => { } }); -// Function to add a new connection function addConnection(topicHex) { console.log(`[DEBUG] Adding connection with topic: ${topicHex}`); if (Object.keys(connections).length === 0) { - console.log('[DEBUG] Hiding welcome page after first connection'); - hideWelcomePage(); // Hide the welcome page when the first connection is added + hideWelcomePage(); } const topic = b4a.from(topicHex, 'hex'); const topicId = topicHex.substring(0, 12); - console.log(`[INFO] Adding connection with topic: ${topicHex}`); - assertVisibility(); // Ensure visibility reflects the added connection + connections[topicId] = { topic, peer: null, swarm: null, topicHex }; + saveConnections(); // Save updated connections to cookies const connectionItem = document.createElement('li'); connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between'; @@ -215,23 +271,14 @@ function addConnection(topicHex) { `; - // Add click event to switch connection connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId)); - - // Add click event to the disconnect button - const disconnectBtn = connectionItem.querySelector('.disconnect-btn'); - disconnectBtn.addEventListener('click', (e) => { - e.stopPropagation(); // Prevent triggering the switch connection event + connectionItem.querySelector('.disconnect-btn').addEventListener('click', (e) => { + e.stopPropagation(); disconnectConnection(topicId, connectionItem); }); connectionList.appendChild(connectionItem); - connections[topicId] = { topic, peer: null, swarm: null }; - - console.log('[DEBUG] Updated connections object:', connections); - - // Create a new swarm for this connection const swarm = new Hyperswarm(); connections[topicId].swarm = swarm; @@ -239,27 +286,18 @@ function addConnection(topicHex) { swarm.on('connection', (peer) => { console.log(`[INFO] Connected to peer for topic: ${topicHex}`); - if (connections[topicId].peer) { - console.warn(`[WARN] Duplicate connection detected for topic: ${topicId}. Closing.`); peer.destroy(); return; } - connections[topicId].peer = peer; updateConnectionStatus(topicId, true); - peer.on('data', (data) => { - handlePeerData(data, topicId, peer); - }); - + peer.on('data', (data) => handlePeerData(data, topicId, peer)); peer.on('close', () => { - console.log(`[INFO] Disconnected from peer for topic: ${topicHex}`); updateConnectionStatus(topicId, false); - if (window.activePeer === peer) { window.activePeer = null; - // connectionTitle.textContent = 'Disconnected'; dashboard.classList.add('hidden'); containerList.innerHTML = ''; } @@ -270,17 +308,28 @@ function addConnection(topicHex) { } }); - // Automatically collapse the sidebar when a new connection is added + // Collapse the sidebar after adding a connection const sidebar = document.getElementById('sidebar'); const collapseSidebarBtn = document.getElementById('collapse-sidebar-btn'); if (!sidebar.classList.contains('collapsed')) { sidebar.classList.add('collapsed'); collapseSidebarBtn.innerHTML = '>'; - console.log('[DEBUG] Sidebar auto-collapsed after adding a new connection'); + console.log('[DEBUG] Sidebar collapsed after adding connection'); } } +// Initialize connections from cookies on page load +document.addEventListener('DOMContentLoaded', () => { + const savedConnections = loadConnections(); + console.log('[INFO] Loading saved connections:', savedConnections); + + Object.keys(savedConnections).forEach((topicId) => { + const topicHex = savedConnections[topicId].topic; + addConnection(topicHex); + }); +}); + function disconnectConnection(topicId, connectionItem) { const connection = connections[topicId]; diff --git a/index.html b/index.html index 43ae919..5871dc9 100644 --- a/index.html +++ b/index.html @@ -22,9 +22,10 @@ color: white; overflow: hidden; } + .hidden { - display: none !important; -} + display: none !important; + } #titlebar { -webkit-app-region: drag; @@ -78,14 +79,18 @@ } #content { - display: flex; - flex-direction: column; /* Keep vertical stacking for child elements */ - margin-left: 250px; /* Leave space for the sidebar */ - flex: 1; /* Allow the content to grow */ - padding: 30px; - overflow-y: auto; /* Allow scrolling if content overflows */ - position: relative; -} + display: flex; + flex-direction: column; + /* Keep vertical stacking for child elements */ + margin-left: 250px; + /* Leave space for the sidebar */ + flex: 1; + /* Allow the content to grow */ + padding: 30px; + overflow-y: auto; + /* Allow scrolling if content overflows */ + position: relative; + } #sidebar.collapsed~#content { margin-left: 50px; @@ -171,17 +176,18 @@ } #status-indicator { - display: none; /* Ensure it's hidden by default */ - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - align-items: center; - justify-content: center; - background-color: rgba(0, 0, 0, 0.75); - z-index: 1050; -} + display: none; + /* Ensure it's hidden by default */ + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.75); + z-index: 1050; + } #status-indicator .spinner-border { width: 3rem; @@ -195,37 +201,126 @@ } #welcome-page { + display: flex; + flex-direction: column; + /* Stack child elements vertically */ + justify-content: center; + /* Center content vertically */ + align-items: center; + /* Center content horizontally */ + text-align: center; + /* Center-align text */ + position: absolute; + /* Overlay it over the content area */ + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + /* Center it perfectly in the content */ + max-width: 800px; + /* Restrict the width of the welcome page */ + width: 100%; + /* Allow it to scale */ + padding: 20px; + background-color: transparent; + /* Match the theme */ + } + + #welcome-page.hidden { + display: none !important; + /* Completely hide when not needed */ + } + + #dashboard { + display: flex; + /* Use flex layout for content within the dashboard */ + flex-direction: column; + /* Stack elements vertically */ + flex: 1; + /* Ensure it uses all available space */ + width: 100%; + /* Take up the full width of the content area */ + padding: 0; + /* Remove extra padding */ + overflow-y: auto; + /* Allow vertical scrolling if needed */ + position: relative; + /* Prevent overlap with other elements */ + } + + #dashboard.hidden { + display: none !important; + /* Hide the dashboard completely when not needed */ + } + #alert-container { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1055; /* Ensure it overlays important elements only */ display: flex; - flex-direction: column; /* Stack child elements vertically */ - justify-content: center; /* Center content vertically */ - align-items: center; /* Center content horizontally */ - text-align: center; /* Center-align text */ - position: absolute; /* Overlay it over the content area */ - top: 50%; - left: 50%; - transform: translate(-50%, -50%); /* Center it perfectly in the content */ - max-width: 800px; /* Restrict the width of the welcome page */ - width: 100%; /* Allow it to scale */ - padding: 20px; - background-color: transparent; /* Match the theme */ + flex-direction: column-reverse; /* Stack alerts upwards */ + gap: 10px; /* Add space between alerts */ + pointer-events: none; /* Prevent container from blocking clicks */ } -#welcome-page.hidden { - display: none !important; /* Completely hide when not needed */ +.alert { + display: flex; + align-items: center; + justify-content: space-between; + max-width: 80%; + padding: 12px 20px; + background-color: #2b2b2b; + color: #e0e0e0; + border-radius: 6px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); + font-family: Arial, sans-serif; + font-size: 14px; + animation: fadeIn 0.3s ease-out, fadeOut 4.5s ease-in forwards; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + pointer-events: auto; /* Allow alerts to be interactive */ } -#dashboard { - display: flex; /* Use flex layout for content within the dashboard */ - flex-direction: column; /* Stack elements vertically */ - flex: 1; /* Ensure it uses all available space */ - width: 100%; /* Take up the full width of the content area */ - padding: 0; /* Remove extra padding */ - overflow-y: auto; /* Allow vertical scrolling if needed */ - position: relative; /* Prevent overlap with other elements */ +.alert.success { + border-left: 6px solid #28a745; /* Green border for success */ } -#dashboard.hidden { - display: none !important; /* Hide the dashboard completely when not needed */ +.alert.danger { + border-left: 6px solid #dc3545; /* Red border for danger */ +} + +.alert .close-btn { + background: none; + border: none; + color: #e0e0e0; + font-size: 16px; + cursor: pointer; + margin-left: auto; /* Align to the far right */ +} + +@media (max-width: 768px) { + #alert-container { + bottom: 10px; + right: 10px; + } + + .alert { + max-width: 90%; + font-size: 12px; + padding: 10px 15px; + white-space: normal; /* Allow wrapping on small screens */ + text-overflow: clip; /* Disable ellipsis when wrapping */ + } +} + +@media (min-width: 768px) { + .alert { + white-space: nowrap; /* Re-enable nowrap for larger screens */ + text-overflow: ellipsis; /* Add ellipsis for overflowed text */ + } +} +#sidebar.collapsed .btn-danger { + display: none; }