From 16fe2687e8def637462d22e72b483f7f88b45937 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Fri, 16 Oct 2026 13:58:54 +0200 Subject: [PATCH] Drop the editor's pass-through wrappers --- .../calendula/ui/detail/EventDetailScreen.kt | 14 +-- .../calendula/ui/edit/EventEditScreen.kt | 89 +++---------------- 2 files changed, 15 insertions(+), 88 deletions(-) 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 6b8ca58..a91eefd 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 @@ -84,7 +84,6 @@ import de.jeanlucmakiola.calendula.domain.EventForm import de.jeanlucmakiola.calendula.domain.EventInstance import de.jeanlucmakiola.calendula.domain.EventStatus import de.jeanlucmakiola.calendula.domain.InlineStyle -import de.jeanlucmakiola.calendula.domain.Reminder import de.jeanlucmakiola.calendula.domain.RichText import de.jeanlucmakiola.calendula.domain.TimeZoneOption import de.jeanlucmakiola.calendula.domain.timeZoneOptionOf @@ -303,7 +302,7 @@ private fun EventDetailContent( val detail = state.detail val instance = detail.instance val dark = LocalFloretDarkTheme.current - val locale = currentDetailLocale() + val locale = currentLocale() val accent = eventAccent(instance.color, dark, LocalSoftenColors.current) Column( @@ -536,7 +535,7 @@ private fun EventDetailContent( .sortedBy { it.minutes } .forEach { reminder -> Text( - text = reminderLeadText(reminder), + text = reminderLeadTimeLabel(reminder.minutes), style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(vertical = 2.dp), ) @@ -683,11 +682,6 @@ private fun SkeletonBar(widthFraction: Float, height: Dp) { // --- helpers ------------------------------------------------------------- -// Observable locale read (shared helper) — avoids NonObservableLocale / -// LocalContextConfigurationRead lint by going through LocalConfiguration. -@Composable -private fun currentDetailLocale(): Locale = currentLocale() - private fun attendeeStatusLabel(status: AttendeeStatus): Int = when (status) { AttendeeStatus.Accepted -> R.string.event_attendee_accepted AttendeeStatus.Declined -> R.string.event_attendee_declined @@ -707,10 +701,6 @@ private fun attendeeRoleLabel(attendee: Attendee): Int? = when { else -> null } -/** Humanise a reminder's lead time, e.g. "10 minutes before" / "At time of event". */ -@Composable -private fun reminderLeadText(reminder: Reminder): String = reminderLeadTimeLabel(reminder.minutes) - /** * [tz] resolved as a [TimeZoneOption], but only when the event is timed and * pinned to a zone different from the device's — the cases where showing it 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 356981f..34a9d82 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 @@ -133,7 +133,6 @@ import de.jeanlucmakiola.calendula.domain.timeZoneOptionOf import de.jeanlucmakiola.calendula.domain.EventFormProblem import de.jeanlucmakiola.calendula.domain.RecurrenceEnd import de.jeanlucmakiola.calendula.domain.RecurrenceFreq -import de.jeanlucmakiola.calendula.domain.RecurringWriteScope import de.jeanlucmakiola.calendula.domain.SimpleRecurrence import de.jeanlucmakiola.calendula.domain.parseSimpleRecurrence import de.jeanlucmakiola.calendula.domain.toRRule @@ -450,10 +449,13 @@ fun EventEditScreen( } if (state?.saveState == SaveUiState.AwaitingScope) { - SaveScopeDialog( - recurrenceChanged = state?.recurrenceChanged == true, + // A changed rule rules out the single occurrence — an exception row + // can't carry its own rule. + RecurringScopeDialog( + title = stringResource(R.string.event_edit_recurring_title), onSelect = viewModel::saveWithScope, onDismiss = viewModel::consumeSaveResult, + allowOccurrence = state?.recurrenceChanged != true, ) } @@ -654,26 +656,6 @@ private fun SaveConflictDialog( } } -/** - * Scope choice when saving a dirty recurring event: one tap writes just the - * tapped occurrence, it and everything after (series split), or the whole - * series. A changed recurrence rule rules out the single occurrence — an - * exception row can't carry its own rule. - */ -@Composable -private fun SaveScopeDialog( - recurrenceChanged: Boolean, - onSelect: (RecurringWriteScope) -> Unit, - onDismiss: () -> Unit, -) { - RecurringScopeDialog( - title = stringResource(R.string.event_edit_recurring_title), - onSelect = onSelect, - onDismiss = onDismiss, - allowOccurrence = !recurrenceChanged, - ) -} - private enum class PickerTarget { StartDate, StartTime, EndDate, EndTime } /** @@ -799,7 +781,7 @@ private fun EventEditContent( // titles wrap instead of scrolling off one line (#33); newlines are // stripped in setTitle, so this stays a single logical line — the Enter // key just has no effect. - InlineField( + InlineTextField( value = form.title, onValueChange = viewModel::setTitle, placeholder = stringResource(R.string.event_edit_title_hint), @@ -1018,7 +1000,7 @@ private fun EventEditContent( ) { Row(verticalAlignment = Alignment.Top) { Column(modifier = Modifier.weight(1f)) { - InlineField( + InlineTextField( value = form.location, onValueChange = viewModel::setLocation, placeholder = stringResource(R.string.event_detail_location), @@ -1086,7 +1068,7 @@ private fun EventEditContent( iconAlignment = Alignment.Top, iconTopPadding = ((firstLineHeight() - 24.dp) / 2).coerceAtLeast(0.dp), ) { - InlineField( + InlineTextField( value = form.description, onValueChange = viewModel::setDescription, placeholder = stringResource(R.string.event_detail_description), @@ -1106,7 +1088,7 @@ private fun EventEditContent( GroupedItemCard( position = positionOf(index, rows), icon = Icons.Default.Notifications, - title = reminderLabel(minutes), + title = reminderLeadTimeLabel(minutes), removeContentDescription = stringResource(R.string.event_edit_remove_reminder), onRemove = { viewModel.removeReminder(minutes) }, ) @@ -1497,9 +1479,6 @@ private fun FieldPickerDialog( } } -/** Quick-pick lead times offered as chips in the reminder dialog. */ -private val REMINDER_QUICK_PICKS = REMINDER_PRESETS - /** * Reminder picker, two steps: the common lead times as a tappable list * (one tap adds and closes), with "Custom" at the bottom switching to an @@ -1533,11 +1512,11 @@ private fun ReminderPickerDialog( }, ) { if (!customMode) { - val presets = REMINDER_QUICK_PICKS.filterNot { it in alreadyChosen } + val presets = REMINDER_PRESETS.filterNot { it in alreadyChosen } val rowCount = presets.size + 1 presets.forEachIndexed { index, minutes -> GroupedRow( - title = reminderLabel(minutes), + title = reminderLeadTimeLabel(minutes), position = positionOf(index, rowCount), onClick = { onSelect(minutes) }, ) @@ -2344,10 +2323,6 @@ private fun accessLevelSummary(level: AccessLevel): Int = when (level) { AccessLevel.Confidential -> R.string.event_access_confidential_summary } -/** Humanise a reminder lead time, mirroring the detail screen's rendering. */ -@Composable -private fun reminderLabel(minutes: Int): String = reminderLeadTimeLabel(minutes) - /** * "2:00 PM – 3:00 PM" in [zone], honouring the user's 12/24-hour setting. Collapses * to one time for an instant event, which shouldn't read as a range against @@ -2369,45 +2344,7 @@ private fun formatTimeRange(start: Instant, end: Instant, zone: TimeZone, locale } /** - * Borderless text input used inside the cards (and as the headline title). - * Thin wrapper over the shared [InlineTextField] so the form and the rest of - * the app share one input style. Sentence-case by default, as floret-kit is; - * a field holding an identifier rather than prose passes - * [KeyboardCapitalization.None]. - */ -@Composable -private fun InlineField( - value: String, - onValueChange: (String) -> Unit, - placeholder: String, - textStyle: TextStyle = MaterialTheme.typography.titleMedium, - singleLine: Boolean = true, - minLines: Int = 1, - enabled: Boolean = true, - keyboardType: KeyboardType = KeyboardType.Text, - capitalization: KeyboardCapitalization = KeyboardCapitalization.Sentences, - imeAction: ImeAction = ImeAction.Default, - modifier: Modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp), -) { - InlineTextField( - value = value, - onValueChange = onValueChange, - placeholder = placeholder, - modifier = modifier, - textStyle = textStyle, - singleLine = singleLine, - minLines = minLines, - enabled = enabled, - keyboardType = keyboardType, - capitalization = capitalization, - imeAction = imeAction, - ) -} - -/** - * A right-aligned "n/max" counter for a capped [InlineField] (#331), hidden + * A right-aligned "n/max" counter for a capped [InlineTextField] (#331), hidden * until [length] gets close enough to [max] to matter — nobody needs to see * "12/255" while typing a short title. */ @@ -2429,7 +2366,7 @@ private fun FieldLengthCounter(length: Int, max: Int, modifier: Modifier = Modif } /** - * Height of one [InlineField] line: the resolved [TextStyle.lineHeight] plus the + * Height of one [InlineTextField] line: the resolved [TextStyle.lineHeight] plus the * field's 4dp vertical padding. In dp, so it tracks the user's font scale * instead of assuming the 24sp line of a 1.0 scale (#165). */