From 9b2fbcbf5622ea4ca0142d45be5b7611b9e9a797 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 31 Mar 2026 20:17:14 -0400 Subject: [PATCH] Fixes image uploading crash --- CHANGELOG.md | 7 +- .../amethyst/ui/actions/EditPostViewModel.kt | 79 ++++++++++--------- .../nip22Comments/CommentPostViewModel.kt | 68 ++++++++-------- .../send/ChannelNewMessageViewModel.kt | 31 ++++---- .../nip23LongForm/LongFormPostViewModel.kt | 49 ++++++------ .../nip99Classifieds/NewProductViewModel.kt | 47 ++++++----- .../loggedIn/home/ShortNotePostViewModel.kt | 68 ++++++++-------- .../NewPublicMessageViewModel.kt | 68 ++++++++-------- 8 files changed, 224 insertions(+), 193 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efd64b454..4c460ab9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ + +# [Release v1.07.5: Image upload fix](https://github.com/vitorpamplona/amethyst/releases/tag/v1.07.5) - 2026-03-31 + +- Fixes Image uploads crashing the app + # [Release v1.07.4: NWC fix](https://github.com/vitorpamplona/amethyst/releases/tag/v1.07.4) - 2026-03-31 -- Fixes Nostr wallet connect receiving the secret. +- Fixes Nostr wallet connect receiving the secret. # [Release v1.07.3: GIF Keyboard](https://github.com/vitorpamplona/amethyst/releases/tag/v1.07.3) - 2026-03-31 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt index 4219ce80d..ec1d6db0a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt @@ -205,47 +205,50 @@ open class EditPostViewModel : ViewModel() { ) if (results.allGood) { - results.successful.forEach { state -> - if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { - val nip95 = - myAccount.createNip95( - byteArray = state.result.bytes, - headerInfo = state.result.fileHeader, - alt = alt, - contentWarningReason = if (sensitiveContent) "" else null, - ) - nip95attachments = nip95attachments + nip95 - val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } + val urls = + results.successful.mapNotNull { state -> + if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { + val nip95 = + myAccount.createNip95( + byteArray = state.result.bytes, + headerInfo = state.result.fileHeader, + alt = alt, + contentWarningReason = if (sensitiveContent) "" else null, + ) + nip95attachments = nip95attachments + nip95 + val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } - note?.let { - message = message.insertUrlAtCursor("nostr:" + it.toNEvent()) + note?.let { + "nostr:" + it.toNEvent() + } + } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + val iMeta = + IMetaTagBuilder(state.result.url) + .apply { + hash(state.result.fileHeader.hash) + size(state.result.fileHeader.size) + state.result.fileHeader.mimeType + ?.let { mimeType(it) } + state.result.fileHeader.dim + ?.let { dims(it) } + state.result.fileHeader.blurHash + ?.let { blurhash(it.blurhash) } + state.result.magnet?.let { magnet(it) } + state.result.uploadedHash?.let { originalHash(it) } + alt?.let { alt(it) } + if (sensitiveContent) sensitiveContent("") + }.build() + + iMetaAttachments = iMetaAttachments.filter { it.url != iMeta.url } + iMeta + + state.result.url + } else { + null } - - urlPreview = findUrlInMessage() - } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - val iMeta = - IMetaTagBuilder(state.result.url) - .apply { - hash(state.result.fileHeader.hash) - size(state.result.fileHeader.size) - state.result.fileHeader.mimeType - ?.let { mimeType(it) } - state.result.fileHeader.dim - ?.let { dims(it) } - state.result.fileHeader.blurHash - ?.let { blurhash(it.blurhash) } - state.result.magnet?.let { magnet(it) } - state.result.uploadedHash?.let { originalHash(it) } - alt?.let { alt(it) } - if (sensitiveContent) sensitiveContent("") - }.build() - - iMetaAttachments = iMetaAttachments.filter { it.url != iMeta.url } + iMeta - - message = message.insertUrlAtCursor(state.result.url) - urlPreview = findUrlInMessage() } - } + + message = message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreview = findUrlInMessage() this@EditPostViewModel.multiOrchestrator = null } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt index ca54a47cb..2e5b91c9a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt @@ -521,41 +521,45 @@ open class CommentPostViewModel : ) if (results.allGood) { - results.successful.forEach { state -> - if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { - val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) - nip95attachments = nip95attachments + nip95 - val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } + val urls = + results.successful.mapNotNull { state -> + if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { + val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) + nip95attachments = nip95attachments + nip95 + val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } - note?.let { - message.insertUrlAtCursor("nostr:" + it.toNEvent()) - urlPreviews.update(message.text.toString()) + note?.let { + "nostr:" + it.toNEvent() + } + } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + val iMeta = + IMetaTagBuilder(state.result.url) + .apply { + hash(state.result.fileHeader.hash) + size(state.result.fileHeader.size) + state.result.fileHeader.mimeType + ?.let { mimeType(it) } + state.result.fileHeader.dim + ?.let { dims(it) } + state.result.fileHeader.blurHash + ?.let { blurhash(it.blurhash) } + state.result.magnet?.let { magnet(it) } + state.result.uploadedHash?.let { originalHash(it) } + + alt?.let { alt(it) } + contentWarningReason?.let { sensitiveContent(contentWarningReason) } + }.build() + + iMetaAttachments.replace(iMeta.url, iMeta) + + state.result.url + } else { + null } - } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - val iMeta = - IMetaTagBuilder(state.result.url) - .apply { - hash(state.result.fileHeader.hash) - size(state.result.fileHeader.size) - state.result.fileHeader.mimeType - ?.let { mimeType(it) } - state.result.fileHeader.dim - ?.let { dims(it) } - state.result.fileHeader.blurHash - ?.let { blurhash(it.blurhash) } - state.result.magnet?.let { magnet(it) } - state.result.uploadedHash?.let { originalHash(it) } - - alt?.let { alt(it) } - contentWarningReason?.let { sensitiveContent(contentWarningReason) } - }.build() - - iMetaAttachments.replace(iMeta.url, iMeta) - - message.insertUrlAtCursor(state.result.url) - urlPreviews.update(message.text.toString()) } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreviews.update(message.text.toString()) multiOrchestrator = null } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt index 317262c9e..7c2434977 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt @@ -360,24 +360,25 @@ open class ChannelNewMessageViewModel : ) if (results.allGood) { - results.successful.forEach { upload -> - if (upload.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { - val nip95 = account.createNip95(upload.result.bytes, headerInfo = upload.result.fileHeader, uploadState.caption, uploadState.contentWarningReason) - nip95attachments = nip95attachments + nip95 - val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } + val urls = + results.successful.mapNotNull { upload -> + if (upload.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { + val nip95 = account.createNip95(upload.result.bytes, headerInfo = upload.result.fileHeader, uploadState.caption, uploadState.contentWarningReason) + nip95attachments = nip95attachments + nip95 + val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } - note?.let { - message.insertUrlAtCursor(it.toNostrUri()) + note?.toNostrUri() + } else if (upload.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + iMetaAttachments.add(upload.result, uploadState.caption, uploadState.contentWarningReason) + + upload.result.url + } else { + null } - - urlPreview = findUrlInMessage() - } else if (upload.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - iMetaAttachments.add(upload.result, uploadState.caption, uploadState.contentWarningReason) - - message.insertUrlAtCursor(upload.result.url) - urlPreview = findUrlInMessage() } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreview = findUrlInMessage() uploadState.reset() onceUploaded() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt index 87bf53a07..1e0dd2324 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt @@ -519,31 +519,36 @@ class LongFormPostViewModel : ) if (results.allGood) { - results.successful.forEach { state -> - if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - val iMeta = - IMetaTagBuilder(state.result.url) - .apply { - hash(state.result.fileHeader.hash) - size(state.result.fileHeader.size) - state.result.fileHeader.mimeType - ?.let { mimeType(it) } - state.result.fileHeader.dim - ?.let { dims(it) } - state.result.fileHeader.blurHash - ?.let { blurhash(it.blurhash) } - state.result.magnet?.let { magnet(it) } - state.result.uploadedHash?.let { originalHash(it) } - alt?.let { alt(it) } - contentWarningReason?.let { sensitiveContent(contentWarningReason) } - }.build() + val urls = + results.successful.mapNotNull { state -> + if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + val iMeta = + IMetaTagBuilder(state.result.url) + .apply { + hash(state.result.fileHeader.hash) + size(state.result.fileHeader.size) + state.result.fileHeader.mimeType + ?.let { mimeType(it) } + state.result.fileHeader.dim + ?.let { dims(it) } + state.result.fileHeader.blurHash + ?.let { blurhash(it.blurhash) } + state.result.magnet?.let { magnet(it) } + state.result.uploadedHash?.let { originalHash(it) } + alt?.let { alt(it) } + contentWarningReason?.let { sensitiveContent(contentWarningReason) } + }.build() - iMetaAttachments.replace(iMeta.url, iMeta) + iMetaAttachments.replace(iMeta.url, iMeta) - val markdownImage = "![${alt ?: ""}](${state.result.url})" - message.insertUrlAtCursor(markdownImage) + val markdownImage = "![${alt ?: ""}](${state.result.url})" + markdownImage + } else { + null + } } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) multiOrchestrator = null } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt index 392024de8..c49d58960 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt @@ -421,30 +421,35 @@ open class NewProductViewModel : ) if (results.allGood) { - results.successful.forEach { - if (it.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - if (it.result.fileHeader.mimeType - ?.startsWith("image") == true - ) { - productImages = productImages + - ProductImageMeta( - it.result.url, - it.result.fileHeader.mimeType, - it.result.fileHeader.blurHash - ?.blurhash, - it.result.fileHeader.dim, - alt, - it.result.fileHeader.hash, - it.result.fileHeader.size, - ) - } else { - iMetaDescription.add(it.result, alt, contentWarningReason) + val urls = + results.successful.mapNotNull { + if (it.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + if (it.result.fileHeader.mimeType + ?.startsWith("image") == true + ) { + productImages = productImages + + ProductImageMeta( + it.result.url, + it.result.fileHeader.mimeType, + it.result.fileHeader.blurHash + ?.blurhash, + it.result.fileHeader.dim, + alt, + it.result.fileHeader.hash, + it.result.fileHeader.size, + ) + } else { + iMetaDescription.add(it.result, alt, contentWarningReason) - message.insertUrlAtCursor(it.result.url) - urlPreviews.update(message.text.toString()) + it.result.url + } + } else { + null } } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreviews.update(message.text.toString()) multiOrchestrator = null } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt index 71e8bacab..6adf94284 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt @@ -996,41 +996,45 @@ open class ShortNotePostViewModel : ) if (results.allGood) { - results.successful.forEach { state -> - if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { - val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) - nip95attachments = nip95attachments + nip95 - val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } + val urls = + results.successful.mapNotNull { state -> + if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { + val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) + nip95attachments = nip95attachments + nip95 + val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } - note?.let { - message.insertUrlAtCursor("nostr:" + it.toNEvent()) - urlPreviews.update(message.text.toString()) + note?.let { + "nostr:" + it.toNEvent() + } + } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + val iMeta = + IMetaTagBuilder(state.result.url) + .apply { + hash(state.result.fileHeader.hash) + size(state.result.fileHeader.size) + state.result.fileHeader.mimeType + ?.let { mimeType(it) } + state.result.fileHeader.dim + ?.let { dims(it) } + state.result.fileHeader.blurHash + ?.let { blurhash(it.blurhash) } + state.result.magnet?.let { magnet(it) } + state.result.uploadedHash?.let { originalHash(it) } + + alt?.let { alt(it) } + contentWarningReason?.let { sensitiveContent(contentWarningReason) } + }.build() + + iMetaAttachments.replace(iMeta.url, iMeta) + + state.result.url + } else { + null } - } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - val iMeta = - IMetaTagBuilder(state.result.url) - .apply { - hash(state.result.fileHeader.hash) - size(state.result.fileHeader.size) - state.result.fileHeader.mimeType - ?.let { mimeType(it) } - state.result.fileHeader.dim - ?.let { dims(it) } - state.result.fileHeader.blurHash - ?.let { blurhash(it.blurhash) } - state.result.magnet?.let { magnet(it) } - state.result.uploadedHash?.let { originalHash(it) } - - alt?.let { alt(it) } - contentWarningReason?.let { sensitiveContent(contentWarningReason) } - }.build() - - iMetaAttachments.replace(iMeta.url, iMeta) - - message.insertUrlAtCursor(state.result.url) - urlPreviews.update(message.text.toString()) } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreviews.update(message.text.toString()) multiOrchestrator = null } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt index 09cd8f47e..bf88579e0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt @@ -464,41 +464,45 @@ class NewPublicMessageViewModel : ) if (results.allGood) { - results.successful.forEach { state -> - if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { - val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) - nip95attachments = nip95attachments + nip95 - val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } + val urls = + results.successful.mapNotNull { state -> + if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) { + val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason) + nip95attachments = nip95attachments + nip95 + val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) } - note?.let { - message.insertUrlAtCursor("nostr:" + it.toNEvent()) - urlPreviews.update(message.text.toString()) + note?.let { + "nostr:" + it.toNEvent() + } + } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { + val iMeta = + IMetaTagBuilder(state.result.url) + .apply { + hash(state.result.fileHeader.hash) + size(state.result.fileHeader.size) + state.result.fileHeader.mimeType + ?.let { mimeType(it) } + state.result.fileHeader.dim + ?.let { dims(it) } + state.result.fileHeader.blurHash + ?.let { blurhash(it.blurhash) } + state.result.magnet?.let { magnet(it) } + state.result.uploadedHash?.let { originalHash(it) } + + alt?.let { alt(it) } + contentWarningReason?.let { sensitiveContent(contentWarningReason) } + }.build() + + iMetaAttachments.replace(iMeta.url, iMeta) + + state.result.url + } else { + null } - } else if (state.result is UploadOrchestrator.OrchestratorResult.ServerResult) { - val iMeta = - IMetaTagBuilder(state.result.url) - .apply { - hash(state.result.fileHeader.hash) - size(state.result.fileHeader.size) - state.result.fileHeader.mimeType - ?.let { mimeType(it) } - state.result.fileHeader.dim - ?.let { dims(it) } - state.result.fileHeader.blurHash - ?.let { blurhash(it.blurhash) } - state.result.magnet?.let { magnet(it) } - state.result.uploadedHash?.let { originalHash(it) } - - alt?.let { alt(it) } - contentWarningReason?.let { sensitiveContent(contentWarningReason) } - }.build() - - iMetaAttachments.replace(iMeta.url, iMeta) - - message.insertUrlAtCursor(state.result.url) - urlPreviews.update(message.text.toString()) } - } + + message.insertUrlAtCursor(urls.joinToString(" ")) + urlPreviews.update(message.text.toString()) multiOrchestrator = null } else {