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:
parent
f6e158af8f
commit
77e8e87b90
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"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",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -7681,6 +7681,10 @@ export default function WeeklyView() {
|
|||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -8269,6 +8273,8 @@ export default function WeeklyView() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{/* End-of-day 24:00 line */}
|
||||||
|
<div className="time-slot hour-start" style={{ height: 0, position: 'relative' }} />
|
||||||
{/* All Day Events Section */}
|
{/* All Day Events Section */}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -396,21 +396,19 @@ export const getCalendarEvents = async (
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user calendars to identify which ones to fetch events from
|
// Use stored calendars from DB for filtering (avoids extra iCloud login)
|
||||||
const freshCalendars = await getAppleCalendars(email, appPassword);
|
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
|
if (storedCalendars.length === 0) {
|
||||||
let calendars = freshCalendars;
|
console.log('[CALENDAR] Apple: no stored calendars, skipping (connect/refresh calendars in settings)');
|
||||||
if (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0) {
|
continue;
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
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`);
|
console.log(`[CALENDAR] Apple: batch fetching ${calendarIds.length} calendars with single connection`);
|
||||||
const batchResults = await getAppleEventsBatch(
|
const batchResults = await getAppleEventsBatch(
|
||||||
email,
|
email,
|
||||||
@ -422,6 +420,7 @@ export const getCalendarEvents = async (
|
|||||||
|
|
||||||
for (const [calendarId, calendarEvents] of batchResults) {
|
for (const [calendarId, calendarEvents] of batchResults) {
|
||||||
console.log(`[CALENDAR] Fetched ${calendarEvents.length} events from Apple calendar ${calendarId}`);
|
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) => {
|
events = events.concat(calendarEvents.map((event: any) => {
|
||||||
const isDateOnly = (s: string) => s && !s.includes('T');
|
const isDateOnly = (s: string) => s && !s.includes('T');
|
||||||
@ -444,8 +443,8 @@ export const getCalendarEvents = async (
|
|||||||
isRecurring: event.isRecurring,
|
isRecurring: event.isRecurring,
|
||||||
source: 'apple' as const,
|
source: 'apple' as const,
|
||||||
calendarId,
|
calendarId,
|
||||||
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
|
calendarTitle: calMeta?.title || 'Apple Calendar',
|
||||||
backgroundColor: calendars.find(c => c.id === calendarId)?.color || '#FF3B30',
|
backgroundColor: calMeta?.color || '#FF3B30',
|
||||||
reminders: event.reminders as EventReminder[] || undefined,
|
reminders: event.reminders as EventReminder[] || undefined,
|
||||||
busyStatus: event.busyStatus as BusyStatus || undefined,
|
busyStatus: event.busyStatus as BusyStatus || undefined,
|
||||||
visibility: event.visibility as EventVisibility || undefined,
|
visibility: event.visibility as EventVisibility || undefined,
|
||||||
@ -463,55 +462,19 @@ export const getCalendarEvents = async (
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch live calendar list from Synology to detect deleted calendars
|
// Use stored calendars from DB (avoids extra Synology login)
|
||||||
let calendarIds: string[] = [];
|
const storedCalendars = (connection.calendars && Array.isArray(connection.calendars) && connection.calendars.length > 0)
|
||||||
let calendars: any[] = [];
|
? (connection.calendars as any[]).filter((c: any) => c.selected !== false)
|
||||||
let freshCalendars: any[] = [];
|
: [];
|
||||||
|
|
||||||
try {
|
if (storedCalendars.length === 0) {
|
||||||
freshCalendars = await getSynologyCalendars(serverUrl, username, password);
|
console.log('[CALENDAR] Synology: no stored calendars, skipping');
|
||||||
} 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');
|
|
||||||
continue;
|
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`);
|
console.log(`[CALENDAR] Synology: batch fetching ${calendarIds.length} calendars with single connection`);
|
||||||
const batchResults = await getSynologyEventsBatch(
|
const batchResults = await getSynologyEventsBatch(
|
||||||
serverUrl,
|
serverUrl,
|
||||||
@ -524,8 +487,7 @@ export const getCalendarEvents = async (
|
|||||||
|
|
||||||
for (const [calendarId, calendarEvents] of batchResults) {
|
for (const [calendarId, calendarEvents] of batchResults) {
|
||||||
console.log(`[CALENDAR] Synology: fetched ${calendarEvents.length} events from calendar ${calendarId}`);
|
console.log(`[CALENDAR] Synology: fetched ${calendarEvents.length} events from calendar ${calendarId}`);
|
||||||
const calendarData = calendars.find(c => c.id === calendarId);
|
const calMeta = storedCalendars.find((c: any) => c.id === calendarId);
|
||||||
const freshCal = freshCalendars.find(c => c.id === calendarId);
|
|
||||||
|
|
||||||
events = events.concat(calendarEvents.map((event: any) => {
|
events = events.concat(calendarEvents.map((event: any) => {
|
||||||
const isDateOnly = (s: string) => s && !s.includes('T');
|
const isDateOnly = (s: string) => s && !s.includes('T');
|
||||||
@ -548,8 +510,8 @@ export const getCalendarEvents = async (
|
|||||||
isRecurring: event.isRecurring,
|
isRecurring: event.isRecurring,
|
||||||
source: 'synology' as const,
|
source: 'synology' as const,
|
||||||
calendarId,
|
calendarId,
|
||||||
calendarTitle: calendarData?.title || 'Synology Calendar',
|
calendarTitle: calMeta?.title || 'Synology Calendar',
|
||||||
backgroundColor: calendarData?.backgroundColor || calendarData?.color || freshCal?.color || '#1b85ff',
|
backgroundColor: calMeta?.backgroundColor || calMeta?.color || '#1b85ff',
|
||||||
reminders: event.reminders as EventReminder[] || undefined,
|
reminders: event.reminders as EventReminder[] || undefined,
|
||||||
busyStatus: event.busyStatus as BusyStatus || undefined,
|
busyStatus: event.busyStatus as BusyStatus || undefined,
|
||||||
visibility: event.visibility as EventVisibility || undefined,
|
visibility: event.visibility as EventVisibility || undefined,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user