refactor(contacts): simplify the sync engine's gates and context
- The worker no longer pre-checks specialDatesEnabled: the engine already gates on it, and the worker now consumes the previously-unreachable Disabled result instead (without recording a run). - ReminderContext drops the timed defaults it could never use — managed events are always all-day — saving two DataStore reads per sync. - createCalendar no longer persists the calendar id itself; reconcileCalendars is the single place ids are recorded (it always followed up with the same write). - The title-template fallback uses the shared resolveTitleTemplate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,9 +83,6 @@ class SpecialDatesSyncWorker(
|
|||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val deps = EntryPointAccessors.fromApplication(applicationContext, Deps::class.java)
|
val deps = EntryPointAccessors.fromApplication(applicationContext, Deps::class.java)
|
||||||
val prefs = deps.settingsPrefs()
|
val prefs = deps.settingsPrefs()
|
||||||
// Respect the toggle even for already-queued work; never revive a
|
|
||||||
// disabled feature.
|
|
||||||
if (!prefs.specialDatesEnabled.first()) return Result.success()
|
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
// A foreground resume can fire often — skip if we synced recently.
|
// A foreground resume can fire often — skip if we synced recently.
|
||||||
@@ -95,13 +92,19 @@ class SpecialDatesSyncWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val result = deps.syncEngine().sync()
|
when (val result = deps.syncEngine().sync()) {
|
||||||
val stalled = if (result == SpecialDatesSyncResult.PermissionMissing) {
|
// The engine re-checks the toggle, so already-queued work never
|
||||||
SpecialDatesStalledReason.PermissionRevoked
|
// revives a disabled feature; don't record a run either.
|
||||||
} else {
|
SpecialDatesSyncResult.Disabled -> return Result.success()
|
||||||
null
|
else -> {
|
||||||
|
val stalled = if (result == SpecialDatesSyncResult.PermissionMissing) {
|
||||||
|
SpecialDatesStalledReason.PermissionRevoked
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
prefs.recordSpecialDatesRun(now, stalled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prefs.recordSpecialDatesRun(now, stalled)
|
|
||||||
if (foreground) prefs.setSpecialDatesLastForegroundSync(now)
|
if (foreground) prefs.setSpecialDatesLastForegroundSync(now)
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import de.jeanlucmakiola.calendula.data.calendar.ManagedEventRow
|
|||||||
import de.jeanlucmakiola.calendula.data.calendar.toWriteTimes
|
import de.jeanlucmakiola.calendula.data.calendar.toWriteTimes
|
||||||
import de.jeanlucmakiola.calendula.data.prefs.CalendarReminderOverride
|
import de.jeanlucmakiola.calendula.data.prefs.CalendarReminderOverride
|
||||||
import de.jeanlucmakiola.calendula.data.prefs.SettingsPrefs
|
import de.jeanlucmakiola.calendula.data.prefs.SettingsPrefs
|
||||||
import de.jeanlucmakiola.calendula.data.prefs.resolveDefaultReminder
|
|
||||||
import de.jeanlucmakiola.calendula.domain.Availability
|
import de.jeanlucmakiola.calendula.domain.Availability
|
||||||
import de.jeanlucmakiola.calendula.domain.EventForm
|
import de.jeanlucmakiola.calendula.domain.EventForm
|
||||||
import de.jeanlucmakiola.calendula.domain.RecurrenceFreq
|
import de.jeanlucmakiola.calendula.domain.RecurrenceFreq
|
||||||
@@ -39,6 +38,16 @@ interface SpecialDatesCalendarSpec {
|
|||||||
fun defaultTitleTemplate(type: SpecialDateType): String
|
fun defaultTitleTemplate(type: SpecialDateType): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stored title template for [type], falling back to the localized default
|
||||||
|
* when blank/absent — the one resolution rule shared by the sync (what gets
|
||||||
|
* written) and the settings editor (what gets shown).
|
||||||
|
*/
|
||||||
|
fun SpecialDatesCalendarSpec.resolveTitleTemplate(
|
||||||
|
type: SpecialDateType,
|
||||||
|
stored: Map<SpecialDateType, String>,
|
||||||
|
): String = stored[type]?.takeIf { it.isNotBlank() } ?: defaultTitleTemplate(type)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mirrors contact birthdays/anniversaries/custom dates into local calendars,
|
* Mirrors contact birthdays/anniversaries/custom dates into local calendars,
|
||||||
* one per type. Every reconcile is an idempotent diff keyed on the deterministic
|
* one per type. Every reconcile is an idempotent diff keyed on the deterministic
|
||||||
@@ -72,8 +81,7 @@ class SpecialDatesSyncEngine @Inject constructor(
|
|||||||
val showYear = prefs.specialDatesShowYear.first()
|
val showYear = prefs.specialDatesShowYear.first()
|
||||||
|
|
||||||
calendarByType.forEach { (type, calendarId) ->
|
calendarByType.forEach { (type, calendarId) ->
|
||||||
val template = templates[type]?.takeIf { it.isNotBlank() }
|
val template = spec.resolveTitleTemplate(type, templates)
|
||||||
?: spec.defaultTitleTemplate(type)
|
|
||||||
syncType(
|
syncType(
|
||||||
calendarId = calendarId,
|
calendarId = calendarId,
|
||||||
type = type,
|
type = type,
|
||||||
@@ -143,8 +151,8 @@ class SpecialDatesSyncEngine @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createCalendar(type: SpecialDateType): Long {
|
private suspend fun createCalendar(type: SpecialDateType): Long {
|
||||||
|
// reconcileCalendars persists the id — the single place ids are recorded.
|
||||||
val id = calendars.createManagedCalendar(spec.displayName(type), spec.color(type), type)
|
val id = calendars.createManagedCalendar(spec.displayName(type), spec.color(type), type)
|
||||||
prefs.setSpecialDatesCalendarId(type, id)
|
|
||||||
// Seed a useful all-day reminder default (on the day + a week before), so
|
// Seed a useful all-day reminder default (on the day + a week before), so
|
||||||
// birthdays get lead time out of the box. Per-calendar, user-adjustable —
|
// birthdays get lead time out of the box. Per-calendar, user-adjustable —
|
||||||
// only set when the user hasn't already configured this calendar.
|
// only set when the user hasn't already configured this calendar.
|
||||||
@@ -212,29 +220,21 @@ class SpecialDatesSyncEngine @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Managed events are always all-day, so only the all-day defaults apply.
|
||||||
private suspend fun readReminderContext(): ReminderContext = ReminderContext(
|
private suspend fun readReminderContext(): ReminderContext = ReminderContext(
|
||||||
timedGlobal = prefs.defaultReminderMinutes.first(),
|
|
||||||
allDayGlobal = prefs.defaultAllDayReminderMinutes.first(),
|
allDayGlobal = prefs.defaultAllDayReminderMinutes.first(),
|
||||||
timedOverrides = prefs.perCalendarReminderOverride.first(),
|
|
||||||
allDayOverrides = prefs.perCalendarAllDayReminderOverride.first(),
|
allDayOverrides = prefs.perCalendarAllDayReminderOverride.first(),
|
||||||
allDayTimeMinutes = prefs.allDayReminderTimeMinutes.first(),
|
allDayTimeMinutes = prefs.allDayReminderTimeMinutes.first(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class ReminderContext(
|
private data class ReminderContext(
|
||||||
val timedGlobal: List<Int>,
|
|
||||||
val allDayGlobal: List<Int>,
|
val allDayGlobal: List<Int>,
|
||||||
val timedOverrides: Map<Long, List<Int>>,
|
|
||||||
val allDayOverrides: Map<Long, List<Int>>,
|
val allDayOverrides: Map<Long, List<Int>>,
|
||||||
val allDayTimeMinutes: Int,
|
val allDayTimeMinutes: Int,
|
||||||
) {
|
) {
|
||||||
fun resolveAllDay(calendarId: Long): List<Int> = resolveDefaultReminder(
|
/** Same semantics as `resolveDefaultReminder`: present-empty = explicit none. */
|
||||||
timedGlobal = timedGlobal,
|
fun resolveAllDay(calendarId: Long): List<Int> =
|
||||||
allDayGlobal = allDayGlobal,
|
allDayOverrides[calendarId] ?: allDayGlobal
|
||||||
timedOverrides = timedOverrides,
|
|
||||||
allDayOverrides = allDayOverrides,
|
|
||||||
calendarId = calendarId,
|
|
||||||
isAllDay = true,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class BuiltManagedEvent(val desired: ManagedEventDesired, val form: EventForm)
|
private data class BuiltManagedEvent(val desired: ManagedEventDesired, val form: EventForm)
|
||||||
|
|||||||
Reference in New Issue
Block a user