fix: eliminate extra iCloud login during sync - use stored calendars

Apple sync was doing 2 logins per cycle: getAppleCalendars (to get calendar
list for filtering) + getUpcomingEventsBatch (to fetch events). Now uses
stored calendar metadata from DB for filtering, reducing to 1 login total.

Same optimization applied to Synology. Also adds 24:00 end-of-day line
to time grid, and throttles tab-focus sync to max once per 5 minutes.

v1.75.3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-26 10:58:09 +01:00
parent f6e158af8f
commit 77e8e87b90
3 changed files with 32 additions and 64 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.75.2",
"version": "1.75.3",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js",
"scripts": {

View File

@ -7681,6 +7681,10 @@ export default function WeeklyView() {
</div>
);
})}
{/* End-of-day 24:00 label */}
<div className="time-slot-label hour-start" style={{ height: '0px', lineHeight: 0 }}>
<span>{timeFormat === '24h' ? '24' : '12 AM'}</span>
</div>
</div>
</div>
)}
@ -8269,6 +8273,8 @@ export default function WeeklyView() {
</div>
);
})}
{/* End-of-day 24:00 line */}
<div className="time-slot hour-start" style={{ height: 0, position: 'relative' }} />
{/* All Day Events Section */}

View File

@ -396,21 +396,19 @@ export const getCalendarEvents = async (
continue;
}
// Get user calendars to identify which ones to fetch events from
const freshCalendars = await getAppleCalendars(email, appPassword);
// Use stored calendars from DB for filtering (avoids extra iCloud login)
const storedCalendars = (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0)
? (connection.calendars as any[]).filter((c: any) => c.selected !== false)
: [];
// Use stored selection state if available, otherwise use all fresh calendars
let calendars = freshCalendars;
if (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0) {
const storedCalendars = connection.calendars as any[];
calendars = freshCalendars.filter(fc => {
const stored = storedCalendars.find((sc: any) => sc.id === fc.id);
return stored ? stored.selected !== false : true;
});
if (storedCalendars.length === 0) {
console.log('[CALENDAR] Apple: no stored calendars, skipping (connect/refresh calendars in settings)');
continue;
}
const calendarIds = calendars.map(c => c.id);
// Batch fetch: single login, single calendar list, then fetch all calendars
const calendarIds = storedCalendars.map((c: any) => c.id);
// Single login: batch fetch events for all selected calendars
console.log(`[CALENDAR] Apple: batch fetching ${calendarIds.length} calendars with single connection`);
const batchResults = await getAppleEventsBatch(
email,
@ -422,6 +420,7 @@ export const getCalendarEvents = async (
for (const [calendarId, calendarEvents] of batchResults) {
console.log(`[CALENDAR] Fetched ${calendarEvents.length} events from Apple calendar ${calendarId}`);
const calMeta = storedCalendars.find((c: any) => c.id === calendarId);
events = events.concat(calendarEvents.map((event: any) => {
const isDateOnly = (s: string) => s && !s.includes('T');
@ -444,8 +443,8 @@ export const getCalendarEvents = async (
isRecurring: event.isRecurring,
source: 'apple' as const,
calendarId,
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
backgroundColor: calendars.find(c => c.id === calendarId)?.color || '#FF3B30',
calendarTitle: calMeta?.title || 'Apple Calendar',
backgroundColor: calMeta?.color || '#FF3B30',
reminders: event.reminders as EventReminder[] || undefined,
busyStatus: event.busyStatus as BusyStatus || undefined,
visibility: event.visibility as EventVisibility || undefined,
@ -463,55 +462,19 @@ export const getCalendarEvents = async (
continue;
}
// Fetch live calendar list from Synology to detect deleted calendars
let calendarIds: string[] = [];
let calendars: any[] = [];
let freshCalendars: any[] = [];
// Use stored calendars from DB (avoids extra Synology login)
const storedCalendars = (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0)
? (connection.calendars as any[]).filter((c: any) => c.selected !== false)
: [];
try {
freshCalendars = await getSynologyCalendars(serverUrl, username, password);
} catch (err) {
console.error('[CALENDAR] Synology: failed to fetch calendar list:', err);
}
const freshIds = new Set(freshCalendars.map(c => c.id));
if (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0) {
const storedCalendars = connection.calendars as any[];
// Remove calendars that no longer exist on the server
calendars = storedCalendars.filter((c: any) => freshIds.has(c.id));
if (calendars.length < storedCalendars.length) {
const removed = storedCalendars.length - calendars.length;
console.log(`[CALENDAR] Synology: pruned ${removed} deleted calendar(s) from stored list`);
// Update stored calendars in DB to remove stale entries
try {
await prisma.calendarConnection.update({
where: { id: connection.id },
data: { calendars: calendars },
});
} catch (dbErr) {
console.error('[CALENDAR] Synology: failed to update stored calendars:', dbErr);
}
}
calendarIds = calendars
.filter((c: any) => c.selected !== false)
.map((c: any) => c.id);
console.log('[CALENDAR] Synology: using stored calendars, selected:', calendarIds.length, 'of', calendars.length);
} else {
// No stored calendars: use fresh list
console.log('[CALENDAR] Synology: no stored calendars, using fresh list');
calendars = freshCalendars;
calendarIds = freshCalendars.map(c => c.id);
}
if (calendarIds.length === 0) {
console.log('[CALENDAR] Synology: no calendars selected, skipping');
if (storedCalendars.length === 0) {
console.log('[CALENDAR] Synology: no stored calendars, skipping');
continue;
}
// Batch fetch: single login, single calendar list, then fetch all calendars
const calendarIds = storedCalendars.map((c: any) => c.id);
// Single login: batch fetch events for all selected calendars
console.log(`[CALENDAR] Synology: batch fetching ${calendarIds.length} calendars with single connection`);
const batchResults = await getSynologyEventsBatch(
serverUrl,
@ -524,8 +487,7 @@ export const getCalendarEvents = async (
for (const [calendarId, calendarEvents] of batchResults) {
console.log(`[CALENDAR] Synology: fetched ${calendarEvents.length} events from calendar ${calendarId}`);
const calendarData = calendars.find(c => c.id === calendarId);
const freshCal = freshCalendars.find(c => c.id === calendarId);
const calMeta = storedCalendars.find((c: any) => c.id === calendarId);
events = events.concat(calendarEvents.map((event: any) => {
const isDateOnly = (s: string) => s && !s.includes('T');
@ -548,8 +510,8 @@ export const getCalendarEvents = async (
isRecurring: event.isRecurring,
source: 'synology' as const,
calendarId,
calendarTitle: calendarData?.title || 'Synology Calendar',
backgroundColor: calendarData?.backgroundColor || calendarData?.color || freshCal?.color || '#1b85ff',
calendarTitle: calMeta?.title || 'Synology Calendar',
backgroundColor: calMeta?.backgroundColor || calMeta?.color || '#1b85ff',
reminders: event.reminders as EventReminder[] || undefined,
busyStatus: event.busyStatus as BusyStatus || undefined,
visibility: event.visibility as EventVisibility || undefined,