diff --git a/web_server.py b/web_server.py index c3d67a2c..62071db0 100644 --- a/web_server.py +++ b/web_server.py @@ -6125,18 +6125,22 @@ def get_mirrored_playlists_list(): @app.route('/api/setup/status', methods=['GET']) def setup_status_endpoint(): """Check if first-run setup has been completed.""" - # Consider setup incomplete if no slskd URL and no download source configured - slskd_url = config_manager.get('soulseek.slskd_url', '') + # The setup wizard sets this flag when completed. download_source.mode is only + # set by user action (wizard or settings page), never by config.json defaults. + setup_done = config_manager.get('setup.completed', False) download_mode = config_manager.get('download_source.mode', '') - transfer_path = config_manager.get('soulseek.transfer_path', '') - has_any_config = bool(slskd_url) or bool(download_mode) or bool(transfer_path) + # Either the explicit flag or a user-configured download source means setup is done + has_user_config = bool(setup_done) or bool(download_mode) return jsonify({ - "setup_complete": has_any_config, - "has_download_source": bool(download_mode), - "has_slskd": bool(slskd_url), - "has_transfer_path": bool(transfer_path), + "setup_complete": has_user_config, }) +@app.route('/api/setup/complete', methods=['POST']) +def setup_complete_endpoint(): + """Mark first-run setup as completed.""" + config_manager.set('setup.completed', True) + return jsonify({"success": True}) + @app.route('/api/test-connection', methods=['POST']) def test_connection_endpoint(): data = request.get_json() diff --git a/webui/index.html b/webui/index.html index d77e8e94..e8550438 100644 --- a/webui/index.html +++ b/webui/index.html @@ -7351,6 +7351,7 @@ + @@ -7376,7 +7377,6 @@ - \ No newline at end of file diff --git a/webui/static/script.js b/webui/static/script.js index e9b08aca..86cadaac 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -2616,6 +2616,36 @@ async function checkAdminPinRequired() { document.addEventListener('DOMContentLoaded', async function () { console.log('SoulSync WebUI initializing...'); + // Check if first-run setup wizard should be shown + const params = new URLSearchParams(window.location.search); + const forceSetup = params.get('setup') === '1'; + let showWizard = forceSetup; + + if (!forceSetup) { + try { + const setupResp = await fetch('/api/setup/status'); + const setupData = await setupResp.json(); + if (!setupData.setup_complete) { + showWizard = true; + localStorage.removeItem('soulsync_setup_complete'); + } + } catch (e) { + console.warn('Setup status check failed, continuing normal init:', e); + } + } + + if (showWizard && typeof openSetupWizard === 'function') { + window._onSetupWizardComplete = function () { + _continueAppInit(); + }; + openSetupWizard(); + return; // Defer init until wizard closes + } + + _continueAppInit(); +}); + +async function _continueAppInit() { // Initialize profile management UI handlers initProfileManagement(); @@ -2627,7 +2657,7 @@ document.addEventListener('DOMContentLoaded', async function () { } initApp(); -}); +} function initApp() { // Initialize components diff --git a/webui/static/setup-wizard.js b/webui/static/setup-wizard.js index 875a4c46..6866e8bb 100644 --- a/webui/static/setup-wizard.js +++ b/webui/static/setup-wizard.js @@ -62,6 +62,16 @@ function openSetupWizard() { function closeSetupWizard() { const overlay = document.getElementById('setup-wizard-overlay'); if (overlay) overlay.style.display = 'none'; + + // Mark as complete so it doesn't show again (server + client) + localStorage.setItem('soulsync_setup_complete', 'true'); + fetch('/api/setup/complete', { method: 'POST' }).catch(() => {}); + + // Continue app initialization if wizard was shown on first run + if (typeof window._onSetupWizardComplete === 'function') { + window._onSetupWizardComplete(); + window._onSetupWizardComplete = null; + } } // ---- Navigation ---- @@ -957,12 +967,26 @@ async function _wizardFinish() { console.error('Wizard final save error:', e); } + // Mark setup complete on both server and client + try { + await fetch('/api/setup/complete', { method: 'POST' }); + } catch (e) { + console.error('Failed to mark setup complete on server:', e); + } localStorage.setItem('soulsync_setup_complete', 'true'); - closeSetupWizard(); + + const overlay = document.getElementById('setup-wizard-overlay'); + if (overlay) overlay.style.display = 'none'; // Reload settings into the main UI if (typeof loadSettings === 'function') loadSettings(); if (typeof showToast === 'function') showToast('Setup complete — welcome to SoulSync!', 'success'); + + // Continue app initialization if wizard was shown on first run + if (typeof window._onSetupWizardComplete === 'function') { + window._onSetupWizardComplete(); + window._onSetupWizardComplete = null; + } } // ---- Utility ---- @@ -973,18 +997,8 @@ function _escHtml(str) { } // ---- Dev Trigger ---- -// Open wizard with: openSetupWizard() from console, or ?setup=1 URL param - -(function _checkWizardAutoOpen() { - const params = new URLSearchParams(window.location.search); - if (params.get('setup') === '1') { - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => setTimeout(openSetupWizard, 300)); - } else { - setTimeout(openSetupWizard, 300); - } - } -})(); +// Open wizard manually: openSetupWizard() from console, or ?setup=1 URL param +// First-run auto-detection is handled in script.js DOMContentLoaded // Expose globally window.openSetupWizard = openSetupWizard;