var _inited = false; document.addEventListener('tabChanged', function(e) { if (e.detail.tab !== 'settings' || _inited) return; _inited = true; function setNextcloudConnectedStatus(url, user) { var statusEl = document.getElementById('nc-status'); if (!statusEl) return; statusEl.textContent = 'Connected to '; var strong = document.createElement('strong'); strong.textContent = url || ''; statusEl.appendChild(strong); statusEl.appendChild(document.createTextNode(' as ' + (user || ''))); } function loadNextcloudStatus() { fetch('/api/auth/me', { headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.user && data.user.nextcloud_url) { setNextcloudConnectedStatus(data.user.nextcloud_url, data.user.nextcloud_user); document.getElementById('nc-url').value = data.user.nextcloud_url; document.getElementById('nc-user').value = data.user.nextcloud_user; document.getElementById('btn-nc-disconnect').classList.remove('hidden'); } else { document.getElementById('nc-status').textContent = 'Not connected'; document.getElementById('btn-nc-disconnect').classList.add('hidden'); } }); } window.loadNextcloudStatus = loadNextcloudStatus; document.getElementById('btn-nc-connect').addEventListener('click', function() { var url = document.getElementById('nc-url').value.trim().replace(/\/+$/, ''); var user = document.getElementById('nc-user').value.trim(); var pass = document.getElementById('nc-pass').value.trim(); if (!url || !user || !pass) { showToast('Fill all Nextcloud fields', 'error'); return; } showLoading('Connecting to Nextcloud...'); fetch('/api/nextcloud/connect', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ nextcloudUrl: url, username: user, appPassword: pass }) }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); if (data.success) { showToast(data.message, 'success'); loadNextcloudStatus(); document.getElementById('nc-pass').value = ''; } else { showToast(data.error || 'Connection failed', 'error'); } }) .catch(function(err) { hideLoading(); showToast(err.message, 'error'); }); }); document.getElementById('btn-nc-disconnect').addEventListener('click', function() { fetch('/api/nextcloud/disconnect', { method: 'POST', headers: getAuthHeaders() }) .then(function() { showToast('Disconnected', 'info'); loadNextcloudStatus(); }); }); // ── Sign in with Nextcloud ──────────────────────────────────────────── // Nextcloud's own login flow. The tab is opened from the click itself, before // any await, or a popup blocker eats it — the request that fetches the URL is // allowed to finish afterwards and point the already-open tab at it. var pollTimer = null; function stopPolling(message, tone) { if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } var status = document.getElementById('nc-login-flow-status'); if (status) { status.textContent = message || ''; status.style.color = tone === 'bad' ? 'var(--red)' : tone === 'good' ? 'var(--green)' : 'var(--g600)'; } } document.getElementById('btn-nc-login-flow').addEventListener('click', function () { var url = (document.getElementById('nc-url').value || '').trim(); if (!url) { showToast('Enter your Nextcloud address first', 'error'); return; } var tab = window.open('', '_blank'); // claimed while the click is still trusted stopPolling('Opening Nextcloud…'); fetch('/api/nextcloud/login-flow/start', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ nextcloudUrl: url }) }) .then(function (r) { return r.json(); }) .then(function (data) { if (!data.success) throw new Error(data.error || 'Could not start sign-in'); if (tab) tab.location.href = data.loginUrl; else window.open(data.loginUrl, '_blank'); stopPolling('Waiting for you to finish signing in…'); var until = Date.now() + 10 * 60 * 1000; pollTimer = setInterval(function () { if (Date.now() > until) return stopPolling('Sign-in timed out. Try again.', 'bad'); fetch('/api/nextcloud/login-flow/poll', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ handle: data.handle }) }) .then(function (r) { return r.json(); }) .then(function (result) { if (result.connected) { stopPolling('Connected as ' + result.username + '.', 'good'); loadNextcloudStatus(); showToast('Nextcloud connected', 'success'); } else if (!result.success) { stopPolling(result.error || 'Sign-in failed.', 'bad'); } }) .catch(function () { /* a dropped poll is not a failed sign-in */ }); }, 3000); }) .catch(function (err) { if (tab) tab.close(); stopPolling(err.message, 'bad'); }); }); console.log('✅ Nextcloud module loaded'); });