fix(calendars): hold the mirror delete on the marker, not the stored ids

The delete lock read the special-dates calendar ids out of preferences, which
the sync only rewrites on its next pass. Restore the app's data (or lose the
preferences any other way) while a mirror still exists and the feature is on,
and the lock is off until then: the delete goes through, looks like it worked,
and the next sync puts the calendar back.

The calendar itself carries the durable CAL_SYNC2 marker the editor lock already
trusts, so read the lock off that. It also covers the mirror of a type since
switched off, which the same sync pass deletes on its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:49:40 +02:00
parent 7ed19b37aa
commit 5b6bd36ece

View File

@@ -75,18 +75,24 @@ class CalendarsViewModel @Inject constructor(
)
/**
* Managed special-dates calendars whose deletion would not stick. While its
* type is enabled, the sync recreates a missing mirror on the next pass
* (`SpecialDatesSyncEngine.reconcileCalendars`), so the delete would appear
* to work and then undo itself. Turning special dates off empties this set,
* and deleting a leftover mirror is a real delete from then on.
* Managed special-dates calendars whose deletion would not stick. While the
* feature is on, the sync owns every mirror: it recreates a missing one for
* an enabled type on the next pass and deletes the leftover of a disabled
* one (`SpecialDatesSyncEngine.reconcileCalendars`), so either way the
* delete would appear to work and then undo itself. Turning special dates
* off empties this set, and deleting a leftover mirror is a real delete from
* then on.
*
* Read off each calendar's own durable marker ([CalendarSource.isManaged],
* the `CAL_SYNC2` one the editor lock already trusts) rather than the stored
* ids, which are only rewritten on the next sync pass — a preferences loss
* would otherwise unlock a live mirror until then.
*/
val deleteLockedCalendarIds: StateFlow<Set<Long>> = combine(
calendars,
settingsPrefs.specialDatesEnabled,
settingsPrefs.specialDatesTypes,
settingsPrefs.specialDatesCalendars,
) { enabled, types, byType ->
if (!enabled) emptySet() else types.mapNotNull { byType[it] }.toSet()
) { sources, enabled ->
if (!enabled) emptySet() else sources.filter { it.isManaged }.map { it.id }.toSet()
}
.flowOn(io)
.stateIn(