From dca0245a4286301c6b4b6a4b1160f145984d68dc Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 11 Jun 2026 09:08:21 +0200 Subject: [PATCH] refactor(detail): show availability only when Free, pin it by the title The Busy availability value is the default for nearly every event, so a "Busy" chip on every detail screen was noise. Show the pill only for Free (the noteworthy case) and move it to the top-right of the title row instead of the under-title chip strip. That strip now carries only status/access and hides entirely when there's nothing noteworthy. Drops the now-unused event_availability_busy string from both locales. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 5 +- .../calendula/ui/detail/EventDetailScreen.kt | 67 ++++++++++--------- app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values/strings.xml | 1 - 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee42242..0c6d112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 "1 day before", "At time of event"), read from `CalendarContract.Reminders` - **Status** — Tentative / Cancelled chip under the title; a cancelled event also strikes through its title (Confirmed shows no chip) - - **Availability** — a Free / Busy chip (`Events.AVAILABILITY`, the iCal - TRANSP field) + - **Availability** — a "Free" pill pinned top-right of the title when the + event doesn't block your time (`Events.AVAILABILITY`, the iCal TRANSP + field); the default "Busy" is left implicit to avoid noise on every event - **Access level** — a Private / Confidential chip when the event isn't public - **Attendee role** — organizer / optional / resource badge under each attendee, plus the device user's own response ("Your response: …") from 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 4031eb9..6d5fb6c 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 @@ -174,18 +174,30 @@ private fun EventDetailContent(state: EventDetailUiState.Success, modifier: Modi .verticalScroll(rememberScrollState()) .padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 40.dp), ) { - // Title with a short accent line in the calendar colour underneath. - // A cancelled event strikes through the title. - Text( - text = instance.title.ifBlank { stringResource(R.string.event_untitled) }, - style = MaterialTheme.typography.headlineMedium, - fontWeight = FontWeight.SemiBold, - textDecoration = if (detail.status == EventStatus.Cancelled) { - TextDecoration.LineThrough - } else { - null - }, - ) + // Title row: title on the left, a "Free" pill pinned top-right when the + // event doesn't block your time. Busy is the default for nearly every + // event, so it's left implicit — only Free is worth surfacing. A + // cancelled event strikes through its title. + Row(verticalAlignment = Alignment.Top) { + Text( + text = instance.title.ifBlank { stringResource(R.string.event_untitled) }, + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.SemiBold, + textDecoration = if (detail.status == EventStatus.Cancelled) { + TextDecoration.LineThrough + } else { + null + }, + modifier = Modifier.weight(1f), + ) + if (detail.availability == Availability.Free) { + Spacer(Modifier.width(12.dp)) + InfoChip( + text = stringResource(R.string.event_availability_free), + modifier = Modifier.padding(top = 6.dp), + ) + } + } Spacer(Modifier.height(10.dp)) Box( modifier = Modifier @@ -194,10 +206,15 @@ private fun EventDetailContent(state: EventDetailUiState.Success, modifier: Modi .background(accent, RoundedCornerShape(2.dp)), ) - // Status / availability / access chips. Availability is always known, so - // this row always shows at least the Free/Busy chip. - Spacer(Modifier.height(16.dp)) - StatusChips(detail.status, detail.availability, detail.accessLevel) + // Status / access chips — shown only when noteworthy (Confirmed status + // and Default/Public access are the silent norm). + val hasStatusChips = detail.status != EventStatus.Confirmed || + detail.accessLevel == AccessLevel.Private || + detail.accessLevel == AccessLevel.Confidential + if (hasStatusChips) { + Spacer(Modifier.height(16.dp)) + StatusChips(detail.status, detail.accessLevel) + } Spacer(Modifier.height(20.dp)) @@ -399,14 +416,10 @@ private fun AttendeeRow(attendee: Attendee) { } } -/** Status / availability / access pills shown directly under the title accent. */ +/** Status / access pills shown directly under the title accent. */ @OptIn(ExperimentalLayoutApi::class) @Composable -private fun StatusChips( - status: EventStatus, - availability: Availability, - accessLevel: AccessLevel, -) { +private fun StatusChips(status: EventStatus, accessLevel: AccessLevel) { FlowRow( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), @@ -425,13 +438,6 @@ private fun StatusChips( EventStatus.Confirmed -> Unit } - val availabilityLabel = if (availability == Availability.Free) { - R.string.event_availability_free - } else { - R.string.event_availability_busy - } - InfoChip(text = stringResource(availabilityLabel)) - when (accessLevel) { AccessLevel.Private -> InfoChip(text = stringResource(R.string.event_access_private)) AccessLevel.Confidential -> @@ -444,10 +450,11 @@ private fun StatusChips( @Composable private fun InfoChip( text: String, + modifier: Modifier = Modifier, container: Color = MaterialTheme.colorScheme.surfaceContainerHighest, content: Color = MaterialTheme.colorScheme.onSurfaceVariant, ) { - Surface(color = container, shape = RoundedCornerShape(8.dp)) { + Surface(color = container, shape = RoundedCornerShape(8.dp), modifier = modifier) { Text( text = text, style = MaterialTheme.typography.labelMedium, diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7967160..68c9641 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -72,7 +72,6 @@ Vorläufig Abgesagt Frei - Gebucht Privat Vertraulich Organisator diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c38a2bd..ba05942 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,7 +73,6 @@ Tentative Cancelled Free - Busy Private Confidential Organizer