diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt index 0ece93c5..63b4aeff 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt @@ -78,6 +78,7 @@ fun MemberListScreen( val localSockets by viewModel.localSockets.collectAsState() val channelOwner by viewModel.channelOwner.collectAsState() val radioMuteFailed by viewModel.radioMuteFailed.collectAsState() + val dmStartFailed by viewModel.dmStartFailed.collectAsState() val radioActive by viewModel.radioActive.collectAsState() val radioTalkState by viewModel.radioTalkState.collectAsState() val transmittingGroup by viewModel.radioAudio.transmittingGroup.collectAsState() @@ -156,6 +157,11 @@ fun MemberListScreen( confirmButton = { TextButton(onClick = viewModel::dismissRadioMuteError) { Text(stringResource(android.R.string.ok)) } } ) } + if (dmStartFailed) AlertDialog( + onDismissRequest = viewModel::dismissDmStartError, + text = { Text(stringResource(R.string.invite_pm_start_failed)) }, + confirmButton = { TextButton(onClick = viewModel::dismissDmStartError) { Text(stringResource(android.R.string.ok)) } } + ) val editingMember = editMember if (showEditDialog && editingMember != null) key(channel.id, editingMember.nodeId) { diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt index 1b1d25ef..7301f009 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt @@ -1339,7 +1339,9 @@ class ChatViewModel : ViewModel() { } fun startDm(channelId: String, nodeId: Long, name: String) { - val r = repo ?: return + val r = repo ?: run { + _dmStartFailed.value = true; LogManager.addLog("WARN", "PM", "Conversation start rejected: repository unavailable"); return + } if (nodeId == 0L || nodeId == NativeLib.getMyNodeId()) return cancelDmStart(); _dmStartFailed.value = false try { diff --git a/tools/chatgui-android/app/src/test/java/com/utun/chat/data/PendingDmOpenTest.kt b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/PendingDmOpenTest.kt new file mode 100644 index 00000000..b9642bd7 --- /dev/null +++ b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/PendingDmOpenTest.kt @@ -0,0 +1,43 @@ +package com.utun.chat.data + +import org.junit.Assert.* +import org.junit.Test + +class PendingDmOpenTest { + @Test fun unrelatedConversationDoesNotConsumeRequestedPeer() { + val pending = PendingDmOpen() + val token = pending.begin("51", 12, "requested") + assertNull(pending.complete("99", 7)) + assertTrue(pending.isPending(token)) + val selected = pending.complete("51", 9)!! + assertEquals("51", selected.convId) + assertEquals(12L, selected.peerNodeId) + assertEquals("requested", selected.peerName) + assertEquals(9L, selected.generation) + assertFalse(pending.pending) + assertNull(pending.complete("51", 10)) + } + @Test fun newRequestRejectsOldConfirmationAndTimeout() { + val pending = PendingDmOpen() + val previous = pending.begin("51", 12, "first") + val current = pending.begin("52", 13, "second") + assertFalse(pending.isPending(previous)) + assertNull(pending.complete("51", 1)) + assertTrue(pending.isPending(current)) + assertEquals(13L, pending.complete("52", 2)!!.peerNodeId) + } + @Test fun navigationOrTimeoutCancellationIgnoresLateConfirmation() { + val pending = PendingDmOpen() + val token = pending.begin("51", 12, "first") + pending.cancel() + assertFalse(pending.isPending(token)) + assertNull(pending.complete("51", 1)) + } + @Test fun invalidGenerationKeepsPendingValidConfirmation() { + val pending = PendingDmOpen() + pending.begin("51", 12, "first") + assertNull(pending.complete("51", 0)) + assertTrue(pending.pending) + assertNotNull(pending.complete("51", 1)) + } +}