mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
feat(nutzap): notifications card surface
Phase 3 of the nutzap UX integration. Inbound NIP-61 nutzaps (kind 9321) now appear in the notifications feed alongside lightning zaps, boosts, likes, and onchain zaps — but with the cashu icon so the rail is visible at a glance. Pieces: - NotificationFeedFilter.NOTIFICATION_KINDS now includes NutzapEvent.KIND. The existing tagsAnEventByUser logic falls through to its `return true` default for nutzap (it's not a BaseNoteEvent / ReactionEvent / Repost / Git / Highlight), and the kind:9321's `p` tag carries the recipient so isTaggedUser matches. - CardFeedContentState gains a parallel nutzap grouping pass. Nutzaps targeting a specific note feed into nutzapsPerEvent and roll into the per-note MultiSetCard; nutzaps without an e-tag target (recipient-only) feed into nutzapsPerUser and surface as a NutzapUserSetCard per sender per day. - MultiSetCard extended with `nutzapEvents: ImmutableList<Note>`, and its min/max createdAt cover the cashu rail too. Adding a new field at the end with a `persistentListOf()` default keeps the existing constructor sites compatible. - New NutzapUserSetCard for the per-sender aggregate. Wraps raw kind:9321 Notes (no request/response pair like LN), keyed by pubkey+createdAt with an "N" suffix so it never collides with the LN ZapUserSetCard. - MultiSetCompose: new RenderNutzapGallery row renders the cashu icon (CustomHashTagIcons.Cashu, tint=Unspecified to preserve the brand colour) followed by the same AuthorGalleryZaps the lightning rail uses. Each kind:9321 is mapped to a ZapAmountCommentNotification with the claimed sat total and the event content as the comment. - New NutzapUserSetCompose modelled on ZapUserSetCompose, also wired into CardFeedView's card switch. The user-facing result: the notifications screen now shows a "X sent you Y sats via cashu" card with the cashu icon, exactly the same shape as the lightning version, and the per-note multi-card grows a cashu rail when the note has cashu zaps. Phase 4 next: the dedicated cashu line in ReactionDetailGallery, modelled on the existing onchain row.
This commit is contained in:
@@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -64,6 +65,8 @@ import androidx.compose.ui.window.PopupProperties
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Cashu
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.NoteState
|
||||
@@ -101,8 +104,11 @@ import com.vitorpamplona.amethyst.ui.theme.bitcoinColor
|
||||
import com.vitorpamplona.amethyst.ui.theme.overPictureBackground
|
||||
import com.vitorpamplona.amethyst.ui.theme.profile35dpModifier
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.claimedSatsTotal
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.ExperimentalTime
|
||||
@@ -182,6 +188,10 @@ private fun Galeries(
|
||||
DecryptAndRenderZapGallery(multiSetCard, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (multiSetCard.nutzapEvents.isNotEmpty()) {
|
||||
RenderNutzapGallery(multiSetCard.nutzapEvents, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (multiSetCard.boostEvents.isNotEmpty()) {
|
||||
RenderBoostGallery(multiSetCard.boostEvents, nav, accountViewModel)
|
||||
}
|
||||
@@ -340,6 +350,53 @@ fun RenderZapGallery(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderNutzapGallery(
|
||||
nutzapEvents: ImmutableList<Note>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
// Convert each kind:9321 Note into the same shape AuthorGalleryZaps
|
||||
// already renders for lightning. Amount comes from the parsed proof
|
||||
// total (lazy via NutzapEvent.claimedSatsTotal), comment from the
|
||||
// event's content. No NIP-44 decryption needed — nutzap doesn't
|
||||
// have a private variant the way NIP-57 does.
|
||||
val nutzapAuthorComments: ImmutableList<ZapAmountCommentNotification> =
|
||||
remember(nutzapEvents) {
|
||||
nutzapEvents
|
||||
.map { note ->
|
||||
val event = note.event as? NutzapEvent
|
||||
val sats = event?.claimedSatsTotal() ?: 0L
|
||||
ZapAmountCommentNotification(
|
||||
user = note.author,
|
||||
comment = event?.content?.ifBlank { null },
|
||||
amount = showAmount(java.math.BigDecimal(sats)),
|
||||
)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
Box(
|
||||
modifier = WidthAuthorPictureModifier,
|
||||
) {
|
||||
// CustomHashTagIcons.Cashu is a multi-tone branded glyph;
|
||||
// tint=Unspecified keeps the brand colours instead of
|
||||
// flattening to onBackground (which would lose the cashu-
|
||||
// orange distinguishing the row from the lightning-bolt
|
||||
// row above it).
|
||||
Icon(
|
||||
imageVector = CustomHashTagIcons.Cashu,
|
||||
contentDescription = stringRes(R.string.nutzap),
|
||||
modifier = Modifier.size(Size20dp).align(Alignment.TopEnd),
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
AuthorGalleryZaps(nutzapAuthorComments, backgroundColor, nav, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderBoostGallery(
|
||||
boostEvents: ImmutableList<Note>,
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Cashu
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NutzapUserSetCard
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.claimedSatsTotal
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Per-sender NIP-61 nutzap aggregate card — mirrors [ZapUserSetCompose] but
|
||||
* renders the cashu icon instead of the lightning bolt so the user sees the
|
||||
* rail at a glance. Built from the kind:9321 events directly: amount comes
|
||||
* from each event's parsed proof total, sender from the event's pubkey.
|
||||
* No NIP-44 decryption needed — nutzap is plaintext over the wire.
|
||||
*/
|
||||
@Composable
|
||||
fun NutzapUserSetCompose(
|
||||
nutzapSetCard: NutzapUserSetCard,
|
||||
isInnerNote: Boolean = false,
|
||||
routeForLastRead: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val backgroundColor =
|
||||
calculateBackgroundColor(
|
||||
createdAt = nutzapSetCard.createdAt,
|
||||
routeForLastRead = routeForLastRead,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
|
||||
val authorComments: ImmutableList<ZapAmountCommentNotification> =
|
||||
remember(nutzapSetCard.nutzapEvents) {
|
||||
nutzapSetCard.nutzapEvents
|
||||
.map { note ->
|
||||
val event = note.event as? NutzapEvent
|
||||
val sats = event?.claimedSatsTotal() ?: 0L
|
||||
ZapAmountCommentNotification(
|
||||
user = note.author,
|
||||
comment = event?.content?.ifBlank { null },
|
||||
amount = showAmount(java.math.BigDecimal(sats)),
|
||||
)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.background(backgroundColor.value)
|
||||
.clickable {
|
||||
nav.nav(routeFor(nutzapSetCard.user))
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
start = if (!isInnerNote) 12.dp else 0.dp,
|
||||
end = if (!isInnerNote) 12.dp else 0.dp,
|
||||
top = 10.dp,
|
||||
),
|
||||
) {
|
||||
if (!isInnerNote) {
|
||||
Box(
|
||||
modifier = Size55Modifier,
|
||||
) {
|
||||
// tint=Unspecified so the multi-tone cashu glyph keeps its
|
||||
// brand colours, the same way it does in the zap chip popup.
|
||||
Icon(
|
||||
imageVector = CustomHashTagIcons.Cashu,
|
||||
contentDescription = stringRes(R.string.nutzap),
|
||||
modifier = Modifier.size(Size25dp).align(Alignment.TopEnd),
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
AuthorGalleryZaps(authorComments, backgroundColor, nav, accountViewModel)
|
||||
}
|
||||
|
||||
Spacer(DoubleVertSpacer)
|
||||
|
||||
Row(
|
||||
Modifier.padding(start = if (!isInnerNote) 10.dp else 0.dp).fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
UserPicture(
|
||||
nutzapSetCard.user,
|
||||
Size55dp,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.padding(start = 10.dp).weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
UsernameDisplay(nutzapSetCard.user, accountViewModel = accountViewModel)
|
||||
}
|
||||
AboutDisplay(nutzapSetCard.user, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(DoubleVertSpacer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
-3
@@ -51,6 +51,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.flattenToSet
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -237,17 +238,46 @@ class CardFeedContentState(
|
||||
}
|
||||
}
|
||||
|
||||
// NIP-61 nutzaps — parallel to the lightning-zap grouping above.
|
||||
// For nutzaps targeting a specific note, accumulate into
|
||||
// [nutzapsPerEvent] so the MultiSetCard can show a cashu rail
|
||||
// alongside the lightning rail. For nutzaps with no e-tagged
|
||||
// target (just the recipient's p-tag), accumulate into
|
||||
// [nutzapsPerUser] so they surface as a NutzapUserSetCard,
|
||||
// mirroring how user-targeted lightning zaps become a
|
||||
// ZapUserSetCard.
|
||||
val nutzapsPerUser = mutableMapOf<User, MutableList<Note>>()
|
||||
val nutzapsPerEvent = mutableMapOf<Note, MutableList<Note>>()
|
||||
notes
|
||||
.filter { it.event is NutzapEvent }
|
||||
.forEach { nutzapNote ->
|
||||
val zappedPost = nutzapNote.replyTo?.lastOrNull()
|
||||
if (zappedPost != null) {
|
||||
nutzapsPerEvent
|
||||
.getOrPut(zappedPost) { mutableListOf() }
|
||||
.add(nutzapNote)
|
||||
} else {
|
||||
val sender = nutzapNote.author
|
||||
if (sender != null) {
|
||||
nutzapsPerUser
|
||||
.getOrPut(sender) { mutableListOf() }
|
||||
.add(nutzapNote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat()
|
||||
|
||||
val allBaseNotes = zapsPerEvent.keys + boostsPerEvent.keys + reactionsPerEvent.keys
|
||||
val allBaseNotes = zapsPerEvent.keys + boostsPerEvent.keys + reactionsPerEvent.keys + nutzapsPerEvent.keys
|
||||
val multiCards =
|
||||
allBaseNotes.flatMap { baseNote ->
|
||||
val boostsInCard = boostsPerEvent[baseNote] ?: emptyList()
|
||||
val reactionsInCard = reactionsPerEvent[baseNote] ?: emptyList()
|
||||
val zapsInCard = zapsPerEvent[baseNote] ?: emptyList()
|
||||
val nutzapsInCard = nutzapsPerEvent[baseNote] ?: emptyList()
|
||||
|
||||
val singleList =
|
||||
(boostsInCard + zapsInCard.map { it.response } + reactionsInCard).groupBy {
|
||||
(boostsInCard + zapsInCard.map { it.response } + reactionsInCard + nutzapsInCard).groupBy {
|
||||
sdf.format(
|
||||
Instant
|
||||
.ofEpochSecond(it.createdAt() ?: 0L)
|
||||
@@ -271,6 +301,7 @@ class CardFeedContentState(
|
||||
boostsInCard.filter { it in chunk }.toImmutableList(),
|
||||
reactionsInCard.filter { it in chunk }.toImmutableList(),
|
||||
zapsInCard.filter { it.response in chunk }.toImmutableList(),
|
||||
nutzapsInCard.filter { it in chunk }.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
@@ -299,6 +330,32 @@ class CardFeedContentState(
|
||||
}
|
||||
}.flatten()
|
||||
|
||||
// Per-sender nutzap aggregate cards — mirror userZaps but render
|
||||
// with a cashu icon so the user sees the rail at a glance. Cards
|
||||
// are bucketed by yyyy-MM-dd so a sender's daily run rolls up.
|
||||
val userNutzaps =
|
||||
nutzapsPerUser
|
||||
.map { user ->
|
||||
val byDay =
|
||||
user.value.groupBy {
|
||||
sdf.format(
|
||||
Instant
|
||||
.ofEpochSecond(it.createdAt() ?: 0L)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime(),
|
||||
)
|
||||
}
|
||||
|
||||
byDay.values.map { nutzaps ->
|
||||
NutzapUserSetCard(
|
||||
user.key,
|
||||
nutzaps
|
||||
.sortedWith(compareByDescending<Note> { it.createdAt() }.thenBy { it.idHex })
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
|
||||
val textNoteCards =
|
||||
notes
|
||||
.filter {
|
||||
@@ -316,7 +373,7 @@ class CardFeedContentState(
|
||||
}
|
||||
}
|
||||
|
||||
return (multiCards + textNoteCards + userZaps)
|
||||
return (multiCards + textNoteCards + userZaps + userNutzaps)
|
||||
.sortedWith(compareByDescending<Card> { it.createdAt() }.thenBy { it.id() })
|
||||
}
|
||||
|
||||
|
||||
+30
-1
@@ -27,6 +27,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.firstFullCharOrEmoji
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
|
||||
@@ -60,18 +61,40 @@ class ZapUserSetCard(
|
||||
override fun id() = user.pubkeyHex + "U" + createdAt
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-sender NIP-61 nutzap aggregate, parallel to [ZapUserSetCard].
|
||||
* Carries raw kind:9321 [Note]s rather than [CombinedZap] pairs because
|
||||
* a nutzap is a single event (no request/receipt split like lightning).
|
||||
* Rendered with a cashu icon so the user can tell at a glance that it's
|
||||
* a cashu rail, not a lightning rail.
|
||||
*/
|
||||
@Immutable
|
||||
class NutzapUserSetCard(
|
||||
val user: User,
|
||||
val nutzapEvents: ImmutableList<Note>,
|
||||
) : Card {
|
||||
val createdAt = nutzapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L
|
||||
|
||||
override fun createdAt(): Long = createdAt
|
||||
|
||||
// Suffix differs from ZapUserSetCard so the two never collide on id.
|
||||
override fun id() = user.pubkeyHex + "N" + createdAt
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class MultiSetCard(
|
||||
val note: Note,
|
||||
val boostEvents: ImmutableList<Note>,
|
||||
val likeEvents: ImmutableList<Note>,
|
||||
val zapEvents: ImmutableList<CombinedZap>,
|
||||
val nutzapEvents: ImmutableList<Note> = persistentListOf(),
|
||||
) : Card {
|
||||
val maxCreatedAt =
|
||||
maxOf(
|
||||
zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L,
|
||||
likeEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L,
|
||||
boostEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L,
|
||||
nutzapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L,
|
||||
)
|
||||
|
||||
val minCreatedAt =
|
||||
@@ -79,6 +102,7 @@ class MultiSetCard(
|
||||
zapEvents.minOfOrNull { it.createdAt() ?: Long.MAX_VALUE } ?: Long.MAX_VALUE,
|
||||
likeEvents.minOfOrNull { it.createdAt() ?: Long.MAX_VALUE } ?: Long.MAX_VALUE,
|
||||
boostEvents.minOfOrNull { it.createdAt() ?: Long.MAX_VALUE } ?: Long.MAX_VALUE,
|
||||
nutzapEvents.minOfOrNull { it.createdAt() ?: Long.MAX_VALUE } ?: Long.MAX_VALUE,
|
||||
)
|
||||
|
||||
val likeEventsByType =
|
||||
@@ -127,11 +151,16 @@ fun Card.containsEventId(eventId: String): Boolean =
|
||||
zapEvents.any { it.response.idHex == eventId || it.request.idHex == eventId }
|
||||
}
|
||||
|
||||
is NutzapUserSetCard -> {
|
||||
nutzapEvents.any { it.idHex == eventId }
|
||||
}
|
||||
|
||||
is MultiSetCard -> {
|
||||
note.idHex == eventId ||
|
||||
zapEvents.any { it.response.idHex == eventId || it.request.idHex == eventId } ||
|
||||
likeEvents.any { it.idHex == eventId } ||
|
||||
boostEvents.any { it.idHex == eventId }
|
||||
boostEvents.any { it.idHex == eventId } ||
|
||||
nutzapEvents.any { it.idHex == eventId }
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
+11
@@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.ui.note.CloseIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.MessageSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.MultiSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.NutzapUserSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ReplyRenderType
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -290,6 +291,16 @@ private fun RenderCardItem(
|
||||
)
|
||||
}
|
||||
|
||||
is NutzapUserSetCard -> {
|
||||
NutzapUserSetCompose(
|
||||
item,
|
||||
isInnerNote = false,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
routeForLastRead = routeForLastRead,
|
||||
)
|
||||
}
|
||||
|
||||
is MultiSetCard -> {
|
||||
MultiSetCompose(
|
||||
item,
|
||||
|
||||
+2
@@ -62,6 +62,7 @@ import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEven
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||
@@ -128,6 +129,7 @@ class NotificationFeedFilter(
|
||||
ReactionEvent.KIND,
|
||||
RepostEvent.KIND,
|
||||
LnZapEvent.KIND,
|
||||
NutzapEvent.KIND,
|
||||
OnchainZapEvent.KIND,
|
||||
LiveActivitiesChatMessageEvent.KIND,
|
||||
PictureEvent.KIND,
|
||||
|
||||
Reference in New Issue
Block a user