diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnGroupChatScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnGroupChatScreen.kt index f32ce5d320..2b21f0c1aa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnGroupChatScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnGroupChatScreen.kt @@ -383,6 +383,10 @@ private fun CordnGroupChat( try { sendAttachment(context, accountViewModel, room, uri) } catch (e: Exception) { + // A failed attachment left no trace anywhere; the + // banner tells the person, this tells whoever has + // to work out why. + Log.w("CordnGroupChat", "attachment failed in ${room.gid}: ${e.message}", e) attachError = e.message ?: uploadFailed } finally { attaching = false @@ -580,6 +584,11 @@ internal fun MessageBody( } } +/** A reason an attachment did not go, in words meant for the person who tried. */ +private class CordnAttachmentException( + message: String, +) : Exception(message) + /** * Encrypts the picked file and sends it as an attachment on an empty message. * @@ -593,15 +602,31 @@ private suspend fun sendAttachment( room: CordnGroupChatroom, uri: Uri, ) { - val session = accountViewModel.account.cordnRuntime?.sessionOrNull(room.coordinatorPubKey) ?: return - val group = session.manager.group(room.gid) ?: return + // Every step here used to `?: return`, which reads as "nothing to do" and + // behaves as "the attach button does nothing at all": the picker closed, + // the spinner ended, and no message and no error appeared. Measured on the + // tablet — attaching a PNG produced no message, no error and no log line. + // Each one is now a reason a person can act on. + val session = + accountViewModel.account.cordnRuntime?.sessionOrNull(room.coordinatorPubKey) + ?: throw CordnAttachmentException(stringRes(context, R.string.cordn_send_no_session)) + val group = + session.manager.group(room.gid) + ?: throw CordnAttachmentException(stringRes(context, R.string.cordn_send_no_session)) val resolver = context.contentResolver val mime = resolver.getType(uri) ?: CordnBlobUpload.OPAQUE val name = uri.lastPathSegment?.substringAfterLast('/') ?: "file" - val bytes = withContext(Dispatchers.IO) { resolver.openInputStream(uri)?.use { it.readBytes() } } ?: return + val bytes = + withContext(Dispatchers.IO) { resolver.openInputStream(uri)?.use { it.readBytes() } } + ?: throw CordnAttachmentException(stringRes(context, R.string.cordn_media_unreadable)) - val tag = CordnMediaService(accountViewModel.account).upload(bytes, mime, name, context) ?: return + // Null means the account has no Blossom server, which is a setting the + // person can change — the one failure here that is entirely actionable, + // and the one that was hardest to notice. + val tag = + CordnMediaService(accountViewModel.account).upload(bytes, mime, name, context) + ?: throw CordnAttachmentException(stringRes(context, R.string.cordn_media_no_server)) // Into the room as well, for the same reason every other send is: an // attachment of your own echoes back as an Echo and would otherwise be // invisible to the person who sent it. diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index b2ce11a2a5..c12666c8ad 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -325,6 +325,8 @@ That did not send. This group\'s coordinator is not open, so nothing can be sent yet. Could not open that file. + No media server is set for this account, so there is nowhere to put the file. Pick one in Settings. + That file could not be read. Record a voice note Stop and send Play voice note