refactor(electron): major ui refactor into modules
This commit is contained in:
339
electron/renderer/event-handlers.js
Normal file
339
electron/renderer/event-handlers.js
Normal file
@@ -0,0 +1,339 @@
|
||||
// Event handlers module for the 4DSTAR Bundle Manager
|
||||
// Extracted from renderer.js to centralize event listener setup and management
|
||||
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
// Import dependencies (these will be injected when integrated)
|
||||
let stateManager, domManager, bundleOperations, fillWorkflow, uiComponents;
|
||||
|
||||
// --- EVENT LISTENERS SETUP ---
|
||||
function setupEventListeners() {
|
||||
const elements = domManager.getElements();
|
||||
|
||||
// Theme updates
|
||||
ipcRenderer.on('theme-updated', (event, { shouldUseDarkColors }) => {
|
||||
document.body.classList.toggle('dark-mode', shouldUseDarkColors);
|
||||
});
|
||||
|
||||
// Sidebar navigation
|
||||
elements.openBundleBtn.addEventListener('click', bundleOperations.handleOpenBundle);
|
||||
elements.createBundleBtn.addEventListener('click', () => {
|
||||
// TODO: Replace with modal
|
||||
domManager.showView('create-bundle-form');
|
||||
domManager.showModal('Not Implemented', 'The create bundle form will be moved to a modal dialog.');
|
||||
});
|
||||
|
||||
// Tab navigation
|
||||
elements.tabLinks.forEach(link => {
|
||||
link.addEventListener('click', () => domManager.switchTab(link.dataset.tab));
|
||||
});
|
||||
|
||||
// Modal close button
|
||||
elements.modalCloseBtn.addEventListener('click', domManager.hideModal);
|
||||
|
||||
// Bundle actions
|
||||
elements.signBundleBtn.addEventListener('click', () => {
|
||||
checkSignatureAndWarn(bundleOperations.handleSignBundle, 'signing');
|
||||
});
|
||||
elements.validateBundleBtn.addEventListener('click', bundleOperations.handleValidateBundle);
|
||||
elements.clearBundleBtn.addEventListener('click', () => {
|
||||
checkSignatureAndWarn(bundleOperations.handleClearBundle, 'clearing binaries');
|
||||
});
|
||||
elements.saveMetadataBtn.addEventListener('click', uiComponents.showSaveOptionsModal);
|
||||
elements.overwriteBundleBtn.addEventListener('click', () => handleSaveMetadata(false));
|
||||
elements.saveAsNewBtn.addEventListener('click', () => handleSaveMetadata(true));
|
||||
|
||||
// Signature warning modal event listeners
|
||||
elements.signatureWarningCancel.addEventListener('click', () => {
|
||||
elements.signatureWarningModal.classList.add('hidden');
|
||||
stateManager.clearPendingOperation();
|
||||
});
|
||||
|
||||
elements.signatureWarningContinue.addEventListener('click', () => {
|
||||
elements.signatureWarningModal.classList.add('hidden');
|
||||
const pendingOperation = stateManager.getPendingOperation();
|
||||
if (pendingOperation) {
|
||||
pendingOperation();
|
||||
stateManager.clearPendingOperation();
|
||||
}
|
||||
});
|
||||
|
||||
// Load fillable targets button
|
||||
elements.loadFillableTargetsBtn.addEventListener('click', async () => {
|
||||
await fillWorkflow.loadFillableTargets();
|
||||
});
|
||||
|
||||
// Category navigation
|
||||
setupCategoryNavigation();
|
||||
|
||||
// Info modal setup
|
||||
setupInfoModal();
|
||||
}
|
||||
|
||||
// Check if bundle is signed and show warning before bundle-modifying operations
|
||||
function checkSignatureAndWarn(operation, operationName = 'operation') {
|
||||
const currentBundle = stateManager.getCurrentBundle();
|
||||
const elements = domManager.getElements();
|
||||
|
||||
if (currentBundle &&
|
||||
currentBundle.report &&
|
||||
currentBundle.report.signature &&
|
||||
currentBundle.report.signature.status &&
|
||||
['TRUSTED', 'UNTRUSTED'].includes(currentBundle.report.signature.status)) {
|
||||
|
||||
// Bundle is signed, show warning
|
||||
stateManager.setPendingOperation(operation);
|
||||
elements.signatureWarningModal.classList.remove('hidden');
|
||||
} else {
|
||||
// Bundle is not signed, proceed directly
|
||||
operation();
|
||||
}
|
||||
}
|
||||
|
||||
// Setup category navigation
|
||||
function setupCategoryNavigation() {
|
||||
const categoryItems = document.querySelectorAll('.category-item');
|
||||
const secondarySidebar = document.getElementById('secondary-sidebar');
|
||||
|
||||
categoryItems.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const category = item.dataset.category;
|
||||
|
||||
// Update active states
|
||||
categoryItems.forEach(cat => cat.classList.remove('active'));
|
||||
item.classList.add('active');
|
||||
|
||||
// Show/hide secondary sidebar based on category
|
||||
if (category === 'home') {
|
||||
if (secondarySidebar) {
|
||||
secondarySidebar.style.display = 'none';
|
||||
}
|
||||
showCategoryHomeScreen('home');
|
||||
} else {
|
||||
if (secondarySidebar) {
|
||||
secondarySidebar.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show appropriate sidebar content
|
||||
const sidebarContents = document.querySelectorAll('.sidebar-content');
|
||||
sidebarContents.forEach(content => {
|
||||
if (content.dataset.category === category) {
|
||||
content.classList.remove('hidden');
|
||||
} else {
|
||||
content.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Show category home screen
|
||||
showCategoryHomeScreen(category);
|
||||
}
|
||||
|
||||
// Update welcome screen
|
||||
updateWelcomeScreen(category);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update welcome screen based on selected category
|
||||
function updateWelcomeScreen(category) {
|
||||
const welcomeTitles = {
|
||||
'home': 'Welcome to 4DSTAR',
|
||||
'libplugin': 'Welcome to libplugin',
|
||||
'libconstants': 'Welcome to libconstants',
|
||||
'opat': 'Welcome to OPAT Core',
|
||||
'serif': 'Welcome to SERiF Libraries'
|
||||
};
|
||||
|
||||
const welcomeMessages = {
|
||||
'home': 'Select a category from the sidebar to get started.',
|
||||
'libplugin': 'Bundle management tools for 4DSTAR plugins.',
|
||||
'libconstants': 'Constants tools coming soon...',
|
||||
'opat': 'OPAT tools coming soon...',
|
||||
'serif': 'SERiF tools coming soon...'
|
||||
};
|
||||
|
||||
const welcomeTitle = document.querySelector('.welcome-title');
|
||||
const welcomeMessage = document.querySelector('.welcome-message');
|
||||
|
||||
if (welcomeTitle) welcomeTitle.textContent = welcomeTitles[category] || welcomeTitles['home'];
|
||||
if (welcomeMessage) welcomeMessage.textContent = welcomeMessages[category] || welcomeMessages['home'];
|
||||
}
|
||||
|
||||
// Show appropriate home screen based on selected category
|
||||
function showCategoryHomeScreen(category) {
|
||||
const views = [
|
||||
'welcome-screen', 'libplugin-home', 'opat-home',
|
||||
'libconstants-home', 'serif-home', 'opat-view', 'libplugin-view',
|
||||
'bundle-view', 'create-bundle-form'
|
||||
];
|
||||
|
||||
// Hide all views
|
||||
views.forEach(viewId => {
|
||||
const view = document.getElementById(viewId);
|
||||
if (view) view.classList.add('hidden');
|
||||
});
|
||||
|
||||
// Show appropriate view
|
||||
const viewMap = {
|
||||
'home': 'welcome-screen',
|
||||
'libplugin': 'libplugin-home',
|
||||
'opat': 'opat-home',
|
||||
'libconstants': 'libconstants-home',
|
||||
'serif': 'serif-home'
|
||||
};
|
||||
|
||||
const viewId = viewMap[category] || 'welcome-screen';
|
||||
const view = document.getElementById(viewId);
|
||||
if (view) view.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Setup info modal
|
||||
function setupInfoModal() {
|
||||
const infoBtn = document.getElementById('info-btn');
|
||||
const infoModal = document.getElementById('info-modal');
|
||||
const closeInfoModalBtn = document.getElementById('close-info-modal');
|
||||
const infoTabLinks = document.querySelectorAll('.info-tab-link');
|
||||
const infoTabPanes = document.querySelectorAll('.info-tab-pane');
|
||||
|
||||
if (infoBtn) {
|
||||
infoBtn.addEventListener('click', () => {
|
||||
if (infoModal) infoModal.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
if (closeInfoModalBtn) {
|
||||
closeInfoModalBtn.addEventListener('click', hideInfoModal);
|
||||
}
|
||||
|
||||
// Info tab navigation
|
||||
infoTabLinks.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const targetTab = link.dataset.tab;
|
||||
|
||||
// Update active states
|
||||
infoTabLinks.forEach(l => l.classList.remove('active'));
|
||||
infoTabPanes.forEach(p => p.classList.remove('active'));
|
||||
|
||||
link.classList.add('active');
|
||||
const targetPane = document.getElementById(targetTab);
|
||||
if (targetPane) targetPane.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// External link handling
|
||||
const githubLink = document.getElementById('github-link');
|
||||
if (githubLink) {
|
||||
githubLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
ipcRenderer.invoke('open-external-url', 'https://github.com/tboudreaux/4DSTAR');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Hide info modal - make it globally accessible
|
||||
function hideInfoModal() {
|
||||
const infoModal = document.getElementById('info-modal');
|
||||
if (infoModal) infoModal.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Handle save metadata with option for save as new
|
||||
async function handleSaveMetadata(saveAsNew = false) {
|
||||
const currentBundlePath = stateManager.getCurrentBundlePath();
|
||||
if (!currentBundlePath) return;
|
||||
|
||||
const elements = domManager.getElements();
|
||||
|
||||
// Collect updated metadata from form fields
|
||||
const inputs = document.querySelectorAll('.field-input');
|
||||
const updatedManifest = {};
|
||||
|
||||
inputs.forEach(input => {
|
||||
const fieldName = input.dataset.field;
|
||||
const value = input.value.trim();
|
||||
if (value) {
|
||||
updatedManifest[fieldName] = value;
|
||||
}
|
||||
});
|
||||
|
||||
let targetPath = currentBundlePath;
|
||||
|
||||
if (saveAsNew) {
|
||||
// Show save dialog for new bundle
|
||||
const saveResult = await ipcRenderer.invoke('show-save-dialog', {
|
||||
filters: [{ name: 'Fbundle Archives', extensions: ['fbundle'] }],
|
||||
defaultPath: currentBundlePath.replace(/\.fbundle$/, '_modified.fbundle')
|
||||
});
|
||||
|
||||
if (saveResult.canceled || !saveResult.filePath) {
|
||||
uiComponents.hideSaveOptionsModal();
|
||||
return;
|
||||
}
|
||||
|
||||
targetPath = saveResult.filePath;
|
||||
|
||||
// Copy original bundle to new location first
|
||||
try {
|
||||
const copyResult = await ipcRenderer.invoke('copy-file', {
|
||||
source: currentBundlePath,
|
||||
destination: targetPath
|
||||
});
|
||||
|
||||
if (!copyResult.success) {
|
||||
domManager.showModal('Copy Error', `Failed to copy bundle: ${copyResult.error}`);
|
||||
uiComponents.hideSaveOptionsModal();
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
domManager.showModal('Copy Error', `Failed to copy bundle: ${error.message}`);
|
||||
uiComponents.hideSaveOptionsModal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Save metadata to target bundle
|
||||
domManager.showSpinner();
|
||||
const result = await ipcRenderer.invoke('edit-bundle', {
|
||||
bundlePath: targetPath,
|
||||
updatedManifest: updatedManifest
|
||||
});
|
||||
domManager.hideSpinner();
|
||||
|
||||
if (result.success) {
|
||||
domManager.showModal('Success', 'Bundle metadata saved successfully. Reloading...');
|
||||
|
||||
// Update current bundle path if we saved as new
|
||||
if (saveAsNew) {
|
||||
stateManager.setBundleState(stateManager.getCurrentBundle(), targetPath);
|
||||
}
|
||||
|
||||
await bundleOperations.reloadCurrentBundle();
|
||||
uiComponents.hideSaveOptionsModal();
|
||||
domManager.hideModal();
|
||||
} else {
|
||||
domManager.showModal('Save Error', `Failed to save metadata: ${result.error}`);
|
||||
}
|
||||
|
||||
uiComponents.hideSaveOptionsModal();
|
||||
}
|
||||
|
||||
// Initialize dependencies (called when module is loaded)
|
||||
function initializeDependencies(deps) {
|
||||
stateManager = deps.stateManager;
|
||||
domManager = deps.domManager;
|
||||
bundleOperations = deps.bundleOperations;
|
||||
fillWorkflow = deps.fillWorkflow;
|
||||
uiComponents = deps.uiComponents;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initializeDependencies,
|
||||
setupEventListeners,
|
||||
checkSignatureAndWarn,
|
||||
setupCategoryNavigation,
|
||||
updateWelcomeScreen,
|
||||
showCategoryHomeScreen,
|
||||
setupInfoModal,
|
||||
hideInfoModal,
|
||||
handleSaveMetadata
|
||||
};
|
||||
Reference in New Issue
Block a user