add draft in the home feed

This commit is contained in:
greenart7c3
2024-03-15 09:08:35 -03:00
parent fa5d992010
commit e292affbe6
17 changed files with 334 additions and 106 deletions
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.Nip19Bech32
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -33,26 +34,128 @@ class DraftEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 31234
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var decryptedContent: Map<HexKey, Event> = mapOf()
@Transient private var citedNotesCache: Set<String>? = null
fun replyTos(): List<HexKey> {
val oldStylePositional = tags.filter { it.size > 1 && it.size <= 3 && it[0] == "e" }.map { it[1] }
val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1)
val newStyleRoot = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "root" }?.get(1)
val newStyleReplyTos = listOfNotNull(newStyleReply, newStyleRoot)
return if (newStyleReplyTos.isNotEmpty()) {
newStyleReplyTos
} else {
oldStylePositional
}
}
fun create(
dTag: String,
originalNote: EventInterface,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (DraftEvent) -> Unit,
) {
val tags =
arrayOf(
arrayOf("d", dTag),
arrayOf("k", "${originalNote.kind()}"),
)
fun findCitations(): Set<HexKey> {
citedNotesCache?.let {
return it
}
signer.nip44Encrypt(originalNote.content(), signer.pubKey) {
signer.sign(createdAt, KIND, tags, it, onReady)
val citations = mutableSetOf<HexKey>()
// Removes citations from replies:
val matcher = tagSearch.matcher(content)
while (matcher.find()) {
try {
val tag = matcher.group(1)?.let { tags[it.toInt()] }
if (tag != null && tag.size > 1 && tag[0] == "e") {
citations.add(tag[1])
}
if (tag != null && tag.size > 1 && tag[0] == "a") {
citations.add(tag[1])
}
} catch (e: Exception) {
}
}
val matcher2 = Nip19Bech32.nip19regex.matcher(content)
while (matcher2.find()) {
val type = matcher2.group(2) // npub1
val key = matcher2.group(3) // bech32
val additionalChars = matcher2.group(4) // additional chars
if (type != null) {
val parsed = Nip19Bech32.parseComponents(type, key, additionalChars)?.entity
if (parsed != null) {
when (parsed) {
is Nip19Bech32.NEvent -> citations.add(parsed.hex)
is Nip19Bech32.NAddress -> citations.add(parsed.atag)
is Nip19Bech32.Note -> citations.add(parsed.hex)
is Nip19Bech32.NEmbed -> citations.add(parsed.event.id)
}
}
}
}
citedNotesCache = citations
return citations
}
fun tagsWithoutCitations(): List<String> {
val repliesTo = replyTos()
val tagAddresses =
taggedAddresses().filter {
it.kind != CommunityDefinitionEvent.KIND &&
it.kind != WikiNoteEvent.KIND
}.map { it.toTag() }
if (repliesTo.isEmpty() && tagAddresses.isEmpty()) return emptyList()
val citations = findCitations()
return if (citations.isEmpty()) {
repliesTo + tagAddresses
} else {
repliesTo.filter { it !in citations }
}
}
fun cachedContentFor(): Event? {
return decryptedContent[dTag()]
}
fun plainContent(
signer: NostrSigner,
onReady: (Event) -> Unit,
) {
decryptedContent[dTag()]?.let {
onReady(it)
return
}
signer.nip44Decrypt(content, signer.pubKey) { retVal ->
val event = runCatching { fromJson(retVal) }.getOrNull() ?: return@nip44Decrypt
decryptedContent = decryptedContent + Pair(dTag(), event)
onReady(event)
}
}
companion object {
const val KIND = 31234
fun create(
dTag: String,
originalNote: EventInterface,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (DraftEvent) -> Unit,
) {
val tags = mutableListOf<Array<String>>()
tags.add(arrayOf("d", dTag))
tags.add(arrayOf("k", "${originalNote.kind()}"))
tags.addAll(originalNote.tags().filter { it.size > 1 && it[0] == "e" })
tags.addAll(originalNote.tags().filter { it.size > 1 && it[0] == "a" })
signer.nip44Encrypt(originalNote.toJson(), signer.pubKey) { encryptedContent ->
signer.sign(createdAt, KIND, tags.toTypedArray(), encryptedContent, onReady)
}
}
}
}
@@ -79,6 +79,7 @@ class EventFactory {
CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
ContactListEvent.KIND -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
DeletionEvent.KIND -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
DraftEvent.KIND -> DraftEvent(id, pubKey, createdAt, tags, content, sig)
EmojiPackEvent.KIND -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
EmojiPackSelectionEvent.KIND ->
EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
@@ -20,7 +20,6 @@
*/
package com.vitorpamplona.quartz.events
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@@ -155,49 +154,4 @@ class NIP24Factory {
}
}
}
fun createTextNoteNIP24(
msg: String,
to: List<HexKey>,
signer: NostrSigner,
replyTos: List<String>? = null,
mentions: List<String>? = null,
addresses: List<ATag>?,
extraTags: List<String>?,
zapReceiver: List<ZapSplitSetup>? = null,
markAsSensitive: Boolean = false,
replyingTo: String?,
root: String?,
directMentions: Set<HexKey>,
zapRaiserAmount: Long? = null,
geohash: String? = null,
onReady: (Result) -> Unit,
) {
val senderPublicKey = signer.pubKey
TextNoteEvent.create(
msg = msg,
signer = signer,
replyTos = replyTos,
mentions = mentions,
zapReceiver = zapReceiver,
root = root,
extraTags = extraTags,
addresses = addresses,
directMentions = directMentions,
replyingTo = replyingTo,
markAsSensitive = markAsSensitive,
zapRaiserAmount = zapRaiserAmount,
geohash = geohash,
) { senderMessage ->
createWraps(senderMessage, to.plus(senderPublicKey).toSet(), signer) { wraps ->
onReady(
Result(
msg = senderMessage,
wraps = wraps,
),
)
}
}
}
}
@@ -60,6 +60,7 @@ class TextNoteEvent(
forkedFrom: Event? = null,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
isDraft: Boolean,
onReady: (TextNoteEvent) -> Unit,
) {
val tags = mutableListOf<Array<String>>()
@@ -121,7 +122,7 @@ class TextNoteEvent(
}
}
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady)
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady, isDraft)
}
}
}
@@ -32,6 +32,7 @@ abstract class NostrSigner(val pubKey: HexKey) {
tags: Array<Array<String>>,
content: String,
onReady: (T) -> Unit,
isDraft: Boolean = false,
)
abstract fun nip04Encrypt(
@@ -40,7 +40,13 @@ class NostrSignerExternal(
tags: Array<Array<String>>,
content: String,
onReady: (T) -> Unit,
isDraft: Boolean,
) {
if (isDraft) {
unsignedEvent(createdAt, kind, tags, content, onReady)
return
}
val id = Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey()
val event =
@@ -86,6 +92,28 @@ class NostrSignerExternal(
}
}
fun <T : Event> unsignedEvent(
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
onReady: (T) -> Unit,
) {
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
onReady(
EventFactory.create(
id.toHexKey(),
pubKey,
createdAt,
kind,
tags,
content,
"",
) as T,
)
}
override fun nip04Encrypt(
decryptedContent: String,
toPublicKey: HexKey,
@@ -38,9 +38,15 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
tags: Array<Array<String>>,
content: String,
onReady: (T) -> Unit,
isDraft: Boolean,
) {
if (keyPair.privKey == null) return
if (isDraft) {
unsignedEvent(createdAt, kind, tags, content, onReady)
return
}
if (isUnsignedPrivateEvent(kind, tags)) {
// this is a private zap
signPrivateZap(createdAt, kind, tags, content, onReady)
@@ -82,6 +88,30 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
)
}
fun <T : Event> unsignedEvent(
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
onReady: (T) -> Unit,
) {
if (keyPair.privKey == null) return
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
onReady(
EventFactory.create(
id.toHexKey(),
pubKey,
createdAt,
kind,
tags,
content,
"",
) as T,
)
}
override fun nip04Encrypt(
decryptedContent: String,
toPublicKey: HexKey,