From bc1f970dd989df110f0a465eed8ef2795254dc53 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 14:29:10 +0000 Subject: [PATCH] fix: stop clipping the follow mark off the Now Playing preview avatar The preview card wrapped the account's avatar in a circular clip. The avatar already rounds its own picture; the follow mark sits in the top-right corner of its square box (and user cards along the bottom), so the extra clip cut them off. The slot is now only sized. Adds a render test that draws a marker in the avatar's top-right corner: with the old clip only 207 of its 576 pixels reached the screen. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N2naGACCZy1GvqmZNCwpBu --- .../settings/NowPlayingSettingsScreen.kt | 4 ++- .../settings/NowPlayingSettingsRenderTest.kt | 36 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsScreen.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsScreen.kt index 973e5381aa..8dc824086f 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsScreen.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsScreen.kt @@ -297,7 +297,9 @@ fun NowPlayingPreviewCard( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 16.dp), verticalAlignment = Alignment.CenterVertically, ) { - Box(Modifier.size(44.dp).clip(CircleShape)) { avatar() } + // Sized, not clipped: the avatar rounds itself, and the follow mark and user cards it + // draws sit in the corners of its square box, which a circular clip would cut off. + Box(Modifier.size(44.dp)) { avatar() } Column( modifier = Modifier.weight(1f).padding(start = 14.dp, end = 12.dp), diff --git a/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsRenderTest.kt b/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsRenderTest.kt index 69ee28bc70..9c5b1c853a 100644 --- a/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsRenderTest.kt +++ b/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/settings/NowPlayingSettingsRenderTest.kt @@ -21,14 +21,19 @@ package com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.settings import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.ImageComposeScene import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.model.nip38UserStatuses.nowPlaying.NowPlayingAccess @@ -125,7 +130,7 @@ class NowPlayingSettingsRenderTest { return try { val png = scene.render().encodeToData(EncodedImageFormat.PNG)!!.bytes System.getenv("NOW_PLAYING_RENDER_DIR")?.let { dir -> - val name = scenarios.entries.first { it.value === content }.key + val name = scenarios.entries.firstOrNull { it.value === content }?.key ?: return@let File(dir, "now-playing-$name-${if (dark) "dark" else "light"}.png").writeBytes(png) } ImageIO.read(ByteArrayInputStream(png)) @@ -142,6 +147,35 @@ class NowPlayingSettingsRenderTest { return seen.size } + @Test + fun theAvatarCornersAreNotClipped() { + // ClickableUserPicture draws the follow mark in the top-right corner of its square box, + // outside the round picture. A circular clip around the slot used to cut it off. + val marker = Color(0xFFFF00FF) + val image = + render(false) { + NowPlayingPreviewCard( + avatar = { + Box(Modifier.fillMaxSize()) { + Box(Modifier.size(12.dp).align(Alignment.TopEnd).background(marker)) + } + }, + name = "Vitor", + liveStatus = null, + isOn = true, + ) + } + + var markerPixels = 0 + for (x in 0 until width) { + for (y in 0 until height) { + if (image.getRGB(x, y) == marker.toArgb()) markerPixels++ + } + } + // 12dp at density 2 is a 24x24 square: all of it must reach the screen. + assertTrue(markerPixels >= 24 * 24 * 95 / 100, "only $markerPixels of ${24 * 24} corner pixels were drawn") + } + @Test fun everyStateRendersInBothThemes() { scenarios.values.forEach { content ->