fix: deduplicate recurring event masters vs expanded instances

Recurring events appeared twice on their creation day because both the
series master (from POST cache) and the expanded instance (from sync)
had different IDs. Now filters out masters when instances exist, and
also deduplicates by title+startTime+calendarId as a fallback.

v1.66.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-24 11:55:39 +01:00
parent 13de53fe0a
commit dbe60dd331
2 changed files with 19 additions and 1 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.66.0",
"version": "1.66.1",
"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

@ -1613,11 +1613,29 @@ export default function WeeklyView() {
const [weatherData, setWeatherData] = useState<Record<string, WeatherHour>>({});
// Extend events with editable flag from connections, deduplicate by id
// Also deduplicate recurring series masters vs expanded instances:
// When a recurring event is created, the master is cached. Then the sync
// returns expanded instances with different IDs but the same recurringEventId.
// We keep instances and discard masters that overlap with them.
const calendarEvents = useMemo(() => {
const seen = new Set<string>();
const seenSlot = new Set<string>();
// Collect recurring event IDs that have expanded instances
const seriesWithInstances = new Set<string>();
for (const event of rawCalendarEvents) {
if (event.recurringEventId && event.id !== event.recurringEventId) {
seriesWithInstances.add(event.recurringEventId);
}
}
return rawCalendarEvents.filter((event) => {
if (seen.has(event.id)) return false;
seen.add(event.id);
// Skip series master if expanded instances exist for this series
if (seriesWithInstances.has(event.id)) return false;
// Deduplicate by title+startTime+calendarId (catches optimistic add + cache read)
const slotKey = `${event.title}|${event.startTime}|${event.calendarId}`;
if (seenSlot.has(slotKey)) return false;
seenSlot.add(slotKey);
return true;
}).map((event) => {
let isEditable = false;