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) <noreply@anthropic.com>
This commit is contained in:
@@ -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`
|
"1 day before", "At time of event"), read from `CalendarContract.Reminders`
|
||||||
- **Status** — Tentative / Cancelled chip under the title; a cancelled event
|
- **Status** — Tentative / Cancelled chip under the title; a cancelled event
|
||||||
also strikes through its title (Confirmed shows no chip)
|
also strikes through its title (Confirmed shows no chip)
|
||||||
- **Availability** — a Free / Busy chip (`Events.AVAILABILITY`, the iCal
|
- **Availability** — a "Free" pill pinned top-right of the title when the
|
||||||
TRANSP field)
|
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
|
- **Access level** — a Private / Confidential chip when the event isn't public
|
||||||
- **Attendee role** — organizer / optional / resource badge under each
|
- **Attendee role** — organizer / optional / resource badge under each
|
||||||
attendee, plus the device user's own response ("Your response: …") from
|
attendee, plus the device user's own response ("Your response: …") from
|
||||||
|
|||||||
@@ -174,18 +174,30 @@ private fun EventDetailContent(state: EventDetailUiState.Success, modifier: Modi
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 40.dp),
|
.padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 40.dp),
|
||||||
) {
|
) {
|
||||||
// Title with a short accent line in the calendar colour underneath.
|
// Title row: title on the left, a "Free" pill pinned top-right when the
|
||||||
// A cancelled event strikes through the title.
|
// event doesn't block your time. Busy is the default for nearly every
|
||||||
Text(
|
// event, so it's left implicit — only Free is worth surfacing. A
|
||||||
text = instance.title.ifBlank { stringResource(R.string.event_untitled) },
|
// cancelled event strikes through its title.
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
Row(verticalAlignment = Alignment.Top) {
|
||||||
fontWeight = FontWeight.SemiBold,
|
Text(
|
||||||
textDecoration = if (detail.status == EventStatus.Cancelled) {
|
text = instance.title.ifBlank { stringResource(R.string.event_untitled) },
|
||||||
TextDecoration.LineThrough
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
} else {
|
fontWeight = FontWeight.SemiBold,
|
||||||
null
|
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))
|
Spacer(Modifier.height(10.dp))
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -194,10 +206,15 @@ private fun EventDetailContent(state: EventDetailUiState.Success, modifier: Modi
|
|||||||
.background(accent, RoundedCornerShape(2.dp)),
|
.background(accent, RoundedCornerShape(2.dp)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status / availability / access chips. Availability is always known, so
|
// Status / access chips — shown only when noteworthy (Confirmed status
|
||||||
// this row always shows at least the Free/Busy chip.
|
// and Default/Public access are the silent norm).
|
||||||
Spacer(Modifier.height(16.dp))
|
val hasStatusChips = detail.status != EventStatus.Confirmed ||
|
||||||
StatusChips(detail.status, detail.availability, detail.accessLevel)
|
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))
|
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)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun StatusChips(
|
private fun StatusChips(status: EventStatus, accessLevel: AccessLevel) {
|
||||||
status: EventStatus,
|
|
||||||
availability: Availability,
|
|
||||||
accessLevel: AccessLevel,
|
|
||||||
) {
|
|
||||||
FlowRow(
|
FlowRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
@@ -425,13 +438,6 @@ private fun StatusChips(
|
|||||||
EventStatus.Confirmed -> Unit
|
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) {
|
when (accessLevel) {
|
||||||
AccessLevel.Private -> InfoChip(text = stringResource(R.string.event_access_private))
|
AccessLevel.Private -> InfoChip(text = stringResource(R.string.event_access_private))
|
||||||
AccessLevel.Confidential ->
|
AccessLevel.Confidential ->
|
||||||
@@ -444,10 +450,11 @@ private fun StatusChips(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun InfoChip(
|
private fun InfoChip(
|
||||||
text: String,
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
container: Color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
container: Color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
content: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
content: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
) {
|
) {
|
||||||
Surface(color = container, shape = RoundedCornerShape(8.dp)) {
|
Surface(color = container, shape = RoundedCornerShape(8.dp), modifier = modifier) {
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
|||||||
@@ -72,7 +72,6 @@
|
|||||||
<string name="event_status_tentative">Vorläufig</string>
|
<string name="event_status_tentative">Vorläufig</string>
|
||||||
<string name="event_status_cancelled">Abgesagt</string>
|
<string name="event_status_cancelled">Abgesagt</string>
|
||||||
<string name="event_availability_free">Frei</string>
|
<string name="event_availability_free">Frei</string>
|
||||||
<string name="event_availability_busy">Gebucht</string>
|
|
||||||
<string name="event_access_private">Privat</string>
|
<string name="event_access_private">Privat</string>
|
||||||
<string name="event_access_confidential">Vertraulich</string>
|
<string name="event_access_confidential">Vertraulich</string>
|
||||||
<string name="event_attendee_organizer">Organisator</string>
|
<string name="event_attendee_organizer">Organisator</string>
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
<string name="event_status_tentative">Tentative</string>
|
<string name="event_status_tentative">Tentative</string>
|
||||||
<string name="event_status_cancelled">Cancelled</string>
|
<string name="event_status_cancelled">Cancelled</string>
|
||||||
<string name="event_availability_free">Free</string>
|
<string name="event_availability_free">Free</string>
|
||||||
<string name="event_availability_busy">Busy</string>
|
|
||||||
<string name="event_access_private">Private</string>
|
<string name="event_access_private">Private</string>
|
||||||
<string name="event_access_confidential">Confidential</string>
|
<string name="event_access_confidential">Confidential</string>
|
||||||
<string name="event_attendee_organizer">Organizer</string>
|
<string name="event_attendee_organizer">Organizer</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user