mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-03-26 17:52:29 +01:00
Adds the ability to see and reply to Git Issues and Patches.
This commit is contained in:
parent
786802b708
commit
81cc985e3b
@ -67,6 +67,7 @@ import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.GeneralListEvent
|
||||
import com.vitorpamplona.quartz.events.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.events.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.events.GitReplyEvent
|
||||
import com.vitorpamplona.quartz.events.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||
@ -1349,6 +1350,63 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
fun sendGitReply(
|
||||
message: String,
|
||||
replyTo: List<Note>?,
|
||||
mentions: List<User>?,
|
||||
repository: ATag?,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
wantsToMarkAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long? = null,
|
||||
replyingTo: String?,
|
||||
root: String?,
|
||||
directMentions: Set<HexKey>,
|
||||
forkedFrom: Event?,
|
||||
relayList: List<Relay>? = null,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
|
||||
val repliesToHex = replyTo?.filter { it.address() == null }?.map { it.idHex }
|
||||
val mentionsHex = mentions?.map { it.pubkeyHex }
|
||||
val addresses = listOfNotNull(repository) + (replyTo?.mapNotNull { it.address() } ?: emptyList())
|
||||
|
||||
GitReplyEvent.create(
|
||||
msg = message,
|
||||
replyTos = repliesToHex,
|
||||
mentions = mentionsHex,
|
||||
addresses = addresses,
|
||||
extraTags = null,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
replyingTo = replyingTo,
|
||||
root = root,
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
nip94attachments = nip94attachments,
|
||||
forkedFrom = forkedFrom,
|
||||
signer = signer,
|
||||
) {
|
||||
Client.send(it, relayList = relayList)
|
||||
LocalCache.justConsume(it, null)
|
||||
|
||||
// broadcast replied notes
|
||||
replyingTo?.let {
|
||||
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
||||
Client.send(it, relayList = relayList)
|
||||
}
|
||||
}
|
||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
||||
addresses?.forEach {
|
||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||
Client.send(it, relayList = relayList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendPost(
|
||||
message: String,
|
||||
replyTo: List<Note>?,
|
||||
|
@ -451,14 +451,21 @@ object LocalCache {
|
||||
return
|
||||
}
|
||||
|
||||
val repository = event.repository()?.toTag()
|
||||
|
||||
val replyTo =
|
||||
event
|
||||
.tagsWithoutCitations()
|
||||
.filter { it != event.repository()?.toTag() }
|
||||
.filter { it != repository }
|
||||
.mapNotNull { checkGetOrCreateNote(it) }
|
||||
|
||||
// println("New GitReply ${event.id} for ${replyTo.firstOrNull()?.event?.id()} ${event.tagsWithoutCitations().filter { it != event.repository()?.toTag() }.firstOrNull()}")
|
||||
|
||||
note.loadEvent(event, author, replyTo)
|
||||
|
||||
// Counts the replies
|
||||
replyTo.forEach { it.addReply(note) }
|
||||
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.events.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.events.GitReplyEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||
import com.vitorpamplona.quartz.events.OtsEvent
|
||||
@ -138,6 +139,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||
PollNoteEvent.KIND,
|
||||
OtsEvent.KIND,
|
||||
TextNoteModificationEvent.KIND,
|
||||
GitReplyEvent.KIND,
|
||||
),
|
||||
tags = mapOf("e" to it.map { it.idHex }),
|
||||
since = findMinimumEOSEs(it),
|
||||
|
@ -59,6 +59,7 @@ import com.vitorpamplona.quartz.events.Event
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.GitIssueEvent
|
||||
import com.vitorpamplona.quartz.events.Price
|
||||
import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
@ -434,6 +435,45 @@ open class NewPostViewModel() : ViewModel() {
|
||||
nip94attachments = usedAttachments,
|
||||
)
|
||||
}
|
||||
} else if (originalNote?.event is GitIssueEvent) {
|
||||
val originalNoteEvent = originalNote?.event as GitIssueEvent
|
||||
// adds markers
|
||||
val rootId =
|
||||
originalNoteEvent.rootIssueOrPatch() // if it has a marker as root
|
||||
?: originalNote
|
||||
?.replyTo
|
||||
?.firstOrNull { it.event != null && it.replyTo?.isEmpty() == true }
|
||||
?.idHex // if it has loaded events with zero replies in the reply list
|
||||
?: originalNote?.replyTo?.firstOrNull()?.idHex // old rules, first item is root.
|
||||
?: originalNote?.idHex
|
||||
|
||||
val replyId = originalNote?.idHex
|
||||
|
||||
val replyToSet =
|
||||
if (forkedFromNote != null) {
|
||||
(listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null }
|
||||
} else {
|
||||
tagger.eTags
|
||||
}
|
||||
|
||||
val repositoryAddress = originalNoteEvent.repository()
|
||||
|
||||
account?.sendGitReply(
|
||||
message = tagger.message,
|
||||
replyTo = replyToSet,
|
||||
mentions = tagger.pTags,
|
||||
repository = repositoryAddress,
|
||||
zapReceiver = zapReceiver,
|
||||
wantsToMarkAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
replyingTo = replyId,
|
||||
root = rootId,
|
||||
directMentions = tagger.directMentions,
|
||||
forkedFrom = forkedFromNote?.event as? Event,
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
nip94attachments = usedAttachments,
|
||||
)
|
||||
} else {
|
||||
if (wantsPoll) {
|
||||
account?.sendPoll(
|
||||
|
@ -41,7 +41,7 @@ class GitIssueEvent(
|
||||
|
||||
private fun repositoryHex() = innerRepository()?.getOrNull(1)
|
||||
|
||||
fun rootIssueOrPath() = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "root" }?.get(1)
|
||||
fun rootIssueOrPatch() = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "root" }?.get(1)
|
||||
|
||||
fun repository() =
|
||||
innerRepository()?.let {
|
||||
|
@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@ -75,5 +76,87 @@ class GitReplyEvent(
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
}
|
||||
|
||||
fun create(
|
||||
msg: String,
|
||||
replyTos: List<String>? = null,
|
||||
mentions: List<String>? = null,
|
||||
addresses: List<ATag>? = null,
|
||||
extraTags: List<String>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
replyingTo: String? = null,
|
||||
root: String? = null,
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (GitReplyEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
replyTos?.let {
|
||||
tags.addAll(
|
||||
it.positionalMarkedTags(
|
||||
tagName = "e",
|
||||
root = root,
|
||||
replyingTo = replyingTo,
|
||||
directMentions = directMentions,
|
||||
forkedFrom = forkedFrom?.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
mentions?.forEach {
|
||||
if (it in directMentions) {
|
||||
tags.add(arrayOf("p", it, "", "mention"))
|
||||
} else {
|
||||
tags.add(arrayOf("p", it))
|
||||
}
|
||||
}
|
||||
replyTos?.forEach {
|
||||
if (it in directMentions) {
|
||||
tags.add(arrayOf("q", it))
|
||||
}
|
||||
}
|
||||
addresses
|
||||
?.map { it.toTag() }
|
||||
?.let {
|
||||
tags.addAll(
|
||||
it.positionalMarkedTags(
|
||||
tagName = "a",
|
||||
root = root,
|
||||
replyingTo = replyingTo,
|
||||
directMentions = directMentions,
|
||||
forkedFrom = (forkedFrom as? AddressableEvent)?.address()?.toTag(),
|
||||
),
|
||||
)
|
||||
}
|
||||
findHashtags(msg).forEach {
|
||||
tags.add(arrayOf("t", it))
|
||||
tags.add(arrayOf("t", it.lowercase()))
|
||||
}
|
||||
extraTags?.forEach { tags.add(arrayOf("t", it)) }
|
||||
zapReceiver?.forEach {
|
||||
tags.add(arrayOf("zap", it.lnAddressOrPubKeyHex, it.relay ?: "", it.weight.toString()))
|
||||
}
|
||||
findURLs(msg).forEach { tags.add(arrayOf("r", it)) }
|
||||
if (markAsSensitive) {
|
||||
tags.add(arrayOf("content-warning", ""))
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
tags.add(arrayOf("alt", "a git issue reply"))
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,45 +123,45 @@ class TextNoteEvent(
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of NIP-10 marked tags that are also ordered at best effort to support the
|
||||
* deprecated method of positional tags to maximize backwards compatibility with clients that
|
||||
* support replies but have not been updated to understand tag markers.
|
||||
*
|
||||
* https://github.com/nostr-protocol/nips/blob/master/10.md
|
||||
*
|
||||
* The tag to the root of the reply chain goes first. The tag to the reply event being responded
|
||||
* to goes last. The order for any other tag does not matter, so keep the relative order.
|
||||
*/
|
||||
private fun List<String>.positionalMarkedTags(
|
||||
tagName: String,
|
||||
root: String?,
|
||||
replyingTo: String?,
|
||||
directMentions: Set<HexKey>,
|
||||
forkedFrom: String?,
|
||||
) = sortedWith { o1, o2 ->
|
||||
when {
|
||||
o1 == o2 -> 0
|
||||
o1 == root -> -1 // root goes first
|
||||
o2 == root -> 1 // root goes first
|
||||
o1 == replyingTo -> 1 // reply event being responded to goes last
|
||||
o2 == replyingTo -> -1 // reply event being responded to goes last
|
||||
else -> 0 // keep the relative order for any other tag
|
||||
}
|
||||
}
|
||||
.map {
|
||||
when (it) {
|
||||
root -> arrayOf(tagName, it, "", "root")
|
||||
replyingTo -> arrayOf(tagName, it, "", "reply")
|
||||
forkedFrom -> arrayOf(tagName, it, "", "fork")
|
||||
in directMentions -> arrayOf(tagName, it, "", "mention")
|
||||
else -> arrayOf(tagName, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findURLs(text: String): List<String> {
|
||||
return UrlDetector(text, UrlDetectorOptions.Default).detect().map { it.originalUrl }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of NIP-10 marked tags that are also ordered at best effort to support the
|
||||
* deprecated method of positional tags to maximize backwards compatibility with clients that
|
||||
* support replies but have not been updated to understand tag markers.
|
||||
*
|
||||
* https://github.com/nostr-protocol/nips/blob/master/10.md
|
||||
*
|
||||
* The tag to the root of the reply chain goes first. The tag to the reply event being responded
|
||||
* to goes last. The order for any other tag does not matter, so keep the relative order.
|
||||
*/
|
||||
fun List<String>.positionalMarkedTags(
|
||||
tagName: String,
|
||||
root: String?,
|
||||
replyingTo: String?,
|
||||
directMentions: Set<HexKey>,
|
||||
forkedFrom: String?,
|
||||
) = sortedWith { o1, o2 ->
|
||||
when {
|
||||
o1 == o2 -> 0
|
||||
o1 == root -> -1 // root goes first
|
||||
o2 == root -> 1 // root goes first
|
||||
o1 == replyingTo -> 1 // reply event being responded to goes last
|
||||
o2 == replyingTo -> -1 // reply event being responded to goes last
|
||||
else -> 0 // keep the relative order for any other tag
|
||||
}
|
||||
}
|
||||
.map {
|
||||
when (it) {
|
||||
root -> arrayOf(tagName, it, "", "root")
|
||||
replyingTo -> arrayOf(tagName, it, "", "reply")
|
||||
forkedFrom -> arrayOf(tagName, it, "", "fork")
|
||||
in directMentions -> arrayOf(tagName, it, "", "mention")
|
||||
else -> arrayOf(tagName, it)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user