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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2naGACCZy1GvqmZNCwpBu
This commit is contained in:
Claude
2026-10-01 14:29:10 +00:00
parent 2cfb9860d1
commit bc1f970dd9
2 changed files with 38 additions and 2 deletions
@@ -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),
@@ -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 ->