Draft event refactoring

This commit is contained in:
Vitor Pamplona
2025-08-13 19:11:29 -04:00
parent 251e5535d0
commit 4c0f223673
30 changed files with 376 additions and 518 deletions
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListDecryptionCache
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatListState
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.NotificationInboxRelayState
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.tags.addressables.taggedAddresses
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.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.taggedUserIds
import com.vitorpamplona.quartz.nip01Core.tags.references.references
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip04Dm.messages.reply
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
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.NRelay
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.nip37Drafts.DraftBuilder
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftEventCache
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip47WalletConnect.Response
@@ -217,6 +213,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.util.Locale
import kotlin.collections.forEach
@OptIn(DelicateCoroutinesApi::class)
@Stable
@@ -297,6 +294,7 @@ class Account(
val serverLists = MergedServerListState(fileStorageServers, blossomServers, scope)
// Relay settings
val homeRelays = AccountHomeRelayState(nip65RelayList, privateStorageRelayList, localRelayList, scope)
val outboxRelays = AccountOutboxRelayState(nip65RelayList, privateStorageRelayList, localRelayList, broadcastRelayList, scope)
val dmRelays = DmInboxRelayState(dmRelayList, nip65RelayList, privateStorageRelayList, localRelayList, scope)
val notificationRelays = NotificationInboxRelayState(nip65RelayList, localRelayList, scope)
@@ -1141,103 +1139,42 @@ class Account(
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(
draftTag: String,
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 draftEvent = DraftBuilder.encryptAndSign(draftTag, rumor, signer)
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) {
val key = DraftEvent.createAddressTag(userProfile().pubkeyHex, draftTag)
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)
val extraRelays = cache.getAddressableNoteIfExists(DraftWrapEvent.createAddressTag(signer.pubKey, draftTag))?.relays ?: emptyList()
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,
)
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(
@@ -1274,7 +1222,18 @@ class Account(
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> =
@@ -1328,7 +1287,15 @@ class Account(
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(
@@ -1363,29 +1330,42 @@ class Account(
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(
value: BigDecimal,
bounty: Note,
draftTag: String?,
) {
if (!isWriteable()) return
val event = bounty.event as? TextNoteEvent ?: return
val eventAuthor = bounty.author ?: return
val bountyEvent = bounty.event as? TextNoteEvent ?: return
val bountyAuthor = bounty.author ?: return
val template =
BountyAddValueEvent.build(
value,
EventHintBundle(event, bounty.relayHintUrl()),
eventAuthor.toPTag(),
amount = value,
bountyRoot = EventHintBundle(bountyEvent, bounty.relayHintUrl()),
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(
@@ -1417,82 +1397,27 @@ class Account(
broadcast.forEach { client.send(it, relayList) }
}
suspend fun sendPrivateMessage(
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?,
) {
suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<PrivateDmEvent>) {
if (!isWriteable()) return
val encryptedContent =
signer.nip04Encrypt(
PrivateDmEvent.prepareMessageToEncrypt(message, imetas),
toUser.pubKey,
)
val newEvent = signer.sign(eventTemplate)
val recipient = newEvent.verifiedRecipientPubKey()
val destinationRelays = recipient?.let { cache.getOrCreateUser(it).dmInboxRelays() } ?: emptyList()
val template =
PrivateDmEvent.build(toUser, encryptedContent) {
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)
cache.justConsumeMyOwnEvent(newEvent)
client.send(newEvent, outboxRelays.flow.value + destinationRelays)
}
suspend fun sendNIP17EncryptedFile(template: EventTemplate<ChatMessageEncryptedFileHeaderEvent>) {
suspend fun sendNip17EncryptedFile(template: EventTemplate<ChatMessageEncryptedFileHeaderEvent>) {
if (!isWriteable()) return
val wraps = NIP17Factory().createEncryptedFileNIP17(template, signer)
broadcastPrivately(wraps)
}
suspend fun sendNIP17PrivateMessage(
template: EventTemplate<ChatMessageEvent>,
draftTag: String? = null,
) {
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 sendNip17PrivateMessage(template: EventTemplate<ChatMessageEvent>) {
val events = NIP17Factory().createMessageNIP17(template, signer)
broadcastPrivately(events)
}
suspend fun broadcastPrivately(signedEvents: NIP17Factory.Result) {
@@ -1645,7 +1570,7 @@ class Account(
privateDMDecryptionCache.cachedDM(event)
} else if (event is LnZapRequestEvent && event.isPrivateZap()) {
privateZapsDecryptionCache.cachedPrivateZap(event)?.content
} else if (event is DraftEvent) {
} else if (event is DraftWrapEvent) {
draftsDecryptionCache.preCachedDraft(event)?.content
} else {
event.content
@@ -1669,7 +1594,7 @@ class Account(
} else {
event.content
}
} else if (event is DraftEvent && isWriteable()) {
} else if (event is DraftWrapEvent && isWriteable()) {
draftsDecryptionCache.cachedDraft(event)?.content
} else {
event?.content
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
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.nip42RelayAuth.RelayAuthEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
@@ -89,7 +89,7 @@ val DefaultIndexerRelayList = setOf(Constants.purplepages, Constants.coracle, Co
val DefaultSignerPermissions =
listOf(
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_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.nip35Torrents.TorrentCommentEvent
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.nip38UserStatus.StatusEvent
import com.vitorpamplona.quartz.nip40Expiration.expiration
@@ -2536,7 +2536,7 @@ object LocalCache : ILocalCache {
}
fun consume(
event: DraftEvent,
event: DraftWrapEvent,
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
): Boolean = !event.isDeleted() && consumeBaseReplaceable(event, relay, wasVerified)
@@ -2744,7 +2744,7 @@ object LocalCache : ILocalCache {
is CommunityPostApprovalEvent -> consume(event, relay, wasVerified)
is ContactListEvent -> 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 EmojiPackSelectionEvent -> 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.Stable
import coil3.util.CoilUtils.result
import com.vitorpamplona.amethyst.model.nip47WalletConnect.NwcSignerState
import com.vitorpamplona.amethyst.model.nip51Lists.HiddenUsersState
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.nip28PublicChat.message.ChannelMessageEvent
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.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceMethod
@@ -236,7 +235,7 @@ open class Note(
open fun createdAt() = event?.createdAt
fun isDraft() = event is DraftEvent
fun isDraft() = event is DraftWrapEvent
fun loadEvent(
event: Event,
@@ -44,8 +44,8 @@ class AccountOutboxRelayState(
privateStorage.flow,
local.flow,
broadcast.flow,
) { nip65Inbox, privateOutBox, localRelays, broadcastRelays ->
nip65Inbox + privateOutBox + localRelays + broadcastRelays
) { nip65Outbox, privateOutBox, localRelays, broadcastRelays ->
nip65Outbox + privateOutBox + localRelays + broadcastRelays
}.flowOn(Dispatchers.Default)
.stateIn(
scope,
@@ -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.filters.Filter
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.nip56Reports.ReportEvent
val ReportsAndBookmarksFromKeyKinds =
listOf(
DraftEvent.KIND,
DraftWrapEvent.KIND,
ReportEvent.KIND,
BookmarkListEvent.KIND,
)
@@ -40,7 +40,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
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.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
@@ -63,7 +63,7 @@ fun routeFor(
noteEvent: Event,
loggedIn: Account,
): Route? {
if (noteEvent is DraftEvent) {
if (noteEvent is DraftWrapEvent) {
val innerEvent = loggedIn.draftsDecryptionCache.preCachedDraft(noteEvent)
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.nip35Torrents.TorrentCommentEvent
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.nip51Lists.PinListEvent
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 (makeItShort) {
@@ -568,7 +568,7 @@ fun InnerNoteWithReactions(
)
}
} else {
if (baseNote.event is DraftEvent) {
if (baseNote.event is DraftWrapEvent) {
Spacer(modifier = DoubleVertSpacer)
}
}
@@ -647,7 +647,7 @@ private fun RenderNoteRow(
is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav)
is AudioTrackEvent -> RenderAudioTrack(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 RepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
is GenericRepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
@@ -901,7 +901,7 @@ fun ObserveDraftEvent(
accountViewModel: AccountViewModel,
render: @Composable (Note) -> Unit,
) {
val noteEvent by observeNoteEvent<DraftEvent>(note, accountViewModel)
val noteEvent by observeNoteEvent<DraftWrapEvent>(note, accountViewModel)
noteEvent?.let {
val innerNote by produceCachedStateAsync(cache = accountViewModel.draftNoteCache, key = it)
@@ -179,7 +179,6 @@ class AddBountyAmountViewModel : ViewModel() {
myAccount.sendAddBounty(
newValue,
bountyInner,
draftTag = null,
)
}
}
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
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.zapraiser.zapraiser
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
@@ -202,7 +202,7 @@ open class CommentPostViewModel :
val noteEvent = draft.event
val noteAuthor = draft.author
if (noteEvent is DraftEvent && noteAuthor != null) {
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
val oldTag = (draft.event as? AddressableEvent)?.dTag()
@@ -324,13 +324,14 @@ open class CommentPostViewModel :
if (message.text.isBlank()) {
accountViewModel.account.deleteDraft(draftTag.current)
} else {
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template)
val attachments = mutableSetOf<Event>()
nip95attachments.forEach {
account.sendToPrivateOutboxAndLocal(it.first)
account.sendToPrivateOutboxAndLocal(it.second)
attachments.add(it.first)
attachments.add(it.second)
}
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
}
}
@@ -115,7 +115,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
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.Response
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
@@ -1510,7 +1510,7 @@ class AccountViewModel(
account.deleteDraft(draftTag)
}
suspend fun createTempDraftNote(noteEvent: DraftEvent): Note? = draftNoteCache.update(noteEvent)
suspend fun createTempDraftNote(noteEvent: DraftWrapEvent): Note? = draftNoteCache.update(noteEvent)
fun createTempDraftNote(
innerEvent: Event,
@@ -1691,8 +1691,8 @@ class AccountViewModel(
class CachedDraftNotes(
val accountViewModel: AccountViewModel,
) : GenericBaseCacheAsync<DraftEvent, Note>(20) {
override suspend fun compute(key: DraftEvent): Note? =
) : GenericBaseCacheAsync<DraftWrapEvent, Note>(20) {
override suspend fun compute(key: DraftWrapEvent): Note? =
withContext(Dispatchers.IO) {
val decrypted = accountViewModel.account.draftsDecryptionCache.cachedDraft(key)
if (decrypted != null) {
@@ -32,7 +32,7 @@ import com.vitorpamplona.quartz.nip01Core.core.IEvent
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
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.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
@@ -74,7 +74,7 @@ class EventProcessor(
when (event) {
is ChatroomKeyable -> chatHandler.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 SealedRumorEvent -> sealHandler.add(event, eventNote, publicNote)
is LnZapRequestEvent -> zapRequest.add(event, eventNote, publicNote)
@@ -98,7 +98,7 @@ class EventProcessor(
when (event) {
is ChatroomKeyable -> chatHandler.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 SealedRumorEvent -> sealHandler.delete(event, note)
is LnZapRequestEvent -> zapRequest.delete(event, note)
@@ -129,7 +129,7 @@ class EventProcessor(
val deletedDrafts =
newNotes.mapNotNull { note ->
val event = note.event
if (event is DraftEvent &&
if (event is DraftWrapEvent &&
event.isDeleted() &&
!cache.deletionIndex.hasBeenDeleted(event)
) {
@@ -193,9 +193,9 @@ class OtsEventHandler(
class DraftEventHandler(
private val account: Account,
private val cache: LocalCache,
) : EventHandler<DraftEvent> {
) : EventHandler<DraftWrapEvent> {
override suspend fun add(
event: DraftEvent,
event: DraftWrapEvent,
eventNote: Note,
publicNote: Note,
) {
@@ -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.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
@Composable
fun RefreshingChatroomFeedView(
@@ -129,7 +129,7 @@ fun ChatFeedLoaded(
) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { index, item ->
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(
baseNote = item,
routeForLastRead = routeForLastRead,
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
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.zapraiser.zapraiserAmount
@@ -162,7 +162,7 @@ fun NormalChatNote(
ChatBubbleLayout(
isLoggedInUser = isLoggedInUser,
isDraft = note.event is DraftEvent,
isDraft = note.event is DraftWrapEvent,
innerQuote = innerQuote,
isComplete = accountViewModel.settings.featureSet == FeatureSetType.COMPLETE,
hasDetailsToShow = note.zaps.isNotEmpty() || note.zapPayments.isNotEmpty() || note.reactions.isNotEmpty(),
@@ -289,7 +289,7 @@ private fun MessageBubbleLines(
accountViewModel: AccountViewModel,
nav: INav,
) {
if (baseNote.event !is DraftEvent) {
if (baseNote.event !is DraftWrapEvent) {
RenderReplyRow(
note = baseNote,
innerQuote = innerQuote,
@@ -388,7 +388,7 @@ fun NoteRow(
when (note.event) {
is ChannelCreateEvent -> RenderCreateChannelNote(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)
else -> RenderRegularTextNote(note, canPreview, innerQuote, bgColor, accountViewModel, nav)
}
@@ -107,7 +107,7 @@ fun NewChatroomSubjectDialog(
groupName.value.ifBlank { null }?.let { changeSubject(it) }
}
accountViewModel.account.sendNIP17PrivateMessage(template)
accountViewModel.account.sendNip17PrivateMessage(template)
}
onClose()
@@ -79,7 +79,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
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.zapSplits
@@ -252,7 +252,7 @@ class ChatNewMessageViewModel :
val noteEvent = draft.event
val noteAuthor = draft.author
if (noteEvent is DraftEvent && noteAuthor != null) {
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
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 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 {
accountViewModel.account.sendPrivateMessage(
message = message,
toUser = room.users.first().let { LocalCache.getOrCreateUser(it).toPTag() },
replyingTo = replyTo.value,
contentWarningReason = null,
imetas = usedAttachments,
draftTag = dTag,
)
val toUser = room.users.first().let { LocalCache.getOrCreateUser(it).toPTag() }
val template =
PrivateDmEvent.build(
toUser = toUser,
message = message,
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)
}
}
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
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.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.files.encryption.AESGCM
@@ -48,7 +49,7 @@ class ChatFileSender(
contentWarningReason: String?,
cipher: AESGCM,
) {
account.sendNIP17EncryptedFile(
account.sendNip17EncryptedFile(
ChatMessageEncryptedFileHeaderEvent.build(
url = result.url,
to = chatroom.users.map { LocalCache.getOrCreateUser(it).toPTag() },
@@ -89,18 +90,18 @@ class ChatFileSender(
val iMetaAttachments = IMetaAttachments()
iMetaAttachments.add(result, caption, contentWarningReason)
account.sendPrivateMessage(
message = result.url,
toUser = chatroom.users.first().let { LocalCache.getOrCreateUser(it).toPTag() },
replyingTo = null,
zapReceiver = null,
contentWarningReason = null,
zapRaiserAmount = null,
geohash = null,
imetas = iMetaAttachments.iMetaAttachments,
emojis = null,
draftTag = null,
)
val toUser = chatroom.users.first().let { LocalCache.getOrCreateUser(it).toPTag() }
val template =
PrivateDmEvent.build(
toUser = toUser,
message = result.url,
imetas = iMetaAttachments.iMetaAttachments,
replyingTo = null,
signer = account.signer,
)
account.sendNip04PrivateMessage(template)
}
suspend fun sendAll(uploads: List<SuccessfulUploads>) {
@@ -82,7 +82,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
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.notify
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
@@ -191,7 +191,7 @@ open class ChannelNewMessageViewModel :
val noteEvent = draft.event
val noteAuthor = draft.author
if (noteEvent is DraftEvent && noteAuthor != null) {
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
val oldTag = (draft.event as? AddressableEvent)?.dTag()
@@ -281,8 +281,14 @@ open class ChannelNewMessageViewModel :
if (message.text.isBlank()) {
account.deleteDraft(draftTag.current)
} else {
val attachments = mutableSetOf<Event>()
nip95attachments.forEach {
attachments.add(it.first)
attachments.add(it.second)
}
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template)
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
}
}
@@ -86,7 +86,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
@Composable
fun ChatroomHeaderCompose(
@@ -113,7 +113,7 @@ fun ChatroomComposeChannelOrUser(
nav: INav,
) {
val baseNoteEvent = baseNote.event
if (baseNoteEvent is DraftEvent) {
if (baseNoteEvent is DraftWrapEvent) {
ObserveDraftEvent(baseNote, accountViewModel) { innerNote ->
ChatroomEntry(innerNote, accountViewModel, nav)
}
@@ -78,7 +78,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
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.zapraiser.zapraiser
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
@@ -192,7 +192,7 @@ open class NewProductViewModel :
val noteEvent = draft.event
val noteAuthor = draft.author
if (noteEvent is DraftEvent && noteAuthor != null) {
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
val oldTag = (draft.event as? AddressableEvent)?.dTag()
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
class DraftEventsFeedFilter(
val account: Account,
@@ -48,7 +48,7 @@ class DraftEventsFeedFilter(
fun acceptableEvent(it: Note): Boolean {
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)
@@ -96,7 +96,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
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.ZapSplitSetupLnAddress
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
@@ -241,7 +241,7 @@ open class ShortNotePostViewModel :
val noteEvent = draft?.event
val noteAuthor = draft?.author
if (draft != null && noteEvent is DraftEvent && noteAuthor != null) {
if (draft != null && noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
val oldTag = (draft.event as? AddressableEvent)?.dTag()
@@ -483,18 +483,17 @@ open class ShortNotePostViewModel :
}
suspend fun sendDraftSync() {
val accountViewModel = accountViewModel
if (message.text.isBlank()) {
accountViewModel.account.deleteDraft(draftTag.current)
} else {
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template)
val attachments = mutableSetOf<Event>()
nip95attachments.forEach {
account.sendToPrivateOutboxAndLocal(it.first)
account.sendToPrivateOutboxAndLocal(it.second)
attachments.add(it.first)
attachments.add(it.second)
}
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template, attachments)
}
}
@@ -85,7 +85,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
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.zapSplits
@@ -235,7 +235,7 @@ class NewPublicMessageViewModel :
val noteEvent = draft.event
val noteAuthor = draft.author
if (noteEvent is DraftEvent && noteAuthor != null) {
if (noteEvent is DraftWrapEvent && noteAuthor != null) {
viewModelScope.launch(Dispatchers.IO) {
accountViewModel.createTempDraftNote(noteEvent)?.let { innerNote ->
val oldTag = (draft.event as? AddressableEvent)?.dTag()
@@ -323,18 +323,17 @@ class NewPublicMessageViewModel :
}
suspend fun sendDraftSync() {
val accountViewModel = accountViewModel
if (message.text.isBlank()) {
accountViewModel.account.deleteDraft(draftTag.current)
} else {
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template)
val broadcast = mutableSetOf<Event>()
nip95attachments.forEach {
account.sendToPrivateOutboxAndLocal(it.first)
account.sendToPrivateOutboxAndLocal(it.second)
broadcast.add(it.first)
broadcast.add(it.second)
}
val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template, broadcast)
}
}
@@ -208,7 +208,7 @@ import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
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.nip51Lists.PinListEvent
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)
} else if (noteEvent is AppDefinitionEvent) {
RenderAppDefinition(baseNote, accountViewModel, nav)
} else if (noteEvent is DraftEvent) {
} else if (noteEvent is DraftWrapEvent) {
RenderDraft(baseNote, 3, true, backgroundColor, accountViewModel, nav)
} else if (noteEvent is HighlightEvent) {
RenderHighlight(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)