Merge pull request #4288 from vitorpamplona/claude/charming-lamport-klffdw

Add NIP-34 Git status event rendering support
This commit is contained in:
Vitor Pamplona
2026-09-30 16:34:02 -04:00
committed by GitHub
10 changed files with 143 additions and 34 deletions
@@ -186,6 +186,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.types.RenderFhirResource
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderFundraiser
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGeocache
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGeocacheFoundLog
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGitStatusEvent
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGoal
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderHighlight
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderInteractiveStory
@@ -332,6 +333,7 @@ import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestEvent
import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestUpdateEvent
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
@@ -1026,6 +1028,8 @@ private fun FullBleedNoteCompose(
RenderGitPullRequestEvent(baseNote, makeItShort = false, canPreview = true, quotesLeft = 3, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav)
} else if (noteEvent is GitPullRequestUpdateEvent) {
RenderGitPullRequestUpdateEvent(baseNote, makeItShort = false, canPreview = true, quotesLeft = 3, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav)
} else if (noteEvent is GitStatusEvent) {
RenderGitStatusEvent(baseNote, quotesLeft = 3, backgroundColor, accountViewModel, nav)
} else if (noteEvent is AppDefinitionEvent) {
RenderAppDefinition(baseNote, accountViewModel, nav)
} else if (noteEvent is AppRecommendationEvent) {
@@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip34Git.status.GitStatusDraftEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent
private fun GitStatusEvent.statusKind(): StatusKind =
fun GitStatusEvent.statusKind(): StatusKind =
when (this) {
is GitStatusAppliedEvent -> StatusKind.APPLIED
is GitStatusClosedEvent -> StatusKind.CLOSED
@@ -194,6 +194,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.types.RenderFhirResource
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderFundraiser
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGeocache
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGeocacheFoundLog
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGitStatusEvent
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderGoal
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderHighlight
import com.vitorpamplona.amethyst.commons.ui.note.types.RenderInteractiveStory
@@ -362,6 +363,7 @@ import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestEvent
import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestUpdateEvent
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
@@ -1393,6 +1395,10 @@ private fun RenderNoteRow(
)
}
is GitStatusEvent -> {
RenderGitStatusEvent(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
}
is EncryptedDmEvent -> {
RenderPrivateMessage(
baseNote,
@@ -89,7 +89,7 @@ fun RenderBolt12Zap(
},
)
RenderZappedPost(note, quotesLeft, cardBackground, accountViewModel, nav)
RenderTargetNote(note, quotesLeft, cardBackground, accountViewModel, nav)
amountSats?.let { ActivityAmountRow(PlatformNumberFormatter().format(it), orange) }
@@ -0,0 +1,69 @@
/*
* 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.commons.ui.note.types
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.nip34Git.ui.statusKind
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.GitStatusPill
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
import com.vitorpamplona.quartz.nip34Git.status.GitStatusEvent
/**
* Renders a NIP-34 status event (kinds 1630-1633). These usually carry an
* empty `content`, so the text fallback drew a blank note: show the new
* status as a pill, the optional comment, and the issue/patch/PR it
* targets (linked through the event's marked-`root` `e` tag) quoted below.
*/
@Composable
fun RenderGitStatusEvent(
note: Note,
quotesLeft: Int,
backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel,
nav: INav,
) {
val event = note.event as? GitStatusEvent ?: return
val kind = remember(event) { event.statusKind() }
GitStatusPill(kind)
if (event.content.isNotBlank()) {
Spacer(modifier = StdVertSpacer)
Text(
text = event.content,
style = MaterialTheme.typography.bodyMedium,
)
}
if (note.replyTo?.lastOrNull() != null) {
Spacer(modifier = StdVertSpacer)
RenderTargetNote(note, quotesLeft, backgroundColor, accountViewModel, nav)
}
}
@@ -93,7 +93,7 @@ fun RenderNutzap(
},
)
RenderZappedPost(note, quotesLeft, cardBackground, accountViewModel, nav)
RenderTargetNote(note, quotesLeft, cardBackground, accountViewModel, nav)
ActivityAmountRow(showAmount(BigDecimal(nutzapEvent.claimedSatsTotal())), orange)
@@ -150,7 +150,7 @@ fun RenderOnchainZap(
nav = nav,
)
RenderZappedPost(note, quotesLeft, cardBackground, accountViewModel, nav)
RenderTargetNote(note, quotesLeft, cardBackground, accountViewModel, nav)
AmountRow(sats = sats, orange = orange)
@@ -81,6 +81,6 @@ fun RenderReaction(
},
)
RenderZappedPost(note, quotesLeft, cardBackground, accountViewModel, nav)
RenderTargetNote(note, quotesLeft, cardBackground, accountViewModel, nav)
}
}
@@ -0,0 +1,58 @@
/*
* 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.commons.ui.note.types
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
/**
* Quotes the note that [note] acts on (the last entry of its `replyTo`) as a
* short embedded card: the post a zap or reaction targets, the issue/patch/PR
* a git status changes, and so on.
*/
@Composable
fun RenderTargetNote(
note: Note,
quotesLeft: Int,
backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel,
nav: INav,
) {
note.replyTo?.lastOrNull()?.let {
NoteCompose(
it,
modifier = Modifier,
isBoostedNote = true,
makeItShort = true,
unPackReply = ReplyRenderType.NONE,
quotesLeft = quotesLeft - 1,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
@@ -52,7 +52,6 @@ import com.vitorpamplona.amethyst.commons.ui.note.ActivityCardFrame
import com.vitorpamplona.amethyst.commons.ui.note.ActivityHeaderRow
import com.vitorpamplona.amethyst.commons.ui.note.CrossfadeToDisplayComment
import com.vitorpamplona.amethyst.commons.ui.note.DisplayBlankAuthor
import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose
import com.vitorpamplona.amethyst.commons.ui.note.UserPicture
import com.vitorpamplona.amethyst.commons.ui.note.ZapIcon
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
@@ -70,33 +69,6 @@ import kotlinx.coroutines.IO
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
/**
* Shows the post a zap targets above the transfer card, mirroring how
* reactions and reposts embed their target.
*/
@Composable
fun RenderZappedPost(
zapNote: Note,
quotesLeft: Int,
backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel,
nav: INav,
) {
zapNote.replyTo?.lastOrNull()?.let {
NoteCompose(
it,
modifier = Modifier,
isBoostedNote = true,
makeItShort = true,
unPackReply = ReplyRenderType.NONE,
quotesLeft = quotesLeft - 1,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
@Composable
fun RenderZapReceipt(
note: Note,
@@ -155,7 +127,7 @@ fun RenderZapReceiptCard(
},
)
RenderZappedPost(note, quotesLeft, cardBackground, accountViewModel, nav)
RenderTargetNote(note, quotesLeft, cardBackground, accountViewModel, nav)
card.amount?.let { ActivityAmountRow(it, orange) }