Localise the untitled-event placeholder (#321)

This commit is contained in:
Jean-Luc Makiola
2026-09-23 22:30:46 +02:00
6 changed files with 14 additions and 12 deletions
@@ -21,8 +21,9 @@ internal fun ColumnReader.toEventInstance(): EventInstance? {
return null return null
} }
val rawTitle = getString(InstanceProjection.IDX_TITLE) // Left blank when the event has no title: the placeholder is a display
val title = if (rawTitle.isNullOrEmpty()) Fallbacks.UNTITLED_EVENT else rawTitle // string, so it belongs to the UI layer where it can be localised (#321).
val title = getString(InstanceProjection.IDX_TITLE).orEmpty()
val color = if (isNull(InstanceProjection.IDX_EVENT_COLOR)) { val color = if (isNull(InstanceProjection.IDX_EVENT_COLOR)) {
getInt(InstanceProjection.IDX_CALENDAR_COLOR) getInt(InstanceProjection.IDX_CALENDAR_COLOR)
@@ -315,5 +315,4 @@ internal object ReminderProjection {
internal object Fallbacks { internal object Fallbacks {
const val UNNAMED_CALENDAR = "(Unbenannter Kalender)" const val UNNAMED_CALENDAR = "(Unbenannter Kalender)"
const val UNTITLED_EVENT = "(Ohne Titel)"
} }
@@ -21,8 +21,7 @@ internal fun ColumnReader.toSearchResult(): EventInstance? {
else -> dtStart + parseRfc2445DurationMillis(getString(SearchProjection.IDX_DURATION)) else -> dtStart + parseRfc2445DurationMillis(getString(SearchProjection.IDX_DURATION))
}.coerceAtLeast(dtStart) }.coerceAtLeast(dtStart)
val rawTitle = getString(SearchProjection.IDX_TITLE) val title = getString(SearchProjection.IDX_TITLE).orEmpty()
val title = if (rawTitle.isNullOrEmpty()) Fallbacks.UNTITLED_EVENT else rawTitle
val color = if (isNull(SearchProjection.IDX_EVENT_COLOR)) { val color = if (isNull(SearchProjection.IDX_EVENT_COLOR)) {
getInt(SearchProjection.IDX_CALENDAR_COLOR) getInt(SearchProjection.IDX_CALENDAR_COLOR)
} else { } else {
@@ -509,6 +509,9 @@ private fun SearchResultRow(
onLongClick: (() -> Unit)? = null, onLongClick: (() -> Unit)? = null,
) { ) {
val event = hit.event val event = hit.event
// Spans are empty for a blank title — the query never matches the
// placeholder — so marking the substitute is safe.
val title = event.title.ifBlank { stringResource(R.string.event_untitled) }
val dark = isSystemInDarkTheme() val dark = isSystemInDarkTheme()
val soften = LocalSoftenColors.current val soften = LocalSoftenColors.current
// On a picked row the headline is already recoloured for the secondary // On a picked row the headline is already recoloured for the secondary
@@ -521,7 +524,7 @@ private fun SearchResultRow(
// Faded like a past event anywhere else in the app — search reaches back // Faded like a past event anywhere else in the app — search reaches back
// through the whole history. // through the whole history.
modifier = if (hit.isPast) modifier.alpha(EventDimAlpha) else modifier, modifier = if (hit.isPast) modifier.alpha(EventDimAlpha) else modifier,
title = declinedTitle(marked(event.title, hit.titleSpans, highlight), event.isDeclined), title = declinedTitle(marked(title, hit.titleSpans, highlight), event.isDeclined),
summary = searchSummary(hit, highlight), summary = searchSummary(hit, highlight),
position = position, position = position,
minHeight = 64.dp, minHeight = 64.dp,
@@ -61,15 +61,15 @@ class InstanceMapperTest {
} }
@Test @Test
fun `null title falls back to placeholder`() { fun `null title is left blank for the UI to fill in`() {
val inst = reader(title = null).toEventInstance() val inst = reader(title = null).toEventInstance()
assertThat(inst!!.title).isEqualTo(Fallbacks.UNTITLED_EVENT) assertThat(inst!!.title).isEmpty()
} }
@Test @Test
fun `empty title falls back to placeholder`() { fun `empty title is left blank for the UI to fill in`() {
val inst = reader(title = "").toEventInstance() val inst = reader(title = "").toEventInstance()
assertThat(inst!!.title).isEqualTo(Fallbacks.UNTITLED_EVENT) assertThat(inst!!.title).isEmpty()
} }
@Test @Test
@@ -242,8 +242,8 @@ class EventSearchTest {
} }
@Test @Test
fun `an untitled event's placeholder is not something the query can match`() { fun `an untitled event's blank title is not something the query can match`() {
val untitled = candidate("(Ohne Titel)", description = "Titel folgt noch").copy(title = null) val untitled = candidate("", description = "Titel folgt noch").copy(title = null)
val results = search(untitled, query = "titel") val results = search(untitled, query = "titel")