From d244338b13ea93e1d9fb12f09bd40438ada76307 Mon Sep 17 00:00:00 2001
From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Date: Mon, 10 Jun 2024 18:18:05 +0400
Subject: [PATCH] ui: differentiate remote ctrl errors, better error texts
(#4302)
---
apps/ios/Shared/Model/SimpleXAPI.swift | 18 +++++-
.../RemoteAccess/ConnectDesktopView.swift | 28 +++++----
apps/ios/SimpleXChat/APITypes.swift | 2 +-
.../chat/simplex/common/model/SimpleXAPI.kt | 57 +++++++++++++++----
.../common/views/remote/ConnectDesktopView.kt | 19 +++++++
.../commonMain/resources/MR/base/strings.xml | 3 +
6 files changed, 103 insertions(+), 24 deletions(-)
diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index a828e1348d..49152283ee 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -1957,12 +1957,28 @@ func processReceivedMsg(_ res: ChatResponse) async {
let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "")
m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state)
}
- case .remoteCtrlStopped:
+ case let .remoteCtrlStopped(_, rcStopReason):
// This delay is needed to cancel the session that fails on network failure,
// e.g. when user did not grant permission to access local network yet.
if let sess = m.remoteCtrlSession {
await MainActor.run {
m.remoteCtrlSession = nil
+ dismissAllSheets() {
+ switch rcStopReason {
+ case .connectionFailed(.errorAgent(.RCP(.identity))):
+ AlertManager.shared.showAlertMsg(
+ title: "Connection with desktop stopped",
+ message: "This link was used with another mobile device, please create a new link on the desktop."
+ )
+ default:
+ AlertManager.shared.showAlert(Alert(
+ title: Text("Connection with desktop stopped"),
+ message: Text("Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers."),
+ primaryButton: .default(Text("Ok")),
+ secondaryButton: .default(Text("Copy error")) { UIPasteboard.general.string = String(describing: rcStopReason) }
+ ))
+ }
+ }
}
if case .connected = sess.sessionState {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
index 3059b049a3..1d47d9a936 100644
--- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
+++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
@@ -181,23 +181,27 @@ struct ConnectDesktopView: View {
}
private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View {
- List {
- Section("Connecting to desktop") {
- ctrlDeviceNameText(session, rc)
- ctrlDeviceVersionText(session)
- }
+ ZStack {
+ List {
+ Section("Connecting to desktop") {
+ ctrlDeviceNameText(session, rc)
+ ctrlDeviceVersionText(session)
+ }
- if let sessCode = session.sessionCode {
- Section("Session code") {
- sessionCodeText(sessCode)
+ if let sessCode = session.sessionCode {
+ Section("Session code") {
+ sessionCodeText(sessCode)
+ }
+ }
+
+ Section {
+ disconnectButton()
}
}
+ .navigationTitle("Connecting to desktop")
- Section {
- disconnectButton()
- }
+ ProgressView().scaleEffect(2)
}
- .navigationTitle("Connecting to desktop")
}
private func searchingDesktopView() -> some View {
diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift
index a3545972d4..7b0a0a6646 100644
--- a/apps/ios/SimpleXChat/APITypes.swift
+++ b/apps/ios/SimpleXChat/APITypes.swift
@@ -980,7 +980,7 @@ public enum ChatResponse: Decodable, Error {
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
- case .remoteCtrlStopped: return noDetails
+ case let .remoteCtrlStopped(rcsState, rcStopReason): return "rcsState: \(String(describing: rcsState))\nrcStopReason: \(String(describing: rcStopReason))"
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
case .cmdOk: return noDetails
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
index 839792e61c..c55ea0a871 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
@@ -1,9 +1,18 @@
package chat.simplex.common.model
+import SectionItemView
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
import chat.simplex.common.views.helpers.*
import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.style.TextAlign
import chat.simplex.common.model.ChatController.getNetCfg
import chat.simplex.common.model.ChatController.setNetCfg
import chat.simplex.common.model.ChatModel.updatingChatsMutex
@@ -12,7 +21,6 @@ import dev.icerock.moko.resources.compose.painterResource
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.call.*
-import chat.simplex.common.views.chat.group.toggleShowMemberMessages
import chat.simplex.common.views.migration.MigrationFileLinkData
import chat.simplex.common.views.onboarding.OnboardingStage
import chat.simplex.common.views.usersettings.*
@@ -20,6 +28,7 @@ import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
import chat.simplex.res.MR
import com.russhwolf.settings.Settings
+import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.sync.withLock
@@ -2194,15 +2203,43 @@ object ChatController {
val sess = chatModel.remoteCtrlSession.value
if (sess != null) {
chatModel.remoteCtrlSession.value = null
+ ModalManager.fullscreen.closeModals()
fun showAlert(chatError: ChatError) {
- AlertManager.shared.showAlertMsg(
- generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
- if (chatError is ChatError.ChatErrorRemoteCtrl) {
- chatError.remoteCtrlError.localizedString
- } else {
- generalGetString(MR.strings.remote_ctrl_disconnected_with_reason).format(chatError.string)
- }
- )
+ when {
+ r.rcStopReason is RemoteCtrlStopReason.ConnectionFailed
+ && r.rcStopReason.chatError is ChatError.ChatErrorAgent
+ && r.rcStopReason.chatError.agentError is AgentErrorType.RCP
+ && r.rcStopReason.chatError.agentError.rcpErr is RCErrorType.IDENTITY ->
+ AlertManager.shared.showAlertMsg(
+ title = generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
+ text = generalGetString(MR.strings.remote_ctrl_connection_stopped_identity_desc)
+ )
+ else ->
+ AlertManager.shared.showAlertDialogButtonsColumn(
+ title = generalGetString(MR.strings.remote_ctrl_was_disconnected_title),
+ text = if (chatError is ChatError.ChatErrorRemoteCtrl) {
+ chatError.remoteCtrlError.localizedString
+ } else {
+ generalGetString(MR.strings.remote_ctrl_connection_stopped_desc)
+ },
+ buttons = {
+ Column {
+ SectionItemView({
+ AlertManager.shared.hideAlert()
+ }) {
+ Text(stringResource(MR.strings.ok), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
+ }
+ val clipboard = LocalClipboardManager.current
+ SectionItemView({
+ clipboard.setText(AnnotatedString(json.encodeToString(r.rcStopReason)))
+ AlertManager.shared.hideAlert()
+ }) {
+ Text(stringResource(MR.strings.copy_error), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
+ }
+ }
+ }
+ )
+ }
}
when (r.rcStopReason) {
is RemoteCtrlStopReason.DiscoveryFailed -> showAlert(r.rcStopReason.chatError)
@@ -4716,7 +4753,7 @@ sealed class CR {
(if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) +
"\nsessionCode: $sessionCode"
is RemoteCtrlConnected -> json.encodeToString(remoteCtrl)
- is RemoteCtrlStopped -> noDetails()
+ is RemoteCtrlStopped -> "rcsState: $rcsState\nrcsStopReason: $rcStopReason"
is ContactPQAllowed -> withUser(user, "contact: ${contact.id}\npqEncryption: $pqEncryption")
is ContactPQEnabled -> withUser(user, "contact: ${contact.id}\npqEnabled: $pqEnabled")
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt
index 76f522c614..d201ac482a 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt
@@ -15,6 +15,7 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.snapshots.SnapshotStateList
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
@@ -166,6 +167,24 @@ private fun ConnectingDesktop(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) {
SectionView {
DisconnectButton(onClick = ::disconnectDesktop)
}
+
+ ProgressIndicator()
+}
+
+@Composable
+private fun ProgressIndicator() {
+ Box(
+ Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ Modifier
+ .padding(horizontal = 2.dp)
+ .size(30.dp),
+ color = MaterialTheme.colors.secondary,
+ strokeWidth = 3.dp
+ )
+ }
}
@Composable
diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
index 71218732d6..b896c4e980 100644
--- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
+++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
@@ -1921,6 +1921,9 @@
Connection stopped
%s with the reason: %s]]>
Disconnected with the reason: %s
+ Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers.
+ This link was used with another mobile device, please create a new link on the desktop.
+ Copy error
Disconnect desktop?
Only one device can work at the same time
Use from desktop in mobile app and scan QR code.]]>