Draft event refactoring
This commit is contained in:
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel
|
|||||||
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListDecryptionCache
|
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListDecryptionCache
|
||||||
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListState
|
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListState
|
||||||
import com.vitorpamplona.amethyst.model.localRelays.LocalRelayListState
|
import com.vitorpamplona.amethyst.model.localRelays.LocalRelayListState
|
||||||
|
import com.vitorpamplona.amethyst.model.nip01UserMetadata.AccountHomeRelayState
|
||||||
import com.vitorpamplona.amethyst.model.nip01UserMetadata.AccountOutboxRelayState
|
import com.vitorpamplona.amethyst.model.nip01UserMetadata.AccountOutboxRelayState
|
||||||
import com.vitorpamplona.amethyst.model.nip01UserMetadata.NotificationInboxRelayState
|
import com.vitorpamplona.amethyst.model.nip01UserMetadata.NotificationInboxRelayState
|
||||||
import com.vitorpamplona.amethyst.model.nip01UserMetadata.UserMetadataState
|
import com.vitorpamplona.amethyst.model.nip01UserMetadata.UserMetadataState
|
||||||
@@ -130,16 +131,13 @@ import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
|||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
|
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
|
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser
|
import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
|
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
||||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
|
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
|
||||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||||
import com.vitorpamplona.quartz.nip04Dm.messages.reply
|
|
||||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||||
@@ -160,12 +158,10 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
|||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
|
||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftBuilder
|
import com.vitorpamplona.quartz.nip37Drafts.DraftBuilder
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEventCache
|
import com.vitorpamplona.quartz.nip37Drafts.DraftEventCache
|
||||||
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.Response
|
import com.vitorpamplona.quartz.nip47WalletConnect.Response
|
||||||
@@ -217,6 +213,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@Stable
|
@Stable
|
||||||
@@ -297,6 +294,7 @@ class Account(
|
|||||||
val serverLists = MergedServerListState(fileStorageServers, blossomServers, scope)
|
val serverLists = MergedServerListState(fileStorageServers, blossomServers, scope)
|
||||||
|
|
||||||
// Relay settings
|
// Relay settings
|
||||||
|
val homeRelays = AccountHomeRelayState(nip65RelayList, privateStorageRelayList, localRelayList, scope)
|
||||||
val outboxRelays = AccountOutboxRelayState(nip65RelayList, privateStorageRelayList, localRelayList, broadcastRelayList, scope)
|
val outboxRelays = AccountOutboxRelayState(nip65RelayList, privateStorageRelayList, localRelayList, broadcastRelayList, scope)
|
||||||
val dmRelays = DmInboxRelayState(dmRelayList, nip65RelayList, privateStorageRelayList, localRelayList, scope)
|
val dmRelays = DmInboxRelayState(dmRelayList, nip65RelayList, privateStorageRelayList, localRelayList, scope)
|
||||||
val notificationRelays = NotificationInboxRelayState(nip65RelayList, localRelayList, scope)
|
val notificationRelays = NotificationInboxRelayState(nip65RelayList, localRelayList, scope)
|
||||||
@@ -1141,103 +1139,42 @@ class Account(
|
|||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T : Event> signAndSend(
|
|
||||||
draftTag: String?,
|
|
||||||
template: EventTemplate<T>,
|
|
||||||
relayList: Set<NormalizedRelayUrl>,
|
|
||||||
broadcastNotes: List<Entity>,
|
|
||||||
) = signAndSend(draftTag, template, relayList, mapEntitiesToNotes(broadcastNotes).toSet())
|
|
||||||
|
|
||||||
suspend fun <T : Event> signAndSend(
|
|
||||||
draftTag: String?,
|
|
||||||
template: EventTemplate<T>,
|
|
||||||
relayList: Set<NormalizedRelayUrl>,
|
|
||||||
broadcastNotes: Set<Note>,
|
|
||||||
) {
|
|
||||||
if (draftTag != null) {
|
|
||||||
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
|
||||||
val draftEvent =
|
|
||||||
DraftEvent.create(
|
|
||||||
dTag = draftTag,
|
|
||||||
innerEvent = rumor,
|
|
||||||
anchorTagArray = emptyList(),
|
|
||||||
signer = signer,
|
|
||||||
)
|
|
||||||
draftsDecryptionCache.preload(draftEvent, rumor)
|
|
||||||
sendDraftEvent(draftEvent)
|
|
||||||
} else {
|
|
||||||
val it = signer.sign(template)
|
|
||||||
cache.justConsumeMyOwnEvent(it)
|
|
||||||
client.send(it, relayList = relayList)
|
|
||||||
|
|
||||||
broadcastNotes.forEach { it.event?.let { client.send(it, relayList = relayList) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <T : Event> signAndSendNIP04Message(
|
|
||||||
draftTag: String?,
|
|
||||||
template: EventTemplate<T>,
|
|
||||||
relayList: Set<NormalizedRelayUrl>,
|
|
||||||
) {
|
|
||||||
if (draftTag != null) {
|
|
||||||
if (template.content.isEmpty()) {
|
|
||||||
deleteDraft(draftTag)
|
|
||||||
} else {
|
|
||||||
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
|
||||||
val draftEvent = DraftEvent.create(draftTag, rumor, emptyList(), signer)
|
|
||||||
draftsDecryptionCache.preload(draftEvent, rumor)
|
|
||||||
sendDraftEvent(draftEvent)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val newEvent = signer.sign(template)
|
|
||||||
cache.justConsumeMyOwnEvent(newEvent)
|
|
||||||
client.send(newEvent, relayList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <T : Event> signAndSendWithList(
|
|
||||||
draftTag: String?,
|
|
||||||
template: EventTemplate<T>,
|
|
||||||
relayList: Collection<NormalizedRelayUrl>,
|
|
||||||
broadcastNotes: Set<Note>,
|
|
||||||
) {
|
|
||||||
if (draftTag != null) {
|
|
||||||
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
|
||||||
val draftEvent = DraftEvent.create(draftTag, rumor, emptyList(), signer)
|
|
||||||
draftsDecryptionCache.preload(draftEvent, rumor)
|
|
||||||
sendDraftEvent(draftEvent)
|
|
||||||
} else {
|
|
||||||
val event = signer.sign(template)
|
|
||||||
cache.justConsumeMyOwnEvent(event)
|
|
||||||
|
|
||||||
val relaySet = relayList.toSet()
|
|
||||||
|
|
||||||
client.send(event, relayList = relaySet)
|
|
||||||
broadcastNotes.forEach { it.event?.let { client.send(it, relayList = relaySet) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createAndSendDraft(
|
suspend fun createAndSendDraft(
|
||||||
draftTag: String,
|
draftTag: String,
|
||||||
template: EventTemplate<out Event>,
|
template: EventTemplate<out Event>,
|
||||||
|
broadcast: Set<Event> = emptySet(),
|
||||||
) {
|
) {
|
||||||
|
val extraRelays = cache.getAddressableNoteIfExists(DraftWrapEvent.createAddressTag(signer.pubKey, draftTag))?.relays ?: emptyList()
|
||||||
|
|
||||||
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
||||||
val draftEvent = DraftBuilder.encryptAndSign(draftTag, rumor, signer)
|
val draftEvent = DraftBuilder.encryptAndSign(draftTag, rumor, signer)
|
||||||
draftsDecryptionCache.preload(draftEvent, rumor)
|
draftsDecryptionCache.preload(draftEvent, rumor)
|
||||||
sendDraftEvent(draftEvent)
|
|
||||||
|
cache.justConsumeMyOwnEvent(draftEvent)
|
||||||
|
|
||||||
|
val relayList = (privateStorageRelayList.flow.value + localRelayList.flow.value + extraRelays).toSet()
|
||||||
|
if (relayList.isNotEmpty()) {
|
||||||
|
client.send(draftEvent, relayList)
|
||||||
|
broadcast.forEach {
|
||||||
|
client.send(it, relayList.toSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteDraft(draftTag: String) {
|
suspend fun deleteDraft(draftTag: String) {
|
||||||
val key = DraftEvent.createAddressTag(userProfile().pubkeyHex, draftTag)
|
val extraRelays = cache.getAddressableNoteIfExists(DraftWrapEvent.createAddressTag(signer.pubKey, draftTag))?.relays ?: emptyList()
|
||||||
cache.getAddressableNoteIfExists(key)?.let { note ->
|
|
||||||
val noteEvent = note.event
|
|
||||||
if (noteEvent is DraftEvent) {
|
|
||||||
val deletedDraftEvent = noteEvent.createDeletedEvent(signer)
|
|
||||||
client.send(deletedDraftEvent, outboxRelays.flow.value + note.relays)
|
|
||||||
cache.justConsumeMyOwnEvent(deletedDraftEvent)
|
|
||||||
|
|
||||||
delete(deletedDraftEvent, note.relays.toSet())
|
val deletedDraft = DraftWrapEvent.createDeletedEvent(draftTag, signer)
|
||||||
}
|
val deletionEvent = signer.sign(DeletionEvent.build(listOf(deletedDraft)))
|
||||||
|
|
||||||
|
val relayList = (privateStorageRelayList.flow.value + localRelayList.flow.value + extraRelays).toSet()
|
||||||
|
|
||||||
|
cache.justConsumeMyOwnEvent(deletedDraft)
|
||||||
|
cache.justConsumeMyOwnEvent(deletionEvent)
|
||||||
|
|
||||||
|
if (relayList.isNotEmpty()) {
|
||||||
|
client.send(deletedDraft, relayList)
|
||||||
|
client.send(deletionEvent, relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1257,7 +1194,18 @@ class Account(
|
|||||||
currentSceneRelay = readingSceneRelay,
|
currentSceneRelay = readingSceneRelay,
|
||||||
)
|
)
|
||||||
|
|
||||||
sendToPrivateOutboxAndLocal(signer.sign(template))
|
val event = signer.sign(template)
|
||||||
|
|
||||||
|
// updates relays that already have this replaceable.
|
||||||
|
val noteRelays = cache.getAddressableNoteIfExists(event.address())?.relays ?: emptyList()
|
||||||
|
|
||||||
|
val relayList = privateStorageRelayList.flow.value + localRelayList.flow.value
|
||||||
|
if (relayList.isNotEmpty()) {
|
||||||
|
client.send(event, relayList + noteRelays)
|
||||||
|
} else {
|
||||||
|
client.send(event, outboxRelays.flow.value + noteRelays)
|
||||||
|
}
|
||||||
|
cache.justConsumeMyOwnEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateInteractiveStoryReadingState(
|
suspend fun updateInteractiveStoryReadingState(
|
||||||
@@ -1274,7 +1222,18 @@ class Account(
|
|||||||
currentSceneRelay = readingSceneRelay,
|
currentSceneRelay = readingSceneRelay,
|
||||||
)
|
)
|
||||||
|
|
||||||
sendToPrivateOutboxAndLocal(signer.sign(template))
|
val event = signer.sign(template)
|
||||||
|
|
||||||
|
// updates relays that already have this replaceable.
|
||||||
|
val noteRelays = cache.getAddressableNoteIfExists(event.address())?.relays ?: emptyList()
|
||||||
|
|
||||||
|
val relayList = privateStorageRelayList.flow.value + localRelayList.flow.value
|
||||||
|
if (relayList.isNotEmpty()) {
|
||||||
|
client.send(event, relayList + noteRelays)
|
||||||
|
} else {
|
||||||
|
client.send(event, outboxRelays.flow.value + noteRelays)
|
||||||
|
}
|
||||||
|
cache.justConsumeMyOwnEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mapEntitiesToNotes(entities: List<Entity>): List<Note> =
|
fun mapEntitiesToNotes(entities: List<Entity>): List<Note> =
|
||||||
@@ -1328,7 +1287,15 @@ class Account(
|
|||||||
contentWarningReason?.let { contentWarning(contentWarningReason) }
|
contentWarningReason?.let { contentWarning(contentWarningReason) }
|
||||||
}
|
}
|
||||||
|
|
||||||
signAndSend(draftTag, template, relayList, quotes)
|
if (draftTag != null) {
|
||||||
|
createAndSendDraft(draftTag, template)
|
||||||
|
} else {
|
||||||
|
val it = signer.sign(template)
|
||||||
|
cache.justConsumeMyOwnEvent(it)
|
||||||
|
client.send(it, relayList = relayList)
|
||||||
|
|
||||||
|
mapEntitiesToNotes(quotes).forEach { it.event?.let { client.send(it, relayList = relayList) } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendInteractiveStoryScene(
|
suspend fun sendInteractiveStoryScene(
|
||||||
@@ -1363,29 +1330,42 @@ class Account(
|
|||||||
contentWarningReason?.let { contentWarning(contentWarningReason) }
|
contentWarningReason?.let { contentWarning(contentWarningReason) }
|
||||||
}
|
}
|
||||||
|
|
||||||
signAndSend(draftTag, template, relayList, mapEntitiesToNotes(quotes).toSet())
|
val broadcastNotes = mapEntitiesToNotes(quotes).toSet()
|
||||||
|
|
||||||
|
if (draftTag != null) {
|
||||||
|
createAndSendDraft(draftTag, template)
|
||||||
|
} else {
|
||||||
|
val it = signer.sign(template)
|
||||||
|
cache.justConsumeMyOwnEvent(it)
|
||||||
|
client.send(it, relayList = relayList)
|
||||||
|
|
||||||
|
broadcastNotes.forEach { it.event?.let { client.send(it, relayList = relayList) } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendAddBounty(
|
suspend fun sendAddBounty(
|
||||||
value: BigDecimal,
|
value: BigDecimal,
|
||||||
bounty: Note,
|
bounty: Note,
|
||||||
draftTag: String?,
|
|
||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val event = bounty.event as? TextNoteEvent ?: return
|
val bountyEvent = bounty.event as? TextNoteEvent ?: return
|
||||||
val eventAuthor = bounty.author ?: return
|
val bountyAuthor = bounty.author ?: return
|
||||||
|
|
||||||
val template =
|
val template =
|
||||||
BountyAddValueEvent.build(
|
BountyAddValueEvent.build(
|
||||||
value,
|
amount = value,
|
||||||
EventHintBundle(event, bounty.relayHintUrl()),
|
bountyRoot = EventHintBundle(bountyEvent, bounty.relayHintUrl()),
|
||||||
eventAuthor.toPTag(),
|
bountyRootAuthor = bountyAuthor.toPTag(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val relays = bounty.relays + outboxRelays.flow.value
|
val relays = (bounty.relays + outboxRelays.flow.value).toSet()
|
||||||
|
|
||||||
signAndSendWithList(draftTag, template, relays, setOf(bounty))
|
val newEvent = signer.sign(template)
|
||||||
|
cache.justConsumeMyOwnEvent(newEvent)
|
||||||
|
|
||||||
|
client.send(newEvent, relayList = relays)
|
||||||
|
client.send(bountyEvent, relayList = relays)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendEdit(
|
suspend fun sendEdit(
|
||||||
@@ -1417,82 +1397,27 @@ class Account(
|
|||||||
broadcast.forEach { client.send(it, relayList) }
|
broadcast.forEach { client.send(it, relayList) }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendPrivateMessage(
|
suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<PrivateDmEvent>) {
|
||||||
message: String,
|
|
||||||
toUser: PTag,
|
|
||||||
replyingTo: Note? = null,
|
|
||||||
zapReceiver: List<ZapSplitSetup>? = null,
|
|
||||||
contentWarningReason: String? = null,
|
|
||||||
zapRaiserAmount: Long? = null,
|
|
||||||
geohash: String? = null,
|
|
||||||
imetas: List<IMetaTag>? = null,
|
|
||||||
emojis: List<EmojiUrlTag>? = null,
|
|
||||||
draftTag: String?,
|
|
||||||
) {
|
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val encryptedContent =
|
val newEvent = signer.sign(eventTemplate)
|
||||||
signer.nip04Encrypt(
|
val recipient = newEvent.verifiedRecipientPubKey()
|
||||||
PrivateDmEvent.prepareMessageToEncrypt(message, imetas),
|
val destinationRelays = recipient?.let { cache.getOrCreateUser(it).dmInboxRelays() } ?: emptyList()
|
||||||
toUser.pubKey,
|
|
||||||
)
|
|
||||||
|
|
||||||
val template =
|
cache.justConsumeMyOwnEvent(newEvent)
|
||||||
PrivateDmEvent.build(toUser, encryptedContent) {
|
client.send(newEvent, outboxRelays.flow.value + destinationRelays)
|
||||||
replyingTo?.let { reply(it.toEId()) }
|
|
||||||
|
|
||||||
geohash?.let { geohash(it) }
|
|
||||||
zapRaiserAmount?.let { zapraiser(it) }
|
|
||||||
zapReceiver?.let { zapSplits(it) }
|
|
||||||
emojis?.let { emojis(it) }
|
|
||||||
contentWarningReason?.let { contentWarning(contentWarningReason) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val destinationRelays = cache.getOrCreateUser(toUser.pubKey).dmInboxRelays()
|
|
||||||
|
|
||||||
signAndSendNIP04Message(draftTag, template, outboxRelays.flow.value + destinationRelays)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendNIP17EncryptedFile(template: EventTemplate<ChatMessageEncryptedFileHeaderEvent>) {
|
suspend fun sendNip17EncryptedFile(template: EventTemplate<ChatMessageEncryptedFileHeaderEvent>) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val wraps = NIP17Factory().createEncryptedFileNIP17(template, signer)
|
val wraps = NIP17Factory().createEncryptedFileNIP17(template, signer)
|
||||||
broadcastPrivately(wraps)
|
broadcastPrivately(wraps)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendNIP17PrivateMessage(
|
suspend fun sendNip17PrivateMessage(template: EventTemplate<ChatMessageEvent>) {
|
||||||
template: EventTemplate<ChatMessageEvent>,
|
val events = NIP17Factory().createMessageNIP17(template, signer)
|
||||||
draftTag: String? = null,
|
broadcastPrivately(events)
|
||||||
) {
|
|
||||||
if (draftTag != null) {
|
|
||||||
if (template.content.isEmpty()) {
|
|
||||||
deleteDraft(draftTag)
|
|
||||||
} else {
|
|
||||||
val rumor = RumorAssembler.assembleRumor(signer.pubKey, template)
|
|
||||||
val draftEvent = DraftEvent.create(draftTag, rumor, emptyList(), signer)
|
|
||||||
draftsDecryptionCache.preload(draftEvent, rumor)
|
|
||||||
sendDraftEvent(draftEvent)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val it = NIP17Factory().createMessageNIP17(template, signer)
|
|
||||||
broadcastPrivately(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendDraftEvent(draftEvent: DraftEvent) {
|
|
||||||
sendToPrivateOutboxAndLocal(draftEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendToPrivateOutboxAndLocal(event: Event?) {
|
|
||||||
if (event == null) return
|
|
||||||
|
|
||||||
val relayList = privateStorageRelayList.flow.value + localRelayList.flow.value
|
|
||||||
if (relayList.isNotEmpty()) {
|
|
||||||
client.send(event, relayList.toSet())
|
|
||||||
} else {
|
|
||||||
client.send(event, outboxRelays.flow.value)
|
|
||||||
}
|
|
||||||
cache.justConsumeMyOwnEvent(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun broadcastPrivately(signedEvents: NIP17Factory.Result) {
|
suspend fun broadcastPrivately(signedEvents: NIP17Factory.Result) {
|
||||||
@@ -1645,7 +1570,7 @@ class Account(
|
|||||||
privateDMDecryptionCache.cachedDM(event)
|
privateDMDecryptionCache.cachedDM(event)
|
||||||
} else if (event is LnZapRequestEvent && event.isPrivateZap()) {
|
} else if (event is LnZapRequestEvent && event.isPrivateZap()) {
|
||||||
privateZapsDecryptionCache.cachedPrivateZap(event)?.content
|
privateZapsDecryptionCache.cachedPrivateZap(event)?.content
|
||||||
} else if (event is DraftEvent) {
|
} else if (event is DraftWrapEvent) {
|
||||||
draftsDecryptionCache.preCachedDraft(event)?.content
|
draftsDecryptionCache.preCachedDraft(event)?.content
|
||||||
} else {
|
} else {
|
||||||
event.content
|
event.content
|
||||||
@@ -1669,7 +1594,7 @@ class Account(
|
|||||||
} else {
|
} else {
|
||||||
event.content
|
event.content
|
||||||
}
|
}
|
||||||
} else if (event is DraftEvent && isWriteable()) {
|
} else if (event is DraftWrapEvent && isWriteable()) {
|
||||||
draftsDecryptionCache.cachedDraft(event)?.content
|
draftsDecryptionCache.cachedDraft(event)?.content
|
||||||
} else {
|
} else {
|
||||||
event?.content
|
event?.content
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
|||||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.list.tags.ChannelTag
|
import com.vitorpamplona.quartz.nip28PublicChat.list.tags.ChannelTag
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
||||||
@@ -89,7 +89,7 @@ val DefaultIndexerRelayList = setOf(Constants.purplepages, Constants.coracle, Co
|
|||||||
val DefaultSignerPermissions =
|
val DefaultSignerPermissions =
|
||||||
listOf(
|
listOf(
|
||||||
Permission(CommandType.SIGN_EVENT, RelayAuthEvent.KIND),
|
Permission(CommandType.SIGN_EVENT, RelayAuthEvent.KIND),
|
||||||
Permission(CommandType.SIGN_EVENT, DraftEvent.KIND),
|
Permission(CommandType.SIGN_EVENT, DraftWrapEvent.KIND),
|
||||||
Permission(CommandType.NIP04_ENCRYPT),
|
Permission(CommandType.NIP04_ENCRYPT),
|
||||||
Permission(CommandType.NIP04_DECRYPT),
|
Permission(CommandType.NIP04_DECRYPT),
|
||||||
Permission(CommandType.NIP44_DECRYPT),
|
Permission(CommandType.NIP44_DECRYPT),
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
|
|||||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
|
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||||
@@ -2536,7 +2536,7 @@ object LocalCache : ILocalCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun consume(
|
fun consume(
|
||||||
event: DraftEvent,
|
event: DraftWrapEvent,
|
||||||
relay: NormalizedRelayUrl?,
|
relay: NormalizedRelayUrl?,
|
||||||
wasVerified: Boolean,
|
wasVerified: Boolean,
|
||||||
): Boolean = !event.isDeleted() && consumeBaseReplaceable(event, relay, wasVerified)
|
): Boolean = !event.isDeleted() && consumeBaseReplaceable(event, relay, wasVerified)
|
||||||
@@ -2744,7 +2744,7 @@ object LocalCache : ILocalCache {
|
|||||||
is CommunityPostApprovalEvent -> consume(event, relay, wasVerified)
|
is CommunityPostApprovalEvent -> consume(event, relay, wasVerified)
|
||||||
is ContactListEvent -> consume(event, relay, wasVerified)
|
is ContactListEvent -> consume(event, relay, wasVerified)
|
||||||
is DeletionEvent -> consume(event, relay, wasVerified)
|
is DeletionEvent -> consume(event, relay, wasVerified)
|
||||||
is DraftEvent -> consume(event, relay, wasVerified)
|
is DraftWrapEvent -> consume(event, relay, wasVerified)
|
||||||
is EmojiPackEvent -> consume(event, relay, wasVerified)
|
is EmojiPackEvent -> consume(event, relay, wasVerified)
|
||||||
is EmojiPackSelectionEvent -> consume(event, relay, wasVerified)
|
is EmojiPackSelectionEvent -> consume(event, relay, wasVerified)
|
||||||
is EphemeralChatEvent -> consume(event, relay, wasVerified)
|
is EphemeralChatEvent -> consume(event, relay, wasVerified)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.model
|
|||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import coil3.util.CoilUtils.result
|
|
||||||
import com.vitorpamplona.amethyst.model.nip47WalletConnect.NwcSignerState
|
import com.vitorpamplona.amethyst.model.nip47WalletConnect.NwcSignerState
|
||||||
import com.vitorpamplona.amethyst.model.nip51Lists.HiddenUsersState
|
import com.vitorpamplona.amethyst.model.nip51Lists.HiddenUsersState
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
@@ -53,7 +52,7 @@ import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
|||||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.LnZapPaymentRequestEvent
|
import com.vitorpamplona.quartz.nip47WalletConnect.LnZapPaymentRequestEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.LnZapPaymentResponseEvent
|
import com.vitorpamplona.quartz.nip47WalletConnect.LnZapPaymentResponseEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceMethod
|
import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceMethod
|
||||||
@@ -236,7 +235,7 @@ open class Note(
|
|||||||
|
|
||||||
open fun createdAt() = event?.createdAt
|
open fun createdAt() = event?.createdAt
|
||||||
|
|
||||||
fun isDraft() = event is DraftEvent
|
fun isDraft() = event is DraftWrapEvent
|
||||||
|
|
||||||
fun loadEvent(
|
fun loadEvent(
|
||||||
event: Event,
|
event: Event,
|
||||||
|
|||||||
+2
-2
@@ -44,8 +44,8 @@ class AccountOutboxRelayState(
|
|||||||
privateStorage.flow,
|
privateStorage.flow,
|
||||||
local.flow,
|
local.flow,
|
||||||
broadcast.flow,
|
broadcast.flow,
|
||||||
) { nip65Inbox, privateOutBox, localRelays, broadcastRelays ->
|
) { nip65Outbox, privateOutBox, localRelays, broadcastRelays ->
|
||||||
nip65Inbox + privateOutBox + localRelays + broadcastRelays
|
nip65Outbox + privateOutBox + localRelays + broadcastRelays
|
||||||
}.flowOn(Dispatchers.Default)
|
}.flowOn(Dispatchers.Default)
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope,
|
scope,
|
||||||
|
|||||||
+2
-2
@@ -24,13 +24,13 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||||
|
|
||||||
val ReportsAndBookmarksFromKeyKinds =
|
val ReportsAndBookmarksFromKeyKinds =
|
||||||
listOf(
|
listOf(
|
||||||
DraftEvent.KIND,
|
DraftWrapEvent.KIND,
|
||||||
ReportEvent.KIND,
|
ReportEvent.KIND,
|
||||||
BookmarkListEvent.KIND,
|
BookmarkListEvent.KIND,
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
|||||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
|
import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||||
@@ -63,7 +63,7 @@ fun routeFor(
|
|||||||
noteEvent: Event,
|
noteEvent: Event,
|
||||||
loggedIn: Account,
|
loggedIn: Account,
|
||||||
): Route? {
|
): Route? {
|
||||||
if (noteEvent is DraftEvent) {
|
if (noteEvent is DraftWrapEvent) {
|
||||||
val innerEvent = loggedIn.draftsDecryptionCache.preCachedDraft(noteEvent)
|
val innerEvent = loggedIn.draftsDecryptionCache.preCachedDraft(noteEvent)
|
||||||
|
|
||||||
if (innerEvent is IsInPublicChatChannel) {
|
if (innerEvent is IsInPublicChatChannel) {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
|||||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||||
@@ -552,7 +552,7 @@ fun InnerNoteWithReactions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isNotRepost = baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent && baseNote.event !is DraftEvent
|
val isNotRepost = baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent && baseNote.event !is DraftWrapEvent
|
||||||
|
|
||||||
if (isNotRepost) {
|
if (isNotRepost) {
|
||||||
if (makeItShort) {
|
if (makeItShort) {
|
||||||
@@ -568,7 +568,7 @@ fun InnerNoteWithReactions(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (baseNote.event is DraftEvent) {
|
if (baseNote.event is DraftWrapEvent) {
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
Spacer(modifier = DoubleVertSpacer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,7 +647,7 @@ private fun RenderNoteRow(
|
|||||||
is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav)
|
is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav)
|
||||||
is AudioTrackEvent -> RenderAudioTrack(baseNote, ContentScale.FillWidth, accountViewModel, nav)
|
is AudioTrackEvent -> RenderAudioTrack(baseNote, ContentScale.FillWidth, accountViewModel, nav)
|
||||||
is AudioHeaderEvent -> RenderAudioHeader(baseNote, ContentScale.FillWidth, accountViewModel, nav)
|
is AudioHeaderEvent -> RenderAudioHeader(baseNote, ContentScale.FillWidth, accountViewModel, nav)
|
||||||
is DraftEvent -> RenderDraft(baseNote, quotesLeft, unPackReply, backgroundColor, accountViewModel, nav)
|
is DraftWrapEvent -> RenderDraft(baseNote, quotesLeft, unPackReply, backgroundColor, accountViewModel, nav)
|
||||||
is ReactionEvent -> RenderReaction(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
is ReactionEvent -> RenderReaction(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||||
is RepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
is RepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||||
is GenericRepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
is GenericRepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||||
@@ -901,7 +901,7 @@ fun ObserveDraftEvent(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
render: @Composable (Note) -> Unit,
|
render: @Composable (Note) -> Unit,
|
||||||
) {
|
) {
|
||||||
val noteEvent by observeNoteEvent<DraftEvent>(note, accountViewModel)
|
val noteEvent by observeNoteEvent<DraftWrapEvent>(note, accountViewModel)
|
||||||
|
|
||||||
noteEvent?.let {
|
noteEvent?.let {
|
||||||
val innerNote by produceCachedStateAsync(cache = accountViewModel.draftNoteCache, key = it)
|
val innerNote by produceCachedStateAsync(cache = accountViewModel.draftNoteCache, key = it)
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ class AddBountyAmountViewModel : ViewModel() {
|
|||||||
myAccount.sendAddBounty(
|
myAccount.sendAddBounty(
|
||||||
newValue,
|
newValue,
|
||||||
bountyInner,
|
bountyInner,
|
||||||
draftTag = null,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
|
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
||||||
@@ -202,7 +202,7 @@ open class CommentPostViewModel :
|
|||||||
val noteEvent = draft.event
|
val noteEvent = draft.event
|
||||||
val noteAuthor = draft.author
|
val noteAuthor = draft.author
|
||||||
|
|
||||||
if (noteEvent is DraftEvent && noteAuthor != null) {
|
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
@@ -324,13 +324,14 @@ open class CommentPostViewModel :
|
|||||||
if (message.text.isBlank()) {
|
if (message.text.isBlank()) {
|
||||||
accountViewModel.account.deleteDraft(draftTag.current)
|
accountViewModel.account.deleteDraft(draftTag.current)
|
||||||
} else {
|
} else {
|
||||||
val template = createTemplate() ?: return
|
val attachments = mutableSetOf<Event>()
|
||||||
accountViewModel.account.createAndSendDraft(draftTag.current, template)
|
|
||||||
|
|
||||||
nip95attachments.forEach {
|
nip95attachments.forEach {
|
||||||
account.sendToPrivateOutboxAndLocal(it.first)
|
attachments.add(it.first)
|
||||||
account.sendToPrivateOutboxAndLocal(it.second)
|
attachments.add(it.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val template = createTemplate() ?: return
|
||||||
|
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -115,7 +115,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
|||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
|
import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
||||||
import com.vitorpamplona.quartz.nip47WalletConnect.Response
|
import com.vitorpamplona.quartz.nip47WalletConnect.Response
|
||||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||||
@@ -1510,7 +1510,7 @@ class AccountViewModel(
|
|||||||
account.deleteDraft(draftTag)
|
account.deleteDraft(draftTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createTempDraftNote(noteEvent: DraftEvent): Note? = draftNoteCache.update(noteEvent)
|
suspend fun createTempDraftNote(noteEvent: DraftWrapEvent): Note? = draftNoteCache.update(noteEvent)
|
||||||
|
|
||||||
fun createTempDraftNote(
|
fun createTempDraftNote(
|
||||||
innerEvent: Event,
|
innerEvent: Event,
|
||||||
@@ -1691,8 +1691,8 @@ class AccountViewModel(
|
|||||||
|
|
||||||
class CachedDraftNotes(
|
class CachedDraftNotes(
|
||||||
val accountViewModel: AccountViewModel,
|
val accountViewModel: AccountViewModel,
|
||||||
) : GenericBaseCacheAsync<DraftEvent, Note>(20) {
|
) : GenericBaseCacheAsync<DraftWrapEvent, Note>(20) {
|
||||||
override suspend fun compute(key: DraftEvent): Note? =
|
override suspend fun compute(key: DraftWrapEvent): Note? =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val decrypted = accountViewModel.account.draftsDecryptionCache.cachedDraft(key)
|
val decrypted = accountViewModel.account.draftsDecryptionCache.cachedDraft(key)
|
||||||
if (decrypted != null) {
|
if (decrypted != null) {
|
||||||
|
|||||||
+6
-6
@@ -32,7 +32,7 @@ import com.vitorpamplona.quartz.nip01Core.core.IEvent
|
|||||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||||
@@ -74,7 +74,7 @@ class EventProcessor(
|
|||||||
when (event) {
|
when (event) {
|
||||||
is ChatroomKeyable -> chatHandler.add(event, eventNote, publicNote)
|
is ChatroomKeyable -> chatHandler.add(event, eventNote, publicNote)
|
||||||
is OtsEvent -> otsHandler.add(event, eventNote, publicNote)
|
is OtsEvent -> otsHandler.add(event, eventNote, publicNote)
|
||||||
is DraftEvent -> draftHandler.add(event, eventNote, publicNote)
|
is DraftWrapEvent -> draftHandler.add(event, eventNote, publicNote)
|
||||||
is GiftWrapEvent -> giftWrapHandler.add(event, eventNote, publicNote)
|
is GiftWrapEvent -> giftWrapHandler.add(event, eventNote, publicNote)
|
||||||
is SealedRumorEvent -> sealHandler.add(event, eventNote, publicNote)
|
is SealedRumorEvent -> sealHandler.add(event, eventNote, publicNote)
|
||||||
is LnZapRequestEvent -> zapRequest.add(event, eventNote, publicNote)
|
is LnZapRequestEvent -> zapRequest.add(event, eventNote, publicNote)
|
||||||
@@ -98,7 +98,7 @@ class EventProcessor(
|
|||||||
when (event) {
|
when (event) {
|
||||||
is ChatroomKeyable -> chatHandler.delete(event, note)
|
is ChatroomKeyable -> chatHandler.delete(event, note)
|
||||||
is OtsEvent -> otsHandler.delete(event, note)
|
is OtsEvent -> otsHandler.delete(event, note)
|
||||||
is DraftEvent -> draftHandler.delete(event, note)
|
is DraftWrapEvent -> draftHandler.delete(event, note)
|
||||||
is GiftWrapEvent -> giftWrapHandler.delete(event, note)
|
is GiftWrapEvent -> giftWrapHandler.delete(event, note)
|
||||||
is SealedRumorEvent -> sealHandler.delete(event, note)
|
is SealedRumorEvent -> sealHandler.delete(event, note)
|
||||||
is LnZapRequestEvent -> zapRequest.delete(event, note)
|
is LnZapRequestEvent -> zapRequest.delete(event, note)
|
||||||
@@ -129,7 +129,7 @@ class EventProcessor(
|
|||||||
val deletedDrafts =
|
val deletedDrafts =
|
||||||
newNotes.mapNotNull { note ->
|
newNotes.mapNotNull { note ->
|
||||||
val event = note.event
|
val event = note.event
|
||||||
if (event is DraftEvent &&
|
if (event is DraftWrapEvent &&
|
||||||
event.isDeleted() &&
|
event.isDeleted() &&
|
||||||
!cache.deletionIndex.hasBeenDeleted(event)
|
!cache.deletionIndex.hasBeenDeleted(event)
|
||||||
) {
|
) {
|
||||||
@@ -193,9 +193,9 @@ class OtsEventHandler(
|
|||||||
class DraftEventHandler(
|
class DraftEventHandler(
|
||||||
private val account: Account,
|
private val account: Account,
|
||||||
private val cache: LocalCache,
|
private val cache: LocalCache,
|
||||||
) : EventHandler<DraftEvent> {
|
) : EventHandler<DraftWrapEvent> {
|
||||||
override suspend fun add(
|
override suspend fun add(
|
||||||
event: DraftEvent,
|
event: DraftWrapEvent,
|
||||||
eventNote: Note,
|
eventNote: Note,
|
||||||
publicNote: Note,
|
publicNote: Note,
|
||||||
) {
|
) {
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@ import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState
|
import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RefreshingChatroomFeedView(
|
fun RefreshingChatroomFeedView(
|
||||||
@@ -129,7 +129,7 @@ fun ChatFeedLoaded(
|
|||||||
) {
|
) {
|
||||||
itemsIndexed(items.list, key = { _, item -> item.idHex }) { index, item ->
|
itemsIndexed(items.list, key = { _, item -> item.idHex }) { index, item ->
|
||||||
val noteEvent = item.event
|
val noteEvent = item.event
|
||||||
if (avoidDraft == null || noteEvent !is DraftEvent || noteEvent.dTag() !in avoidDraft.usedDraftTags) {
|
if (avoidDraft == null || noteEvent !is DraftWrapEvent || noteEvent.dTag() !in avoidDraft.usedDraftTags) {
|
||||||
ChatroomMessageCompose(
|
ChatroomMessageCompose(
|
||||||
baseNote = item,
|
baseNote = item,
|
||||||
routeForLastRead = routeForLastRead,
|
routeForLastRead = routeForLastRead,
|
||||||
|
|||||||
+4
-4
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
|||||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ fun NormalChatNote(
|
|||||||
|
|
||||||
ChatBubbleLayout(
|
ChatBubbleLayout(
|
||||||
isLoggedInUser = isLoggedInUser,
|
isLoggedInUser = isLoggedInUser,
|
||||||
isDraft = note.event is DraftEvent,
|
isDraft = note.event is DraftWrapEvent,
|
||||||
innerQuote = innerQuote,
|
innerQuote = innerQuote,
|
||||||
isComplete = accountViewModel.settings.featureSet == FeatureSetType.COMPLETE,
|
isComplete = accountViewModel.settings.featureSet == FeatureSetType.COMPLETE,
|
||||||
hasDetailsToShow = note.zaps.isNotEmpty() || note.zapPayments.isNotEmpty() || note.reactions.isNotEmpty(),
|
hasDetailsToShow = note.zaps.isNotEmpty() || note.zapPayments.isNotEmpty() || note.reactions.isNotEmpty(),
|
||||||
@@ -289,7 +289,7 @@ private fun MessageBubbleLines(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
if (baseNote.event !is DraftEvent) {
|
if (baseNote.event !is DraftWrapEvent) {
|
||||||
RenderReplyRow(
|
RenderReplyRow(
|
||||||
note = baseNote,
|
note = baseNote,
|
||||||
innerQuote = innerQuote,
|
innerQuote = innerQuote,
|
||||||
@@ -388,7 +388,7 @@ fun NoteRow(
|
|||||||
when (note.event) {
|
when (note.event) {
|
||||||
is ChannelCreateEvent -> RenderCreateChannelNote(note, bgColor, accountViewModel, nav)
|
is ChannelCreateEvent -> RenderCreateChannelNote(note, bgColor, accountViewModel, nav)
|
||||||
is ChannelMetadataEvent -> RenderChangeChannelMetadataNote(note, bgColor, accountViewModel, nav)
|
is ChannelMetadataEvent -> RenderChangeChannelMetadataNote(note, bgColor, accountViewModel, nav)
|
||||||
is DraftEvent -> RenderDraftEvent(note, canPreview, innerQuote, onWantsToReply, onWantsToEditDraft, bgColor, accountViewModel, nav)
|
is DraftWrapEvent -> RenderDraftEvent(note, canPreview, innerQuote, onWantsToReply, onWantsToEditDraft, bgColor, accountViewModel, nav)
|
||||||
is ChatMessageEncryptedFileHeaderEvent -> RenderEncryptedFile(note, bgColor, accountViewModel, nav)
|
is ChatMessageEncryptedFileHeaderEvent -> RenderEncryptedFile(note, bgColor, accountViewModel, nav)
|
||||||
else -> RenderRegularTextNote(note, canPreview, innerQuote, bgColor, accountViewModel, nav)
|
else -> RenderRegularTextNote(note, canPreview, innerQuote, bgColor, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -107,7 +107,7 @@ fun NewChatroomSubjectDialog(
|
|||||||
groupName.value.ifBlank { null }?.let { changeSubject(it) }
|
groupName.value.ifBlank { null }?.let { changeSubject(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
accountViewModel.account.sendNIP17PrivateMessage(template)
|
accountViewModel.account.sendNip17PrivateMessage(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose()
|
onClose()
|
||||||
|
|||||||
+25
-13
@@ -79,7 +79,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
||||||
@@ -252,7 +252,7 @@ class ChatNewMessageViewModel :
|
|||||||
val noteEvent = draft.event
|
val noteEvent = draft.event
|
||||||
val noteAuthor = draft.author
|
val noteAuthor = draft.author
|
||||||
|
|
||||||
if (noteEvent is DraftEvent && noteAuthor != null) {
|
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
@@ -421,7 +421,7 @@ class ChatNewMessageViewModel :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun innerSendPost(dTag: String?) {
|
private suspend fun innerSendPost(draftTag: String?) {
|
||||||
val room = room ?: return
|
val room = room ?: return
|
||||||
|
|
||||||
val urls = findURLs(message.text)
|
val urls = findURLs(message.text)
|
||||||
@@ -468,19 +468,31 @@ class ChatNewMessageViewModel :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountViewModel.account.sendNIP17PrivateMessage(template, dTag)
|
if (draftTag != null) {
|
||||||
|
accountViewModel.account.createAndSendDraft(draftTag, template)
|
||||||
|
} else {
|
||||||
|
accountViewModel.account.sendNip17PrivateMessage(template)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
accountViewModel.account.sendPrivateMessage(
|
val toUser = room.users.first().let { LocalCache.getOrCreateUser(it).toPTag() }
|
||||||
message = message,
|
|
||||||
toUser = room.users.first().let { LocalCache.getOrCreateUser(it).toPTag() },
|
val template =
|
||||||
replyingTo = replyTo.value,
|
PrivateDmEvent.build(
|
||||||
contentWarningReason = null,
|
toUser = toUser,
|
||||||
imetas = usedAttachments,
|
message = message,
|
||||||
draftTag = dTag,
|
imetas = usedAttachments,
|
||||||
)
|
replyingTo = replyTo.value?.toEId(),
|
||||||
|
signer = accountViewModel.account.signer,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (draftTag != null) {
|
||||||
|
accountViewModel.account.createAndSendDraft(draftTag, template)
|
||||||
|
} else {
|
||||||
|
accountViewModel.account.sendNip04PrivateMessage(template)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dTag == null) {
|
if (draftTag == null) {
|
||||||
ChatFileSender(room, accountViewModel.account).sendAll(uploadsWaitingToBeSent)
|
ChatFileSender(room, accountViewModel.account).sendAll(uploadsWaitingToBeSent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-13
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
|
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
|
||||||
|
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||||
import com.vitorpamplona.quartz.nip17Dm.files.encryption.AESGCM
|
import com.vitorpamplona.quartz.nip17Dm.files.encryption.AESGCM
|
||||||
@@ -48,7 +49,7 @@ class ChatFileSender(
|
|||||||
contentWarningReason: String?,
|
contentWarningReason: String?,
|
||||||
cipher: AESGCM,
|
cipher: AESGCM,
|
||||||
) {
|
) {
|
||||||
account.sendNIP17EncryptedFile(
|
account.sendNip17EncryptedFile(
|
||||||
ChatMessageEncryptedFileHeaderEvent.build(
|
ChatMessageEncryptedFileHeaderEvent.build(
|
||||||
url = result.url,
|
url = result.url,
|
||||||
to = chatroom.users.map { LocalCache.getOrCreateUser(it).toPTag() },
|
to = chatroom.users.map { LocalCache.getOrCreateUser(it).toPTag() },
|
||||||
@@ -89,18 +90,18 @@ class ChatFileSender(
|
|||||||
val iMetaAttachments = IMetaAttachments()
|
val iMetaAttachments = IMetaAttachments()
|
||||||
iMetaAttachments.add(result, caption, contentWarningReason)
|
iMetaAttachments.add(result, caption, contentWarningReason)
|
||||||
|
|
||||||
account.sendPrivateMessage(
|
val toUser = chatroom.users.first().let { LocalCache.getOrCreateUser(it).toPTag() }
|
||||||
message = result.url,
|
|
||||||
toUser = chatroom.users.first().let { LocalCache.getOrCreateUser(it).toPTag() },
|
val template =
|
||||||
replyingTo = null,
|
PrivateDmEvent.build(
|
||||||
zapReceiver = null,
|
toUser = toUser,
|
||||||
contentWarningReason = null,
|
message = result.url,
|
||||||
zapRaiserAmount = null,
|
imetas = iMetaAttachments.iMetaAttachments,
|
||||||
geohash = null,
|
replyingTo = null,
|
||||||
imetas = iMetaAttachments.iMetaAttachments,
|
signer = account.signer,
|
||||||
emojis = null,
|
)
|
||||||
draftTag = null,
|
|
||||||
)
|
account.sendNip04PrivateMessage(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendAll(uploads: List<SuccessfulUploads>) {
|
suspend fun sendAll(uploads: List<SuccessfulUploads>) {
|
||||||
|
|||||||
+9
-3
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.notify
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.notify
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||||
@@ -191,7 +191,7 @@ open class ChannelNewMessageViewModel :
|
|||||||
val noteEvent = draft.event
|
val noteEvent = draft.event
|
||||||
val noteAuthor = draft.author
|
val noteAuthor = draft.author
|
||||||
|
|
||||||
if (noteEvent is DraftEvent && noteAuthor != null) {
|
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
@@ -281,8 +281,14 @@ open class ChannelNewMessageViewModel :
|
|||||||
if (message.text.isBlank()) {
|
if (message.text.isBlank()) {
|
||||||
account.deleteDraft(draftTag.current)
|
account.deleteDraft(draftTag.current)
|
||||||
} else {
|
} else {
|
||||||
|
val attachments = mutableSetOf<Event>()
|
||||||
|
nip95attachments.forEach {
|
||||||
|
attachments.add(it.first)
|
||||||
|
attachments.add(it.second)
|
||||||
|
}
|
||||||
|
|
||||||
val template = createTemplate() ?: return
|
val template = createTemplate() ?: return
|
||||||
accountViewModel.account.createAndSendDraft(draftTag.current, template)
|
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -86,7 +86,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
|||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatroomHeaderCompose(
|
fun ChatroomHeaderCompose(
|
||||||
@@ -113,7 +113,7 @@ fun ChatroomComposeChannelOrUser(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val baseNoteEvent = baseNote.event
|
val baseNoteEvent = baseNote.event
|
||||||
if (baseNoteEvent is DraftEvent) {
|
if (baseNoteEvent is DraftWrapEvent) {
|
||||||
ObserveDraftEvent(baseNote, accountViewModel) { innerNote ->
|
ObserveDraftEvent(baseNote, accountViewModel) { innerNote ->
|
||||||
ChatroomEntry(innerNote, accountViewModel, nav)
|
ChatroomEntry(innerNote, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -78,7 +78,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
|
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
||||||
@@ -192,7 +192,7 @@ open class NewProductViewModel :
|
|||||||
val noteEvent = draft.event
|
val noteEvent = draft.event
|
||||||
val noteAuthor = draft.author
|
val noteAuthor = draft.author
|
||||||
|
|
||||||
if (noteEvent is DraftEvent && noteAuthor != null) {
|
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
|
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
|
|
||||||
class DraftEventsFeedFilter(
|
class DraftEventsFeedFilter(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
@@ -48,7 +48,7 @@ class DraftEventsFeedFilter(
|
|||||||
|
|
||||||
fun acceptableEvent(it: Note): Boolean {
|
fun acceptableEvent(it: Note): Boolean {
|
||||||
val noteEvent = it.event
|
val noteEvent = it.event
|
||||||
return noteEvent is DraftEvent && noteEvent.pubKey == account.userProfile().pubkeyHex
|
return noteEvent is DraftWrapEvent && noteEvent.pubKey == account.userProfile().pubkeyHex
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sort(items: Set<Note>): List<Note> = items.sortedWith(DefaultFeedOrder)
|
override fun sort(items: Set<Note>): List<Note> = items.sortedWith(DefaultFeedOrder)
|
||||||
|
|||||||
+8
-9
@@ -96,7 +96,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
|||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
||||||
@@ -241,7 +241,7 @@ open class ShortNotePostViewModel :
|
|||||||
val noteEvent = draft?.event
|
val noteEvent = draft?.event
|
||||||
val noteAuthor = draft?.author
|
val noteAuthor = draft?.author
|
||||||
|
|
||||||
if (draft != null && noteEvent is DraftEvent && noteAuthor != null) {
|
if (draft != null && noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
@@ -483,18 +483,17 @@ open class ShortNotePostViewModel :
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendDraftSync() {
|
suspend fun sendDraftSync() {
|
||||||
val accountViewModel = accountViewModel
|
|
||||||
|
|
||||||
if (message.text.isBlank()) {
|
if (message.text.isBlank()) {
|
||||||
accountViewModel.account.deleteDraft(draftTag.current)
|
accountViewModel.account.deleteDraft(draftTag.current)
|
||||||
} else {
|
} else {
|
||||||
val template = createTemplate() ?: return
|
val attachments = mutableSetOf<Event>()
|
||||||
accountViewModel.account.createAndSendDraft(draftTag.current, template)
|
|
||||||
|
|
||||||
nip95attachments.forEach {
|
nip95attachments.forEach {
|
||||||
account.sendToPrivateOutboxAndLocal(it.first)
|
attachments.add(it.first)
|
||||||
account.sendToPrivateOutboxAndLocal(it.second)
|
attachments.add(it.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val template = createTemplate() ?: return
|
||||||
|
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-9
@@ -85,7 +85,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
||||||
@@ -235,7 +235,7 @@ class NewPublicMessageViewModel :
|
|||||||
val noteEvent = draft.event
|
val noteEvent = draft.event
|
||||||
val noteAuthor = draft.author
|
val noteAuthor = draft.author
|
||||||
|
|
||||||
if (noteEvent is DraftEvent && noteAuthor != null) {
|
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
|
||||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||||
@@ -323,18 +323,17 @@ class NewPublicMessageViewModel :
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendDraftSync() {
|
suspend fun sendDraftSync() {
|
||||||
val accountViewModel = accountViewModel
|
|
||||||
|
|
||||||
if (message.text.isBlank()) {
|
if (message.text.isBlank()) {
|
||||||
accountViewModel.account.deleteDraft(draftTag.current)
|
accountViewModel.account.deleteDraft(draftTag.current)
|
||||||
} else {
|
} else {
|
||||||
val template = createTemplate() ?: return
|
val broadcast = mutableSetOf<Event>()
|
||||||
accountViewModel.account.createAndSendDraft(draftTag.current, template)
|
|
||||||
|
|
||||||
nip95attachments.forEach {
|
nip95attachments.forEach {
|
||||||
account.sendToPrivateOutboxAndLocal(it.first)
|
broadcast.add(it.first)
|
||||||
account.sendToPrivateOutboxAndLocal(it.second)
|
broadcast.add(it.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val template = createTemplate() ?: return
|
||||||
|
accountViewModel.account.createAndSendDraft(draftTag.current, template, broadcast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -208,7 +208,7 @@ import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
|||||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||||
@@ -641,7 +641,7 @@ private fun FullBleedNoteCompose(
|
|||||||
RenderGitIssueEvent(baseNote, makeItShort = false, canPreview = true, quotesLeft = 3, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav)
|
RenderGitIssueEvent(baseNote, makeItShort = false, canPreview = true, quotesLeft = 3, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav)
|
||||||
} else if (noteEvent is AppDefinitionEvent) {
|
} else if (noteEvent is AppDefinitionEvent) {
|
||||||
RenderAppDefinition(baseNote, accountViewModel, nav)
|
RenderAppDefinition(baseNote, accountViewModel, nav)
|
||||||
} else if (noteEvent is DraftEvent) {
|
} else if (noteEvent is DraftWrapEvent) {
|
||||||
RenderDraft(baseNote, 3, true, backgroundColor, accountViewModel, nav)
|
RenderDraft(baseNote, 3, true, backgroundColor, accountViewModel, nav)
|
||||||
} else if (noteEvent is HighlightEvent) {
|
} else if (noteEvent is HighlightEvent) {
|
||||||
RenderHighlight(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
RenderHighlight(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
|
|||||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
|
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
|
||||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||||
@@ -216,7 +216,7 @@ class EventFactory {
|
|||||||
CommunityPostApprovalEvent.KIND -> CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
|
CommunityPostApprovalEvent.KIND -> CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ContactListEvent.KIND -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
|
ContactListEvent.KIND -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
DeletionEvent.KIND -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
DeletionEvent.KIND -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
DraftEvent.KIND -> DraftEvent(id, pubKey, createdAt, tags, content, sig)
|
DraftWrapEvent.KIND -> DraftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EmojiPackEvent.KIND -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
|
EmojiPackEvent.KIND -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EmojiPackSelectionEvent.KIND -> EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
|
EmojiPackSelectionEvent.KIND -> EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EphemeralChatEvent.KIND -> EphemeralChatEvent(id, pubKey, createdAt, tags, content, sig)
|
EphemeralChatEvent.KIND -> EphemeralChatEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
|||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
||||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||||
@@ -121,16 +122,37 @@ class PrivateDmEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(
|
suspend fun build(
|
||||||
to: PTag,
|
toUser: PTag,
|
||||||
encryptedMessage: String,
|
message: String,
|
||||||
|
imetas: List<IMetaTag>? = null,
|
||||||
|
replyingTo: EventReference? = null,
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
signer: NostrSigner,
|
||||||
initializer: TagArrayBuilder<PrivateDmEvent>.() -> Unit = {},
|
initializer: TagArrayBuilder<PrivateDmEvent>.() -> Unit = {},
|
||||||
) = eventTemplate(KIND, encryptedMessage, createdAt) {
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description =
|
||||||
|
signer.nip04Encrypt(
|
||||||
|
prepareMessageToEncrypt(message, imetas),
|
||||||
|
toUser.pubKey,
|
||||||
|
),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
alt(ALT)
|
alt(ALT)
|
||||||
pTag(to)
|
pTag(toUser)
|
||||||
|
replyingTo?.let { reply(it) }
|
||||||
|
|
||||||
initializer()
|
initializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
to: PTag,
|
||||||
|
message: String,
|
||||||
|
imetas: List<IMetaTag>? = null,
|
||||||
|
replyingTo: EventReference?,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
) = signer.sign(build(to, message, imetas, replyingTo, createdAt, signer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ class DraftBuilder {
|
|||||||
draft: T,
|
draft: T,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): DraftEvent {
|
): DraftWrapEvent {
|
||||||
val encryptedContent = signer.nip44Encrypt(draft.toJson(), signer.pubKey)
|
val encryptedContent = signer.nip44Encrypt(draft.toJson(), signer.pubKey)
|
||||||
val template =
|
val template =
|
||||||
eventTemplate<DraftEvent>(DraftEvent.KIND, encryptedContent, createdAt) {
|
eventTemplate<DraftWrapEvent>(DraftWrapEvent.KIND, encryptedContent, createdAt) {
|
||||||
alt(DraftEvent.ALT_DESCRIPTION)
|
alt(DraftWrapEvent.ALT_DESCRIPTION)
|
||||||
dTag(dTag)
|
dTag(dTag)
|
||||||
kind(draft.kind)
|
kind(draft.kind)
|
||||||
|
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.quartz.nip37Drafts
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException
|
|
||||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
|
|
||||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
|
||||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
|
||||||
import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
|
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class DraftEvent(
|
|
||||||
id: HexKey,
|
|
||||||
pubKey: HexKey,
|
|
||||||
createdAt: Long,
|
|
||||||
tags: Array<Array<String>>,
|
|
||||||
content: String,
|
|
||||||
sig: HexKey,
|
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
|
||||||
EventHintProvider,
|
|
||||||
AddressHintProvider,
|
|
||||||
PubKeyHintProvider {
|
|
||||||
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
|
|
||||||
|
|
||||||
override fun linkedPubKeys() = tags.mapNotNull(PTag::parseKey)
|
|
||||||
|
|
||||||
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
|
|
||||||
|
|
||||||
override fun linkedEventIds() = tags.mapNotNull(ETag::parseId)
|
|
||||||
|
|
||||||
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
|
|
||||||
|
|
||||||
override fun linkedAddressIds() = tags.mapNotNull(ATag::parseAddressId)
|
|
||||||
|
|
||||||
override fun isContentEncoded() = true
|
|
||||||
|
|
||||||
fun isDeleted() = content == ""
|
|
||||||
|
|
||||||
fun canDecrypt(signer: NostrSigner) = signer.pubKey == pubKey
|
|
||||||
|
|
||||||
suspend fun createDeletedEvent(signer: NostrSigner): DraftEvent = signer.sign(createdAt, KIND, tags, "")
|
|
||||||
|
|
||||||
suspend fun decryptInnerEvent(signer: NostrSigner): Event {
|
|
||||||
if (!canDecrypt(signer)) throw SignerExceptions.UnauthorizedDecryptionException()
|
|
||||||
|
|
||||||
val json = signer.nip44Decrypt(content, pubKey)
|
|
||||||
return try {
|
|
||||||
fromJson(json)
|
|
||||||
} catch (e: JsonParseException) {
|
|
||||||
Log.w("DraftEvent", "Unable to parse inner event of a draft: $json")
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KIND = 31234
|
|
||||||
const val ALT_DESCRIPTION = "Draft Event"
|
|
||||||
|
|
||||||
fun createAddressTag(
|
|
||||||
pubKey: HexKey,
|
|
||||||
dTag: String,
|
|
||||||
): String = Address.assemble(KIND, pubKey, dTag)
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: TorrentCommentEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tagsWithMarkers =
|
|
||||||
originalNote.tags.filter {
|
|
||||||
it.size > 3 && (it[0] == "e" || it[0] == "a") && (it[3] == "root" || it[3] == "reply")
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tagsWithMarkers, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: InteractiveStoryBaseEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tags = mutableListOf<Array<String>>()
|
|
||||||
return create(dTag, originalNote, tags, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: LiveActivitiesChatMessageEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tags = mutableListOf<Array<String>>()
|
|
||||||
originalNote.activity()?.let { tags.add(arrayOf("a", it.toTag(), "", "root")) }
|
|
||||||
originalNote.replyingTo()?.let { tags.add(arrayOf("e", it, "", "reply")) }
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tags, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: ChannelMessageEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tags = mutableListOf<Array<String>>()
|
|
||||||
originalNote.channelId()?.let { tags.add(arrayOf("e", it)) }
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tags, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: GitReplyEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tags = mutableListOf<Array<String>>()
|
|
||||||
originalNote.repository()?.let { tags.add(arrayOf("a", it.toTag())) }
|
|
||||||
originalNote.replyingTo()?.let { tags.add(arrayOf("e", it)) }
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tags, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: PollNoteEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tagsWithMarkers =
|
|
||||||
originalNote.tags.filter {
|
|
||||||
it.size > 3 && (it[0] == "e" || it[0] == "a") && (it[3] == "root" || it[3] == "reply")
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tagsWithMarkers, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: CommentEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tagsWithMarkers = originalNote.rootScopes() + originalNote.directReplies()
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tagsWithMarkers, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
originalNote: TextNoteEvent,
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tagsWithMarkers =
|
|
||||||
originalNote.tags.filter {
|
|
||||||
it.size > 3 && (it[0] == "e" || it[0] == "a") && (it[3] == "root" || it[3] == "reply")
|
|
||||||
}
|
|
||||||
|
|
||||||
return create(dTag, originalNote, tagsWithMarkers, signer, createdAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(
|
|
||||||
dTag: String,
|
|
||||||
innerEvent: Event,
|
|
||||||
anchorTagArray: List<Array<String>> = emptyList(),
|
|
||||||
signer: NostrSigner,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
|
||||||
): DraftEvent {
|
|
||||||
val tags = mutableListOf<Array<String>>()
|
|
||||||
tags.add(arrayOf("d", dTag))
|
|
||||||
tags.add(arrayOf("k", "${innerEvent.kind}"))
|
|
||||||
|
|
||||||
if (anchorTagArray.isNotEmpty()) {
|
|
||||||
tags.addAll(anchorTagArray)
|
|
||||||
}
|
|
||||||
|
|
||||||
val draft =
|
|
||||||
signer.sign<DraftEvent>(
|
|
||||||
createdAt = createdAt,
|
|
||||||
kind = KIND,
|
|
||||||
tags = tags.toTypedArray(),
|
|
||||||
content = signer.nip44Encrypt(innerEvent.toJson(), signer.pubKey),
|
|
||||||
)
|
|
||||||
|
|
||||||
return draft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,8 @@ class DraftEventCache(
|
|||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
) {
|
) {
|
||||||
private val decryptionCache =
|
private val decryptionCache =
|
||||||
object : LruCache<DraftEvent, DraftEventDecryptCache>(1000) {
|
object : LruCache<DraftWrapEvent, DraftEventDecryptCache>(1000) {
|
||||||
override fun create(key: DraftEvent): DraftEventDecryptCache? =
|
override fun create(key: DraftWrapEvent): DraftEventDecryptCache? =
|
||||||
if (!key.isDeleted() && key.pubKey == signer.pubKey) {
|
if (!key.isDeleted() && key.pubKey == signer.pubKey) {
|
||||||
DraftEventDecryptCache(signer)
|
DraftEventDecryptCache(signer)
|
||||||
} else {
|
} else {
|
||||||
@@ -38,23 +38,23 @@ class DraftEventCache(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(event: DraftEvent) = decryptionCache.remove(event)
|
fun delete(event: DraftWrapEvent) = decryptionCache.remove(event)
|
||||||
|
|
||||||
fun preload(
|
fun preload(
|
||||||
event: DraftEvent,
|
event: DraftWrapEvent,
|
||||||
result: Event,
|
result: Event,
|
||||||
) = decryptionCache[event]?.preload(result)
|
) = decryptionCache[event]?.preload(result)
|
||||||
|
|
||||||
fun preCachedDraft(event: DraftEvent): Event? = decryptionCache[event]?.cached()
|
fun preCachedDraft(event: DraftWrapEvent): Event? = decryptionCache[event]?.cached()
|
||||||
|
|
||||||
suspend fun cachedDraft(event: DraftEvent) = decryptionCache[event]?.decrypt(event)
|
suspend fun cachedDraft(event: DraftWrapEvent) = decryptionCache[event]?.decrypt(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DraftEventDecryptCache(
|
class DraftEventDecryptCache(
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
) : DecryptCache<DraftEvent, Event>(signer) {
|
) : DecryptCache<DraftWrapEvent, Event>(signer) {
|
||||||
override suspend fun decryptAndParse(
|
override suspend fun decryptAndParse(
|
||||||
event: DraftEvent,
|
event: DraftWrapEvent,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
): Event = event.decryptInnerEvent(signer)
|
): Event = event.decryptInnerEvent(signer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 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.quartz.nip37Drafts
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class DraftWrapEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
override fun isContentEncoded() = true
|
||||||
|
|
||||||
|
fun isDeleted() = content == ""
|
||||||
|
|
||||||
|
fun canDecrypt(signer: NostrSigner) = signer.pubKey == pubKey
|
||||||
|
|
||||||
|
suspend fun decryptInnerEvent(signer: NostrSigner): Event {
|
||||||
|
if (!canDecrypt(signer)) throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
|
||||||
|
val json = signer.nip44Decrypt(content, pubKey)
|
||||||
|
return try {
|
||||||
|
fromJson(json)
|
||||||
|
} catch (e: JsonParseException) {
|
||||||
|
Log.w("DraftEvent", "Unable to parse inner event of a draft: $json")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 31234
|
||||||
|
const val ALT_DESCRIPTION = "Draft Event"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
): Address = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
fun createAddressTag(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
): String = Address.assemble(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun build(
|
||||||
|
dTag: String,
|
||||||
|
draft: Event,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<DraftWrapEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = signer.nip44Encrypt(draft.toJson(), signer.pubKey),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
alt(ALT_DESCRIPTION)
|
||||||
|
dTag(dTag)
|
||||||
|
kind(draft.kind)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun buildDeleted(
|
||||||
|
dTag: String,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<DraftWrapEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = "",
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
alt(ALT_DESCRIPTION)
|
||||||
|
dTag(dTag)
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
dTag: String,
|
||||||
|
draft: Event,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = signer.sign(build(dTag, draft, signer, createdAt))
|
||||||
|
|
||||||
|
suspend fun createDeletedEvent(
|
||||||
|
dTag: String,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): DraftWrapEvent = signer.sign(buildDeleted(dTag, createdAt))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user