brand(art): wire Messages-empty (A8) + Bucket List-empty (A6); add BrandIllustration helper

EmptyState already supports illustrationResId (rounded-tile clip), so Bucket List just
passes illustration_bucket_list_empty. Messages inbox gained a proper empty state
("Your private conversation starts here") with illustration_messages_empty. Added
BrandIllustration() helper (theme-safe rounded tile / tile=false for transparent art)
for the upcoming header/hero placements. Verified live both themes: rounded illustration
tile reads cleanly on dark (card) and light (white card on blush); 0 FATAL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-06-26 09:36:53 -05:00
parent 077a408785
commit 4aec224f0d
3 changed files with 67 additions and 0 deletions

View File

@ -0,0 +1,49 @@
package app.closer.ui.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* Brand illustration that reads on BOTH the light and dark themes.
*
* Most of the generated empty-state / header illustrations ship with a soft
* near-white background; rendered raw on the dark (aubergine) theme that background
* would float as a pale block. Clipping to a generous rounded tile with a hairline
* outline turns it into an intentional, modern illustration card on either surface.
*
* Transparent art (e.g. the pairing-success celebration) should pass [tile] = false
* so it floats freely with no card edge.
*/
@Composable
fun BrandIllustration(
@DrawableRes res: Int,
contentDescription: String?,
modifier: Modifier = Modifier,
tile: Boolean = true,
cornerRadius: Dp = 28.dp,
) {
val shape = RoundedCornerShape(cornerRadius)
val shaped = if (tile) {
modifier
.clip(shape)
.border(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = 0.10f), shape)
} else {
modifier
}
Image(
painter = painterResource(res),
contentDescription = contentDescription,
contentScale = ContentScale.Fit,
modifier = shaped,
)
}

View File

@ -276,6 +276,7 @@ private fun BucketListItems(
app.closer.ui.components.EmptyState(
title = "Your shared list is empty",
body = "Add something you've been dreaming about doing together — big or small. Tap + to start.",
illustrationResId = app.closer.R.drawable.illustration_bucket_list_empty,
modifier = Modifier.padding(top = 80.dp)
)
return

View File

@ -30,8 +30,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.R
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Conversation
import app.closer.ui.components.EmptyState
import app.closer.ui.theme.CloserPalette
import coil.compose.AsyncImage
import java.util.concurrent.TimeUnit
@ -74,6 +76,21 @@ fun MessagesInboxScreen(
return
}
if (state.conversations.isEmpty()) {
Column(
modifier = Modifier.fillMaxSize().padding(24.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
EmptyState(
title = "Your private conversation starts here",
body = "Say hi, share a thought, or pick a question to talk through together. Everything you send stays end-to-end encrypted, just for the two of you.",
illustrationResId = R.drawable.illustration_messages_empty
)
}
return
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 4.dp)