Fix Wear NDR gate and transfer failure status

This commit is contained in:
Dev 2026-08-07 03:36:58 +03:00
parent 1eb98de4c0
commit 0020d61e0d
5 changed files with 74 additions and 5 deletions

View File

@ -628,8 +628,9 @@ class MediaSendingManager(
senderPeerID = meshService.myPeerID
)
// Preparation already built and admitted the exact final packet. Map
// progress before commit so the first asynchronous event cannot race us.
// Preparation already built the exact final packet. Map the transfer before commit so the
// first asynchronous event cannot race us, but keep the local status at Sending until a
// real progress event proves transport admission.
if (!messageManager.addPrivateMessageDurably(conversationID, msg, forceRead = true)) {
Log.e(TAG, "Prepared private-media message could not be persisted; send aborted")
addPrivateMediaSystemMessage(
@ -644,7 +645,7 @@ class MediaSendingManager(
}
messageManager.updateMessageDeliveryStatus(
msg.id,
com.bitchat.android.model.DeliveryStatus.PartiallyDelivered(0, 100)
com.bitchat.android.model.DeliveryStatus.Sending
)
if (!preparation.transfer.commit()) {
@ -708,10 +709,10 @@ class MediaSendingManager(
messageTransferMap[message.id] = transferId
}
// Seed progress so animations start immediately
// Seed the pending state without fabricating delivery progress.
messageManager.updateMessageDeliveryStatus(
message.id,
com.bitchat.android.model.DeliveryStatus.PartiallyDelivered(0, 100)
com.bitchat.android.model.DeliveryStatus.Sending
)
withContext(mediaWorkDispatcher) {

View File

@ -445,6 +445,30 @@ class MediaSendingManagerMigrationTest {
}
}
@Test
fun `public transfer failure replaces the local sending seed`() {
manager.sendImageNote(null, null, file.absolutePath)
val fileEcho = state.getMessagesValue()
.single { it.type == BitchatMessageType.Image }
assertTrue(fileEcho.deliveryStatus is DeliveryStatus.Sending)
manager.updateTransferProgress("failed-transfer", fileEcho.id)
manager.handleTransferProgressEvent(
com.bitchat.android.mesh.TransferProgressEvent(
transferId = "failed-transfer",
sent = 0,
total = 100,
completed = true,
failed = true
)
)
val failedEcho = state.getMessagesValue()
.single { it.id == fileEcho.id }
assertTrue(failedEcho.deliveryStatus is DeliveryStatus.Failed)
}
@Test
fun `cancelled consent cannot later send or echo`() {
whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))

View File

@ -98,6 +98,9 @@ val sharedSourceIncludes = listOf(
)
val sharedSourceExcludes = listOf(
"com/bitchat/android/model/FileSharingManager.kt",
// The phone gate reads phone-only BuildConfig fields. Wear shares mesh consumers of the
// gate, but does not ship NDR, so it provides a same-FQN fail-closed implementation.
"com/bitchat/android/model/NdrFeatureGate.kt",
// Legacy phone monolith and Wi-Fi Aware multiplexer; the watch composes its own service
// (MeshCore-style) in M2 instead of reusing these.
"com/bitchat/android/mesh/BluetoothMeshService.kt",

View File

@ -0,0 +1,21 @@
package com.bitchat.android.model
import com.bitchat.watch.BuildConfig
/**
* Wear OS does not currently ship the Nostr double-ratchet runtime.
*
* Keep shared mesh consumers fail-closed while retaining the debug-only override used by the
* shared JVM tests. Production Wear builds can never advertise or accept NDR through this gate.
*/
object NdrFeatureGate {
@Volatile
private var debugTestOverride = false
fun isEnabled(): Boolean = BuildConfig.DEBUG && debugTestOverride
internal fun setEnabledForTests(enabled: Boolean) {
check(BuildConfig.DEBUG) { "The NDR test override is unavailable in release builds" }
debugTestOverride = enabled
}
}

View File

@ -0,0 +1,20 @@
package com.bitchat.android.model
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Test
class NdrFeatureGateTest {
@After
fun resetGate() {
NdrFeatureGate.setEnabledForTests(false)
}
@Test
fun `wear does not advertise NDR by default`() {
NdrFeatureGate.setEnabledForTests(false)
assertFalse(NdrFeatureGate.isEnabled())
assertFalse(PeerCapabilities.LOCAL_SUPPORTED.contains(PeerCapabilities.NOSTR_DOUBLE_RATCHET))
}
}