Recover join dialog after clipboard and connection errors
This commit is contained in:
1 parent
87b7c6f36c
commit
bd4bd47e00
3 files changed
+51
-22
No files matched your search
+47
-22
@@ -1,7 +1,6 @@
|
||||
package com.utun.chat.ui.screens
|
||||
|
||||
import com.utun.chat.R
|
||||
import com.utun.chat.data.AppLanguage
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -10,42 +9,65 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.utun.chat.data.InviteData
|
||||
import com.utun.chat.data.InviteLink
|
||||
import com.utun.chat.data.LogManager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
/** Автоматически передаёт валидную ссылку из ввода/QR/буфера в onConnect; сетевой результат показывает вызывающий экран.
|
||||
* Callback должен принять запрос и закрыть диалог. Ошибка запуска разрешает повторить ту же ссылку. Секреты ссылки не логируются. */
|
||||
@Composable
|
||||
fun JoinChannelDialog(onConnect: (InviteData) -> Unit, onDismiss: () -> Unit) {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
var link by remember { mutableStateOf("") }
|
||||
var error by remember { mutableStateOf<String?>(null) }
|
||||
var link by rememberSaveable { mutableStateOf("") }
|
||||
var error by remember { mutableStateOf<Int?>(null) }
|
||||
var connecting by remember { mutableStateOf(false) }
|
||||
var retry by remember { mutableIntStateOf(0) }
|
||||
val connect by rememberUpdatedState(onConnect)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val text = clipboard.getText()?.text?.trim().orEmpty()
|
||||
if (text.startsWith("utun://")) {
|
||||
LogManager.addLog("INFO", "INVITE", "Invite link pasted from clipboard")
|
||||
link = text
|
||||
fun paste(onlyInvite: Boolean) {
|
||||
try {
|
||||
val text = clipboard.getText()?.text?.trim().orEmpty()
|
||||
if (!onlyInvite || text.startsWith("utun://")) {
|
||||
LogManager.addLog("INFO", "INVITE", "Invite input read from clipboard")
|
||||
link = text
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "INVITE", "Clipboard read failed: $e")
|
||||
error = R.string.invite_paste_failed
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(link) {
|
||||
error = null
|
||||
LaunchedEffect(Unit) {
|
||||
if (link.isEmpty()) paste(onlyInvite = true)
|
||||
}
|
||||
|
||||
LaunchedEffect(link, retry) {
|
||||
val text = link.trim()
|
||||
if (text.isEmpty() || connecting) return@LaunchedEffect
|
||||
val invite = InviteLink.decode(text)
|
||||
if (invite == null) {
|
||||
LogManager.addLog("WARN", "INVITE", "Invalid invite link in join dialog")
|
||||
error = AppLanguage.text(R.string.invalid_invite_link)
|
||||
} else {
|
||||
connecting = true
|
||||
LogManager.addLog("INFO", "INVITE", "connect: ch=0x%016x node=0x%016x addrs=%d"
|
||||
.format(invite.channelId, invite.nodeId, invite.addrs.size))
|
||||
connect(invite)
|
||||
error = null
|
||||
try {
|
||||
val invite = InviteLink.decode(text)
|
||||
if (invite == null) {
|
||||
LogManager.addLog("WARN", "INVITE", "Invalid invite link in join dialog")
|
||||
error = R.string.invalid_invite_link
|
||||
} else {
|
||||
connecting = true
|
||||
LogManager.addLog("INFO", "INVITE", "connect: ch=0x%016x node=0x%016x addrs=%d"
|
||||
.format(invite.channelId, invite.nodeId, invite.addrs.size))
|
||||
connect(invite)
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
connecting = false
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "INVITE", "Invite connection request failed: ${e.javaClass.simpleName}")
|
||||
connecting = false
|
||||
error = R.string.invite_connect_failed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +87,11 @@ fun JoinChannelDialog(onConnect: (InviteData) -> Unit, onDismiss: () -> Unit) {
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = link,
|
||||
onValueChange = { link = it },
|
||||
onValueChange = { error = null; link = it },
|
||||
label = { Text(stringResource(R.string.invite_link)) },
|
||||
placeholder = { Text("utun://...") },
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { link = clipboard.getText()?.text.orEmpty() }, enabled = !connecting) {
|
||||
IconButton(onClick = { paste(onlyInvite = false) }, enabled = !connecting) {
|
||||
Icon(Icons.Filled.ContentPaste, stringResource(R.string.paste_invite_link))
|
||||
}
|
||||
},
|
||||
@@ -78,7 +100,10 @@ fun JoinChannelDialog(onConnect: (InviteData) -> Unit, onDismiss: () -> Unit) {
|
||||
isError = error != null,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
error?.let { Text(it, color = MaterialTheme.colorScheme.error) }
|
||||
error?.let { Text(stringResource(it), color = MaterialTheme.colorScheme.error) }
|
||||
if (error == R.string.invite_connect_failed) {
|
||||
TextButton(onClick = { retry++ }) { Text(stringResource(R.string.retry)) }
|
||||
}
|
||||
if (connecting) Text(stringResource(R.string.connecting_peer), color = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -161,6 +161,8 @@
|
||||
<string name="qr_code">"QR-код"</string>
|
||||
<string name="invite_build_failed">"Не удалось собрать ссылку"</string>
|
||||
<string name="invite_copy_failed">Не удалось скопировать ссылку-приглашение</string>
|
||||
<string name="invite_paste_failed">Не удалось прочитать буфер обмена. Введите ссылку вручную.</string>
|
||||
<string name="invite_connect_failed">Не удалось начать подключение. Повторите попытку.</string>
|
||||
<string name="invite_delivery_status">%1$s: %2$s</string>
|
||||
<string name="copied">"Скопировано!"</string>
|
||||
<string name="copy_link">"Копировать ссылку"</string>
|
||||
|
||||
@@ -162,6 +162,8 @@
|
||||
<string name="qr_code">"QR code"</string>
|
||||
<string name="invite_build_failed">"Could not build invite link"</string>
|
||||
<string name="invite_copy_failed">Could not copy the invite link</string>
|
||||
<string name="invite_paste_failed">Could not read the clipboard. Enter an invite link manually.</string>
|
||||
<string name="invite_connect_failed">Could not start connecting. Please try again.</string>
|
||||
<string name="invite_delivery_status">%1$s: %2$s</string>
|
||||
<string name="copied">"Copied!"</string>
|
||||
<string name="copy_link">"Copy link"</string>
|
||||
|
||||
Reference in new issue
Block a user