Drop the editor's pass-through wrappers

This commit is contained in:
2026-10-16 14:07:39 +02:00
parent f55cfffa3a
commit 16fe2687e8
2 changed files with 15 additions and 88 deletions
@@ -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
@@ -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).
*/