haufen sachen
This commit is contained in:
1 parent
416ca9e854
commit
e6243701a3
20 files changed
+1562
-116
No files matched your search
+70
-22
@@ -21,7 +21,7 @@ const CAL_COLORS = ['#38bdf8', '#8b5cf6', '#fb7185', '#fbbf24', '#4ade80', '#fb9
|
||||
let calendarConfigs = {}; // slug → { color, icon }
|
||||
const CAL_CONFIG_KEY = 'hyprcrd-cal-configs';
|
||||
|
||||
const overlay = $('caldav-settings');
|
||||
const settingsOverlay = $('settings-overlay');
|
||||
const urlInput = $('caldav-url');
|
||||
const userInput = $('caldav-user');
|
||||
const passInput = $('caldav-pass');
|
||||
@@ -30,12 +30,15 @@ const syncStatus = $('caldav-sync-status');
|
||||
const logEl = $('caldav-log');
|
||||
|
||||
export function openCaldavSettings() {
|
||||
overlay?.classList.add('open');
|
||||
// Open settings overlay on sync tab
|
||||
settingsOverlay?.classList.add('open');
|
||||
settingsOverlay?.querySelectorAll('.settings-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === 'sync'));
|
||||
settingsOverlay?.querySelectorAll('[id^="settings-panel-"]').forEach(p => p.classList.toggle('hidden', p.id !== 'settings-panel-sync'));
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
export function closeCaldavSettings() {
|
||||
overlay?.classList.remove('open');
|
||||
settingsOverlay?.classList.remove('open');
|
||||
}
|
||||
|
||||
function loadCalConfigs() {
|
||||
@@ -97,7 +100,7 @@ function renderCalList(calendars) {
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
export async function loadSettings() {
|
||||
const s = await api('api/caldav/settings');
|
||||
if (!s) return;
|
||||
if (urlInput) urlInput.value = s.url || '';
|
||||
@@ -115,8 +118,6 @@ async function loadSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
$('caldav-close')?.addEventListener('click', closeCaldavSettings);
|
||||
overlay?.addEventListener('click', e => { if (e.target === overlay) closeCaldavSettings(); });
|
||||
|
||||
$('caldav-test')?.addEventListener('click', async () => {
|
||||
testStatus.textContent = 'testing...';
|
||||
@@ -149,6 +150,19 @@ $('caldav-save')?.addEventListener('click', async () => {
|
||||
});
|
||||
|
||||
$('caldav-sync-now')?.addEventListener('click', () => triggerSync());
|
||||
$('notes-reset')?.addEventListener('click', async () => {
|
||||
if (syncStatus) { syncStatus.textContent = 'resetting notes...'; syncStatus.style.color = 'var(--t-dim)'; }
|
||||
const res = await fetch('api/notes/reset', {
|
||||
method: 'POST', headers: { 'X-CSRF-Token': getCsrf() },
|
||||
}).then(r => r.json()).catch(() => null);
|
||||
if (res?.ok) {
|
||||
const s = res.stats || {};
|
||||
if (syncStatus) { syncStatus.textContent = `reset: ${res.reset || 0} cleared, ${s.pulled || 0} re-pulled`; syncStatus.style.color = 'var(--t-accent)'; }
|
||||
await window._reloadAll?.();
|
||||
} else {
|
||||
if (syncStatus) { syncStatus.textContent = res?.error || 'reset failed'; syncStatus.style.color = 'var(--t-danger)'; }
|
||||
}
|
||||
});
|
||||
|
||||
export async function triggerSync() {
|
||||
const btn = $('cal-sync');
|
||||
@@ -161,14 +175,22 @@ export async function triggerSync() {
|
||||
|
||||
if (btn) btn.style.opacity = '';
|
||||
|
||||
// Also sync WebDAV Notes
|
||||
const notesRes = await fetch('api/notes/sync', {
|
||||
method: 'POST', headers: { 'X-CSRF-Token': getCsrf() },
|
||||
}).then(r => r.json()).catch(() => null);
|
||||
|
||||
if (res.ok) {
|
||||
const s = res.stats || {};
|
||||
const hasErrors = (res.log || []).some(l => l.status === 'error');
|
||||
const msg = `pushed:${s.pushed || 0} pulled:${s.pulled || 0} del:${s.deleted || 0}${s.conflicts ? ' conflicts:' + s.conflicts : ''}`;
|
||||
const ns = notesRes?.stats || {};
|
||||
const total = { pushed: (s.pushed || 0) + (ns.pushed || 0), pulled: (s.pulled || 0) + (ns.pulled || 0), deleted: (s.deleted || 0) + (ns.deleted || 0) };
|
||||
const allLog = [...(res.log || []), ...(notesRes?.log || [])];
|
||||
const hasErrors = allLog.some(l => l.status === 'error');
|
||||
const msg = `pushed:${total.pushed} pulled:${total.pulled} del:${total.deleted}${ns.pulled ? ' notes:' + ns.pulled : ''}`;
|
||||
if (syncStatus) { syncStatus.textContent = msg; syncStatus.style.color = hasErrors ? 'var(--t-danger)' : 'var(--t-accent)'; }
|
||||
await window._reloadAll?.();
|
||||
showLog(res.log);
|
||||
showSyncNotification(s);
|
||||
showLog(allLog);
|
||||
showSyncNotification(total);
|
||||
} else {
|
||||
if (syncStatus) { syncStatus.textContent = res.error || 'sync failed'; syncStatus.style.color = 'var(--t-danger)'; }
|
||||
}
|
||||
@@ -190,6 +212,25 @@ function showConnectionLog(lines) {
|
||||
).join('');
|
||||
}
|
||||
|
||||
$('notes-rekey')?.addEventListener('click', async () => {
|
||||
const { showDialog } = await import('./dialog.js');
|
||||
const oldPass = await showDialog({ title: 're-key notes', input: 'password', placeholder: 'old password', okLabel: 're-key' });
|
||||
if (!oldPass) return;
|
||||
if (syncStatus) { syncStatus.textContent = 're-keying notes...'; syncStatus.style.color = 'var(--t-dim)'; }
|
||||
const res = await fetch('api/notes/rekey', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf() },
|
||||
body: JSON.stringify({ old_password: oldPass }),
|
||||
}).then(r => r.json()).catch(() => null);
|
||||
if (res?.ok) {
|
||||
if (syncStatus) { syncStatus.textContent = `re-keyed ${res.rekeyed} notes — syncing...`; syncStatus.style.color = 'var(--t-accent)'; }
|
||||
// Re-pull with correct key
|
||||
const pullRes = await fetch('api/notes/reset', { method: 'POST', headers: { 'X-CSRF-Token': getCsrf() } }).then(r => r.json()).catch(() => null);
|
||||
await window._reloadAll?.();
|
||||
if (syncStatus) syncStatus.textContent = `re-keyed ${res.rekeyed} notes, ${pullRes?.stats?.pulled || 0} re-pulled`;
|
||||
} else {
|
||||
if (syncStatus) { syncStatus.textContent = res?.error || 're-key failed'; syncStatus.style.color = 'var(--t-danger)'; }
|
||||
}
|
||||
});
|
||||
$('cal-caldav-settings')?.addEventListener('click', openCaldavSettings);
|
||||
$('cal-sync')?.addEventListener('click', () => triggerSync());
|
||||
|
||||
@@ -218,25 +259,32 @@ function showSyncNotification(stats) {
|
||||
setTimeout(() => { note.style.transition = 'opacity 0.3s'; note.style.opacity = '0'; setTimeout(() => note.remove(), 350); }, 4000);
|
||||
}
|
||||
|
||||
let bgSyncRunning = false;
|
||||
async function backgroundSync() {
|
||||
if (bgSyncRunning) return;
|
||||
bgSyncRunning = true;
|
||||
try {
|
||||
const res = await fetch('api/caldav/sync', {
|
||||
method: 'POST', headers: { 'X-CSRF-Token': getCsrf() },
|
||||
}).then(r => r.json()).catch(() => null);
|
||||
if (res?.ok) {
|
||||
const s = res.stats || {};
|
||||
if (s.pulled > 0 || s.pushed > 0 || s.deleted > 0) {
|
||||
await window._reloadAll?.();
|
||||
showSyncNotification(s);
|
||||
}
|
||||
const headers = { 'X-CSRF-Token': getCsrf() };
|
||||
const [res, notesRes] = await Promise.all([
|
||||
fetch('api/caldav/sync', { method: 'POST', headers }).then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
fetch('api/notes/sync', { method: 'POST', headers }).then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
]);
|
||||
const s = res?.stats || {};
|
||||
const ns = notesRes?.stats || {};
|
||||
const total = { pushed: (s.pushed || 0) + (ns.pushed || 0), pulled: (s.pulled || 0) + (ns.pulled || 0), deleted: (s.deleted || 0) + (ns.deleted || 0) };
|
||||
if (total.pulled > 0 || total.pushed > 0 || total.deleted > 0) {
|
||||
await window._reloadAll?.();
|
||||
showSyncNotification(total);
|
||||
}
|
||||
} catch {}
|
||||
} catch {} finally {
|
||||
bgSyncRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start background sync after app is ready
|
||||
let bgSyncInterval = null;
|
||||
export function startBackgroundSync() {
|
||||
if (bgSyncInterval) return;
|
||||
backgroundSync(); // first sync immediately
|
||||
bgSyncInterval = setInterval(backgroundSync, 120000); // then every 2 minutes
|
||||
backgroundSync();
|
||||
bgSyncInterval = setInterval(backgroundSync, 45000); // every 45 seconds
|
||||
}
|
||||
Reference in new issue
Block a user