revert Add Name Modal On Add
This commit is contained in:
snxraven 2024-11-30 07:12:27 -05:00
parent ad67461cf5
commit a3c5f18736
2 changed files with 152 additions and 231 deletions

346
app.js
View File

@ -103,50 +103,39 @@ function waitForPeerResponse(expectedMessageFragment, timeout = 900000) {
}); });
} }
function saveConnections() { // Utility functions for managing cookies
console.log('[DEBUG] Saving connections:', connections); function setCookie(name, value, days = 365) {
const date = new Date();
const serializableConnections = {}; date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
const expires = `expires=${date.toUTCString()}`;
for (const topicId in connections) { document.cookie = `${name}=${encodeURIComponent(value)};${expires};path=/`;
const { topic, topicHex, connectionName } = connections[topicId];
if (!serializableConnections[topicId] && topicHex) {
serializableConnections[topicId] = {
topicHex,
connectionName: connectionName || 'Unnamed Connection',
topic: b4a.toString(topic, 'hex'),
};
} else {
console.warn(`[WARN] Skipping duplicate or invalid connection: ${topicId}`);
}
}
localStorage.setItem('connections', JSON.stringify(serializableConnections));
} }
function loadConnections() { function getCookie(name) {
// Clear any previously loaded connections in memory const cookies = document.cookie.split('; ');
Object.keys(connections).forEach((topicId) => { for (let i = 0; i < cookies.length; i++) {
delete connections[topicId]; const [key, value] = cookies[i].split('=');
}); if (key === name) return decodeURIComponent(value);
const savedConnections = localStorage.getItem('connections');
const connectionsData = savedConnections ? JSON.parse(savedConnections) : {};
for (const topicId in connectionsData) {
const { topicHex, connectionName } = connectionsData[topicId];
if (!topicHex) {
console.warn(`[WARN] Skipping connection with missing topicHex: ${topicId}`);
continue;
} }
return null;
}
console.log(`[DEBUG] Loading connection: ${topicHex}, Name: ${connectionName}`); 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');
const connections = savedConnections ? JSON.parse(savedConnections) : {};
// Recreate the topic Buffer from the hex string
for (const topicId in connections) {
const { topicHex } = connections[topicId];
connections[topicId] = { connections[topicId] = {
topic: b4a.from(topicHex, 'hex'), topic: b4a.from(topicHex, 'hex'),
topicHex, topicHex,
connectionName: connectionName || 'Unnamed Connection', peer: null, // Initialize additional properties
peer: null,
swarm: null, swarm: null,
}; };
} }
@ -155,44 +144,19 @@ function loadConnections() {
} }
function renderConnections() { // Save connections to cookies
console.log('[DEBUG] Rendering connections in the UI...'); function saveConnections() {
connectionList.innerHTML = ''; // Clear the current list const serializableConnections = {};
Object.keys(connections).forEach((topicId) => { for (const topicId in connections) {
const { topicHex, connectionName } = connections[topicId]; const { topic, topicHex } = connections[topicId]; // Only serialize simple properties
serializableConnections[topicId] = {
topicHex,
topic: b4a.toString(topic, 'hex'), // Convert Buffer to hex string
};
}
// Render the connection setCookie('connections', JSON.stringify(serializableConnections));
const connectionItem = document.createElement('li');
connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between';
connectionItem.dataset.topicId = topicId;
connectionItem.innerHTML = `
<span>
<span class="connection-status status-disconnected"></span>${connectionName || 'Unnamed Connection'} (${topicId})
</span>
<button class="btn btn-sm btn-danger disconnect-btn">
<i class="fas fa-plug"></i>
</button>
`;
connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId));
connectionItem.querySelector('.disconnect-btn').addEventListener('click', (e) => {
e.stopPropagation();
disconnectConnection(topicId, connectionItem);
});
connectionList.appendChild(connectionItem);
});
console.log('[DEBUG] Connections rendered successfully.');
}
function deleteConnections() {
localStorage.removeItem('connections');
} }
@ -211,19 +175,47 @@ const resetConnectionsBtn = document.createElement('button');
resetConnectionsBtn.textContent = 'Reset Connections'; resetConnectionsBtn.textContent = 'Reset Connections';
resetConnectionsBtn.className = 'btn btn-danger w-100 mt-2'; resetConnectionsBtn.className = 'btn btn-danger w-100 mt-2';
resetConnectionsBtn.addEventListener('click', () => { resetConnectionsBtn.addEventListener('click', () => {
console.log('[INFO] Resetting connections and clearing local storage.'); console.log('[INFO] Resetting connections and clearing cookies.');
Object.keys(connections).forEach((topicId) => { Object.keys(connections).forEach((topicId) => {
disconnectConnection(topicId); disconnectConnection(topicId);
}); });
deleteConnections(); deleteCookie('connections');
resetConnectionsView(); resetConnectionsView();
showWelcomePage(); showWelcomePage();
toggleResetButtonVisibility(); toggleResetButtonVisibility(); // Ensure button visibility is updated
}); });
document.getElementById('sidebar').appendChild(resetConnectionsBtn); document.getElementById('sidebar').appendChild(resetConnectionsBtn);
// Initialize the app
console.log('[INFO] Client app initialized');
// Load connections from cookies and restore them
document.addEventListener('DOMContentLoaded', () => {
const savedConnections = loadConnections();
console.log('[INFO] Restoring saved connections:', savedConnections);
// Restore saved connections
Object.keys(savedConnections).forEach((topicId) => {
let topicHex = savedConnections[topicId].topic;
// Ensure topicHex is a string
if (typeof topicHex !== 'string') {
topicHex = b4a.toString(topicHex, 'hex');
}
addConnection(topicHex);
});
if (Object.keys(connections).length > 0) {
hideWelcomePage();
} else {
showWelcomePage();
}
assertVisibility(); // Ensure visibility reflects the restored connections
});
// Show Status Indicator // Show Status Indicator
// Modify showStatusIndicator to recreate it dynamically // Modify showStatusIndicator to recreate it dynamically
function showStatusIndicator(message = 'Processing...') { function showStatusIndicator(message = 'Processing...') {
@ -366,88 +358,47 @@ addConnectionForm.addEventListener('submit', (e) => {
const topicHex = newConnectionTopic.value.trim(); const topicHex = newConnectionTopic.value.trim();
if (topicHex) { if (topicHex) {
openConnectionNameModal(topicHex); // Open the modal to ask for the connection name addConnection(topicHex);
newConnectionTopic.value = ''; newConnectionTopic.value = '';
} }
}); });
function addConnection(topicHex) {
console.log(`[DEBUG] Adding connection with topic: ${topicHex}`);
function openConnectionNameModal(topicHex) { if (Object.keys(connections).length === 0) {
const connectionNameModalElement = document.getElementById('connectionNameModal'); hideWelcomePage();
const connectionNameModal = new bootstrap.Modal(connectionNameModalElement);
const connectionNameInput = document.getElementById('connection-name-input');
const saveConnectionNameBtn = document.getElementById('save-connection-name-btn');
// Clear the input and show the modal
connectionNameInput.value = '';
connectionNameModal.show();
// Add event listener for the save button
saveConnectionNameBtn.onclick = () => {
const connectionName = connectionNameInput.value.trim();
if (!connectionName) {
showAlert('danger', 'Please enter a connection name.');
return;
} }
// Hide the modal and add the connection const topic = b4a.from(topicHex, 'hex');
connectionNameModal.hide();
addConnection(topicHex, connectionName);
};
}
function addConnection(topicHex, connectionName) {
const topicId = topicHex.substring(0, 12); const topicId = topicHex.substring(0, 12);
// Check if the connection exists connections[topicId] = { topic, peer: null, swarm: null, topicHex };
if (connections[topicId]) { saveConnections(); // Save updated connections to cookies
console.warn(`[WARN] Connection with topic ${topicHex} already exists.`);
if (!connections[topicId].swarm || !connections[topicId].peer) {
console.log(`[INFO] Reinitializing connection: ${topicHex}`);
connections[topicId].swarm = new Hyperswarm();
const topic = b4a.from(topicHex, 'hex');
connections[topicId].topic = topic;
const swarm = connections[topicId].swarm; const connectionItem = document.createElement('li');
swarm.join(topic, { client: true, server: false }); connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between';
connectionItem.dataset.topicId = topicId;
connectionItem.innerHTML = `
<span>
<span class="connection-status status-disconnected"></span>${topicId}
</span>
<button class="btn btn-sm btn-danger disconnect-btn">
<i class="fas fa-plug"></i>
</button>
`;
swarm.on('connection', (peer) => { connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId));
console.log(`[INFO] Connected to peer for topic: ${topicHex}`); connectionItem.querySelector('.disconnect-btn').addEventListener('click', (e) => {
if (connections[topicId].peer) { e.stopPropagation();
peer.destroy(); disconnectConnection(topicId, connectionItem);
return;
}
connections[topicId].peer = peer;
updateConnectionStatus(topicId, true);
peer.on('data', (data) => handlePeerData(data, topicId, peer));
peer.on('close', () => {
console.log(`[INFO] Peer disconnected for topic: ${topicId}`);
updateConnectionStatus(topicId, false);
if (window.activePeer === peer) {
window.activePeer = null;
dashboard.classList.add('hidden');
containerList.innerHTML = '';
stopStatsInterval();
}
}); });
if (!window.activePeer) { refreshContainerStats();
window.activePeer = connections[topicId].peer;
} else {
console.warn(`[WARN] Switching active peer. Current: ${window.activePeer}, New: ${connections[topicId].peer}`);
}
});
}
renderConnections(); // Ensure the sidebar list is updated
return;
}
console.log(`[DEBUG] Adding connection with topic: ${topicHex} and name: ${connectionName}`); connectionList.appendChild(connectionItem);
const topic = b4a.from(topicHex, 'hex');
const swarm = new Hyperswarm(); const swarm = new Hyperswarm();
connections[topicId] = { topic, topicHex, connectionName, peer: null, swarm }; connections[topicId].swarm = swarm;
swarm.join(topic, { client: true, server: false }); swarm.join(topic, { client: true, server: false });
@ -467,55 +418,47 @@ function addConnection(topicHex, connectionName) {
window.activePeer = null; window.activePeer = null;
dashboard.classList.add('hidden'); dashboard.classList.add('hidden');
containerList.innerHTML = ''; containerList.innerHTML = '';
stopStatsInterval(); stopStatsInterval(); // Stop stats polling
} }
}); });
if (!window.activePeer) { if (!window.activePeer) {
switchConnection(topicId); switchConnection(topicId);
} }
startStatsInterval();
}); });
saveConnections(); // Collapse the sidebar after adding a connection
renderConnections(); // Ensure the sidebar list is updated const sidebar = document.getElementById('sidebar');
const collapseSidebarBtn = document.getElementById('collapse-sidebar-btn');
if (!sidebar.classList.contains('collapsed')) {
sidebar.classList.add('collapsed');
collapseSidebarBtn.innerHTML = '&gt;';
console.log('[DEBUG] Sidebar collapsed after adding connection');
}
} }
// Initialize connections from cookies on page load
// Initialize the app
console.log('[INFO] Client app initialized');
// Load connections from cookies and restore them
// Initialize the app
console.log('[INFO] Client app initialized');
// Load connections from cookies and restore them
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
console.log('[INFO] Initializing the app...');
const savedConnections = loadConnections(); const savedConnections = loadConnections();
console.log('[INFO] Restoring saved connections:', savedConnections); console.log('[INFO] Loading saved connections:', savedConnections);
Object.keys(savedConnections).forEach((topicId) => { Object.keys(savedConnections).forEach((topicId) => {
const { topicHex, connectionName } = savedConnections[topicId]; const topicHex = savedConnections[topicId].topic;
addConnection(topicHex, connectionName); // Initialize each connection addConnection(topicHex);
}); });
if (Object.keys(connections).length > 0) { if (Object.keys(connections).length > 0) {
hideWelcomePage(); hideWelcomePage();
const firstConnection = Object.keys(connections)[0]; startStatsInterval(); // Start stats polling for active peers
switchConnection(firstConnection); // Auto-switch to the first connection
} else { } else {
showWelcomePage(); showWelcomePage();
} }
console.log('[INFO] App initialized successfully.'); assertVisibility();
}); });
function disconnectConnection(topicId, connectionItem) { function disconnectConnection(topicId, connectionItem) {
const connection = connections[topicId]; const connection = connections[topicId];
if (!connection) { if (!connection) {
@ -523,6 +466,20 @@ function disconnectConnection(topicId, connectionItem) {
return; return;
} }
// Clean up terminals
if (window.openTerminals[topicId]) {
console.log(`[INFO] Closing terminals for topic: ${topicId}`);
window.openTerminals[topicId].forEach((terminalId) => {
try {
cleanUpTerminal(terminalId);
} catch (err) {
console.error(`[ERROR] Failed to clean up terminal ${terminalId}: ${err.message}`);
}
});
delete window.openTerminals[topicId];
}
// Destroy the peer and swarm
if (connection.peer) { if (connection.peer) {
connection.peer.destroy(); connection.peer.destroy();
} }
@ -530,28 +487,35 @@ function disconnectConnection(topicId, connectionItem) {
connection.swarm.destroy(); connection.swarm.destroy();
} }
// Remove from global connections
delete connections[topicId]; delete connections[topicId];
// Save the updated connections to cookies
saveConnections(); saveConnections();
// Remove the connection item from the UI
if (connectionItem) { if (connectionItem) {
connectionList.removeChild(connectionItem); connectionList.removeChild(connectionItem);
} }
// Reset the connection title if this was the active peer
if (window.activePeer === connection.peer) { if (window.activePeer === connection.peer) {
window.activePeer = null; window.activePeer = null;
const connectionTitle = document.getElementById('connection-title'); const connectionTitle = document.getElementById('connection-title');
if (connectionTitle) { if (connectionTitle) {
connectionTitle.textContent = 'Choose a Connection'; connectionTitle.textContent = 'Choose a Connection'; // Reset the title
} }
const dashboard = document.getElementById('dashboard'); const dashboard = document.getElementById('dashboard');
if (dashboard) { if (dashboard) {
dashboard.classList.add('hidden'); dashboard.classList.add('hidden');
} }
resetContainerList();
resetContainerList(); // Clear containers
} }
renderConnections(); // Ensure the sidebar list is updated // Show welcome page if no connections remain
if (Object.keys(connections).length === 0) { if (Object.keys(connections).length === 0) {
showWelcomePage(); showWelcomePage();
} }
@ -601,54 +565,36 @@ function resetConnectionsView() {
// Update connection status // Update connection status
function updateConnectionStatus(topicId, isConnected) { function updateConnectionStatus(topicId, isConnected) {
if (!connections[topicId]) {
console.error(`[ERROR] No connection found for topic: ${topicId}`);
return;
}
const connectionItem = document.querySelector(`[data-topic-id="${topicId}"] .connection-status`); const connectionItem = document.querySelector(`[data-topic-id="${topicId}"] .connection-status`);
if (connectionItem) { if (connectionItem) {
connectionItem.className = `connection-status ${isConnected ? 'status-connected' : 'status-disconnected'}`; connectionItem.className = `connection-status ${isConnected ? 'status-connected' : 'status-disconnected'}`;
} }
console.log(`[DEBUG] Connection ${topicId} status updated to: ${isConnected ? 'connected' : 'disconnected'}`);
} }
setInterval(() => {
Object.keys(connections).forEach((topicId) => {
const connection = connections[topicId];
if (connection.peer && !connection.peer.destroyed) {
updateConnectionStatus(topicId, true);
} else {
updateConnectionStatus(topicId, false);
}
});
}, 1000); // Adjust interval as needed
// Switch between connections // Switch between connections
function switchConnection(topicId) { function switchConnection(topicId) {
const connection = connections[topicId]; const connection = connections[topicId];
if (!connection || !connection.peer) { if (!connection || !connection.peer) {
console.warn(`[WARN] No active peer for connection: ${topicId}`); console.error('[ERROR] No connection found or no active peer.');
return; // Skip switching if no active peer is found showWelcomePage();
stopStatsInterval(); // Stop stats interval if no active peer
return;
} }
console.log(`[INFO] Switching to connection: ${topicId}`); // Update the active peer
window.activePeer = connection.peer; window.activePeer = connection.peer;
// Clear container list before loading new data
resetContainerList(); resetContainerList();
const connectionTitle = document.getElementById('connection-title');
if (connectionTitle) {
connectionTitle.textContent = connection.connectionName || 'Unnamed Connection';
}
hideWelcomePage(); console.log(`[INFO] Switched to connection: ${topicId}`);
// Start the stats interval
startStatsInterval(); startStatsInterval();
sendCommand('listContainers');
}
sendCommand('listContainers'); // Request containers for the new connection
}
// Attach switchConnection to the global window object // Attach switchConnection to the global window object

View File

@ -449,29 +449,6 @@
</div> </div>
</div> </div>
<!-- Connection Name Modal -->
<div class="modal fade" id="connectionNameModal" tabindex="-1" aria-labelledby="connectionNameModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="connectionNameModalLabel">Enter Connection Name</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="connection-name-input" class="form-label">Connection Name</label>
<input type="text" class="form-control" id="connection-name-input" placeholder="Enter a name for the connection">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" id="save-connection-name-btn" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
<!-- Status Indicator Overlay --> <!-- Status Indicator Overlay -->
<div id="status-indicator" <div id="status-indicator"
class="position-fixed top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75" class="position-fixed top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75"
@ -484,8 +461,6 @@
</div> </div>
</div> </div>
<!-- Alert Container --> <!-- Alert Container -->
<div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3" <div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3"
style="z-index: 1051; max-width: 90%;"></div> style="z-index: 1051; max-width: 90%;"></div>