From 8bd2ac7a9a922ae5b02c49b290ae5ae4cc51f4a7 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 15 Oct 2026 16:36:08 +0200 Subject: [PATCH] Remove dead code and unused imports (#407) First step of the simplification review. No behaviour change. - Unreachable paged-month path (`MonthContent` / `MonthPager` / `MonthPage`); the month branch is now scrolling vs split - `ui/day/` and its test, `MonthViewModel.anchorPage`, `PagedTimelineViewModel.goToDate`, `SettingsPrefs.setQuickSwitchConfig`, `TimelineHours.span` - `PermissionViewModel` / `PermissionUiState`, replaced by a saveable `denied` flag in `PermissionScreen` - `ReminderNotifier.post` no longer returns a value nobody read - 38 unused imports outside the widget code Co-authored-by: Jean-Luc Makiola Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/407 --- .../data/calendar/CalendarDataSource.kt | 2 - .../calendula/data/prefs/SettingsPrefs.kt | 5 - .../data/reminders/ReminderNotifier.kt | 12 +- .../calendula/ui/agenda/AgendaRows.kt | 1 - .../calendula/ui/calendars/CalendarsScreen.kt | 11 -- .../calendula/ui/common/EventSelectionHost.kt | 1 - .../calendula/ui/common/Picker.kt | 2 - .../ui/common/RescheduleViewModel.kt | 1 - .../calendula/ui/common/TimeZonePicker.kt | 1 - .../calendula/ui/common/TimelineDrag.kt | 1 - .../calendula/ui/common/TimelineHours.kt | 2 - .../calendula/ui/day/DayPaging.kt | 22 --- .../calendula/ui/detail/EventDetailScreen.kt | 1 - .../calendula/ui/edit/EventEditScreen.kt | 4 - .../calendula/ui/edit/EventEditViewModel.kt | 1 - .../calendula/ui/month/MonthScreen.kt | 132 +----------------- .../calendula/ui/month/MonthViewModel.kt | 17 +-- .../ui/onboarding/PermissionScreen.kt | 37 ++--- .../ui/onboarding/PermissionUiState.kt | 7 - .../ui/onboarding/PermissionViewModel.kt | 29 ---- .../calendula/ui/search/SearchScreen.kt | 1 - .../calendula/ui/week/WeekViewModel.kt | 1 - .../calendula/data/prefs/SettingsPrefsTest.kt | 2 +- .../calendula/data/time/FakeZoneSource.kt | 1 - .../calendula/domain/InsertEventFormTest.kt | 1 - .../calendula/ui/day/DayPagingTest.kt | 22 --- .../ui/edit/EventEditViewModelTest.kt | 1 - 27 files changed, 22 insertions(+), 296 deletions(-) delete mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayPaging.kt delete mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionUiState.kt delete mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionViewModel.kt delete mode 100644 app/src/test/java/de/jeanlucmakiola/calendula/ui/day/DayPagingTest.kt diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/calendar/CalendarDataSource.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/calendar/CalendarDataSource.kt index 6505417..78c2d78 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/calendar/CalendarDataSource.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/calendar/CalendarDataSource.kt @@ -1,6 +1,5 @@ package de.jeanlucmakiola.calendula.data.calendar -import de.jeanlucmakiola.floret.time.toEpochMillis import de.jeanlucmakiola.floret.time.toKotlinInstantFromEpochMillis import android.Manifest import android.content.ContentResolver @@ -28,7 +27,6 @@ import de.jeanlucmakiola.calendula.domain.curatedForPicker import de.jeanlucmakiola.calendula.domain.EventForm import de.jeanlucmakiola.calendula.domain.EventInstance import de.jeanlucmakiola.calendula.domain.EventSearch -import de.jeanlucmakiola.calendula.domain.EventStatus import de.jeanlucmakiola.calendula.domain.Reminder import de.jeanlucmakiola.calendula.domain.SearchCandidate import de.jeanlucmakiola.calendula.domain.contacts.MANAGED_UID_PREFIX diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt index 0fad52f..d051ab9 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt @@ -11,7 +11,6 @@ import de.jeanlucmakiola.floret.reminders.ReminderOverride import de.jeanlucmakiola.floret.reminders.ReminderOverrideCodec import de.jeanlucmakiola.floret.reminders.applyReminderOverride import de.jeanlucmakiola.floret.reminders.normalizeReminders -import de.jeanlucmakiola.floret.reminders.reminderOverrideFor import de.jeanlucmakiola.calendula.domain.EventFormField import de.jeanlucmakiola.calendula.domain.FontRole import de.jeanlucmakiola.calendula.domain.contacts.SpecialDateType @@ -452,10 +451,6 @@ class SettingsPrefs @Inject constructor( parseQuickSwitch(prefs[QUICK_SWITCH_VIEWS_KEY]) } - suspend fun setQuickSwitchConfig(config: QuickSwitchConfig) { - store.edit { it[QUICK_SWITCH_VIEWS_KEY] = serializeQuickSwitch(config) } - } - /** * Atomic read-modify-write of the quick-switch config: [transform] sees the * value currently stored, parsed inside the edit block, not the async-echoed diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt index 7ca24b7..8a1009b 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt @@ -63,13 +63,9 @@ class ReminderNotifier @Inject constructor( calendarId in calendarPrefs.pendingDisabledCalendarIds.first() || calendarDataSource.isCalendarVisible(calendarId) == false - /** - * Post [alert], unless its calendar is switched off. Returns whether the - * notification was put up, which the snooze re-show path uses to tell a - * silenced reminder from a delivered one. - */ - suspend fun post(alert: ReminderAlert): Boolean { - if (isSilenced(alert.calendarId)) return false + /** Post [alert], unless its calendar is switched off. */ + suspend fun post(alert: ReminderAlert) { + if (isSilenced(alert.calendarId)) return ensureChannel() val title = alert.title.ifBlank { context.getString(R.string.event_untitled) } val is24Hour = settingsPrefs.timeFormat.first() @@ -122,8 +118,6 @@ class ReminderNotifier @Inject constructor( // POST_NOTIFICATIONS was revoked between canPost() and here. Log.w(TAG, "Could not post reminder for event ${alert.eventId}", e) } - // Handled either way — a retry hits the same revoked permission. - return true } /** Remove a posted reminder (snooze re-shows it later; dismiss is final). */ diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaRows.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaRows.kt index 34ab368..b7d29ff 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaRows.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaRows.kt @@ -34,7 +34,6 @@ import de.jeanlucmakiola.calendula.ui.common.declinedTitle import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat import de.jeanlucmakiola.calendula.ui.common.eventAccent -import de.jeanlucmakiola.calendula.ui.common.eventFill import de.jeanlucmakiola.calendula.ui.common.formatTimeOfDay import de.jeanlucmakiola.floret.components.GroupedRow import de.jeanlucmakiola.floret.components.Position diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/calendars/CalendarsScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/calendars/CalendarsScreen.kt index a99bf79..1c761e7 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/calendars/CalendarsScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/calendars/CalendarsScreen.kt @@ -12,27 +12,21 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight -import androidx.compose.material.icons.automirrored.filled.Notes import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Backup import androidx.compose.material.icons.filled.CalendarMonth -import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.PhoneAndroid import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff @@ -40,20 +34,15 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt index c26c48d..88fb727 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt @@ -9,7 +9,6 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import de.jeanlucmakiola.calendula.R import de.jeanlucmakiola.calendula.domain.EventForm diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt index 6c25a2c..41c857d 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt @@ -4,9 +4,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons import androidx.compose.material3.Checkbox -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/RescheduleViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/RescheduleViewModel.kt index 669dcc5..72d58e3 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/RescheduleViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/RescheduleViewModel.kt @@ -38,7 +38,6 @@ import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.atStartOfDayIn import kotlinx.datetime.plus -import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Instant diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimeZonePicker.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimeZonePicker.kt index fad9d51..33c29f8 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimeZonePicker.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimeZonePicker.kt @@ -1,6 +1,5 @@ package de.jeanlucmakiola.calendula.ui.common -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt index 45bb3b4..9a60592 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt @@ -5,7 +5,6 @@ import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.ScrollState import androidx.compose.foundation.MutatePriority -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineHours.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineHours.kt index 1efb986..56ab4c6 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineHours.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineHours.kt @@ -16,8 +16,6 @@ data class TimelineHours( val startHour: Int = DEFAULT_START_HOUR, val endHour: Int = DEFAULT_END_HOUR, ) { - val span: Int get() = endHour - startHour - companion object { const val DEFAULT_START_HOUR = 7 const val DEFAULT_END_HOUR = 22 diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayPaging.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayPaging.kt deleted file mode 100644 index b932d7d..0000000 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayPaging.kt +++ /dev/null @@ -1,22 +0,0 @@ -package de.jeanlucmakiola.calendula.ui.day - -import kotlinx.datetime.DateTimeUnit -import kotlinx.datetime.LocalDate -import kotlinx.datetime.daysUntil -import kotlinx.datetime.plus - -/** Pages the day pager spans — a little under four centuries, centred on [PAGE_EPOCH]. */ -internal const val DAY_PAGE_COUNT: Int = 140_000 - -private const val EPOCH_PAGE = DAY_PAGE_COUNT / 2 - -/** The day at [EPOCH_PAGE]; any date works, it only has to stay fixed. */ -private val PAGE_EPOCH = LocalDate(2000, 1, 1) - -/** The day shown on pager [page]. */ -internal fun dayForPage(page: Int): LocalDate = - PAGE_EPOCH.plus(page - EPOCH_PAGE, DateTimeUnit.DAY) - -/** The pager page showing [date]. */ -internal fun dayPageFor(date: LocalDate): Int = - (EPOCH_PAGE + PAGE_EPOCH.daysUntil(date)).coerceIn(0, DAY_PAGE_COUNT - 1) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/detail/EventDetailScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/detail/EventDetailScreen.kt index 0d99c05..eb8fcc4 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/detail/EventDetailScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/detail/EventDetailScreen.kt @@ -104,7 +104,6 @@ import de.jeanlucmakiola.calendula.ui.common.rememberFieldCopier import de.jeanlucmakiola.calendula.ui.common.icuTimeZoneRegion import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors import de.jeanlucmakiola.calendula.ui.common.eventAccent -import de.jeanlucmakiola.calendula.ui.common.eventFill import de.jeanlucmakiola.floret.locale.currentLocale import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat import de.jeanlucmakiola.calendula.ui.common.timeOfDayFormatter diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditScreen.kt index 3252cba..fc35dcc 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Notes @@ -95,8 +94,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext @@ -180,7 +177,6 @@ import de.jeanlucmakiola.calendula.ui.common.recurrenceText import kotlinx.datetime.DayOfWeek import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.LocalTime import kotlinx.datetime.TimeZone import kotlinx.datetime.isoDayNumber import kotlinx.datetime.toJavaDayOfWeek diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModel.kt index 9c19c09..b96db93 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModel.kt @@ -23,7 +23,6 @@ import de.jeanlucmakiola.calendula.domain.EventAttendee import de.jeanlucmakiola.calendula.domain.EventColorOption import de.jeanlucmakiola.calendula.domain.EventDetail import de.jeanlucmakiola.calendula.domain.EventForm -import de.jeanlucmakiola.calendula.domain.endInstant import de.jeanlucmakiola.calendula.domain.nextFullHour import de.jeanlucmakiola.calendula.domain.toFormTime import de.jeanlucmakiola.calendula.domain.resolvedZone diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt index 27ec981..8e49ce8 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt @@ -21,7 +21,6 @@ import de.jeanlucmakiola.calendula.ui.common.LocalViewFocus import de.jeanlucmakiola.calendula.ui.common.ViewMorphKey import de.jeanlucmakiola.calendula.ui.common.morphDay import de.jeanlucmakiola.calendula.ui.common.morphEvent -import de.jeanlucmakiola.calendula.ui.common.morphPageActive import de.jeanlucmakiola.calendula.ui.common.jumpToPage import de.jeanlucmakiola.calendula.ui.common.rememberCalendarPageSpec import de.jeanlucmakiola.floret.identity.LocalFloretDarkTheme @@ -75,11 +74,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.InputChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -167,8 +162,6 @@ import de.jeanlucmakiola.calendula.ui.agenda.agendaDayLabel import de.jeanlucmakiola.calendula.ui.common.formatCalendarTitle import de.jeanlucmakiola.calendula.ui.common.AppBarSpacing import de.jeanlucmakiola.calendula.ui.common.CalendarFailure -import de.jeanlucmakiola.calendula.ui.common.CalendarView -import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha import de.jeanlucmakiola.calendula.ui.common.LocalDimCutoff import de.jeanlucmakiola.calendula.ui.common.isPastOn @@ -534,7 +527,7 @@ fun MonthScreen( onOpenDay = onOpenDay, onEventClick = onEventClick, ) - } else if (viewStyle == MonthViewStyle.Split || viewStyle == MonthViewStyle.Paged) { + } else { SplitMonthContent( state = state, selected = selectedDate, @@ -560,17 +553,6 @@ fun MonthScreen( onEventClick = onEventClick, onCreateEvent = { onCreateEvent(it, null) }, ) - } else { - MonthContent( - state = state, - pagerState = pagerState, - weekStart = weekStart, - monthState = viewModel::month, - showWeekNumbers = showWeekNumbers, - onRetry = jumpToToday, - onOpenDay = onOpenDay, - onEventClick = onEventClick, - ) } } } @@ -679,118 +661,6 @@ private fun MonthDragOverlay(controller: MonthDragController) { } } -@Composable -private fun MonthContent( - state: MonthUiState, - pagerState: PagerState, - weekStart: DayOfWeek, - monthState: (YearMonth, DayOfWeek) -> StateFlow, - showWeekNumbers: Boolean, - onRetry: () -> Unit, - onOpenDay: (LocalDate) -> Unit, - onEventClick: (EventInstance) -> Unit, -) { - val fadeSpec = rememberCalendarFadeSpec() - // Loading and failure concern the provider as a whole, so they stand in for - // the pager; a page still loading its own month handles that itself. - AnimatedContent( - targetState = state, - modifier = Modifier.fillMaxSize(), - contentKey = { it::class }, - transitionSpec = { fadeIn(fadeSpec) togetherWith fadeOut(fadeSpec) }, - label = "month-state", - ) { s -> - when (s) { - MonthUiState.Loading -> MonthGridLoading() - is MonthUiState.Failure -> CalendarFailure(reason = s.reason, onRetry = onRetry) - is MonthUiState.Success -> MonthPager( - pagerState = pagerState, - weekStart = weekStart, - monthState = monthState, - modifier = Modifier.fillMaxSize(), - ) { _, page -> - MonthGrid( - state = page, - showWeekNumbers = showWeekNumbers, - onOpenDay = onOpenDay, - onEventClick = onEventClick, - ) - } - } - } -} - -/** - * The paged and split styles' months as pages that follow the finger and snap - * once a swipe passes half a page or is flung (#336). Only the page on screen - * carries the chip drag and the morph tags: its neighbours show some of its - * dates among their leading and trailing days, and would otherwise claim its - * drops and its shared elements. - */ -@Composable -private fun MonthPager( - pagerState: PagerState, - weekStart: DayOfWeek, - monthState: (YearMonth, DayOfWeek) -> StateFlow, - modifier: Modifier = Modifier, - loading: @Composable () -> Unit = { MonthGridLoading() }, - page: @Composable (index: Int, state: MonthUiState.Success) -> Unit, -) { - val chipDrag = LocalMonthDrag.current - HorizontalPager( - state = pagerState, - modifier = modifier, - // The months either side are composed ahead, so a swipe lands on a loaded - // month rather than watching it fill in. - beyondViewportPageCount = 1, - verticalAlignment = Alignment.Top, - // A held chip moves within its month; the page stays put under it. - userScrollEnabled = chipDrag?.isDragging != true, - flingBehavior = PagerDefaults.flingBehavior( - state = pagerState, - snapAnimationSpec = rememberCalendarPageSpec(), - ), - ) { index -> - MonthPage( - index = index, - active = index == pagerState.currentPage, - morphActive = morphPageActive(index, pagerState.settledPage), - weekStart = weekStart, - monthState = monthState, - loading = loading, - page = page, - ) - } -} - -@Composable -private fun MonthPage( - index: Int, - active: Boolean, - morphActive: Boolean, - weekStart: DayOfWeek, - monthState: (YearMonth, DayOfWeek) -> StateFlow, - loading: @Composable () -> Unit, - page: @Composable (index: Int, state: MonthUiState.Success) -> Unit, -) { - val ym = yearMonthForIndex(index) - val state by remember(ym, weekStart) { monthState(ym, weekStart) }.collectAsStateWithLifecycle() - val today = LocalMonthToday.current - val chipDrag = LocalMonthDrag.current - val morph = LocalMonthMorph.current - val viewMorph = LocalViewMorph.current - CompositionLocalProvider( - LocalMonthDrag provides chipDrag.takeIf { active }, - LocalMonthMorph provides morph.takeIf { active }, - LocalViewMorph provides viewMorph.takeIf { morphActive }, - ) { - when (val s = state) { - is MonthUiState.Success -> page(index, s.withToday(today)) - else -> loading() - } - } -} - /** * Content for both scrolling styles. No swipe detector and no [AnimatedContent]: * vertical scrolling owns the gesture, and the list never swaps wholesale — it diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthViewModel.kt index 7964eec..f6ef83b 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthViewModel.kt @@ -108,15 +108,6 @@ class MonthViewModel @Inject constructor( private val _month = MutableStateFlow(YearMonth(todayDate.year, todayDate.month)) val month: StateFlow = _month - /** The pager page the anchor month sits on, for the paged and split styles. */ - val anchorPage: StateFlow = _month - .map { monthIndexOf(it) } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000L), - initialValue = monthIndexOf(_month.value), - ) - /** * The anchor month's state. Once loaded it never falls back to * [MonthUiState.Loading]: moving the anchor keeps the last result until the @@ -170,7 +161,8 @@ class MonthViewModel @Inject constructor( fun onPageSettled(page: Int) { if (monthIndexOf(_month.value) == page) return _month.value = yearMonthForIndex(page) - realignSelection() + // Moving the grid to another month restores its full agenda. + _selectedDate.value = null } // --- Continuous + Dense styles (#38) ---------------------------------- @@ -339,11 +331,6 @@ class MonthViewModel @Inject constructor( } } - /** Moving the grid to another month restores its full agenda. */ - private fun realignSelection() { - _selectedDate.value = null - } - fun goToToday() { _month.value = YearMonth(todayDate.year, todayDate.month) _selectedDate.value = null diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionScreen.kt index da7d022..0df89a9 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionScreen.kt @@ -26,15 +26,15 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle import de.jeanlucmakiola.calendula.R private val CALENDAR_PERMISSIONS = arrayOf( @@ -47,9 +47,8 @@ fun PermissionScreen( onGranted: () -> Unit, modifier: Modifier = Modifier, progress: (@Composable () -> Unit)? = null, - viewModel: PermissionViewModel = hiltViewModel(), ) { - val state by viewModel.state.collectAsStateWithLifecycle() + var denied by rememberSaveable { mutableStateOf(false) } // READ and WRITE are requested together (one system dialog — same // permission group), but only READ gates the app: declining write keeps @@ -58,33 +57,27 @@ fun PermissionScreen( contract = ActivityResultContracts.RequestMultiplePermissions(), ) { results -> if (results[Manifest.permission.READ_CALENDAR] == true) { - viewModel.onGranted() + onGranted() } else { - viewModel.onDenied() + denied = true } } - LaunchedEffect(state) { - if (state == PermissionUiState.Granted) onGranted() - } - - when (state) { - is PermissionUiState.Rationale -> RationaleContent( - onRequest = { launcher.launch(CALENDAR_PERMISSIONS) }, - progress = progress, - modifier = modifier, - ) - is PermissionUiState.Denied -> DeniedContent( + if (denied) { + DeniedContent( onRetry = { - viewModel.onRetry() + denied = false launcher.launch(CALENDAR_PERMISSIONS) }, progress = progress, modifier = modifier, ) - is PermissionUiState.Granted -> { - // Transient — LaunchedEffect above fires and parent replaces us. - } + } else { + RationaleContent( + onRequest = { launcher.launch(CALENDAR_PERMISSIONS) }, + progress = progress, + modifier = modifier, + ) } } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionUiState.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionUiState.kt deleted file mode 100644 index 49e8cd1..0000000 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionUiState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package de.jeanlucmakiola.calendula.ui.onboarding - -sealed interface PermissionUiState { - data object Rationale : PermissionUiState - data object Denied : PermissionUiState - data object Granted : PermissionUiState -} diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionViewModel.kt deleted file mode 100644 index f5a5ac9..0000000 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/onboarding/PermissionViewModel.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.jeanlucmakiola.calendula.ui.onboarding - -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject - -@HiltViewModel -class PermissionViewModel @Inject constructor() : ViewModel() { - - private val _state = MutableStateFlow(PermissionUiState.Rationale) - val state: StateFlow = _state.asStateFlow() - - // The visibility reconcile a grant owes (#75) hangs off RootScreen, which - // also catches grants made outside the app. - fun onGranted() { - _state.value = PermissionUiState.Granted - } - - fun onDenied() { - _state.value = PermissionUiState.Denied - } - - fun onRetry() { - _state.value = PermissionUiState.Rationale - } -} diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/search/SearchScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/search/SearchScreen.kt index 2c456c0..c4777e3 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/search/SearchScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/search/SearchScreen.kt @@ -99,7 +99,6 @@ import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors import de.jeanlucmakiola.calendula.ui.common.RecurringScopeDialog import de.jeanlucmakiola.calendula.ui.common.eventAccent -import de.jeanlucmakiola.calendula.ui.common.eventFill import de.jeanlucmakiola.calendula.domain.spanFirstDay import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat import de.jeanlucmakiola.calendula.ui.common.timeOfDayFormatter diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekViewModel.kt index 336c465..d929dd4 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekViewModel.kt @@ -207,7 +207,6 @@ abstract class PagedTimelineViewModel( val grid = rule.value(anchor) if (grid.pageFor(anchor) == grid.pageFor(old)) _anchor.value = new } - fun goToDate(date: LocalDate) = jumpTo(date) private fun buildState( span: DaySpan, diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefsTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefsTest.kt index ad38ee3..8502ec1 100644 --- a/app/src/test/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefsTest.kt +++ b/app/src/test/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefsTest.kt @@ -258,7 +258,7 @@ class SettingsPrefsTest { order = listOf(CalendarView.Agenda, CalendarView.Month, CalendarView.Week, CalendarView.Day), enabled = setOf(CalendarView.Agenda, CalendarView.Month), ) - prefs.setQuickSwitchConfig(config) + prefs.updateQuickSwitch { config } val loaded = prefs.quickSwitchConfig.first() assertThat(loaded.order).containsExactly( CalendarView.Agenda, CalendarView.Month, CalendarView.Week, diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/data/time/FakeZoneSource.kt b/app/src/test/java/de/jeanlucmakiola/calendula/data/time/FakeZoneSource.kt index 3a9a017..efc9389 100644 --- a/app/src/test/java/de/jeanlucmakiola/calendula/data/time/FakeZoneSource.kt +++ b/app/src/test/java/de/jeanlucmakiola/calendula/data/time/FakeZoneSource.kt @@ -1,7 +1,6 @@ package de.jeanlucmakiola.calendula.data.time import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.datetime.TimeZone /** A device zone a test sets, by default the machine's own. */ diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/domain/InsertEventFormTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/domain/InsertEventFormTest.kt index 86afaeb..d6f9e66 100644 --- a/app/src/test/java/de/jeanlucmakiola/calendula/domain/InsertEventFormTest.kt +++ b/app/src/test/java/de/jeanlucmakiola/calendula/domain/InsertEventFormTest.kt @@ -8,7 +8,6 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.atStartOfDayIn import kotlinx.datetime.toInstant import org.junit.jupiter.api.Test -import kotlin.time.Instant class InsertEventFormTest { diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/ui/day/DayPagingTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/ui/day/DayPagingTest.kt deleted file mode 100644 index ac37f9b..0000000 --- a/app/src/test/java/de/jeanlucmakiola/calendula/ui/day/DayPagingTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package de.jeanlucmakiola.calendula.ui.day - -import com.google.common.truth.Truth.assertThat -import kotlinx.datetime.LocalDate -import org.junit.jupiter.api.Test - -class DayPagingTest { - - @Test - fun `a page and its day round-trip`() { - listOf(LocalDate(2026, 9, 27), LocalDate(1990, 5, 16), LocalDate(2100, 2, 28)).forEach { day -> - assertThat(dayForPage(dayPageFor(day))).isEqualTo(day) - } - } - - @Test - fun `adjacent pages are adjacent days across a month end`() { - val page = dayPageFor(LocalDate(2026, 2, 28)) - assertThat(dayForPage(page + 1)).isEqualTo(LocalDate(2026, 3, 1)) - assertThat(dayForPage(page - 1)).isEqualTo(LocalDate(2026, 2, 27)) - } -} diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModelTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModelTest.kt index 92e869a..e068c68 100644 --- a/app/src/test/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModelTest.kt +++ b/app/src/test/java/de/jeanlucmakiola/calendula/ui/edit/EventEditViewModelTest.kt @@ -3,7 +3,6 @@ package de.jeanlucmakiola.calendula.ui.edit import android.content.ContextWrapper import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory -import androidx.datastore.preferences.core.Preferences import com.google.common.truth.Truth.assertThat import de.jeanlucmakiola.calendula.data.calendar.CalendarRepositoryImpl import de.jeanlucmakiola.calendula.data.calendar.FakeCalendarDataSource