From d1d1135e32c73fb725c0ac4e62a55b37217beb3d Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 8 Apr 2026 22:14:08 +0200 Subject: [PATCH] =?UTF-8?q?perf/fix:=20Kalender-Toggle=20ohne=20Ladescreen?= =?UTF-8?q?=20+=20Mehrfach-Tint=20als=20Verlauf=20Ausblenden:=20Events=20w?= =?UTF-8?q?erden=20sofort=20client-seitig=20aus=20dem=20Cache=20gefiltert?= =?UTF-8?q?=20(calendar=5Fid-Match),=20kein=20Netzwerkaufruf=20f=C3=BCr=20?= =?UTF-8?q?die=20Ansicht=20n=C3=B6tig.=20Einblenden:=20fetchAndRender(forc?= =?UTF-8?q?e,=20silent=3Dtrue)=20=C3=BCberspringt=20showLoading(),=20die?= =?UTF-8?q?=20aktuelle=20Ansicht=20bleibt=20sichtbar=20und=20wird=20nach?= =?UTF-8?q?=20dem=20Fetch=20aktualisiert.=20Mehrere=20mehrt=C3=A4gige=20Ev?= =?UTF-8?q?ents=20am=20selben=20Tag=20erzeugen=20jetzt=20einen=20vertikale?= =?UTF-8?q?n=20Farbverlauf=20(linear-gradient)=20statt=20gestapelter=20Ebe?= =?UTF-8?q?nen,=20bei=20denen=20nur=20die=20letzte=20Farbe=20sichtbar=20wa?= =?UTF-8?q?r.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/js/calendar.js | 23 ++++++++++++++++++++--- frontend/js/views/week.js | 22 ++++++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js index 6a8c6fb..5e924d1 100644 --- a/frontend/js/calendar.js +++ b/frontend/js/calendar.js @@ -141,7 +141,7 @@ function prefetchIfNeeded(viewStart, viewEnd) { } // ── Data fetching ───────────────────────────────────────── -async function fetchAndRender(force = false) { +async function fetchAndRender(force = false, silent = false) { const { start, end } = getViewRange(); // Cache hit: requested range is fully within what we already have @@ -159,7 +159,7 @@ async function fetchAndRender(force = false) { const fetchStart = new Date(start.getTime() - CACHE_BUF); const fetchEnd = new Date(end.getTime() + CACHE_BUF); - showLoading(); + if (!silent) showLoading(); try { const resp = await api.get(`/caldav/events?start=${fetchStart.toISOString()}&end=${fetchEnd.toISOString()}`); const events = resp.events || resp; @@ -460,6 +460,8 @@ function renderCalendarList() { container.querySelectorAll('input[type=checkbox]').forEach(cb => { cb.addEventListener('change', async () => { const source = cb.dataset.source; + let cacheCalId = null; // calendar_id value used in cached events + if (source === 'caldav') { const calId = parseInt(cb.dataset.calId); await api.put(`/caldav/calendars/${calId}`, { enabled: cb.checked }); @@ -468,16 +470,19 @@ function renderCalendarList() { if (cal.id === calId) cal.enabled = cb.checked; } } + cacheCalId = calId; // numeric integer in cached events } else if (source === 'local') { const calId = parseInt(cb.dataset.calId); await api.put(`/local/calendars/${calId}`, { enabled: cb.checked }); const cal = state.localCalendars.find(c => c.id === calId); if (cal) cal.enabled = cb.checked; + cacheCalId = `local-${calId}`; } else if (source === 'ical') { const subId = parseInt(cb.dataset.subId); await api.put(`/ical/subscriptions/${subId}`, { enabled: cb.checked }); const sub = state.icalSubscriptions.find(s => s.id === subId); if (sub) sub.enabled = cb.checked; + cacheCalId = `ical-${subId}`; } else if (source === 'google') { const calId = parseInt(cb.dataset.calId); await api.put(`/google/calendars/${calId}`, { enabled: cb.checked }); @@ -485,8 +490,20 @@ function renderCalendarList() { const cal = acc.calendars.find(c => c.id === calId); if (cal) cal.enabled = cb.checked; } + cacheCalId = `google-${calId}`; + } + + if (!cb.checked && cacheCalId !== null) { + // Hiding: filter from cache instantly, no network call needed + eventCache.events = eventCache.events.filter(ev => ev.calendar_id !== cacheCalId); + state.events = eventCache.events; + renderView(); + updateTitle(); + renderMiniCal(); + } else { + // Showing: refetch silently — view stays visible, updates when done + fetchAndRender(true, true); } - fetchAndRender(true); }); }); diff --git a/frontend/js/views/week.js b/frontend/js/views/week.js index 087ed6d..d16efe4 100644 --- a/frontend/js/views/week.js +++ b/frontend/js/views/week.js @@ -131,10 +131,24 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC }).join(''); // Background tint for days covered by multi-day events (timed or all-day) - const tintHtml = tintEvs.filter(ev => spansDay(ev, day)).map(ev => { - const color = ev.color || ev.calendarColor || '#4285f4'; - return `
`; - }).join(''); + const dayTintEvs = tintEvs.filter(ev => spansDay(ev, day)); + const tintHtml = (() => { + if (!dayTintEvs.length) return ''; + const colors = dayTintEvs.map(ev => ev.color || ev.calendarColor || '#4285f4'); + let bg; + if (colors.length === 1) { + bg = colors[0] + '26'; + } else { + // Vertical gradient bands for multiple overlapping multi-day events + const stops = colors.flatMap((c, i) => { + const p1 = ((i / colors.length) * 100).toFixed(1); + const p2 = (((i + 1) / colors.length) * 100).toFixed(1); + return [`${c}26 ${p1}%`, `${c}26 ${p2}%`]; + }).join(','); + bg = `linear-gradient(to bottom,${stops})`; + } + return `
`; + })(); return `
${tintHtml}