mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
Merge remote-tracking branch 'origin/main' into claude/cashu-wallet-wizard-0zr280
This commit is contained in:
@@ -2045,6 +2045,14 @@ class Account(
|
||||
extraNotesToBroadcast.forEach { client.publish(it, relays) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The live [AddressableNote] backing a draft tag for this account. It is the same cached
|
||||
* note that draft events are consumed into, so its `event` tracks the draft over time. The
|
||||
* composer holds onto it (via DraftTagState) so [LocalCache]'s weak reference can't collect
|
||||
* it before a deletion needs it, which would otherwise orphan the draft on the relays.
|
||||
*/
|
||||
fun getOrCreateDraftNote(draftTag: String): AddressableNote = cache.getOrCreateAddressableNote(DraftWrapEvent.createAddress(signer.pubKey, draftTag))
|
||||
|
||||
suspend fun createAndSendDraftIgnoreErrors(
|
||||
draftTag: String,
|
||||
template: EventTemplate<out Event>,
|
||||
@@ -2081,18 +2089,25 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteDraftIgnoreErrors(draftTag: String) {
|
||||
suspend fun deleteDraftIgnoreErrors(draftNote: AddressableNote?) {
|
||||
try {
|
||||
deleteDraftInner(draftTag)
|
||||
deleteDraftInner(draftNote)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteDraftInner(draftTag: String) {
|
||||
suspend fun deleteDraftInner(draftNote: AddressableNote?) {
|
||||
if (!isWriteable()) return
|
||||
|
||||
val extraRelays = cache.getAddressableNoteIfExists(DraftWrapEvent.createAddressTag(signer.pubKey, draftTag))?.relays ?: emptyList()
|
||||
// Only a real, still-present draft needs a deletion signed. The note's event is null when
|
||||
// no draft was ever saved (e.g. auto-drafts disabled) and already empty once it has been
|
||||
// deleted — in both cases there is nothing to delete, so we avoid prompting the signer.
|
||||
val draftEvent = draftNote?.event as? DraftWrapEvent
|
||||
if (draftEvent == null || draftEvent.isDeleted()) return
|
||||
|
||||
val draftTag = draftNote.dTag()
|
||||
val extraRelays = draftNote.relays
|
||||
|
||||
val deletedDraft = DraftWrapEvent.createDeletedEvent(draftTag, signer)
|
||||
val deletionEvent = signer.sign(DeletionEvent.build(listOf(deletedDraft)))
|
||||
|
||||
+12
-4
@@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.setTextAndPlaceCursorAtBeginning
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -136,11 +137,17 @@ open class CommentPostViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
sendDraftSync()
|
||||
}
|
||||
}
|
||||
@@ -261,6 +268,7 @@ open class CommentPostViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -489,7 +497,7 @@ open class CommentPostViewModel :
|
||||
}
|
||||
}
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
val anonymous = wantsAnonymousPost
|
||||
cancel()
|
||||
|
||||
@@ -500,14 +508,14 @@ open class CommentPostViewModel :
|
||||
}
|
||||
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
} else {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val attachments = mutableSetOf<Event>()
|
||||
nip95attachments.forEach {
|
||||
attachments.add(it.first)
|
||||
|
||||
+11
-3
@@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.currentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -126,6 +127,11 @@ class ChatNewMessageViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
@@ -134,6 +140,7 @@ class ChatNewMessageViewModel :
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -327,6 +334,7 @@ class ChatNewMessageViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -418,17 +426,17 @@ class ChatNewMessageViewModel :
|
||||
}
|
||||
|
||||
suspend fun sendPostSync() {
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
innerSendPost(null)
|
||||
cancel()
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
innerSendPost(draftTag.current)
|
||||
}
|
||||
|
||||
+11
-3
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.currentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -112,11 +113,17 @@ open class ChannelNewMessageViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -229,6 +236,7 @@ open class ChannelNewMessageViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -301,20 +309,20 @@ open class ChannelNewMessageViewModel :
|
||||
val template = createTemplate() ?: return
|
||||
val channelRelays = channel?.relays() ?: emptySet()
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
cancel()
|
||||
|
||||
accountViewModel.account.signAndSendPrivatelyOrBroadcast(template) {
|
||||
channelRelays.toList()
|
||||
}
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val attachments = mutableSetOf<Event>()
|
||||
nip95attachments.forEach {
|
||||
|
||||
+11
-3
@@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.currentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -128,6 +129,11 @@ class LongFormPostViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
@@ -136,6 +142,7 @@ class LongFormPostViewModel :
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -237,6 +244,7 @@ class LongFormPostViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -324,7 +332,7 @@ class LongFormPostViewModel :
|
||||
suspend fun sendPostSync() {
|
||||
val template = createTemplate() ?: return
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
cancel()
|
||||
|
||||
if (accountViewModel.settings.useTrackedBroadcasts()) {
|
||||
@@ -342,13 +350,13 @@ class LongFormPostViewModel :
|
||||
}
|
||||
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank() && title.text.isBlank()) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val template = createTemplate() ?: return
|
||||
accountViewModel.account.createAndSendDraftIgnoreErrors(draftTag.current, template, emptySet())
|
||||
|
||||
+11
-3
@@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.currentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -114,6 +115,11 @@ open class NewProductViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
@@ -122,6 +128,7 @@ open class NewProductViewModel :
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -218,6 +225,7 @@ open class NewProductViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -315,18 +323,18 @@ open class NewProductViewModel :
|
||||
suspend fun sendPostSync() {
|
||||
val template = createTemplate() ?: return
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
cancel()
|
||||
|
||||
accountViewModel.account.signAndSendPrivatelyOrBroadcast(template, relayList = { relayList })
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val template = createTemplate() ?: return
|
||||
accountViewModel.account.createAndSendDraftIgnoreErrors(draftTag.current, template)
|
||||
|
||||
+13
-5
@@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.setTextAndPlaceCursorAtBeginning
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.BooleanType
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
@@ -183,6 +184,11 @@ open class ShortNotePostViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
@@ -191,6 +197,7 @@ open class ShortNotePostViewModel :
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -483,6 +490,7 @@ open class ShortNotePostViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -889,7 +897,7 @@ open class ShortNotePostViewModel :
|
||||
}
|
||||
}
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
val anonymous = wantsAnonymousPost
|
||||
val scheduledFor = scheduledForSec
|
||||
val privately = wantsPrivateNote
|
||||
@@ -903,7 +911,7 @@ open class ShortNotePostViewModel :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
accountViewModel.account.sendPrivateNote(template as EventTemplate<TextNoteEvent>)
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -935,7 +943,7 @@ open class ShortNotePostViewModel :
|
||||
),
|
||||
)
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -961,13 +969,13 @@ open class ShortNotePostViewModel :
|
||||
}
|
||||
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val attachments = mutableSetOf<Event>()
|
||||
nip95attachments.forEach {
|
||||
|
||||
+10
-3
@@ -118,11 +118,17 @@ open class NestNewMessageViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -235,6 +241,7 @@ open class NestNewMessageViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -303,7 +310,7 @@ open class NestNewMessageViewModel :
|
||||
suspend fun sendPostSync() {
|
||||
val template = createTemplate() ?: return
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
cancel()
|
||||
|
||||
// Broadcast to the user's default relays — the nest has no
|
||||
@@ -312,13 +319,13 @@ open class NestNewMessageViewModel :
|
||||
// `account.signAndComputeBroadcast(...)`.
|
||||
accountViewModel.account.signAndComputeBroadcast(template)
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val attachments = mutableSetOf<Event>()
|
||||
nip95attachments.forEach {
|
||||
|
||||
+11
-3
@@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.currentWord
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -129,6 +130,11 @@ class NewPublicMessageViewModel :
|
||||
IExpiration {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
// Strong reference to the live cache note for the current draft tag (derived from the
|
||||
// versions flow below), so LocalCache cannot weakly collect it before a deletion needs it.
|
||||
var draftNote: AddressableNote? = null
|
||||
private set
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
@@ -137,6 +143,7 @@ class NewPublicMessageViewModel :
|
||||
draftTag.versions.collectLatest {
|
||||
// don't save the first
|
||||
if (it > 0) {
|
||||
draftNote = account.getOrCreateDraftNote(draftTag.current)
|
||||
accountViewModel.launchSigner {
|
||||
sendDraftSync()
|
||||
}
|
||||
@@ -262,6 +269,7 @@ class NewPublicMessageViewModel :
|
||||
val oldTag = (draft.event as? AddressableEvent)?.dTag()
|
||||
if (oldTag != null) {
|
||||
draftTag.set(oldTag)
|
||||
draftNote = account.getOrCreateDraftNote(oldTag)
|
||||
}
|
||||
loadFromDraft(innerNote)
|
||||
}
|
||||
@@ -340,18 +348,18 @@ class NewPublicMessageViewModel :
|
||||
}
|
||||
}
|
||||
|
||||
val version = draftTag.current
|
||||
val draftToDelete = draftNote
|
||||
cancel()
|
||||
|
||||
accountViewModel.account.signAndComputeBroadcast(template, extraNotesToBroadcast)
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(version)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
if (message.text.toString().isBlank()) {
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftTag.current)
|
||||
accountViewModel.account.deleteDraftIgnoreErrors(draftNote)
|
||||
} else if (accountViewModel.settings.automaticallyCreateDrafts()) {
|
||||
val broadcast = mutableSetOf<Event>()
|
||||
nip95attachments.forEach {
|
||||
|
||||
@@ -3,31 +3,34 @@
|
||||
~
|
||||
~ Notification small icons are rendered by the system as flat, alpha-only
|
||||
~ silhouettes, so the regular `amethyst` gem becomes an identical solid gem
|
||||
~ for both real notifications and this persistent service. To stop the
|
||||
~ service from reading as a new notification, this variant draws the gem as
|
||||
~ a hollow outline — same brand shape, clearly different at a glance.
|
||||
~ for both real notifications and this persistent service. To set the
|
||||
~ always-on service apart, this variant centres the brand gem inside a ring
|
||||
~ of three "server" nodes — signalling a background service quietly connected
|
||||
~ to a network of relays, clearly different from a one-off notification at a
|
||||
~ glance. The gem is grown to the largest size that still clears the nodes.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<!--
|
||||
The gem outline below is authored at ~half the viewport size, leaving it
|
||||
visibly smaller than every other notification small icon (the filled
|
||||
`amethyst` gem fills the viewport). Scale it up ~1.8x about its centre so it
|
||||
matches the others, keeping a margin so the stroke doesn't clip at the edges.
|
||||
-->
|
||||
<group
|
||||
android:pivotX="256"
|
||||
android:pivotY="261"
|
||||
android:scaleX="1.8"
|
||||
android:scaleY="1.8">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeWidth="22"
|
||||
android:strokeLineJoin="round"
|
||||
android:pathData="M370.18,326.86C369.91,326.04 307.3,176.97 290.09,135.63c-20.96,0 -65.23,0 -96.46,0 -9.17,22.67 -32.82,80.44 -51.8,126.98 10.4,24.81 19.74,47.1 26.95,64.29l39.28,0a2.68,2.68 135,0 0,2.51 -3.15c-0.34,-7.93 -21.93,-36.76 -15.49,-67.94 1.72,-6.71 3.03,-13.53 4.91,-20.21a235.88,235.88 135,0 1,18.04 -46.7c1.19,-2.27 1.74,-3.29 4.5,-1.57 5.86,3.65 12.41,3.44 18.94,2.97 5.62,-1.22 8.36,-3.92 18.67,-1.18 2.04,0 4.03,0 6.11,0.19 4.95,0.39 9.6,1.57 12.57,6.05 3.25,4.9 3.77,10.44 3.25,16.1 -0.79,8.47 -0.23,16.36 6.94,22.33a77.19,77.19 0,0 0,8.05 5.37c3.56,2.28 7.85,3.27 10.9,6.44 1.8,1.87 2.86,3.85 2.08,6.51 -0.78,2.66 -2.86,3.91 -5.6,4.2 -6.92,0.75 -13.69,-0.66 -20.49,-1.48 -0.89,-0.11 -1.76,-0.17 -2.68,-0.23a15.39,15.39 0,0 1,-5.11 0.17l-1.6,0a35.83,35.83 0,0 0,-7.92 1.89c-6.41,2.34 -12.25,4.91 -19.82,4.78a5.8,5.8 0,0 0,-2.75 1.19c-5.93,5.27 -9.5,11.93 -6.54,21.66 8.13,22.72 29,77.53 37.96,101.18 -0.25,-16.02 -0.32,-44.71 -0.38,-58.64 35.8,0.05 90.7,0.03 95.08,0.03z" />
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M384.871,330.838C384.567,329.917,313.907,161.667,294.473,115c-23.662,0,-73.63,0,-108.877,0c-10.343,25.583,-37.047,90.79,-58.468,143.318c11.74,28.006,22.282,53.163,30.412,72.57l44.332,0a3.03,3.03 0,0,0,2.831,-3.56c-0.381,-8.953,-24.749,-41.49,-17.48,-76.687c1.937,-7.573,3.422,-15.267,5.542,-22.812a266.236,266.236 135,0,1,20.356,-52.711c1.347,-2.561,1.97,-3.709,5.072,-1.772c6.618,4.118,14.008,3.88,21.371,3.345c6.347,-1.38,9.438,-4.421,21.068,-1.33c2.302,0,4.543,0,6.894,0.21c5.591,0.442,10.829,1.772,14.191,6.833c3.665,5.53,4.255,11.784,3.665,18.176c-0.894,9.56,-0.259,18.463,7.832,25.202a87.125,87.125 0,0,0,9.091,6.06c4.013,2.578,8.859,3.698,12.297,7.269c2.031,2.103,3.229,4.349,2.346,7.346c-0.878,2.997,-3.229,4.41,-6.314,4.741c-7.816,0.85,-15.449,-0.74,-23.127,-1.667c-0.999,-0.121,-1.981,-0.199,-3.03,-0.259a17.375,17.375 0,0,1,-5.773,0.199l-1.805,0a40.441,40.441 0,0,0,-8.936,2.136c-7.242,2.638,-13.832,5.542,-22.37,5.393a6.546,6.546 0,0,0,-3.107,1.347c-6.695,5.95,-10.724,13.467,-7.374,24.446c9.179,25.643,32.73,87.505,42.853,114.209c-0.287,-18.087,-0.364,-50.47,-0.425,-66.189c40.413,0.061,102.375,0.028,107.315,0.028ZM248.64,201.534a8.544,8.693 90,1,0,17.386,0a8.544,8.693 90,1,0,-17.386,0Z" />
|
||||
<path
|
||||
android:strokeColor="#000000"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineJoin="round"
|
||||
android:pathData="M256,46A210,210 0,1,1 256,466A210,210 0,1,1 256,46Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M256,10A36,36 0,1,1 256,82A36,36 0,1,1 256,10Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M74.13,325A36,36 0,1,1 74.13,397A36,36 0,1,1 74.13,325Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M437.87,325A36,36 0,1,1 437.87,397A36,36 0,1,1 437.87,325Z" />
|
||||
</vector>
|
||||
|
||||
Reference in New Issue
Block a user