class RekordboxSyncRenderer { constructor() { this.initializeElements(); this.setupEventListeners(); this.setupElectronListeners(); this.loadInitialState(); } /** * Initialize DOM elements */ initializeElements() { // Buttons this.exportEnvBtn = document.getElementById('exportEnvBtn'); // Status elements this.syncStatusElement = document.getElementById('syncStatus'); this.filesSyncedElement = document.getElementById('filesSynced'); this.syncDetailsElement = document.getElementById('syncDetails'); this.syncModeElement = document.getElementById('syncMode'); // Activity log this.activityLogElement = document.getElementById('activityLog'); // Configuration elements this.s3EndpointInput = document.getElementById('s3Endpoint'); this.s3AccessKeyInput = document.getElementById('s3AccessKey'); this.s3SecretKeyInput = document.getElementById('s3SecretKey'); this.s3BucketInput = document.getElementById('s3Bucket'); this.s3RegionInput = document.getElementById('s3Region'); this.localPathInput = document.getElementById('localPath'); this.syncIntervalInput = document.getElementById('syncInterval'); // Save button this.saveConfigBtn = document.getElementById('saveConfigBtn'); } /** * Setup event listeners for UI elements */ setupEventListeners() { // Sync control buttons if (this.startBtn) { this.startBtn.addEventListener('click', () => this.startSync()); } if (this.stopBtn) { this.stopBtn.addEventListener('click', () => this.stopSync()); } if (this.exportEnvBtn) { this.exportEnvBtn.addEventListener('click', () => this.exportToEnv()); } // Configuration save button if (this.saveConfigBtn) { this.saveConfigBtn.addEventListener('click', () => this.saveConfiguration()); } // Force sync button const forceSyncBtn = document.getElementById('forceSyncBtn'); if (forceSyncBtn) { forceSyncBtn.addEventListener('click', () => this.forceFullSync()); } // Immediate sync button const immediateSyncBtn = document.getElementById('immediateSyncBtn'); if (immediateSyncBtn) { immediateSyncBtn.addEventListener('click', () => this.triggerImmediateSync()); } } /** * Setup Electron IPC listeners */ setupElectronListeners() { if (!window.electronAPI) { console.error('â Electron API not available'); return; } // Sync status updates window.electronAPI.on('sync-status-changed', (status) => { this.updateSyncStatus(status); }); // File change events window.electronAPI.on('file-changed', (event) => { console.log('đ File changed:', event); this.addActivityLog('info', `File changed: ${event.path}`); }); window.electronAPI.on('file-added', (event) => { console.log('â File added:', event); this.addActivityLog('success', `File added: ${event.path}`); }); window.electronAPI.on('file-removed', (event) => { console.log('â File removed:', event); this.addActivityLog('info', `File removed: ${event.path}`); }); // Sync operation updates window.electronAPI.on('sync-operation-started', (operation) => { console.log('đ Operation started:', operation); this.addActivityLog('info', `Started ${operation.type}: ${operation.s3Key || operation.localPath}`); }); window.electronAPI.on('sync-operation-completed', (operation) => { console.log('â Operation completed:', operation); this.addActivityLog('success', `Completed ${operation.type}: ${operation.s3Key || operation.localPath}`); }); window.electronAPI.on('sync-operation-failed', (operation) => { console.log('â Operation failed:', operation); this.addActivityLog('error', `Failed ${operation.type}: ${operation.s3Key || operation.localPath} - ${operation.error || 'Unknown error'}`); }); // Sync lifecycle events window.electronAPI.on('sync-started', (type) => { console.log('đ Sync started:', type); this.addActivityLog('info', `Sync started: ${type}`); }); window.electronAPI.on('sync-completed', (type) => { console.log('đ Sync completed:', type); this.addActivityLog('success', `Sync completed: ${type}`); }); window.electronAPI.on('sync-error', (error) => { console.log('đĨ Sync error:', error); this.addActivityLog('error', `Sync error: ${error.message || 'Unknown error'}`); // Update UI to show error state if (this.syncStatusElement) { this.syncStatusElement.textContent = 'Error - Sync failed'; this.syncStatusElement.className = 'status-value error'; } // Re-enable start button on error if (this.startBtn) this.startBtn.disabled = false; if (this.stopBtn) this.stopBtn.disabled = true; }); // Engine events window.electronAPI.on('sync-engine-started', () => { console.log('â Sync engine started'); this.addActivityLog('success', 'Sync engine started'); }); window.electronAPI.on('sync-engine-stopped', () => { console.log('âšī¸ Sync engine stopped'); this.addActivityLog('info', 'Sync engine stopped'); }); // MinIO output events window.electronAPI.on('aws-output', (output) => { console.log('đ AWS S3 output received:', output); this.addActivityLog('info', `AWS S3 ${output.direction}: ${output.output}`); }); // File change events window.electronAPI.on('file-changed', (event) => { console.log('đ File changed:', event); this.addActivityLog('info', `File changed: ${event.path}`); }); } /** * Load initial application state */ async loadInitialState() { try { // Load configuration const config = await window.electronAPI.invoke('config:get'); this.populateConfigurationForm(config); // Load current sync status const status = await window.electronAPI.invoke('sync:get-status'); if (status) { this.updateSyncStatus(status); } } catch (error) { console.error('â Failed to load initial state:', error); this.addActivityLog('error', `Failed to load initial state: ${error.message || 'Unknown error'}`); } } /** * Force full sync */ async forceFullSync() { try { this.addActivityLog('info', 'Forcing full sync...'); await window.electronAPI.invoke('sync:force-full'); this.addActivityLog('success', 'Full sync initiated'); } catch (error) { console.error('â Failed to force full sync:', error); this.addActivityLog('error', `Failed to force full sync: ${error.message || 'Unknown error'}`); } } /** * Update sync status display */ updateSyncStatus(status) { if (!status) return; // Update main status with concise information if (this.syncStatusElement) { if (status.isRunning) { let statusText = 'Running'; // Add current phase information (shortened) if (status.currentPhase) { const phase = status.currentPhase === 'watching' ? 'Watching' : status.currentPhase === 'downloading' ? 'Downloading' : status.currentPhase === 'uploading' ? 'Uploading' : status.currentPhase === 'completed' ? 'Complete' : status.currentPhase; statusText = phase; } this.syncStatusElement.textContent = statusText; this.syncStatusElement.className = 'status-value running'; } else { this.syncStatusElement.textContent = 'Stopped'; this.syncStatusElement.className = 'status-value stopped'; } } // Also update the force sync button state const forceSyncBtn = document.getElementById('forceSyncBtn'); if (forceSyncBtn) { forceSyncBtn.disabled = status.isRunning; } } // Update files synced count with more detail if (this.filesSyncedElement) { console.log('đ Updating files synced element with:', { actualFileCount: status.actualFileCount, statsTotalFiles: status.stats?.totalFilesSynced }); // Use actual file count from main process if available if (status.actualFileCount !== undefined) { const text = `${status.actualFileCount} files in local folder`; console.log('đ Setting files synced text to:', text); this.filesSyncedElement.textContent = text; } else if (status.stats && status.stats.totalFilesSynced > 0) { const text = `${status.stats.totalFilesSynced} files in local folder`; console.log('đ Setting files synced text to:', text); this.filesSyncedElement.textContent = text; } else { console.log('đ Setting files synced text to: 0 files'); this.filesSyncedElement.textContent = '0 files'; } } else { console.warn('â ī¸ filesSyncedElement not found!'); } // Update detailed status this.updateDetailedStatus(status); // Update phase and progress if (status.currentPhase && status.progressMessage) { this.addActivityLog('info', `${status.currentPhase}: ${status.progressMessage}`); } // Log progress changes to reduce spam if (status.progress && status.progress.percent > 0) { const currentProgress = status.progress.percent; if (!this.lastLoggedProgress || Math.abs(currentProgress - this.lastLoggedProgress) >= 10) { const progressText = `Progress: ${currentProgress}% - ${status.progress.message || 'Syncing files...'}`; this.addActivityLog('info', progressText); this.lastLoggedProgress = currentProgress; } } // Add file count updates (but only when they change significantly) if (status.stats && status.stats.totalFilesSynced > 0) { const currentFileCount = status.stats.totalFilesSynced; if (!this.lastLoggedFileCount || Math.abs(currentFileCount - this.lastLoggedFileCount) >= 50) { const fileText = `Local files: ${currentFileCount} (${status.stats.filesDownloaded} downloaded, ${status.stats.filesUploaded} uploaded)`; this.addActivityLog('success', fileText); this.lastLoggedFileCount = currentFileCount; } } } /** * Update detailed status display */ updateDetailedStatus(status) { if (!this.syncDetailsElement) return; let detailsHTML = ''; if (status.pendingCount > 0) { detailsHTML += `