fix(dvm-card): star top-right + move reactions to bottom row

Putting the star back where the user actually sees DVMs first — the
Discover Content card. Two changes that together stop the layout
from breaking:

- FavoriteDvmToggle now uses ClickableBox + small Icon (no IconButton
  padding) so it occupies the same compact footprint as LikeReaction.
  Adding it to LeftPictureLayout's title row no longer inflates the
  row height.
- DVMCard's title row now holds only the name (weight 1) plus the
  star toggle on the right. LikeReaction and ZapReaction move down
  to the bottom row, pushed to the far right with a Spacer(weight 1)
  so the amount/personalised chips stay on the left.
This commit is contained in:
Claude
2026-04-18 23:02:53 +00:00
parent 8555074c3f
commit 309e474a3d
2 changed files with 61 additions and 61 deletions
@@ -21,11 +21,9 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -40,8 +38,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
@@ -51,12 +49,12 @@ import com.vitorpamplona.amethyst.ui.note.LikeReaction
import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.FavoriteDvmToggle
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.observeAppDefinition
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing5dp
import com.vitorpamplona.amethyst.ui.theme.SimpleImageBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.bitcoinColor
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.nip05
@@ -118,25 +116,12 @@ fun RenderContentDVMThumb(
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f),
)
Spacer(modifier = StdVertSpacer)
Row(
verticalAlignment = CenterVertically,
horizontalArrangement = RowColSpacing5dp,
) {
LikeReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
if (baseNote is AddressableNote) {
FavoriteDvmToggle(
appDefinitionNote = baseNote,
accountViewModel = accountViewModel,
nav,
)
}
Spacer(modifier = StdHorzSpacer)
ZapReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel,
nav = nav,
)
},
onDescription = {
card.description?.let {
@@ -168,22 +153,16 @@ fun RenderContentDVMThumb(
color = MaterialTheme.colorScheme.primary
amount = card.amount + " Sats"
}
Row(
verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.Absolute.Right,
) {
Text(
textAlign = TextAlign.End,
text = " $amount ",
color = color,
maxLines = 3,
modifier =
Modifier
.weight(1f, fill = false)
.border(Dp(.1f), color, shape = RoundedCornerShape(20)),
fontSize = 12.sp,
)
}
Text(
textAlign = TextAlign.End,
text = " $amount ",
color = color,
maxLines = 1,
modifier =
Modifier
.border(Dp(.1f), color, shape = RoundedCornerShape(20)),
fontSize = 12.sp,
)
}
Spacer(modifier = StdHorzSpacer)
card.personalized?.let {
@@ -196,24 +175,35 @@ fun RenderContentDVMThumb(
color = MaterialTheme.colorScheme.nip05
name = "Generic"
}
Spacer(modifier = StdVertSpacer)
Row(
verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.Absolute.Right,
) {
Text(
textAlign = TextAlign.End,
text = " $name ",
color = color,
maxLines = 3,
modifier =
Modifier
.padding(start = 4.dp)
.weight(1f, fill = false)
.border(Dp(.1f), color, shape = RoundedCornerShape(20)),
fontSize = 12.sp,
)
}
Text(
textAlign = TextAlign.End,
text = " $name ",
color = color,
maxLines = 1,
modifier =
Modifier
.border(Dp(.1f), color, shape = RoundedCornerShape(20)),
fontSize = 12.sp,
)
}
Spacer(modifier = Modifier.weight(1f))
Row(
verticalAlignment = CenterVertically,
horizontalArrangement = RowColSpacing5dp,
) {
LikeReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel,
nav,
)
Spacer(modifier = StdHorzSpacer)
ZapReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel,
nav = nav,
)
}
},
)
@@ -24,7 +24,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.StarBorder
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -33,6 +32,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap
import com.vitorpamplona.amethyst.ui.components.ClickableBox
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
@@ -40,14 +40,24 @@ import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent
/**
* Inline star toggle that follows / unfollows a NIP-90 content-discovery DVM.
*
* Uses [ClickableBox] (no [androidx.compose.material3.IconButton] padding) so it
* fits in a `LeftPictureLayout` title row alongside other compact reactions
* without inflating the row to 48dp.
*
* Hidden until the underlying [AppDefinitionEvent] loads and we can confirm the
* DVM advertises kind 5300 — favouriting any other DVM type would only stall on
* a 6300 reply that never comes.
*/
@Composable
fun FavoriteDvmToggle(
appDefinitionNote: AddressableNote,
accountViewModel: AccountViewModel,
modifier: Modifier = Modifier,
iconSizeModifier: Modifier = Size20Modifier,
) {
// Only NIP-90 content-discovery DVMs (kind 5300) produce a feed; hide the toggle
// for any other DVM type so users don't favourite something that would never reply.
val supportsContentDiscovery by
observeNoteAndMap(appDefinitionNote, accountViewModel) { note ->
(note.event as? AppDefinitionEvent)?.includeKind(NIP90ContentDiscoveryRequestEvent.KIND) == true
@@ -60,7 +70,8 @@ fun FavoriteDvmToggle(
val isFavorite = favorites.contains(appDefinitionNote.address)
IconButton(
ClickableBox(
modifier = modifier,
onClick = {
if (isFavorite) {
accountViewModel.unfollowFavoriteDvm(appDefinitionNote.address)
@@ -73,20 +84,19 @@ fun FavoriteDvmToggle(
)
}
},
modifier = modifier,
) {
if (isFavorite) {
Icon(
imageVector = Icons.Filled.Star,
contentDescription = stringRes(R.string.remove_dvm_from_favorites),
modifier = Size20Modifier,
modifier = iconSizeModifier,
tint = MaterialTheme.colorScheme.primary,
)
} else {
Icon(
imageVector = Icons.Outlined.StarBorder,
contentDescription = stringRes(R.string.add_dvm_to_favorites),
modifier = Size20Modifier,
modifier = iconSizeModifier,
tint = MaterialTheme.colorScheme.onSurface,
)
}