From 0721c39f89415fb3e4935b01cc720906aaf7d018 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:24:02 +0200 Subject: [PATCH] Route oversize send failure to the active conversation Addresses review feedback: the size-cap error was posted to the main mesh timeline, so a user sending from a private chat or channel never saw it. rejectIfOversized now posts to the private conversation (addPrivateMessageNoUnread) or channel (addChannelMessage) the send originated from, falling back to the main timeline for public sends. Adds regression tests for both routings. --- .../bitchat/android/ui/MediaSendingManager.kt | 42 +++++++++++++++---- .../ui/MediaSendingManagerMigrationTest.kt | 37 ++++++++++++++++ 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/ui/MediaSendingManager.kt b/app/src/main/java/com/bitchat/android/ui/MediaSendingManager.kt index 41431cd1..68797e08 100644 --- a/app/src/main/java/com/bitchat/android/ui/MediaSendingManager.kt +++ b/app/src/main/java/com/bitchat/android/ui/MediaSendingManager.kt @@ -82,18 +82,42 @@ class MediaSendingManager( private var pendingAutomaticTimeoutRequestId: String? = null /** - * Enforce the send-size cap with a user-visible failure. - * Returns true if the file is oversized and the send was aborted. + * Enforce the send-size cap with a user-visible failure posted to the + * conversation the user is sending from. Returns true if the file is + * oversized and the send was aborted. */ - private fun rejectIfOversized(file: java.io.File): Boolean { + private fun rejectIfOversized( + file: java.io.File, + toPeerIDOrNull: String?, + channelOrNull: String? + ): Boolean { val size = file.length() if (size <= MAX_FILE_SIZE) return false Log.e(TAG, "❌ File too large: $size bytes (max: $MAX_FILE_SIZE)") val sizeMb = size / (1024 * 1024) val maxMb = MAX_FILE_SIZE / (1024 * 1024) - messageManager.addSystemMessage( - "cannot send ${file.name}: file is too large (${sizeMb} MB, max $maxMb MB)" - ) + val text = "cannot send ${file.name}: file is too large (${sizeMb} MB, max $maxMb MB)" + when { + toPeerIDOrNull != null -> { + val sys = BitchatMessage( + sender = "system", + content = text, + timestamp = Date(), + isRelay = false + ) + messageManager.addPrivateMessageNoUnread(toPeerIDOrNull, sys) + } + channelOrNull != null -> { + val sys = BitchatMessage( + sender = "system", + content = text, + timestamp = Date(), + isRelay = false + ) + messageManager.addChannelMessage(channelOrNull, sys) + } + else -> messageManager.addSystemMessage(text) + } return true } @@ -119,7 +143,7 @@ class MediaSendingManager( return@withContext null } - if (rejectIfOversized(file)) { + if (rejectIfOversized(file, toPeerIDOrNull, channelOrNull)) { return@withContext null } @@ -163,7 +187,7 @@ class MediaSendingManager( return@withContext null } - if (rejectIfOversized(file)) { + if (rejectIfOversized(file, toPeerIDOrNull, channelOrNull)) { return@withContext null } @@ -207,7 +231,7 @@ class MediaSendingManager( return@withContext null } - if (rejectIfOversized(file)) { + if (rejectIfOversized(file, toPeerIDOrNull, channelOrNull)) { return@withContext null } diff --git a/app/src/test/kotlin/com/bitchat/android/ui/MediaSendingManagerMigrationTest.kt b/app/src/test/kotlin/com/bitchat/android/ui/MediaSendingManagerMigrationTest.kt index ebe5f744..60c57198 100644 --- a/app/src/test/kotlin/com/bitchat/android/ui/MediaSendingManagerMigrationTest.kt +++ b/app/src/test/kotlin/com/bitchat/android/ui/MediaSendingManagerMigrationTest.kt @@ -287,6 +287,43 @@ class MediaSendingManagerMigrationTest { assertTrue(messages.none { it.type == com.bitchat.android.model.BitchatMessageType.Image }) } + @Test + fun `oversized file failure is posted to the private conversation and nothing is sent`() { + val bigFile = kotlin.io.path.createTempFile("oversized-private", ".jpg").toFile() + try { + bigFile.writeBytes(ByteArray(11 * 1024 * 1024) { 0x42 }) + + manager.sendImageNote(peerID, null, bigFile.absolutePath) + + val messages = state.privateChats.value[peerID].orEmpty() + assertEquals(1, messages.size) + assertTrue(messages.single().content.contains("too large")) + assertTrue(messages.none { it.type == com.bitchat.android.model.BitchatMessageType.Image }) + assertTrue(state.getMessagesValue().none { it.content.contains("too large") }) + verify(mesh, never()).prepareFilePrivate(any(), any(), any(), any()) + } finally { + bigFile.delete() + } + } + + @Test + fun `oversized file failure is posted to the channel and nothing is sent`() { + val bigFile = kotlin.io.path.createTempFile("oversized-channel", ".jpg").toFile() + try { + bigFile.writeBytes(ByteArray(11 * 1024 * 1024) { 0x42 }) + + manager.sendImageNote(null, "#test", bigFile.absolutePath) + + val channelMessages = state.getChannelMessagesValue()["#test"].orEmpty() + assertEquals(1, channelMessages.size) + assertTrue(channelMessages.single().content.contains("too large")) + assertTrue(state.getMessagesValue().none { it.content.contains("too large") }) + verify(mesh, never()).prepareFilePrivate(any(), any(), any(), any()) + } finally { + bigFile.delete() + } + } + @Test fun `cancelled consent cannot later send or echo`() { whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))