From 4d3529a3e0295361563da048476dee7d81a71d36 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:00:13 +0000 Subject: [PATCH 01/18] desktop: fix incorrect remote host for active user (#3441) --- .../kotlin/chat/simplex/common/model/ChatModel.kt | 3 +++ .../kotlin/chat/simplex/common/model/SimpleXAPI.kt | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 7541b7f34..6739cdee7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -638,6 +638,9 @@ data class User( val addressShared: Boolean = profile.contactLink != null + fun updateRemoteHostId(rh: Long?): User = + if (rh == null) this else this.copy(remoteHostId = rh) + companion object { val sampleData = User( remoteHostId = null, 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 bfcec69da..8f837e1c5 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 @@ -468,7 +468,7 @@ object ChatController { suspend fun apiGetActiveUser(rh: Long?): User? { val r = sendCmd(rh, CC.ShowActiveUser()) - if (r is CR.ActiveUser) return r.user + if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh) Log.d(TAG, "apiGetActiveUser: ${r.responseType} ${r.details}") chatModel.userCreated.value = false return null @@ -476,7 +476,7 @@ object ChatController { suspend fun apiCreateActiveUser(rh: Long?, p: Profile?, sameServers: Boolean = false, pastTimestamp: Boolean = false): User? { val r = sendCmd(rh, CC.CreateActiveUser(p, sameServers = sameServers, pastTimestamp = pastTimestamp)) - if (r is CR.ActiveUser) return r.user + if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh) else if ( r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore && r.chatError.storeError is StoreError.DuplicateName || r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat && r.chatError.errorType is ChatErrorType.UserExists @@ -501,7 +501,7 @@ object ChatController { suspend fun apiSetActiveUser(rh: Long?, userId: Long, viewPwd: String?): User { val r = sendCmd(rh, CC.ApiSetActiveUser(userId, viewPwd)) - if (r is CR.ActiveUser) return if (rh == null) r.user else r.user.copy(remoteHostId = rh) + if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh) Log.d(TAG, "apiSetActiveUser: ${r.responseType} ${r.details}") throw Exception("failed to set the user as active ${r.responseType} ${r.details}") } @@ -538,7 +538,7 @@ object ChatController { private suspend fun setUserPrivacy(rh: Long?, cmd: CC): User { val r = sendCmd(rh, cmd) - if (r is CR.UserPrivacy) return if (rh == null) r.updatedUser else r.updatedUser.copy(remoteHostId = rh) + if (r is CR.UserPrivacy) return r.updatedUser.updateRemoteHostId(rh) else throw Exception("Failed to change user privacy: ${r.responseType} ${r.details}") } From f7903c5c83329d6bd1a8d28e082cc3583acff414 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Fri, 24 Nov 2023 03:49:53 +0800 Subject: [PATCH 02/18] desktop: close remote host connecting screen on stop of host (#3440) --- .../kotlin/chat/simplex/common/model/ChatModel.kt | 2 +- .../kotlin/chat/simplex/common/model/SimpleXAPI.kt | 4 ++-- .../simplex/common/views/remote/ConnectMobileView.kt | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 6739cdee7..18eba71fb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -113,7 +113,7 @@ object ChatModel { val currentRemoteHost = mutableStateOf(null) val remoteHostId: Long? @Composable get() = remember { currentRemoteHost }.value?.remoteHostId fun remoteHostId(): Long? = currentRemoteHost.value?.remoteHostId - val newRemoteHostPairing = mutableStateOf?>(null) + val remoteHostPairing = mutableStateOf?>(null) val remoteCtrlSession = mutableStateOf(null) fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) { 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 8f837e1c5..5a706efe6 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 @@ -1836,7 +1836,7 @@ object ChatController { is CR.GroupMemberRatchetSync -> chatModel.updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats) is CR.RemoteHostSessionCode -> { - chatModel.newRemoteHostPairing.value = r.remoteHost_ to RemoteHostSessionState.PendingConfirmation(r.sessionCode) + chatModel.remoteHostPairing.value = r.remoteHost_ to RemoteHostSessionState.PendingConfirmation(r.sessionCode) } is CR.RemoteHostConnected -> { // TODO needs to update it instead in sessions @@ -1845,7 +1845,7 @@ object ChatController { } is CR.RemoteHostStopped -> { val disconnectedHost = chatModel.remoteHosts.firstOrNull { it.remoteHostId == r.remoteHostId_ } - chatModel.newRemoteHostPairing.value = null + chatModel.remoteHostPairing.value = null if (disconnectedHost != null) { showToast( generalGetString(MR.strings.remote_host_was_disconnected_toast).format(disconnectedHost.hostDeviceName.ifEmpty { disconnectedHost.remoteHostId.toString() }) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt index 163d15ce6..a22cbfd9b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt @@ -235,7 +235,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { ModalManager.start.showModalCloseable { close -> val invitation = rememberSaveable { mutableStateOf(null) } val port = rememberSaveable { mutableStateOf(null) } - val pairing = remember { chatModel.newRemoteHostPairing } + val pairing = remember { chatModel.remoteHostPairing } val sessionCode = when (val state = pairing.value?.second) { is RemoteHostSessionState.PendingConfirmation -> state.sessionCode else -> null @@ -271,6 +271,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { connecting.value = true invitation.value = r.second port.value = r.third + chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting } } onDispose { @@ -279,7 +280,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { chatController.stopRemoteHost(null) } } - chatModel.newRemoteHostPairing.value = null + chatModel.remoteHostPairing.value = null } } } @@ -287,7 +288,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState) { ModalManager.start.showModalCloseable { close -> - val pairing = remember { chatModel.newRemoteHostPairing } + val pairing = remember { chatModel.remoteHostPairing } val invitation = rememberSaveable { mutableStateOf(null) } val port = rememberSaveable { mutableStateOf(null) } val sessionCode = when (val state = pairing.value?.second) { @@ -315,6 +316,7 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState remoteHostId = rh_?.remoteHostId invitation.value = inv port.value = r.third + chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting } } LaunchedEffect(remember { chatModel.currentRemoteHost }.value) { @@ -334,7 +336,7 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState chatController.stopRemoteHost(remoteHostId) } } - chatModel.newRemoteHostPairing.value = null + chatModel.remoteHostPairing.value = null } } } From b2dbb558f9a242f94ad5a7963ffebb34f0b3ea11 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Fri, 24 Nov 2023 05:00:11 +0800 Subject: [PATCH 03/18] android, desktop: connect remote desktop via multicast (#3442) * android, desktop: connect remote desktop via multicast * changes * string --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- .../chat/simplex/common/model/ChatModel.kt | 5 +- .../chat/simplex/common/model/SimpleXAPI.kt | 48 ++++- .../views/helpers/DefaultBasicTextField.kt | 2 +- .../common/views/remote/ConnectDesktopView.kt | 166 +++++++++++++++--- .../common/views/remote/ConnectMobileView.kt | 8 +- .../commonMain/resources/MR/base/strings.xml | 7 +- 6 files changed, 200 insertions(+), 36 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 18eba71fb..144f461a9 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2921,7 +2921,7 @@ enum class NotificationPreviewMode { } data class RemoteCtrlSession( - val ctrlAppInfo: CtrlAppInfo, + val ctrlAppInfo: CtrlAppInfo?, val appVersion: String, val sessionState: UIRemoteCtrlSessionState ) { @@ -2939,6 +2939,7 @@ data class RemoteCtrlSession( @Serializable sealed class RemoteCtrlSessionState { @Serializable @SerialName("starting") object Starting: RemoteCtrlSessionState() + @Serializable @SerialName("searching") object Searching: RemoteCtrlSessionState() @Serializable @SerialName("connecting") object Connecting: RemoteCtrlSessionState() @Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val sessionCode: String): RemoteCtrlSessionState() @Serializable @SerialName("connected") data class Connected(val sessionCode: String): RemoteCtrlSessionState() @@ -2946,6 +2947,8 @@ sealed class RemoteCtrlSessionState { sealed class UIRemoteCtrlSessionState { @Serializable @SerialName("starting") object Starting: UIRemoteCtrlSessionState() + @Serializable @SerialName("searching") object Searching: UIRemoteCtrlSessionState() + @Serializable @SerialName("found") data class Found(val remoteCtrl: RemoteCtrlInfo, val compatible: Boolean): UIRemoteCtrlSessionState() @Serializable @SerialName("connecting") data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState() @Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState() @Serializable @SerialName("connected") data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState() 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 5a706efe6..20fa4abf5 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 @@ -170,6 +170,7 @@ class AppPreferences { val confirmRemoteSessions = mkBoolPreference(SHARED_PREFS_CONFIRM_REMOTE_SESSIONS, false) val connectRemoteViaMulticast = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST, false) + val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true) val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true) private fun mkIntPreference(prefName: String, default: Int) = @@ -314,6 +315,7 @@ class AppPreferences { private const val SHARED_PREFS_DEVICE_NAME_FOR_REMOTE_ACCESS = "DeviceNameForRemoteAccess" private const val SHARED_PREFS_CONFIRM_REMOTE_SESSIONS = "ConfirmRemoteSessions" private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST = "ConnectRemoteViaMulticast" + private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "ConnectRemoteViaMulticastAuto" private const val SHARED_PREFS_OFFER_REMOTE_MULTICAST = "OfferRemoteMulticast" } } @@ -1432,14 +1434,25 @@ object ChatController { suspend fun connectRemoteCtrl(desktopAddress: String): Pair { val r = sendCmd(null, CC.ConnectRemoteCtrl(desktopAddress)) - if (r is CR.RemoteCtrlConnecting) return SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null - else if (r is CR.ChatCmdError) return null to r - else throw Exception("connectRemoteCtrl error: ${r.responseType} ${r.details}") + return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null + else if (r is CR.ChatCmdError) null to r + else { + apiErrorAlert("connectRemoteCtrl", generalGetString(MR.strings.error_alert_title), r) + null to null + } } suspend fun findKnownRemoteCtrl(): Boolean = sendCommandOkResp(null, CC.FindKnownRemoteCtrl()) - suspend fun confirmRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(null, CC.ConfirmRemoteCtrl(rcId)) + suspend fun confirmRemoteCtrl(rcId: Long): Pair { + val r = sendCmd(null, CC.ConfirmRemoteCtrl(remoteCtrlId = rcId)) + return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null + else if (r is CR.ChatCmdError) null to r + else { + apiErrorAlert("confirmRemoteCtrl", generalGetString(MR.strings.error_alert_title), r) + null to null + } + } suspend fun verifyRemoteCtrlSession(sessionCode: String): RemoteCtrlInfo? { val r = sendCmd(null, CC.VerifyRemoteCtrlSession(sessionCode)) @@ -1857,8 +1870,15 @@ object ChatController { } } is CR.RemoteCtrlFound -> { - // TODO multicast - Log.d(TAG, "RemoteCtrlFound: ${r.remoteCtrl}") + val sess = chatModel.remoteCtrlSession.value + if (sess != null && sess.sessionState is UIRemoteCtrlSessionState.Searching) { + val state = UIRemoteCtrlSessionState.Found(remoteCtrl = r.remoteCtrl, compatible = r.compatible) + chatModel.remoteCtrlSession.value = RemoteCtrlSession( + ctrlAppInfo = r.ctrlAppInfo_, + appVersion = r.appVersion, + sessionState = state + ) + } } is CR.RemoteCtrlSessionCode -> { val state = UIRemoteCtrlSessionState.PendingConfirmation(remoteCtrl_ = r.remoteCtrl_, sessionCode = r.sessionCode) @@ -1870,7 +1890,13 @@ object ChatController { chatModel.remoteCtrlSession.value = chatModel.remoteCtrlSession.value?.copy(sessionState = state) } is CR.RemoteCtrlStopped -> { - switchToLocalSession() + val sess = chatModel.remoteCtrlSession.value + if (sess != null) { + chatModel.remoteCtrlSession.value = null + if (sess.sessionState is UIRemoteCtrlSessionState.Connected) { + switchToLocalSession() + } + } } else -> Log.d(TAG , "unsupported event: ${r.responseType}") @@ -3782,7 +3808,7 @@ sealed class CR { @Serializable @SerialName("remoteFileStored") class RemoteFileStored(val remoteHostId: Long, val remoteFileSource: CryptoFile): CR() // remote events (mobile) @Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List): CR() - @Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo): CR() + @Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo, val ctrlAppInfo_: CtrlAppInfo?, val appVersion: String, val compatible: Boolean): CR() @Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrl_: RemoteCtrlInfo?, val ctrlAppInfo: CtrlAppInfo, val appVersion: String): CR() @Serializable @SerialName("remoteCtrlSessionCode") class RemoteCtrlSessionCode(val remoteCtrl_: RemoteCtrlInfo?, val sessionCode: String): CR() @Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrl: RemoteCtrlInfo): CR() @@ -4080,7 +4106,11 @@ sealed class CR { is RemoteHostStopped -> "remote host ID: $remoteHostId_" is RemoteFileStored -> "remote host ID: $remoteHostId\nremoteFileSource:\n" + json.encodeToString(remoteFileSource) is RemoteCtrlList -> json.encodeToString(remoteCtrls) - is RemoteCtrlFound -> json.encodeToString(remoteCtrl) + is RemoteCtrlFound -> "remote ctrl: " + json.encodeToString(remoteCtrl) + + "\nctrlAppInfo: " + + (if (ctrlAppInfo_ == null) "null" else json.encodeToString(ctrlAppInfo_)) + + "\nappVersion: $appVersion" + + "\ncompatible: $compatible" is RemoteCtrlConnecting -> "remote ctrl: " + (if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) + diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt index 71801e7a5..08dfaa0df 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt @@ -125,7 +125,7 @@ fun DefaultConfigurableTextField( keyboardType: KeyboardType = KeyboardType.Text, dependsOn: State? = null, ) { - var valid by remember { mutableStateOf(validKey(state.value.text)) } + var valid by remember { mutableStateOf(isValid(state.value.text)) } var showKey by remember { mutableStateOf(false) } val icon = if (valid) { if (showKey) painterResource(MR.images.ic_visibility_off_filled) else painterResource(MR.images.ic_visibility_filled) 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 8d3cfddce..44e6969b8 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 @@ -21,6 +21,7 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -37,6 +38,7 @@ import chat.simplex.common.views.newchat.QRCodeScanner import chat.simplex.common.views.usersettings.PreferenceToggle import chat.simplex.common.views.usersettings.SettingsActionItem import chat.simplex.res.MR +import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource @@ -69,15 +71,21 @@ fun ConnectDesktopView(close: () -> Unit) { @Composable private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) { + val showConnectScreen = remember { mutableStateOf(true) } val sessionAddress = remember { mutableStateOf("") } val remoteCtrls = remember { mutableStateListOf() } val session = remember { chatModel.remoteCtrlSession }.value Column( Modifier.fillMaxWidth().verticalScroll(rememberScrollState()), ) { - if (session != null) { + val discovery = if (session == null) null else session.sessionState is UIRemoteCtrlSessionState.Searching + if (discovery == true || (discovery == null && !showConnectScreen.value)) { + SearchingDesktop(deviceName, remoteCtrls) + } else if (session != null) { when (session.sessionState) { is UIRemoteCtrlSessionState.Starting -> ConnectingDesktop(session, null) + is UIRemoteCtrlSessionState.Searching -> SearchingDesktop(deviceName, remoteCtrls) + is UIRemoteCtrlSessionState.Found -> FoundDesktop(session, session.sessionState.remoteCtrl, session.sessionState.compatible, remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }, deviceName, remoteCtrls, sessionAddress) is UIRemoteCtrlSessionState.Connecting -> ConnectingDesktop(session, session.sessionState.remoteCtrl_) is UIRemoteCtrlSessionState.PendingConfirmation -> { if (controller.appPrefs.confirmRemoteSessions.get() || session.sessionState.remoteCtrl_ == null) { @@ -97,11 +105,21 @@ private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) { } SectionBottomSpacer() } - DisposableEffect(Unit) { + LaunchedEffect(Unit) { setDeviceName(deviceName) updateRemoteCtrls(remoteCtrls) + val useMulticast = useMulticast(remoteCtrls) + showConnectScreen.value = !useMulticast + if (chatModel.remoteCtrlSession.value != null) { + disconnectDesktop() + } else if (useMulticast) { + findKnownDesktop(showConnectScreen) + } + } + DisposableEffect(Unit) { onDispose { if (chatModel.remoteCtrlSession.value != null) { + showConnectScreen.value = false disconnectDesktop() } } @@ -146,7 +164,75 @@ private fun ConnectingDesktop(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) { SectionSpacer() SectionView { - DisconnectButton(::disconnectDesktop) + DisconnectButton(onClick = ::disconnectDesktop) + } +} + +@Composable +private fun SearchingDesktop(deviceName: String, remoteCtrls: SnapshotStateList) { + AppBarTitle(stringResource(MR.strings.connecting_to_desktop)) + SectionView(stringResource(MR.strings.this_device_name).uppercase()) { + DevicesView(deviceName, remoteCtrls) { + if (it != "") { + setDeviceName(it) + controller.appPrefs.deviceNameForRemoteAccess.set(it) + } + } + } + SectionDividerSpaced() + SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) { + Text(stringResource(MR.strings.waiting_for_desktop), fontStyle = FontStyle.Italic) + } + SectionSpacer() + DisconnectButton(stringResource(MR.strings.scan_QR_code).replace('\n', ' '), MR.images.ic_qr_code, ::disconnectDesktop) +} + +@Composable +private fun FoundDesktop( + session: RemoteCtrlSession, + rc: RemoteCtrlInfo, + compatible: Boolean, + connectRemoteViaMulticastAuto: State, + deviceName: String, + remoteCtrls: SnapshotStateList, + sessionAddress: MutableState, +) { + AppBarTitle(stringResource(MR.strings.found_desktop)) + SectionView(stringResource(MR.strings.this_device_name).uppercase()) { + DevicesView(deviceName, remoteCtrls) { + if (it != "") { + setDeviceName(it) + controller.appPrefs.deviceNameForRemoteAccess.set(it) + } + } + } + SectionDividerSpaced() + SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) { + CtrlDeviceNameText(session, rc) + CtrlDeviceVersionText(session) + if (!compatible) { + Text(stringResource(MR.strings.not_compatible), color = MaterialTheme.colors.error) + } + } + + SectionSpacer() + + if (compatible) { + SectionItemView({ confirmKnownDesktop(sessionAddress, rc) }) { + Icon(painterResource(MR.images.ic_check), generalGetString(MR.strings.connect_button), tint = MaterialTheme.colors.secondary) + TextIconSpaced(false) + Text(generalGetString(MR.strings.connect_button)) + } + } + + if (!compatible || !connectRemoteViaMulticastAuto.value) { + DisconnectButton(stringResource(MR.strings.cancel_verb), onClick = ::disconnectDesktop) + } + + if (compatible && connectRemoteViaMulticastAuto.value) { + LaunchedEffect(Unit) { + confirmKnownDesktop(sessionAddress, rc) + } } } @@ -174,7 +260,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC } SectionView { - DisconnectButton(::disconnectDesktop) + DisconnectButton(onClick = ::disconnectDesktop) } } @@ -182,7 +268,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) { val newDesktop = annotatedStringResource(MR.strings.new_desktop) val text = remember(rc) { - var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo.deviceName) + var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo?.deviceName ?: "") if (rc == null) { t = t + AnnotatedString(" ") + newDesktop } @@ -195,7 +281,7 @@ private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) private fun CtrlDeviceVersionText(session: RemoteCtrlSession) { val thisDeviceVersion = annotatedStringResource(MR.strings.this_device_version, session.appVersion) val text = remember(session) { - val v = AnnotatedString(session.ctrlAppInfo.appVersionRange.maxVersion) + val v = AnnotatedString(session.ctrlAppInfo?.appVersionRange?.maxVersion ?: "") var t = AnnotatedString("v$v") if (v.text != session.appVersion) { t = t + AnnotatedString(" ") + thisDeviceVersion @@ -243,7 +329,8 @@ private fun SessionCodeText(code: String) { private fun DevicesView(deviceName: String, remoteCtrls: SnapshotStateList, updateDeviceName: (String) -> Unit) { DeviceNameField(deviceName) { updateDeviceName(it) } if (remoteCtrls.isNotEmpty()) { - SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) } }) { + SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) } + }) { Text(generalGetString(MR.strings.linked_desktops)) } } @@ -336,8 +423,13 @@ private fun LinkedDesktopsView(remoteCtrls: SnapshotStateList) { PreferenceToggle(stringResource(MR.strings.verify_connections), remember { controller.appPrefs.confirmRemoteSessions.state }.value) { controller.appPrefs.confirmRemoteSessions.set(it) } - PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value && false) { - controller.appPrefs.confirmRemoteSessions.set(it) + PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) { + controller.appPrefs.connectRemoteViaMulticast.set(it) + } + if (remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) { + PreferenceToggle(stringResource(MR.strings.multicast_connect_automatically), remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }.value) { + controller.appPrefs.connectRemoteViaMulticastAuto.set(it) + } } } SectionBottomSpacer() @@ -355,13 +447,11 @@ private fun setDeviceName(name: String) { } } -private fun updateRemoteCtrls(remoteCtrls: SnapshotStateList) { - withBGApi { - val res = controller.listRemoteCtrls() - if (res != null) { - remoteCtrls.clear() - remoteCtrls.addAll(res) - } +private suspend fun updateRemoteCtrls(remoteCtrls: SnapshotStateList) { + val res = controller.listRemoteCtrls() + if (res != null) { + remoteCtrls.clear() + remoteCtrls.addAll(res) } } @@ -369,9 +459,34 @@ private fun processDesktopQRCode(sessionAddress: MutableState, resp: Str connectDesktopAddress(sessionAddress, resp) } -private fun connectDesktopAddress(sessionAddress: MutableState, addr: String) { +private fun findKnownDesktop(showConnectScreen: MutableState) { withBGApi { - val res = controller.connectRemoteCtrl(desktopAddress = addr) + if (controller.findKnownRemoteCtrl()) { + chatModel.remoteCtrlSession.value = RemoteCtrlSession( + ctrlAppInfo = null, + appVersion = "", + sessionState = UIRemoteCtrlSessionState.Searching + ) + showConnectScreen.value = true + } + } +} + +private fun confirmKnownDesktop(sessionAddress: MutableState, rc: RemoteCtrlInfo) { + connectDesktop(sessionAddress) { + controller.confirmRemoteCtrl(rc.remoteCtrlId) + } +} + +private fun connectDesktopAddress(sessionAddress: MutableState, addr: String) { + connectDesktop(sessionAddress) { + controller.connectRemoteCtrl(addr) + } +} + +private fun connectDesktop(sessionAddress: MutableState, connect: suspend () -> Pair) { + withBGApi { + val res = connect() if (res.first != null) { val (rc_, ctrlAppInfo, v) = res.first!! sessionAddress.value = "" @@ -409,18 +524,25 @@ private fun verifyDesktopSessionCode(remoteCtrls: SnapshotStateList Unit) { +private fun DisconnectButton(label: String = generalGetString(MR.strings.disconnect_remote_host), icon: ImageResource = MR.images.ic_close, onClick: () -> Unit) { SectionItemView(onClick) { - Icon(painterResource(MR.images.ic_close), generalGetString(MR.strings.disconnect_remote_host), tint = MaterialTheme.colors.secondary) + Icon(painterResource(icon), label, tint = MaterialTheme.colors.secondary) TextIconSpaced(false) - Text(generalGetString(MR.strings.disconnect_remote_host)) + Text(label) } } +private fun useMulticast(remoteCtrls: List): Boolean = + controller.appPrefs.connectRemoteViaMulticast.get() && remoteCtrls.isNotEmpty() + private fun disconnectDesktop(close: (() -> Unit)? = null) { withBGApi { controller.stopRemoteCtrl() - switchToLocalSession() + if (chatModel.remoteCtrlSession.value?.sessionState is UIRemoteCtrlSessionState.Connected) { + switchToLocalSession() + } else { + chatModel.remoteCtrlSession.value = null + } close?.invoke() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt index a22cbfd9b..53f0339be 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt @@ -30,6 +30,7 @@ import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.helpers.* import chat.simplex.common.views.newchat.QRCode +import chat.simplex.common.views.usersettings.PreferenceToggle import chat.simplex.common.views.usersettings.SettingsActionItemWithContent import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource @@ -90,6 +91,9 @@ fun ConnectMobileLayout( SectionView(generalGetString(MR.strings.this_device_name).uppercase()) { DeviceNameField(deviceName.value ?: "") { updateDeviceName(it) } SectionTextFooter(generalGetString(MR.strings.this_device_name_shared_with_mobile)) + PreferenceToggle(stringResource(MR.strings.multicast_discoverable_via_local_network), remember { controller.appPrefs.offerRemoteMulticast.state }.value) { + controller.appPrefs.offerRemoteMulticast.set(it) + } SectionDividerSpaced(maxBottomPadding = false) } SectionView(stringResource(MR.strings.devices).uppercase()) { @@ -266,7 +270,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { } DisposableEffect(Unit) { withBGApi { - val r = chatModel.controller.startRemoteHost(null) + val r = chatModel.controller.startRemoteHost(null, controller.appPrefs.offerRemoteMulticast.get()) if (r != null) { connecting.value = true invitation.value = r.second @@ -309,7 +313,7 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState ) var remoteHostId by rememberSaveable { mutableStateOf(null) } LaunchedEffect(Unit) { - val r = chatModel.controller.startRemoteHost(rh.remoteHostId) + val r = chatModel.controller.startRemoteHost(rh.remoteHostId, controller.appPrefs.offerRemoteMulticast.get()) if (r != null) { val (rh_, inv) = r connecting.value = true 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 44f950c32..22f5d2fbe 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1670,6 +1670,8 @@ Connection terminated Session code Connecting to desktop + Waiting for desktop… + Found desktop Connect to desktop Connected to desktop Connected desktop @@ -1681,9 +1683,12 @@ Scan QR code from desktop Desktop address Verify connections - Discover on network + Discover via local network + Discoverable via local network + Connect automatically Paste desktop address Desktop + Not compatible! Coming soon! From 8f0a9cd6090920c89667557e082c1449c8a2ca0d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:22:29 +0000 Subject: [PATCH 04/18] ios: connect remote desktop via multicast (#3436) * ios: connect remote desktop via multicast * works * fix camera freeze when leaving linked devices view * label * fix linked devices * fix compatible * string --- apps/ios/Shared/Model/ChatModel.swift | 8 +- apps/ios/Shared/Model/SimpleXAPI.swift | 29 +++- .../RemoteAccess/ConnectDesktopView.swift | 152 ++++++++++++++++-- .../Views/UserSettings/SettingsView.swift | 6 +- apps/ios/SimpleXChat/APITypes.swift | 5 +- .../chat/simplex/common/model/SimpleXAPI.kt | 2 +- 6 files changed, 173 insertions(+), 29 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 90e4272b0..4c0f36102 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -770,7 +770,7 @@ final class GMember: ObservableObject, Identifiable { } struct RemoteCtrlSession { - var ctrlAppInfo: CtrlAppInfo + var ctrlAppInfo: CtrlAppInfo? var appVersion: String var sessionState: UIRemoteCtrlSessionState @@ -782,6 +782,10 @@ struct RemoteCtrlSession { if case .connected = sessionState { true } else { false } } + var discovery: Bool { + if case .searching = sessionState { true } else { false } + } + var sessionCode: String? { switch sessionState { case let .pendingConfirmation(_, sessionCode): sessionCode @@ -793,6 +797,8 @@ struct RemoteCtrlSession { enum UIRemoteCtrlSessionState { case starting + case searching + case found(remoteCtrl: RemoteCtrlInfo, compatible: Bool) case connecting(remoteCtrl_: RemoteCtrlInfo?) case pendingConfirmation(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) case connected(remoteCtrl: RemoteCtrlInfo, sessionCode: String) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 9e4cc7cd0..e010de3e8 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -919,8 +919,10 @@ func findKnownRemoteCtrl() async throws { try await sendCommandOkResp(.findKnownRemoteCtrl) } -func confirmRemoteCtrl(_ rcId: Int64) async throws { - try await sendCommandOkResp(.confirmRemoteCtrl(remoteCtrlId: rcId)) +func confirmRemoteCtrl(_ rcId: Int64) async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String) { + let r = await chatSendCmd(.confirmRemoteCtrl(remoteCtrlId: rcId)) + if case let .remoteCtrlConnecting(rc_, ctrlAppInfo, v) = r { return (rc_, ctrlAppInfo, v) } + throw r } func verifyRemoteCtrlSession(_ sessCode: String) async throws -> RemoteCtrlInfo { @@ -1714,9 +1716,17 @@ func processReceivedMsg(_ res: ChatResponse) async { await MainActor.run { m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats) } - case let .remoteCtrlFound(remoteCtrl): - // TODO multicast - logger.debug("\(String(describing: remoteCtrl))") + case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): + await MainActor.run { + if let sess = m.remoteCtrlSession, case .searching = sess.sessionState { + let state = UIRemoteCtrlSessionState.found(remoteCtrl: remoteCtrl, compatible: compatible) + m.remoteCtrlSession = RemoteCtrlSession( + ctrlAppInfo: ctrlAppInfo_, + appVersion: appVersion, + sessionState: state + ) + } + } case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): await MainActor.run { let state = UIRemoteCtrlSessionState.pendingConfirmation(remoteCtrl_: remoteCtrl_, sessionCode: sessionCode) @@ -1731,8 +1741,13 @@ func processReceivedMsg(_ res: ChatResponse) async { case .remoteCtrlStopped: // 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. - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - switchToLocalSession() + if let sess = m.remoteCtrlSession { + m.remoteCtrlSession = nil + if case .connected = sess.sessionState { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + switchToLocalSession() + } + } } default: logger.debug("unsupported event: \(res.responseType)") diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift index 0ee231288..1f120860e 100644 --- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift +++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift @@ -16,12 +16,19 @@ struct ConnectDesktopView: View { var viaSettings = false @AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name @AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false - @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = false - @AppStorage(DEFAULT_OFFER_REMOTE_MULTICAST) private var offerRemoteMulticast = true + @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = true + @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO) private var connectRemoteViaMulticastAuto = true @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @State private var sessionAddress: String = "" @State private var remoteCtrls: [RemoteCtrlInfo] = [] @State private var alert: ConnectDesktopAlert? + @State private var showConnectScreen = true + @State private var showQRCodeScanner = true + @State private var firstAppearance = true + + private var useMulticast: Bool { + connectRemoteViaMulticast && !remoteCtrls.isEmpty + } private enum ConnectDesktopAlert: Identifiable { case unlinkDesktop(rc: RemoteCtrlInfo) @@ -67,9 +74,14 @@ struct ConnectDesktopView: View { var viewBody: some View { Group { - if let session = m.remoteCtrlSession { + let discovery = m.remoteCtrlSession?.discovery + if discovery == true || (discovery == nil && !showConnectScreen) { + searchingDesktopView() + } else if let session = m.remoteCtrlSession { switch session.sessionState { case .starting: connectingDesktopView(session, nil) + case .searching: searchingDesktopView() + case let .found(rc, compatible): foundDesktopView(session, rc, compatible) case let .connecting(rc_): connectingDesktopView(session, rc_) case let .pendingConfirmation(rc_, sessCode): if confirmRemoteSessions || rc_ == nil { @@ -81,16 +93,35 @@ struct ConnectDesktopView: View { } case let .connected(rc, _): activeSessionView(session, rc) } - } else { + // The hack below prevents camera freezing when exiting linked devices view. + // Using showQRCodeScanner inside connectDesktopView or passing it as parameter still results in freezing. + } else if showQRCodeScanner || firstAppearance { connectDesktopView() + } else { + connectDesktopView(showScanner: false) } } .onAppear { setDeviceName(deviceName) updateRemoteCtrls() + showConnectScreen = !useMulticast + if m.remoteCtrlSession != nil { + disconnectDesktop() + } else if useMulticast { + findKnownDesktop() + } + // The hack below prevents camera freezing when exiting linked devices view. + // `firstAppearance` prevents camera flicker when the view first opens. + // moving `showQRCodeScanner = false` to `onDisappear` (to avoid `firstAppearance`) does not prevent freeze. + showQRCodeScanner = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + firstAppearance = false + showQRCodeScanner = true + } } .onDisappear { if m.remoteCtrlSession != nil { + showConnectScreen = false disconnectDesktop() } } @@ -134,12 +165,14 @@ struct ConnectDesktopView: View { .interactiveDismissDisabled(m.activeRemoteCtrl) } - private func connectDesktopView() -> some View { + private func connectDesktopView(showScanner: Bool = true) -> some View { List { Section("This device name") { devicesView() } - scanDesctopAddressView() + if showScanner { + scanDesctopAddressView() + } if developerTools { desktopAddressView() } @@ -167,6 +200,56 @@ struct ConnectDesktopView: View { .navigationTitle("Connecting to desktop") } + private func searchingDesktopView() -> some View { + List { + Section("This device name") { + devicesView() + } + Section("Found desktop") { + Text("Waiting for desktop...").italic() + Button { + disconnectDesktop(.dismiss) + } label: { + Label("Scan QR code", systemImage: "qrcode") + } + } + } + .navigationTitle("Connecting to desktop") + } + + @ViewBuilder private func foundDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo, _ compatible: Bool) -> some View { + let v = List { + Section("This device name") { + devicesView() + } + Section("Found desktop") { + ctrlDeviceNameText(session, rc) + ctrlDeviceVersionText(session) + if !compatible { + Text("Not compatible!").foregroundColor(.red) + } else if !connectRemoteViaMulticastAuto { + Button { + confirmKnownDesktop(rc) + } label: { + Label("Connect", systemImage: "checkmark") + } + } + } + if !compatible && !connectRemoteViaMulticastAuto { + Section { + disconnectButton("Cancel") + } + } + } + .navigationTitle("Found desktop") + + if compatible && connectRemoteViaMulticastAuto { + v.onAppear { confirmKnownDesktop(rc) } + } else { + v + } + } + private func verifySessionView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?, _ sessCode: String) -> some View { List { Section("Connected to desktop") { @@ -191,7 +274,7 @@ struct ConnectDesktopView: View { } private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text { - var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo.deviceName) + var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo?.deviceName ?? "") if (rc == nil) { t = t + Text(" ") + Text("(new)").italic() } @@ -199,8 +282,8 @@ struct ConnectDesktopView: View { } private func ctrlDeviceVersionText(_ session: RemoteCtrlSession) -> Text { - let v = session.ctrlAppInfo.appVersionRange.maxVersion - var t = Text("v\(v)") + let v = session.ctrlAppInfo?.appVersionRange.maxVersion + var t = Text("v\(v ?? "")") if v != session.appVersion { t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic() } @@ -301,7 +384,10 @@ struct ConnectDesktopView: View { Section("Linked desktop options") { Toggle("Verify connections", isOn: $confirmRemoteSessions) - Toggle("Discover on network", isOn: $connectRemoteViaMulticast).disabled(true) + Toggle("Discover via local network", isOn: $connectRemoteViaMulticast) + if connectRemoteViaMulticast { + Toggle("Connect automatically", isOn: $connectRemoteViaMulticastAuto) + } } } .navigationTitle("Linked desktops") @@ -335,10 +421,42 @@ struct ConnectDesktopView: View { } } - private func connectDesktopAddress(_ addr: String) { + private func findKnownDesktop() { Task { do { - let (rc_, ctrlAppInfo, v) = try await connectRemoteCtrl(desktopAddress: addr) + try await findKnownRemoteCtrl() + await MainActor.run { + m.remoteCtrlSession = RemoteCtrlSession( + ctrlAppInfo: nil, + appVersion: "", + sessionState: .searching + ) + showConnectScreen = true + } + } catch let e { + await MainActor.run { + errorAlert(e) + } + } + } + } + + private func confirmKnownDesktop(_ rc: RemoteCtrlInfo) { + connectDesktop_ { + try await confirmRemoteCtrl(rc.remoteCtrlId) + } + } + + private func connectDesktopAddress(_ addr: String) { + connectDesktop_ { + try await connectRemoteCtrl(desktopAddress: addr) + } + } + + private func connectDesktop_(_ connect: @escaping () async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String)) { + Task { + do { + let (rc_, ctrlAppInfo, v) = try await connect() await MainActor.run { sessionAddress = "" m.remoteCtrlSession = RemoteCtrlSession( @@ -380,11 +498,11 @@ struct ConnectDesktopView: View { } } - private func disconnectButton() -> some View { + private func disconnectButton(_ label: LocalizedStringKey = "Disconnect") -> some View { Button { disconnectDesktop(.dismiss) } label: { - Label("Disconnect", systemImage: "multiply") + Label(label, systemImage: "multiply") } } @@ -393,7 +511,11 @@ struct ConnectDesktopView: View { do { try await stopRemoteCtrl() await MainActor.run { - switchToLocalSession() + if case .connected = m.remoteCtrlSession?.sessionState { + switchToLocalSession() + } else { + m.remoteCtrlSession = nil + } switch action { case .back: dismiss() case .dismiss: dismiss() diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 423786eb6..f889d9c39 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -56,7 +56,7 @@ let DEFAULT_SHOW_UNREAD_AND_FAVORITES = "showUnreadAndFavorites" let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess" let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions" let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast" -let DEFAULT_OFFER_REMOTE_MULTICAST = "offerRemoteMulticast" +let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "connectRemoteViaMulticastAuto" let appDefaults: [String: Any] = [ DEFAULT_SHOW_LA_NOTICE: false, @@ -91,8 +91,8 @@ let appDefaults: [String: Any] = [ DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME: 300, DEFAULT_SHOW_UNREAD_AND_FAVORITES: false, DEFAULT_CONFIRM_REMOTE_SESSIONS: false, - DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: false, - DEFAULT_OFFER_REMOTE_MULTICAST: true + DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true, + DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true, ] enum SimpleXLinkMode: String, Identifiable { diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index e7409a072..ad0e5ee10 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -609,7 +609,7 @@ public enum ChatResponse: Decodable, Error { case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection) // remote desktop responses/events case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo]) - case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo) + case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo, ctrlAppInfo_: CtrlAppInfo?, appVersion: String, compatible: Bool) case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String) case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) @@ -903,7 +903,7 @@ public enum ChatResponse: Decodable, Error { case let .newContactConnection(u, connection): return withUser(u, String(describing: connection)) case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls) - case let .remoteCtrlFound(remoteCtrl): return String(describing: remoteCtrl) + case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)" 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) @@ -1546,6 +1546,7 @@ public struct RemoteCtrlInfo: Decodable { public enum RemoteCtrlSessionState: Decodable { case starting + case searching case connecting case pendingConfirmation(sessionCode: String) case connected(sessionCode: String) 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 20fa4abf5..56b4b0a13 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 @@ -1394,7 +1394,7 @@ object ChatController { chatModel.remoteHosts.addAll(hosts) } - suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = false): Triple? { + suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = true): Triple? { val r = sendCmd(null, CC.StartRemoteHost(rhId, multicast)) if (r is CR.RemoteHostStarted) return Triple(r.remoteHost_, r.invitation, r.ctrlPort) apiErrorAlert("startRemoteHost", generalGetString(MR.strings.error_alert_title), r) From 6f3174d0a10f4174bb2e44fdc39c184578343c00 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:25:32 +0000 Subject: [PATCH 05/18] android, desktop: remove unnecessary serialization --- .../kotlin/chat/simplex/common/model/ChatModel.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 144f461a9..754e9dfe2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2946,10 +2946,10 @@ sealed class RemoteCtrlSessionState { } sealed class UIRemoteCtrlSessionState { - @Serializable @SerialName("starting") object Starting: UIRemoteCtrlSessionState() - @Serializable @SerialName("searching") object Searching: UIRemoteCtrlSessionState() - @Serializable @SerialName("found") data class Found(val remoteCtrl: RemoteCtrlInfo, val compatible: Boolean): UIRemoteCtrlSessionState() - @Serializable @SerialName("connecting") data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState() - @Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState() - @Serializable @SerialName("connected") data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState() + object Starting: UIRemoteCtrlSessionState() + object Searching: UIRemoteCtrlSessionState() + data class Found(val remoteCtrl: RemoteCtrlInfo, val compatible: Boolean): UIRemoteCtrlSessionState() + data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState() + data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState() + data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState() } From 74e80eb34837bbb4d1c118e5362ef0f2637a1050 Mon Sep 17 00:00:00 2001 From: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:00:20 +0200 Subject: [PATCH 06/18] core: add remote stop reason and state (#3444) * add remote stop reason and state * rename --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- src/Simplex/Chat/Controller.hs | 25 +++++++++++++++--- src/Simplex/Chat/Remote.hs | 46 ++++++++++++++++++---------------- src/Simplex/Chat/View.hs | 8 +++--- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index d4f9c9331..d40810167 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -662,14 +662,14 @@ data ChatResponse | CRRemoteHostSessionCode {remoteHost_ :: Maybe RemoteHostInfo, sessionCode :: Text} | CRNewRemoteHost {remoteHost :: RemoteHostInfo} | CRRemoteHostConnected {remoteHost :: RemoteHostInfo} - | CRRemoteHostStopped {remoteHostId_ :: Maybe RemoteHostId} + | CRRemoteHostStopped {remoteHostId_ :: Maybe RemoteHostId, rhsState :: RemoteHostSessionState, rhStopReason :: RemoteHostStopReason} | CRRemoteFileStored {remoteHostId :: RemoteHostId, remoteFileSource :: CryptoFile} | CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]} | CRRemoteCtrlFound {remoteCtrl :: RemoteCtrlInfo, ctrlAppInfo_ :: Maybe CtrlAppInfo, appVersion :: AppVersion, compatible :: Bool} | CRRemoteCtrlConnecting {remoteCtrl_ :: Maybe RemoteCtrlInfo, ctrlAppInfo :: CtrlAppInfo, appVersion :: AppVersion} | CRRemoteCtrlSessionCode {remoteCtrl_ :: Maybe RemoteCtrlInfo, sessionCode :: Text} | CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo} - | CRRemoteCtrlStopped + | CRRemoteCtrlStopped {rcsState :: RemoteCtrlSessionState, rcStopReason :: RemoteCtrlStopReason} | CRSQLResult {rows :: [Text]} | CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]} | CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks} @@ -700,14 +700,14 @@ allowRemoteEvent = \case CRRemoteHostSessionCode {} -> False CRNewRemoteHost _ -> False CRRemoteHostConnected _ -> False - CRRemoteHostStopped _ -> False + CRRemoteHostStopped {} -> False CRRemoteFileStored {} -> False CRRemoteCtrlList _ -> False CRRemoteCtrlFound {} -> False CRRemoteCtrlConnecting {} -> False CRRemoteCtrlSessionCode {} -> False CRRemoteCtrlConnected _ -> False - CRRemoteCtrlStopped -> False + CRRemoteCtrlStopped {} -> False CRSQLResult _ -> False CRSlowSQLQueries {} -> False _ -> True @@ -1083,6 +1083,12 @@ data RemoteHostError | RHEProtocolError RemoteProtocolError deriving (Show, Exception) +data RemoteHostStopReason + = RHSRConnectionFailed ChatError + | RHSRCrashed ChatError + | RHSRDisconnected + deriving (Show, Exception) + -- TODO review errors, some of it can be covered by HTTP2 errors data RemoteCtrlError = RCEInactive -- ^ No session is running @@ -1098,6 +1104,13 @@ data RemoteCtrlError | RCEProtocolError {protocolError :: RemoteProtocolError} deriving (Show, Exception) +data RemoteCtrlStopReason + = RCSRDiscoveryFailed ChatError + | RCSRConnectionFailed ChatError + | RCSRSetupFailed ChatError + | RCSRDisconnected + deriving (Show, Exception) + data ArchiveError = AEImport {chatError :: ChatError} | AEImportFile {file :: String, chatError :: ChatError} @@ -1323,6 +1336,10 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCS") ''RemoteCtrlSessionState) $(JQ.deriveJSON defaultJSON ''RemoteCtrlInfo) +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCSR") ''RemoteCtrlStopReason) + +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RHSR") ''RemoteHostStopReason) + $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CR") ''ChatResponse) $(JQ.deriveFromJSON defaultJSON ''ArchiveConfig) diff --git a/src/Simplex/Chat/Remote.hs b/src/Simplex/Chat/Remote.hs index ff271ba1c..e2137b35a 100644 --- a/src/Simplex/Chat/Remote.hs +++ b/src/Simplex/Chat/Remote.hs @@ -169,12 +169,12 @@ startRemoteHost rh_ = do handleConnectError :: ChatMonad m => RHKey -> SessionSeq -> m a -> m a handleConnectError rhKey sessSeq action = action `catchChatError` \err -> do logError $ "startRemoteHost.rcConnectHost crashed: " <> tshow err - cancelRemoteHostSession (Just sessSeq) rhKey + cancelRemoteHostSession (Just (sessSeq, RHSRConnectionFailed err)) rhKey throwError err handleHostError :: ChatMonad m => SessionSeq -> TVar RHKey -> m () -> m () handleHostError sessSeq rhKeyVar action = action `catchChatError` \err -> do logError $ "startRemoteHost.waitForHostSession crashed: " <> tshow err - readTVarIO rhKeyVar >>= cancelRemoteHostSession (Just sessSeq) + readTVarIO rhKeyVar >>= cancelRemoteHostSession (Just (sessSeq, RHSRCrashed err)) waitForHostSession :: ChatMonad m => Maybe RemoteHostInfo -> RHKey -> SessionSeq -> TVar RHKey -> RCStepTMVar (ByteString, TLS, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)) -> m () waitForHostSession remoteHost_ rhKey sseq rhKeyVar vars = do (sessId, tls, vars') <- timeoutThrow (ChatErrorRemoteHost rhKey RHETimeout) 60000000 $ takeRCStep vars @@ -220,7 +220,7 @@ startRemoteHost rh_ = do onDisconnected :: ChatMonad m => RHKey -> SessionSeq -> m () onDisconnected rhKey sseq = do logDebug $ "HTTP2 client disconnected: " <> tshow (rhKey, sseq) - cancelRemoteHostSession (Just sseq) rhKey + cancelRemoteHostSession (Just (sseq, RHSRDisconnected)) rhKey pollEvents :: ChatMonad m => RemoteHostId -> RemoteHostClient -> m () pollEvents rhId rhClient = do oq <- asks outputQ @@ -246,24 +246,25 @@ closeRemoteHost rhKey = do logNote $ "Closing remote host session for " <> tshow rhKey cancelRemoteHostSession Nothing rhKey -cancelRemoteHostSession :: ChatMonad m => Maybe SessionSeq -> RHKey -> m () -cancelRemoteHostSession sseq_ rhKey = do +cancelRemoteHostSession :: ChatMonad m => Maybe (SessionSeq, RemoteHostStopReason) -> RHKey -> m () +cancelRemoteHostSession handlerInfo_ rhKey = do sessions <- asks remoteHostSessions crh <- asks currentRemoteHost deregistered <- atomically $ TM.lookup rhKey sessions >>= \case Nothing -> pure Nothing - Just (sessSeq, _) | maybe False (/= sessSeq) sseq_ -> pure Nothing -- ignore cancel from a ghost session handler + Just (sessSeq, _) | maybe False (/= sessSeq) (fst <$> handlerInfo_) -> pure Nothing -- ignore cancel from a ghost session handler Just (_, rhs) -> do TM.delete rhKey sessions modifyTVar' crh $ \cur -> if (RHId <$> cur) == Just rhKey then Nothing else cur -- only wipe the closing RH pure $ Just rhs forM_ deregistered $ \session -> do liftIO $ cancelRemoteHost handlingError session `catchAny` (logError . tshow) - when handlingError $ toView $ CRRemoteHostStopped rhId_ + forM_ (snd <$> handlerInfo_) $ \rhStopReason -> + toView $ CRRemoteHostStopped {remoteHostId_, rhsState = rhsSessionState session, rhStopReason} where - handlingError = isJust sseq_ - rhId_ = case rhKey of + handlingError = isJust handlerInfo_ + remoteHostId_ = case rhKey of RHNew -> Nothing RHId rhId -> Just rhId @@ -395,7 +396,7 @@ findKnownRemoteCtrl = do sseq <- startRemoteCtrlSession foundCtrl <- newEmptyTMVarIO cmdOk <- newEmptyTMVarIO - action <- async $ handleCtrlError sseq "findKnownRemoteCtrl.discover" $ do + action <- async $ handleCtrlError sseq RCSRDiscoveryFailed "findKnownRemoteCtrl.discover" $ do atomically $ takeTMVar cmdOk (RCCtrlPairing {ctrlFingerprint}, inv@(RCVerifiedInvitation RCInvitation {app})) <- timeoutThrow (ChatErrorRemoteCtrl RCETimeout) discoveryTimeout . withAgent $ \a -> rcDiscoverCtrl a pairings @@ -441,7 +442,7 @@ startRemoteCtrlSession = do Right sseq <$ writeTVar session (Just (sseq, RCSessionStarting)) connectRemoteCtrl :: ChatMonad m => RCVerifiedInvitation -> SessionSeq -> m (Maybe RemoteCtrlInfo, CtrlAppInfo) -connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app}) sseq = handleCtrlError sseq "connectRemoteCtrl" $ do +connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app}) sseq = handleCtrlError sseq RCSRConnectionFailed "connectRemoteCtrl" $ do ctrlInfo@CtrlAppInfo {deviceName = ctrlDeviceName} <- parseCtrlAppInfo app v <- checkAppVersion ctrlInfo rc_ <- withStore' $ \db -> getRemoteCtrlByFingerprint db ca @@ -452,7 +453,7 @@ connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app}) cmdOk <- newEmptyTMVarIO rcsWaitSession <- async $ do atomically $ takeTMVar cmdOk - handleCtrlError sseq "waitForCtrlSession" $ waitForCtrlSession rc_ ctrlDeviceName rcsClient vars + handleCtrlError sseq RCSRConnectionFailed "waitForCtrlSession" $ waitForCtrlSession rc_ ctrlDeviceName rcsClient vars updateRemoteCtrlSession sseq $ \case RCSessionStarting -> Right RCSessionConnecting {remoteCtrlId_ = remoteCtrlId' <$> rc_, rcsClient, rcsWaitSession} _ -> Left $ ChatErrorRemoteCtrl RCEBadState @@ -602,7 +603,7 @@ verifyRemoteCtrlSession execChatCommand sessCode' = do Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive Just (sseq, RCSessionPendingConfirmation {rcsClient, ctrlDeviceName = ctrlName, sessionCode, rcsWaitConfirmation}) -> pure (sseq, rcsClient, ctrlName, sessionCode, rcsWaitConfirmation) _ -> throwError $ ChatErrorRemoteCtrl RCEBadState - handleCtrlError sseq "verifyRemoteCtrlSession" $ do + handleCtrlError sseq RCSRSetupFailed "verifyRemoteCtrlSession" $ do let verified = sameVerificationCode sessCode' sessionCode timeoutThrow (ChatErrorRemoteCtrl RCETimeout) networkIOTimeout . liftIO $ confirmCtrlSession client verified -- signal verification result before crashing unless verified $ throwError $ ChatErrorRemoteCtrl $ RCEProtocolError PRESessionCode @@ -630,31 +631,32 @@ verifyRemoteCtrlSession execChatCommand sessCode' = do monitor sseq server = do res <- waitCatch server logInfo $ "HTTP2 server stopped: " <> tshow res - cancelActiveRemoteCtrl (Just sseq) + cancelActiveRemoteCtrl $ Just (sseq, RCSRDisconnected) stopRemoteCtrl :: ChatMonad m => m () stopRemoteCtrl = cancelActiveRemoteCtrl Nothing -handleCtrlError :: ChatMonad m => SessionSeq -> Text -> m a -> m a -handleCtrlError sseq name action = +handleCtrlError :: ChatMonad m => SessionSeq -> (ChatError -> RemoteCtrlStopReason) -> Text -> m a -> m a +handleCtrlError sseq mkReason name action = action `catchChatError` \e -> do logError $ name <> " remote ctrl error: " <> tshow e - cancelActiveRemoteCtrl (Just sseq) + cancelActiveRemoteCtrl $ Just (sseq, mkReason e) throwError e -- | Stop session controller, unless session update key is present but stale -cancelActiveRemoteCtrl :: ChatMonad m => Maybe SessionSeq -> m () -cancelActiveRemoteCtrl sseq_ = handleAny (logError . tshow) $ do +cancelActiveRemoteCtrl :: ChatMonad m => Maybe (SessionSeq, RemoteCtrlStopReason) -> m () +cancelActiveRemoteCtrl handlerInfo_ = handleAny (logError . tshow) $ do var <- asks remoteCtrlSession session_ <- atomically $ readTVar var >>= \case Nothing -> pure Nothing - Just (oldSeq, _) | maybe False (/= oldSeq) sseq_ -> pure Nothing + Just (oldSeq, _) | maybe False (/= oldSeq) (fst <$> handlerInfo_) -> pure Nothing Just (_, s) -> Just s <$ writeTVar var Nothing forM_ session_ $ \session -> do liftIO $ cancelRemoteCtrl handlingError session - when handlingError $ toView CRRemoteCtrlStopped + forM_ (snd <$> handlerInfo_) $ \rcStopReason -> + toView CRRemoteCtrlStopped {rcsState = rcsSessionState session, rcStopReason} where - handlingError = isJust sseq_ + handlingError = isJust handlerInfo_ cancelRemoteCtrl :: Bool -> RemoteCtrlSession -> IO () cancelRemoteCtrl handlingError = \case diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 1867ee2f6..9cd26650f 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -299,8 +299,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe ] CRNewRemoteHost RemoteHostInfo {remoteHostId = rhId, hostDeviceName} -> ["new remote host " <> sShow rhId <> " added: " <> plain hostDeviceName] CRRemoteHostConnected RemoteHostInfo {remoteHostId = rhId} -> ["remote host " <> sShow rhId <> " connected"] - CRRemoteHostStopped rhId_ -> - [ maybe "new remote host" (mappend "remote host " . sShow) rhId_ <> " stopped" + CRRemoteHostStopped {remoteHostId_} -> + [ maybe "new remote host" (mappend "remote host " . sShow) remoteHostId_ <> " stopped" ] CRRemoteFileStored rhId (CryptoFile filePath cfArgs_) -> [plain $ "file " <> filePath <> " stored on remote host " <> show rhId] @@ -311,7 +311,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe <> maybe (deviceName <> "not compatible") (\info -> viewRemoteCtrl info appVersion compatible) ctrlAppInfo_ ] where - deviceName = if T.null ctrlDeviceName then "" else plain ctrlDeviceName <> ", " + deviceName = if T.null ctrlDeviceName then "" else plain ctrlDeviceName <> ", " CRRemoteCtrlConnecting {remoteCtrl_, ctrlAppInfo, appVersion} -> [ (maybe "connecting new remote controller" (\RemoteCtrlInfo {remoteCtrlId} -> "connecting remote controller " <> sShow remoteCtrlId) remoteCtrl_ <> ": ") <> viewRemoteCtrl ctrlAppInfo appVersion True @@ -323,7 +323,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe ] CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} -> ["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName] - CRRemoteCtrlStopped -> ["remote controller stopped"] + CRRemoteCtrlStopped {} -> ["remote controller stopped"] CRSQLResult rows -> map plain rows CRSlowSQLQueries {chatQueries, agentQueries} -> let viewQuery SlowSQLQuery {query, queryStats = SlowQueryStats {count, timeMax, timeAvg}} = From b1cf1656a0ad2b2775ea0613cac3bed555d2a4bf Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:48:14 +0000 Subject: [PATCH 07/18] core: cli remote control help section (#3445) --- src/Simplex/Chat.hs | 4 ++-- src/Simplex/Chat/Controller.hs | 2 +- src/Simplex/Chat/Help.hs | 31 ++++++++++++++++++++++++++++++- src/Simplex/Chat/View.hs | 2 ++ tests/RemoteTests.hs | 1 + 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 2ad92b4ef..cf3767b5c 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -6073,8 +6073,9 @@ chatCommandP = ("/help groups" <|> "/help group" <|> "/hg") $> ChatHelp HSGroups, ("/help contacts" <|> "/help contact" <|> "/hc") $> ChatHelp HSContacts, ("/help address" <|> "/ha") $> ChatHelp HSMyAddress, - "/help incognito" $> ChatHelp HSIncognito, + ("/help incognito" <|> "/hi") $> ChatHelp HSIncognito, ("/help messages" <|> "/hm") $> ChatHelp HSMessages, + ("/help remote" <|> "/hr") $> ChatHelp HSRemote, ("/help settings" <|> "/hs") $> ChatHelp HSSettings, ("/help db" <|> "/hd") $> ChatHelp HSDatabase, ("/help" <|> "/h") $> ChatHelp HSMain, @@ -6181,7 +6182,6 @@ chatCommandP = "/set disappear " *> (SetUserTimedMessages <$> (("yes" $> True) <|> ("no" $> False))), ("/incognito" <* optional (A.space *> onOffP)) $> ChatHelp HSIncognito, "/set device name " *> (SetLocalDeviceName <$> textP), - -- "/create remote host" $> CreateRemoteHost, "/list remote hosts" $> ListRemoteHosts, "/switch remote host " *> (SwitchRemoteHost <$> ("local" $> Nothing <|> (Just <$> A.decimal))), "/start remote host " *> (StartRemoteHost <$> ("new" $> Nothing <|> (Just <$> ((,) <$> A.decimal <*> (" multicast=" *> onOffP <|> pure False))))), diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index d40810167..3c0054ec1 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -203,7 +203,7 @@ data ChatController = ChatController contactMergeEnabled :: TVar Bool } -data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSSettings | HSDatabase +data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase deriving (Show) data ChatCommand diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index c2a572063..5d0548ca3 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -10,6 +10,7 @@ module Simplex.Chat.Help myAddressHelpInfo, incognitoHelpInfo, messagesHelpInfo, + remoteHelpInfo, markdownInfo, settingsInfo, databaseHelpInfo, @@ -87,7 +88,7 @@ chatHelpInfo = green "Create your address: " <> highlight "/address", "", green "Other commands:", - indent <> highlight "/help " <> " - help on: " <> listHighlight ["groups", "contacts", "messages", "files", "address", "settings", "db"], + indent <> highlight "/help " <> " - help on: " <> listHighlight ["groups", "contacts", "messages", "files", "address", "incognito", "remote", "settings", "db"], indent <> highlight "/profile " <> " - show / update user profile", indent <> highlight "/delete " <> " - delete contact and all messages with them", indent <> highlight "/chats " <> " - most recent chats", @@ -272,6 +273,34 @@ messagesHelpInfo = indent <> highlight "! #team (hi) " <> " - to edit your message in the group #team" ] +remoteHelpInfo :: [StyledString] +remoteHelpInfo = + map + styleMarkdown + [ green "Remote control", + "You can use CLI as a remote controller for a mobile app or as a remote host for a desktop app (or another CLI).", + "For example, you can run CLI on a server and use it from a desktop computer, connecting via SSH port forwarding.", + "", + indent <> highlight "/set device name " <> " - set CLI name for remote connections", + "", + green "Using as remote controller", + indent <> highlight "/start remote host new " <> " - pair and connect a new remote host", + indent <> highlight "/start remote host [multicast=on] " <> " - start connection with a known (paired) remote host", + indent <> highlight "/stop remote host new " <> " - cancel pairing with a new remote host", + indent <> highlight "/stop remote host " <> " - stop connection with connected remote host", + indent <> highlight "/switch remote host local " <> " - switch to using local database", + indent <> highlight "/switch remote host " <> " - switch to connected remote host", + indent <> highlight "/list remote hosts " <> " - list known remote hosts", + indent <> highlight "/delete remote host " <> " - delete (unpair) known remote hosts - also deletes all host files from controller", + "", + green "Using as remote host", + indent <> highlight "/connect remote ctrl " <> " - connect to remote controller via the adress from /start remote host", + indent <> highlight "/stop remote ctrl " <> " - stop connection with remote controller", + indent <> highlight "/find remote ctrl " <> " - find known remote controller on the local network (it should be started with multicast=on)", + indent <> highlight "/list remote ctrls " <> " - list known remote controllers", + indent <> highlight "/delete remote ctrl " <> " - delete known remote controller" + ] + markdownInfo :: [StyledString] markdownInfo = map diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 9cd26650f..4dbda6da6 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -136,6 +136,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe HSIncognito -> incognitoHelpInfo HSMessages -> messagesHelpInfo HSMarkdown -> markdownInfo + HSRemote -> remoteHelpInfo HSSettings -> settingsInfo HSDatabase -> databaseHelpInfo CRWelcome user -> chatWelcome user @@ -310,6 +311,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe [ "remote controller " <> sShow remoteCtrlId <> " found: " <> maybe (deviceName <> "not compatible") (\info -> viewRemoteCtrl info appVersion compatible) ctrlAppInfo_ ] + <> [ "use " <> highlight ("/confirm remote ctrl " <> show remoteCtrlId) <> " to connect" | isJust ctrlAppInfo_ && compatible] where deviceName = if T.null ctrlDeviceName then "" else plain ctrlDeviceName <> ", " CRRemoteCtrlConnecting {remoteCtrl_, ctrlAppInfo, appVersion} -> diff --git a/tests/RemoteTests.hs b/tests/RemoteTests.hs index 9f77245d9..147f1d939 100644 --- a/tests/RemoteTests.hs +++ b/tests/RemoteTests.hs @@ -460,6 +460,7 @@ startRemoteDiscover mobile desktop = do mobile ##> "/find remote ctrl" mobile <## "ok" mobile <## ("remote controller 1 found: My desktop, v" <> versionNumber) + mobile <## "use /confirm remote ctrl 1 to connect" mobile ##> "/confirm remote ctrl 1" mobile <## ("connecting remote controller 1: My desktop, v" <> versionNumber) From c9aec88c3934e77a7dbd85c346ba8b8c925c6fe1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:14:52 +0000 Subject: [PATCH 08/18] desktop: fix sending videos via connected mobile --- .../commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 56b4b0a13..5b44312cc 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 @@ -2348,7 +2348,7 @@ sealed class CC { is DeleteRemoteHost -> "/delete remote host $remoteHostId" is StoreRemoteFile -> "/store remote file $remoteHostId " + - (if (storeEncrypted == null) "" else " encrypt=${onOff(storeEncrypted)} ") + + (if (storeEncrypted == null) "" else "encrypt=${onOff(storeEncrypted)} ") + localPath is GetRemoteFile -> "/get remote file $remoteHostId ${json.encodeToString(file)}" is ConnectRemoteCtrl -> "/connect remote ctrl $xrcpInvitation" From bfd13f059a2320b98e80530931627cd5a3545dfb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:18:31 +0000 Subject: [PATCH 09/18] ui: translations (#3446) * Translated using Weblate (Dutch) Currently translated at 100.0% (1502 of 1502 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1503 of 1503 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1503 of 1503 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ * Translated using Weblate (Russian) Currently translated at 97.7% (1459 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Russian) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Italian) Currently translated at 100.0% (1493 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1502 of 1502 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1503 of 1503 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1503 of 1503 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ * Translated using Weblate (Russian) Currently translated at 97.7% (1459 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Russian) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Italian) Currently translated at 100.0% (1493 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/ * Translated using Weblate (Russian) Currently translated at 100.0% (1493 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Russian) Currently translated at 99.8% (1339 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Arabic) Currently translated at 92.4% (1380 of 1493 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1494 of 1494 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Added translation using Weblate (Hungarian) * Translated using Weblate (Dutch) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Translated using Weblate (Hungarian) Currently translated at 0.5% (8 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 1.4% (21 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 2.0% (30 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 2.1% (32 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 10.9% (163 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 12.3% (184 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Hungarian) Currently translated at 14.8% (222 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Translated using Weblate (Hungarian) Currently translated at 21.6% (323 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (German) Currently translated at 97.7% (1461 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (French) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/ * Translated using Weblate (French) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/ * Translated using Weblate (Hungarian) Currently translated at 21.9% (328 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (German) Currently translated at 100.0% (1495 of 1495 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (German) Currently translated at 100.0% (1500 of 1500 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (1341 of 1341 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1500 of 1500 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1500 of 1500 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/ * Translated using Weblate (Turkish) Currently translated at 70.0% (1051 of 1500 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/ * ru: corrections * export/import * ios: export * ru: corrections --------- Co-authored-by: M1K4 Co-authored-by: Float Co-authored-by: Hosted Weblate Co-authored-by: J R Co-authored-by: Random Co-authored-by: jonnysemon Co-authored-by: Istvan Novak Co-authored-by: Eric Co-authored-by: mlanp Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com> Co-authored-by: xe1st --- .../bg.xcloc/Localized Contents/bg.xliff | 24 +- .../cs.xcloc/Localized Contents/cs.xliff | 24 +- .../de.xcloc/Localized Contents/de.xliff | 66 +++- .../en.xcloc/Localized Contents/en.xliff | 31 +- .../es.xcloc/Localized Contents/es.xliff | 24 +- .../fi.xcloc/Localized Contents/fi.xliff | 24 +- .../fr.xcloc/Localized Contents/fr.xliff | 66 +++- .../it.xcloc/Localized Contents/it.xliff | 36 +- .../ja.xcloc/Localized Contents/ja.xliff | 24 +- .../nl.xcloc/Localized Contents/nl.xliff | 36 +- .../pl.xcloc/Localized Contents/pl.xliff | 24 +- .../ru.xcloc/Localized Contents/ru.xliff | 140 ++++++- .../th.xcloc/Localized Contents/th.xliff | 24 +- .../uk.xcloc/Localized Contents/uk.xliff | 24 +- .../Localized Contents/zh-Hans.xliff | 24 +- apps/ios/de.lproj/Localizable.strings | 117 ++++++ .../de.lproj/SimpleX--iOS--InfoPlist.strings | 3 + apps/ios/fr.lproj/Localizable.strings | 117 ++++++ .../fr.lproj/SimpleX--iOS--InfoPlist.strings | 3 + apps/ios/it.lproj/Localizable.strings | 30 +- apps/ios/nl.lproj/Localizable.strings | 30 +- apps/ios/ru.lproj/Localizable.strings | 321 ++++++++++++++++ .../ru.lproj/SimpleX--iOS--InfoPlist.strings | 3 + .../commonMain/resources/MR/ar/strings.xml | 8 + .../commonMain/resources/MR/de/strings.xml | 63 +++ .../commonMain/resources/MR/fr/strings.xml | 58 +++ .../commonMain/resources/MR/hu/strings.xml | 359 ++++++++++++++++++ .../commonMain/resources/MR/it/strings.xml | 14 + .../commonMain/resources/MR/nl/strings.xml | 21 +- .../commonMain/resources/MR/ru/strings.xml | 124 +++++- .../commonMain/resources/MR/tr/strings.xml | 23 ++ .../resources/MR/zh-rCN/strings.xml | 21 +- 32 files changed, 1858 insertions(+), 48 deletions(-) create mode 100644 apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index b5ae218ab..7a2afea08 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -1172,6 +1172,10 @@ Свързване server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Свързване инкогнито @@ -1862,8 +1866,8 @@ This cannot be undone! Открийте и се присъединете към групи No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2499,6 +2503,10 @@ This cannot be undone! За конзолата No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Френски интерфейс @@ -3449,6 +3457,10 @@ This is your link for group %@! Няма получени или изпратени файлове No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Известия @@ -5396,6 +5408,10 @@ To connect, please ask your contact to create another connection link and check Гласово съобщение… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Изчаква се получаването на файла @@ -5926,6 +5942,10 @@ SimpleX сървърите не могат да видят вашия профи аудио разговор (не е e2e криптиран) No comment provided by engineer. + + author + member role + bad message ID лошо ID на съобщението diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index 1e7b2c1fb..be8b23658 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -1172,6 +1172,10 @@ Připojit server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Spojit se inkognito @@ -1862,8 +1866,8 @@ This cannot be undone! Objevte a připojte skupiny No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2499,6 +2503,10 @@ This cannot be undone! Pro konzoli No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Francouzské rozhraní @@ -3449,6 +3457,10 @@ This is your link for group %@! Žádné přijaté ani odeslané soubory No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Oznámení @@ -5396,6 +5408,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Hlasová zpráva… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Čekání na soubor @@ -5926,6 +5942,10 @@ Servery SimpleX nevidí váš profil. zvukový hovor (nešifrovaný e2e) No comment provided by engineer. + + author + member role + bad message ID špatné ID zprávy diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index 4d37da1f3..29f5dd4e3 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -299,10 +299,12 @@ (new) + (Neu) No comment provided by engineer. (this device v%@) + (Dieses Gerät hat v%@) No comment provided by engineer. @@ -397,6 +399,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - Optionale Benachrichtigung von gelöschten Kontakten. +- Profilnamen mit Leerzeichen. +- Und mehr! No comment provided by engineer. @@ -885,6 +890,7 @@ Bad desktop address + Falsche Desktop-Adresse No comment provided by engineer. @@ -899,6 +905,7 @@ Better groups + Bessere Gruppen No comment provided by engineer. @@ -913,6 +920,7 @@ Block group members + Gruppenmitglieder blockieren No comment provided by engineer. @@ -1186,6 +1194,10 @@ Verbinden server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Inkognito verbinden @@ -1193,6 +1205,7 @@ Connect to desktop + Mit dem Desktop verbinden No comment provided by engineer. @@ -1241,10 +1254,12 @@ Das ist Ihr eigener Einmal-Link! Connected desktop + Verbundener Desktop No comment provided by engineer. Connected to desktop + Mit dem Desktop verbunden No comment provided by engineer. @@ -1259,6 +1274,7 @@ Das ist Ihr eigener Einmal-Link! Connecting to desktop + Mit dem Desktop verbinden No comment provided by engineer. @@ -1283,6 +1299,7 @@ Das ist Ihr eigener Einmal-Link! Connection terminated + Verbindung beendet No comment provided by engineer. @@ -1367,6 +1384,7 @@ Das ist Ihr eigener Einmal-Link! Create a group using a random profile. + Erstellen Sie eine Gruppe mit einem zufälligen Profil. No comment provided by engineer. @@ -1781,14 +1799,17 @@ Das kann nicht rückgängig gemacht werden! Desktop address + Desktop-Adresse No comment provided by engineer. Desktop app version %@ is not compatible with this app. + Desktop App-Version %@ ist mit dieser App nicht kompatibel. No comment provided by engineer. Desktop devices + Desktop-Geräte No comment provided by engineer. @@ -1883,6 +1904,7 @@ Das kann nicht rückgängig gemacht werden! Disconnect desktop? + Desktop-Verbindung trennen? No comment provided by engineer. @@ -1890,8 +1912,8 @@ Das kann nicht rückgängig gemacht werden! Gruppen entdecken und ihnen beitreten No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2066,10 +2088,12 @@ Das kann nicht rückgängig gemacht werden! Encryption re-negotiation error + Fehler bei der Neuverhandlung der Verschlüsselung message decrypt error item Encryption re-negotiation failed. + Neuverhandlung der Verschlüsselung fehlgeschlagen. No comment provided by engineer. @@ -2104,6 +2128,7 @@ Das kann nicht rückgängig gemacht werden! Enter this device name… + Geben Sie diesen Gerätenamen ein… No comment provided by engineer. @@ -2433,6 +2458,7 @@ Das kann nicht rückgängig gemacht werden! Faster joining and more reliable messages. + Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung. No comment provided by engineer. @@ -2530,6 +2556,10 @@ Das kann nicht rückgängig gemacht werden! Für Konsole No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Französische Bedienoberfläche @@ -2857,6 +2887,7 @@ Das kann nicht rückgängig gemacht werden! Incognito groups + Inkognito-Gruppen No comment provided by engineer. @@ -2891,6 +2922,7 @@ Das kann nicht rückgängig gemacht werden! Incompatible version + Inkompatible Version No comment provided by engineer. @@ -3065,6 +3097,7 @@ Das ist Ihr Link für die Gruppe %@! Keep the app open to use it from desktop + Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können No comment provided by engineer. @@ -3129,14 +3162,17 @@ Das ist Ihr Link für die Gruppe %@! Link mobile and desktop apps! 🔗 + Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗 No comment provided by engineer. Linked desktop options + Verknüpfte Desktop-Optionen No comment provided by engineer. Linked desktops + Verknüpfte Desktops No comment provided by engineer. @@ -3489,6 +3525,10 @@ Das ist Ihr Link für die Gruppe %@! Keine empfangenen oder gesendeten Dateien No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Benachrichtigungen @@ -3710,6 +3750,7 @@ Das ist Ihr Link für die Gruppe %@! Paste desktop address + Desktop-Adresse einfügen No comment provided by engineer. @@ -4309,6 +4350,7 @@ Das ist Ihr Link für die Gruppe %@! Scan QR code from desktop + Den QR-Code vom Desktop scannen No comment provided by engineer. @@ -4533,6 +4575,7 @@ Das ist Ihr Link für die Gruppe %@! Session code + Sitzungscode No comment provided by engineer. @@ -5034,6 +5077,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro This device name + Dieser Gerätename No comment provided by engineer. @@ -5073,6 +5117,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro To hide unwanted messages. + Um unerwünschte Nachrichten zu verbergen. No comment provided by engineer. @@ -5236,10 +5281,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Unlink + Entkoppeln No comment provided by engineer. Unlink desktop? + Desktop entkoppeln? No comment provided by engineer. @@ -5334,6 +5381,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Use from desktop + Vom Desktop aus nutzen No comment provided by engineer. @@ -5368,10 +5416,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verify code with desktop + Code mit dem Desktop überprüfen No comment provided by engineer. Verify connection + Verbindung überprüfen No comment provided by engineer. @@ -5381,6 +5431,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verify connections + Verbindungen überprüfen No comment provided by engineer. @@ -5395,6 +5446,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Via secure quantum resistant protocol. + Über ein sicheres quantenbeständiges Protokoll. No comment provided by engineer. @@ -5447,6 +5499,10 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Sprachnachrichten… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Warte auf Datei @@ -5992,6 +6048,10 @@ SimpleX-Server können Ihr Profil nicht einsehen. Audioanruf (nicht E2E verschlüsselt) No comment provided by engineer. + + author + member role + bad message ID Ungültige Nachrichten-ID @@ -6576,6 +6636,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. v%@ + v%@ No comment provided by engineer. @@ -6717,6 +6778,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX nutzt den lokalen Netzwerkzugriff, um die Nutzung von Benutzer-Chatprofilen über eine Desktop-App im gleichen Netzwerk zu erlauben. Privacy - Local Network Usage Description diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 6d70bd122..23498b212 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -1194,6 +1194,11 @@ Connect server test step + + Connect automatically + Connect automatically + No comment provided by engineer. + Connect incognito Connect incognito @@ -1908,9 +1913,9 @@ This cannot be undone! Discover and join groups No comment provided by engineer. - - Discover on network - Discover on network + + Discover via local network + Discover via local network No comment provided by engineer. @@ -2553,6 +2558,11 @@ This cannot be undone! For console No comment provided by engineer. + + Found desktop + Found desktop + No comment provided by engineer. + French interface French interface @@ -3518,6 +3528,11 @@ This is your link for group %@! No received or sent files No comment provided by engineer. + + Not compatible! + Not compatible! + No comment provided by engineer. + Notifications Notifications @@ -5488,6 +5503,11 @@ To connect, please ask your contact to create another connection link and check Voice message… No comment provided by engineer. + + Waiting for desktop... + Waiting for desktop... + No comment provided by engineer. + Waiting for file Waiting for file @@ -6033,6 +6053,11 @@ SimpleX servers cannot see your profile. audio call (not e2e encrypted) No comment provided by engineer. + + author + author + member role + bad message ID bad message ID diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index cc93c0531..ccc7ee344 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -1172,6 +1172,10 @@ Conectar server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Conectar incognito @@ -1862,8 +1866,8 @@ This cannot be undone! Descubre y únete a grupos No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2499,6 +2503,10 @@ This cannot be undone! Para consola No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Interfaz en francés @@ -3449,6 +3457,10 @@ This is your link for group %@! Sin archivos recibidos o enviados No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Notificaciones @@ -5397,6 +5409,10 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Mensaje de voz… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Esperando archivo @@ -5927,6 +5943,10 @@ Los servidores de SimpleX no pueden ver tu perfil. llamada (sin cifrar) No comment provided by engineer. + + author + member role + bad message ID ID de mensaje erróneo diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index c1c46aa42..cf161efae 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -1167,6 +1167,10 @@ Yhdistä server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Yhdistä Incognito @@ -1857,8 +1861,8 @@ This cannot be undone! Löydä ryhmiä ja liity niihin No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2491,6 +2495,10 @@ This cannot be undone! Konsoliin No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Ranskalainen käyttöliittymä @@ -3440,6 +3448,10 @@ This is your link for group %@! Ei vastaanotettuja tai lähetettyjä tiedostoja No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Ilmoitukset @@ -5383,6 +5395,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Ääniviesti… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Odottaa tiedostoa @@ -5913,6 +5929,10 @@ SimpleX-palvelimet eivät näe profiiliasi. äänipuhelu (ei e2e-salattu) No comment provided by engineer. + + author + member role + bad message ID virheellinen viestin tunniste diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 78a23315c..af54b7ceb 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -299,10 +299,12 @@ (new) + (nouveau) No comment provided by engineer. (this device v%@) + (cet appareil v%@) No comment provided by engineer. @@ -397,6 +399,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - option pour notifier les contacts supprimés. +- noms de profil avec espaces. +- et plus encore ! No comment provided by engineer. @@ -885,6 +890,7 @@ Bad desktop address + Mauvaise adresse de bureau No comment provided by engineer. @@ -899,6 +905,7 @@ Better groups + Des groupes plus performants No comment provided by engineer. @@ -913,6 +920,7 @@ Block group members + Bloquer des membres d'un groupe No comment provided by engineer. @@ -1186,6 +1194,10 @@ Se connecter server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Se connecter incognito @@ -1193,6 +1205,7 @@ Connect to desktop + Se connecter au bureau No comment provided by engineer. @@ -1241,10 +1254,12 @@ Il s'agit de votre propre lien unique ! Connected desktop + Bureau connecté No comment provided by engineer. Connected to desktop + Connecté au bureau No comment provided by engineer. @@ -1259,6 +1274,7 @@ Il s'agit de votre propre lien unique ! Connecting to desktop + Connexion au bureau No comment provided by engineer. @@ -1283,6 +1299,7 @@ Il s'agit de votre propre lien unique ! Connection terminated + Connexion terminée No comment provided by engineer. @@ -1367,6 +1384,7 @@ Il s'agit de votre propre lien unique ! Create a group using a random profile. + Création de groupes via un profil aléatoire. No comment provided by engineer. @@ -1781,14 +1799,17 @@ Cette opération ne peut être annulée ! Desktop address + Adresse de bureau No comment provided by engineer. Desktop app version %@ is not compatible with this app. + La version de l'application de bureau %@ n'est pas compatible avec cette application. No comment provided by engineer. Desktop devices + Appareils de bureau No comment provided by engineer. @@ -1883,6 +1904,7 @@ Cette opération ne peut être annulée ! Disconnect desktop? + Déconnecter le bureau ? No comment provided by engineer. @@ -1890,8 +1912,8 @@ Cette opération ne peut être annulée ! Découvrir et rejoindre des groupes No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2066,10 +2088,12 @@ Cette opération ne peut être annulée ! Encryption re-negotiation error + Erreur lors de la renégociation du chiffrement message decrypt error item Encryption re-negotiation failed. + La renégociation du chiffrement a échoué. No comment provided by engineer. @@ -2104,6 +2128,7 @@ Cette opération ne peut être annulée ! Enter this device name… + Entrez le nom de l'appareil… No comment provided by engineer. @@ -2433,6 +2458,7 @@ Cette opération ne peut être annulée ! Faster joining and more reliable messages. + Connexion plus rapide et messages plus fiables. No comment provided by engineer. @@ -2530,6 +2556,10 @@ Cette opération ne peut être annulée ! Pour la console No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Interface en français @@ -2857,6 +2887,7 @@ Cette opération ne peut être annulée ! Incognito groups + Groupes incognito No comment provided by engineer. @@ -2891,6 +2922,7 @@ Cette opération ne peut être annulée ! Incompatible version + Version incompatible No comment provided by engineer. @@ -3065,6 +3097,7 @@ Voici votre lien pour le groupe %@ ! Keep the app open to use it from desktop + Garder l'application ouverte pour l'utiliser depuis le bureau No comment provided by engineer. @@ -3129,14 +3162,17 @@ Voici votre lien pour le groupe %@ ! Link mobile and desktop apps! 🔗 + Liez vos applications mobiles et de bureau ! 🔗 No comment provided by engineer. Linked desktop options + Options de bureau lié No comment provided by engineer. Linked desktops + Bureaux liés No comment provided by engineer. @@ -3489,6 +3525,10 @@ Voici votre lien pour le groupe %@ ! Aucun fichier reçu ou envoyé No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Notifications @@ -3710,6 +3750,7 @@ Voici votre lien pour le groupe %@ ! Paste desktop address + Coller l'adresse du bureau No comment provided by engineer. @@ -4309,6 +4350,7 @@ Voici votre lien pour le groupe %@ ! Scan QR code from desktop + Scanner le code QR du bureau No comment provided by engineer. @@ -4533,6 +4575,7 @@ Voici votre lien pour le groupe %@ ! Session code + Code de session No comment provided by engineer. @@ -5034,6 +5077,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. This device name + Ce nom d'appareil No comment provided by engineer. @@ -5073,6 +5117,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. To hide unwanted messages. + Pour cacher les messages indésirables. No comment provided by engineer. @@ -5236,10 +5281,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Unlink + Délier No comment provided by engineer. Unlink desktop? + Délier le bureau ? No comment provided by engineer. @@ -5334,6 +5381,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Use from desktop + Utilisation depuis le bureau No comment provided by engineer. @@ -5368,10 +5416,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Verify code with desktop + Vérifier le code avec le bureau No comment provided by engineer. Verify connection + Vérifier la connexion No comment provided by engineer. @@ -5381,6 +5431,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Verify connections + Vérifier les connexions No comment provided by engineer. @@ -5395,6 +5446,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Via secure quantum resistant protocol. + Via un protocole sécurisé de cryptographie post-quantique. No comment provided by engineer. @@ -5447,6 +5499,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Message vocal… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file En attente du fichier @@ -5992,6 +6048,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. appel audio (sans chiffrement) No comment provided by engineer. + + author + member role + bad message ID ID de message incorrecte @@ -6576,6 +6636,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. v%@ + v%@ No comment provided by engineer. @@ -6717,6 +6778,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX utilise un accès au réseau local pour permettre l'utilisation du profil de chat de l'utilisateur via l'application de bureau au sein de ce même réseau. Privacy - Local Network Usage Description diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 6f99b16b7..bf1b1ee38 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -399,6 +399,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - avvisa facoltativamente i contatti eliminati. +- nomi del profilo con spazi. +- e molto altro! No comment provided by engineer. @@ -902,6 +905,7 @@ Better groups + Gruppi migliorati No comment provided by engineer. @@ -916,6 +920,7 @@ Block group members + Blocca i membri dei gruppi No comment provided by engineer. @@ -1189,6 +1194,10 @@ Connetti server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Connetti in incognito @@ -1375,6 +1384,7 @@ Questo è il tuo link una tantum! Create a group using a random profile. + Crea un gruppo usando un profilo casuale. No comment provided by engineer. @@ -1902,9 +1912,8 @@ Non è reversibile! Scopri ed unisciti ai gruppi No comment provided by engineer. - - Discover on network - Trova nella rete + + Discover via local network No comment provided by engineer. @@ -2449,6 +2458,7 @@ Non è reversibile! Faster joining and more reliable messages. + Ingresso più veloce e messaggi più affidabili. No comment provided by engineer. @@ -2546,6 +2556,10 @@ Non è reversibile! Per console No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Interfaccia francese @@ -2873,6 +2887,7 @@ Non è reversibile! Incognito groups + Gruppi in incognito No comment provided by engineer. @@ -3147,6 +3162,7 @@ Questo è il tuo link per il gruppo %@! Link mobile and desktop apps! 🔗 + Collega le app mobile e desktop! 🔗 No comment provided by engineer. @@ -3509,6 +3525,10 @@ Questo è il tuo link per il gruppo %@! Nessun file ricevuto o inviato No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Notifiche @@ -5097,6 +5117,7 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa. To hide unwanted messages. + Per nascondere messaggi indesiderati. No comment provided by engineer. @@ -5425,6 +5446,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Via secure quantum resistant protocol. + Tramite protocollo sicuro resistente alla quantistica. No comment provided by engineer. @@ -5477,6 +5499,10 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Messaggio vocale… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file In attesa del file @@ -6022,6 +6048,10 @@ I server di SimpleX non possono vedere il tuo profilo. chiamata audio (non crittografata e2e) No comment provided by engineer. + + author + member role + bad message ID ID messaggio errato diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index bae6d393d..11ca09ba3 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -1169,6 +1169,10 @@ 接続 server test step + + Connect automatically + No comment provided by engineer. + Connect incognito シークレットモードで接続 @@ -1859,8 +1863,8 @@ This cannot be undone! グループを見つけて参加する No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2494,6 +2498,10 @@ This cannot be undone! コンソール No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface フランス語UI @@ -3443,6 +3451,10 @@ This is your link for group %@! 送受信済みのファイルがありません No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications 通知 @@ -5379,6 +5391,10 @@ To connect, please ask your contact to create another connection link and check 音声メッセージ… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file ファイル待ち @@ -5909,6 +5925,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 音声通話 (エンドツーエンド暗号化なし) No comment provided by engineer. + + author + member role + bad message ID メッセージ ID が正しくありません diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index 8b6633c3d..d2be34ec6 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -399,6 +399,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - optioneel verwijderde contacten op de hoogte stellen. +- profielnamen met spaties. +- en meer! No comment provided by engineer. @@ -902,6 +905,7 @@ Better groups + Betere groepen No comment provided by engineer. @@ -916,6 +920,7 @@ Block group members + Groepsleden blokkeren No comment provided by engineer. @@ -1189,6 +1194,10 @@ Verbind server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Verbind incognito @@ -1375,6 +1384,7 @@ Dit is uw eigen eenmalige link! Create a group using a random profile. + Maak een groep met een willekeurig profiel. No comment provided by engineer. @@ -1902,9 +1912,8 @@ Dit kan niet ongedaan gemaakt worden! Ontdek en sluit je aan bij groepen No comment provided by engineer. - - Discover on network - Ontdek via netwerk + + Discover via local network No comment provided by engineer. @@ -2449,6 +2458,7 @@ Dit kan niet ongedaan gemaakt worden! Faster joining and more reliable messages. + Snellere deelname en betrouwbaardere berichten. No comment provided by engineer. @@ -2546,6 +2556,10 @@ Dit kan niet ongedaan gemaakt worden! Voor console No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Franse interface @@ -2873,6 +2887,7 @@ Dit kan niet ongedaan gemaakt worden! Incognito groups + Incognitogroepen No comment provided by engineer. @@ -3147,6 +3162,7 @@ Dit is jouw link voor groep %@! Link mobile and desktop apps! 🔗 + Koppel mobiele en desktop-apps! 🔗 No comment provided by engineer. @@ -3509,6 +3525,10 @@ Dit is jouw link voor groep %@! Geen ontvangen of verzonden bestanden No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Meldingen @@ -5097,6 +5117,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. To hide unwanted messages. + Om ongewenste berichten te verbergen. No comment provided by engineer. @@ -5425,6 +5446,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Via secure quantum resistant protocol. + Via een beveiligd kwantumbestendig protocol. No comment provided by engineer. @@ -5477,6 +5499,10 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Spraakbericht… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Wachten op bestand @@ -6022,6 +6048,10 @@ SimpleX servers kunnen uw profiel niet zien. audio oproep (niet e2e versleuteld) No comment provided by engineer. + + author + member role + bad message ID Onjuiste bericht-ID diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index 22d10aa43..61147d7f9 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -1186,6 +1186,10 @@ Połącz server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Połącz incognito @@ -1890,8 +1894,8 @@ To nie może być cofnięte! Odkrywaj i dołączaj do grup No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2530,6 +2534,10 @@ To nie może być cofnięte! Dla konsoli No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Francuski interfejs @@ -3489,6 +3497,10 @@ To jest twój link do grupy %@! Brak odebranych lub wysłanych plików No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Powiadomienia @@ -5447,6 +5459,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Wiadomość głosowa… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Oczekiwanie na plik @@ -5992,6 +6008,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. połączenie audio (nie szyfrowane e2e) No comment provided by engineer. + + author + member role + bad message ID zły identyfikator wiadomości diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index 7fd4cd51b..abd3d01d8 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -89,6 +89,7 @@ %@ and %@ + %@ и %@ No comment provided by engineer. @@ -103,6 +104,7 @@ %@ connected + %@ соединен(а) No comment provided by engineer. @@ -132,6 +134,7 @@ %@, %@ and %lld members + %@, %@ и %lld членов группы No comment provided by engineer. @@ -201,6 +204,7 @@ %lld group events + %lld событий No comment provided by engineer. @@ -210,14 +214,17 @@ %lld messages blocked + %lld сообщений заблокировано No comment provided by engineer. %lld messages marked deleted + %lld сообщений помечено удалёнными No comment provided by engineer. %lld messages moderated by %@ + %lld сообщений модерировано членом %@ No comment provided by engineer. @@ -292,10 +299,12 @@ (new) + (новое) No comment provided by engineer. (this device v%@) + (это устройство v%@) No comment provided by engineer. @@ -390,6 +399,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - опционально уведомляйте удалённые контакты. +- имена профилей с пробелами. +- и прочее! No comment provided by engineer. @@ -408,6 +420,7 @@ 0 sec + 0 сек time to disappear @@ -637,6 +650,7 @@ All new messages from %@ will be hidden! + Все новые сообщения от %@ будут скрыты! No comment provided by engineer. @@ -746,10 +760,12 @@ Already connecting! + Уже соединяется! No comment provided by engineer. Already joining the group! + Вступление в группу уже начато! No comment provided by engineer. @@ -874,6 +890,7 @@ Bad desktop address + Неверный адрес компьютера No comment provided by engineer. @@ -888,6 +905,7 @@ Better groups + Улучшенные группы No comment provided by engineer. @@ -897,18 +915,22 @@ Block + Заблокировать No comment provided by engineer. Block group members + Блокируйте членов группы No comment provided by engineer. Block member + Заблокировать члена группы No comment provided by engineer. Block member? + Заблокировать члена группы? No comment provided by engineer. @@ -1172,6 +1194,10 @@ Соединиться server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Соединиться Инкогнито @@ -1179,24 +1205,31 @@ Connect to desktop + Подключиться к компьютеру No comment provided by engineer. Connect to yourself? + Соединиться с самим собой? No comment provided by engineer. Connect to yourself? This is your own SimpleX address! + Соединиться с самим собой? +Это ваш собственный адрес SimpleX! No comment provided by engineer. Connect to yourself? This is your own one-time link! + Соединиться с самим собой? +Это ваша собственная одноразовая ссылка! No comment provided by engineer. Connect via contact address + Соединиться через адрес No comment provided by engineer. @@ -1216,14 +1249,17 @@ This is your own one-time link! Connect with %@ + Соединиться с %@ No comment provided by engineer. Connected desktop + Подключенный компьютер No comment provided by engineer. Connected to desktop + Компьютер подключен No comment provided by engineer. @@ -1238,6 +1274,7 @@ This is your own one-time link! Connecting to desktop + Подключение к компьютеру No comment provided by engineer. @@ -1262,6 +1299,7 @@ This is your own one-time link! Connection terminated + Подключение прервано No comment provided by engineer. @@ -1331,6 +1369,7 @@ This is your own one-time link! Correct name to %@? + Исправить имя на %@? No comment provided by engineer. @@ -1345,6 +1384,7 @@ This is your own one-time link! Create a group using a random profile. + Создайте группу, используя случайный профиль. No comment provided by engineer. @@ -1359,6 +1399,7 @@ This is your own one-time link! Create group + Создать группу No comment provided by engineer. @@ -1383,6 +1424,7 @@ This is your own one-time link! Create profile + Создать профиль No comment provided by engineer. @@ -1545,6 +1587,7 @@ This is your own one-time link! Delete %lld messages? + Удалить %lld сообщений? No comment provided by engineer. @@ -1574,6 +1617,7 @@ This is your own one-time link! Delete and notify contact + Удалить и уведомить контакт No comment provided by engineer. @@ -1609,6 +1653,8 @@ This is your own one-time link! Delete contact? This cannot be undone! + Удалить контакт? +Это не может быть отменено! No comment provided by engineer. @@ -1753,14 +1799,17 @@ This cannot be undone! Desktop address + Адрес компьютера No comment provided by engineer. Desktop app version %@ is not compatible with this app. + Версия настольного приложения %@ несовместима с этим приложением. No comment provided by engineer. Desktop devices + Компьютеры No comment provided by engineer. @@ -1855,6 +1904,7 @@ This cannot be undone! Disconnect desktop? + Отключить компьютер? No comment provided by engineer. @@ -1862,8 +1912,8 @@ This cannot be undone! Найдите и вступите в группы No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2038,10 +2088,12 @@ This cannot be undone! Encryption re-negotiation error + Ошибка нового соглашения о шифровании message decrypt error item Encryption re-negotiation failed. + Ошибка нового соглашения о шифровании. No comment provided by engineer. @@ -2056,6 +2108,7 @@ This cannot be undone! Enter group name… + Введите имя группы… No comment provided by engineer. @@ -2075,6 +2128,7 @@ This cannot be undone! Enter this device name… + Введите имя этого устройства… No comment provided by engineer. @@ -2089,6 +2143,7 @@ This cannot be undone! Enter your name… + Введите ваше имя… No comment provided by engineer. @@ -2148,6 +2203,7 @@ This cannot be undone! Error creating member contact + Ошибка создания контакта с членом группы No comment provided by engineer. @@ -2282,6 +2338,7 @@ This cannot be undone! Error sending member contact invitation + Ошибка отправки приглашения члену группы No comment provided by engineer. @@ -2366,6 +2423,7 @@ This cannot be undone! Expand + Раскрыть chat item action @@ -2400,6 +2458,7 @@ This cannot be undone! Faster joining and more reliable messages. + Быстрое вступление и надежная доставка сообщений. No comment provided by engineer. @@ -2497,6 +2556,10 @@ This cannot be undone! Для консоли No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Французский интерфейс @@ -2519,6 +2582,7 @@ This cannot be undone! Fully decentralized – visible only to members. + Группа полностью децентрализована – она видна только членам. No comment provided by engineer. @@ -2543,10 +2607,12 @@ This cannot be undone! Group already exists + Группа уже существует No comment provided by engineer. Group already exists! + Группа уже существует! No comment provided by engineer. @@ -2821,6 +2887,7 @@ This cannot be undone! Incognito groups + Инкогнито группы No comment provided by engineer. @@ -2855,6 +2922,7 @@ This cannot be undone! Incompatible version + Несовместимая версия No comment provided by engineer. @@ -2906,6 +2974,7 @@ This cannot be undone! Invalid name! + Неверное имя! No comment provided by engineer. @@ -3001,6 +3070,7 @@ This cannot be undone! Join group? + Вступить в группу? No comment provided by engineer. @@ -3010,11 +3080,14 @@ This cannot be undone! Join with current profile + Вступить с активным профилем No comment provided by engineer. Join your group? This is your link for group %@! + Вступить в вашу группу? +Это ваша ссылка на группу %@! No comment provided by engineer. @@ -3024,6 +3097,7 @@ This is your link for group %@! Keep the app open to use it from desktop + Оставьте приложение открытым, чтобы использовать его с компьютера No comment provided by engineer. @@ -3088,14 +3162,17 @@ This is your link for group %@! Link mobile and desktop apps! 🔗 + Свяжите мобильное и настольное приложения! 🔗 No comment provided by engineer. Linked desktop options + Опции связанных компьютеров No comment provided by engineer. Linked desktops + Связанные компьютеры No comment provided by engineer. @@ -3250,6 +3327,7 @@ This is your link for group %@! Messages from %@ will be shown! + Сообщения от %@ будут показаны! No comment provided by engineer. @@ -3447,6 +3525,10 @@ This is your link for group %@! Нет полученных или отправленных файлов No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Уведомления @@ -3583,6 +3665,7 @@ This is your link for group %@! Open + Открыть No comment provided by engineer. @@ -3602,6 +3685,7 @@ This is your link for group %@! Open group + Открыть группу No comment provided by engineer. @@ -3666,6 +3750,7 @@ This is your link for group %@! Paste desktop address + Вставить адрес компьютера No comment provided by engineer. @@ -3815,10 +3900,12 @@ This is your link for group %@! Profile name + Имя профиля No comment provided by engineer. Profile name: + Имя профиля: No comment provided by engineer. @@ -4068,10 +4155,12 @@ This is your link for group %@! Repeat connection request? + Повторить запрос на соединение? No comment provided by engineer. Repeat join request? + Повторить запрос на вступление? No comment provided by engineer. @@ -4261,6 +4350,7 @@ This is your link for group %@! Scan QR code from desktop + Сканировать QR код с компьютера No comment provided by engineer. @@ -4345,6 +4435,7 @@ This is your link for group %@! Send direct message to connect + Отправьте сообщение чтобы соединиться No comment provided by engineer. @@ -4484,6 +4575,7 @@ This is your link for group %@! Session code + Код сессии No comment provided by engineer. @@ -4798,6 +4890,7 @@ This is your link for group %@! Tap to Connect + Нажмите чтобы соединиться No comment provided by engineer. @@ -4984,6 +5077,7 @@ It can happen because of some bug or when the connection is compromised. This device name + Имя этого устройства No comment provided by engineer. @@ -4998,10 +5092,12 @@ It can happen because of some bug or when the connection is compromised. This is your own SimpleX address! + Это ваш собственный адрес SimpleX! No comment provided by engineer. This is your own one-time link! + Это ваша собственная одноразовая ссылка! No comment provided by engineer. @@ -5021,6 +5117,7 @@ It can happen because of some bug or when the connection is compromised. To hide unwanted messages. + Чтобы скрыть нежелательные сообщения. No comment provided by engineer. @@ -5107,14 +5204,17 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock + Разблокировать No comment provided by engineer. Unblock member + Разблокировать члена группы No comment provided by engineer. Unblock member? + Разблокировать члена группы? No comment provided by engineer. @@ -5181,10 +5281,12 @@ To connect, please ask your contact to create another connection link and check Unlink + Забыть No comment provided by engineer. Unlink desktop? + Забыть компьютер? No comment provided by engineer. @@ -5279,6 +5381,7 @@ To connect, please ask your contact to create another connection link and check Use from desktop + Использовать с компьютера No comment provided by engineer. @@ -5313,10 +5416,12 @@ To connect, please ask your contact to create another connection link and check Verify code with desktop + Сверьте код с компьютером No comment provided by engineer. Verify connection + Проверить соединение No comment provided by engineer. @@ -5326,6 +5431,7 @@ To connect, please ask your contact to create another connection link and check Verify connections + Проверять соединения No comment provided by engineer. @@ -5340,6 +5446,7 @@ To connect, please ask your contact to create another connection link and check Via secure quantum resistant protocol. + Через безопасный квантово-устойчивый протокол. No comment provided by engineer. @@ -5392,6 +5499,10 @@ To connect, please ask your contact to create another connection link and check Голосовое сообщение… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Ожидается прием файла @@ -5494,31 +5605,39 @@ To connect, please ask your contact to create another connection link and check You are already connecting to %@. + Вы уже соединяетесь с %@. No comment provided by engineer. You are already connecting via this one-time link! + Вы уже соединяетесь по этой одноразовой ссылке! No comment provided by engineer. You are already in group %@. + Вы уже состоите в группе %@. No comment provided by engineer. You are already joining the group %@. + Вы уже вступаете в группу %@. No comment provided by engineer. You are already joining the group via this link! + Вы уже вступаете в группу по этой ссылке! No comment provided by engineer. You are already joining the group via this link. + Вы уже вступаете в группу по этой ссылке. No comment provided by engineer. You are already joining the group! Repeat join request? + Вы уже вступаете в группу! +Повторить запрос на вступление? No comment provided by engineer. @@ -5618,11 +5737,14 @@ Repeat join request? You have already requested connection via this address! + Вы уже запросили соединение через этот адрес! No comment provided by engineer. You have already requested connection! Repeat connection request? + Вы уже запросили соединение! +Повторить запрос? No comment provided by engineer. @@ -5677,6 +5799,7 @@ Repeat connection request? You will be connected when group link host's device is online, please wait or check later! + Соединение будет установлено, когда владелец ссылки группы будет онлайн. Пожалуйста, подождите или проверьте позже! No comment provided by engineer. @@ -5696,6 +5819,7 @@ Repeat connection request? You will connect to all group members. + Вы соединитесь со всеми членами группы. No comment provided by engineer. @@ -5819,6 +5943,7 @@ You can change it in Settings. Your profile + Ваш профиль No comment provided by engineer. @@ -5915,6 +6040,7 @@ SimpleX серверы не могут получить доступ к Ваше and %lld other events + и %lld других событий No comment provided by engineer. @@ -5922,6 +6048,10 @@ SimpleX серверы не могут получить доступ к Ваше аудиозвонок (не e2e зашифрованный) No comment provided by engineer. + + author + member role + bad message ID ошибка ID сообщения @@ -5934,6 +6064,7 @@ SimpleX серверы не могут получить доступ к Ваше blocked + заблокировано No comment provided by engineer. @@ -6008,6 +6139,7 @@ SimpleX серверы не могут получить доступ к Ваше connected directly + соединен(а) напрямую rcv group event chat item @@ -6107,6 +6239,7 @@ SimpleX серверы не могут получить доступ к Ваше deleted contact + удалил(а) контакт rcv direct event chat item @@ -6473,6 +6606,7 @@ SimpleX серверы не могут получить доступ к Ваше send direct message + отправьте сообщение No comment provided by engineer. @@ -6502,6 +6636,7 @@ SimpleX серверы не могут получить доступ к Ваше v%@ + v%@ No comment provided by engineer. @@ -6643,6 +6778,7 @@ SimpleX серверы не могут получить доступ к Ваше SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX использует доступ к локальной сети, чтобы разрешить использование профиля чата через компьютер в той же сети. Privacy - Local Network Usage Description diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index 6ada6dbbb..bcb39e6e0 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -1159,6 +1159,10 @@ เชื่อมต่อ server test step + + Connect automatically + No comment provided by engineer. + Connect incognito No comment provided by engineer. @@ -1844,8 +1848,8 @@ This cannot be undone! Discover and join groups No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2476,6 +2480,10 @@ This cannot be undone! สำหรับคอนโซล No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface อินเทอร์เฟซภาษาฝรั่งเศส @@ -3421,6 +3429,10 @@ This is your link for group %@! ไม่มีไฟล์ที่ได้รับหรือส่ง No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications การแจ้งเตือน @@ -5354,6 +5366,10 @@ To connect, please ask your contact to create another connection link and check ข้อความเสียง… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file กำลังรอไฟล์ @@ -5882,6 +5898,10 @@ SimpleX servers cannot see your profile. การโทรด้วยเสียง (ไม่ได้ encrypt จากต้นจนจบ) No comment provided by engineer. + + author + member role + bad message ID ID ข้อความที่ไม่ดี diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index 868f42063..abd58231a 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -1166,6 +1166,10 @@ Підключіться server test step + + Connect automatically + No comment provided by engineer. + Connect incognito Підключайтеся інкогніто @@ -1854,8 +1858,8 @@ This cannot be undone! Discover and join groups No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2486,6 +2490,10 @@ This cannot be undone! Для консолі No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface Французький інтерфейс @@ -3435,6 +3443,10 @@ This is your link for group %@! Немає отриманих або відправлених файлів No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications Сповіщення @@ -5378,6 +5390,10 @@ To connect, please ask your contact to create another connection link and check Голосове повідомлення… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file Очікування файлу @@ -5908,6 +5924,10 @@ SimpleX servers cannot see your profile. аудіовиклик (без шифрування e2e) No comment provided by engineer. + + author + member role + bad message ID невірний ідентифікатор повідомлення diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 77374aefa..d96537be3 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -1172,6 +1172,10 @@ 连接 server test step + + Connect automatically + No comment provided by engineer. + Connect incognito 在隐身状态下连接 @@ -1862,8 +1866,8 @@ This cannot be undone! 发现和加入群组 No comment provided by engineer. - - Discover on network + + Discover via local network No comment provided by engineer. @@ -2499,6 +2503,10 @@ This cannot be undone! 用于控制台 No comment provided by engineer. + + Found desktop + No comment provided by engineer. + French interface 法语界面 @@ -3449,6 +3457,10 @@ This is your link for group %@! 未收到或发送文件 No comment provided by engineer. + + Not compatible! + No comment provided by engineer. + Notifications 通知 @@ -5396,6 +5408,10 @@ To connect, please ask your contact to create another connection link and check 语音消息…… No comment provided by engineer. + + Waiting for desktop... + No comment provided by engineer. + Waiting for file 等待文件中 @@ -5926,6 +5942,10 @@ SimpleX 服务器无法看到您的资料。 语音通话(非端到端加密) No comment provided by engineer. + + author + member role + bad message ID 错误消息 ID diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 00489be82..c860a2499 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilere Zustellung von Nachrichten.\n- ein bisschen verbesserte Gruppen.\n- und mehr!"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- Optionale Benachrichtigung von gelöschten Kontakten.\n- Profilnamen mit Leerzeichen.\n- Und mehr!"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- Bis zu 5 Minuten lange Sprachnachrichten.\n- Zeitdauer für verschwindende Nachrichten anpassen.\n- Nachrichten-Historie bearbeiten."; @@ -43,6 +46,12 @@ /* No comment provided by engineer. */ "(" = "("; +/* No comment provided by engineer. */ +"(new)" = "(Neu)"; + +/* No comment provided by engineer. */ +"(this device v%@)" = "(Dieses Gerät hat v%@)"; + /* No comment provided by engineer. */ ")" = ")"; @@ -548,6 +557,9 @@ /* No comment provided by engineer. */ "Back" = "Zurück"; +/* No comment provided by engineer. */ +"Bad desktop address" = "Falsche Desktop-Adresse"; + /* integrity error chat item */ "bad message hash" = "Ungültiger Nachrichten-Hash"; @@ -560,12 +572,18 @@ /* No comment provided by engineer. */ "Bad message ID" = "Falsche Nachrichten-ID"; +/* No comment provided by engineer. */ +"Better groups" = "Bessere Gruppen"; + /* No comment provided by engineer. */ "Better messages" = "Verbesserungen bei Nachrichten"; /* No comment provided by engineer. */ "Block" = "Blockieren"; +/* No comment provided by engineer. */ +"Block group members" = "Gruppenmitglieder blockieren"; + /* No comment provided by engineer. */ "Block member" = "Mitglied blockieren"; @@ -771,6 +789,9 @@ /* No comment provided by engineer. */ "Connect incognito" = "Inkognito verbinden"; +/* No comment provided by engineer. */ +"Connect to desktop" = "Mit dem Desktop verbinden"; + /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "Mit den SimpleX Chat-Entwicklern verbinden."; @@ -801,9 +822,15 @@ /* No comment provided by engineer. */ "connected" = "Verbunden"; +/* No comment provided by engineer. */ +"Connected desktop" = "Verbundener Desktop"; + /* rcv group event chat item */ "connected directly" = "Direkt miteinander verbunden"; +/* No comment provided by engineer. */ +"Connected to desktop" = "Mit dem Desktop verbunden"; + /* No comment provided by engineer. */ "connecting" = "verbinde"; @@ -828,6 +855,9 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Mit dem Server verbinden… (Fehler: %@)"; +/* No comment provided by engineer. */ +"Connecting to desktop" = "Mit dem Desktop verbinden"; + /* chat list item title */ "connecting…" = "Verbinde…"; @@ -846,6 +876,9 @@ /* No comment provided by engineer. */ "Connection request sent!" = "Verbindungsanfrage wurde gesendet!"; +/* No comment provided by engineer. */ +"Connection terminated" = "Verbindung beendet"; + /* No comment provided by engineer. */ "Connection timeout" = "Verbindungszeitüberschreitung"; @@ -900,6 +933,9 @@ /* No comment provided by engineer. */ "Create" = "Erstellen"; +/* No comment provided by engineer. */ +"Create a group using a random profile." = "Erstellen Sie eine Gruppe mit einem zufälligen Profil."; + /* No comment provided by engineer. */ "Create an address to let people connect with you." = "Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können."; @@ -1173,6 +1209,15 @@ /* No comment provided by engineer. */ "Description" = "Beschreibung"; +/* No comment provided by engineer. */ +"Desktop address" = "Desktop-Adresse"; + +/* No comment provided by engineer. */ +"Desktop app version %@ is not compatible with this app." = "Desktop App-Version %@ ist mit dieser App nicht kompatibel."; + +/* No comment provided by engineer. */ +"Desktop devices" = "Desktop-Geräte"; + /* No comment provided by engineer. */ "Develop" = "Entwicklung"; @@ -1236,6 +1281,9 @@ /* server test step */ "Disconnect" = "Trennen"; +/* No comment provided by engineer. */ +"Disconnect desktop?" = "Desktop-Verbindung trennen?"; + /* No comment provided by engineer. */ "Discover and join groups" = "Gruppen entdecken und ihnen beitreten"; @@ -1374,6 +1422,12 @@ /* chat item text */ "encryption re-negotiation allowed for %@" = "Neuaushandlung der Verschlüsselung von %@ erlaubt"; +/* message decrypt error item */ +"Encryption re-negotiation error" = "Fehler bei der Neuverhandlung der Verschlüsselung"; + +/* No comment provided by engineer. */ +"Encryption re-negotiation failed." = "Neuverhandlung der Verschlüsselung fehlgeschlagen."; + /* chat item text */ "encryption re-negotiation required" = "Neuaushandlung der Verschlüsselung notwendig"; @@ -1404,6 +1458,9 @@ /* No comment provided by engineer. */ "Enter server manually" = "Geben Sie den Server manuell ein"; +/* No comment provided by engineer. */ +"Enter this device name…" = "Geben Sie diesen Gerätenamen ein…"; + /* placeholder */ "Enter welcome message…" = "Geben Sie eine Begrüßungsmeldung ein …"; @@ -1605,6 +1662,9 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Schnell und ohne warten auf den Absender, bis er online ist!"; +/* No comment provided by engineer. */ +"Faster joining and more reliable messages." = "Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung."; + /* No comment provided by engineer. */ "Favorite" = "Favorit"; @@ -1866,6 +1926,9 @@ /* No comment provided by engineer. */ "Incognito" = "Inkognito"; +/* No comment provided by engineer. */ +"Incognito groups" = "Inkognito-Gruppen"; + /* No comment provided by engineer. */ "Incognito mode" = "Inkognito-Modus"; @@ -1893,6 +1956,9 @@ /* No comment provided by engineer. */ "Incompatible database version" = "Inkompatible Datenbank-Version"; +/* No comment provided by engineer. */ +"Incompatible version" = "Inkompatible Version"; + /* PIN entry */ "Incorrect passcode" = "Zugangscode ist falsch"; @@ -2028,6 +2094,9 @@ /* No comment provided by engineer. */ "Joining group" = "Der Gruppe beitreten"; +/* No comment provided by engineer. */ +"Keep the app open to use it from desktop" = "Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können"; + /* No comment provided by engineer. */ "Keep your connections" = "Ihre Verbindungen beibehalten"; @@ -2064,6 +2133,15 @@ /* No comment provided by engineer. */ "Limitations" = "Einschränkungen"; +/* No comment provided by engineer. */ +"Link mobile and desktop apps! 🔗" = "Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗"; + +/* No comment provided by engineer. */ +"Linked desktop options" = "Verknüpfte Desktop-Optionen"; + +/* No comment provided by engineer. */ +"Linked desktops" = "Verknüpfte Desktops"; + /* No comment provided by engineer. */ "LIVE" = "LIVE"; @@ -2462,6 +2540,9 @@ /* No comment provided by engineer. */ "Paste" = "Einfügen"; +/* No comment provided by engineer. */ +"Paste desktop address" = "Desktop-Adresse einfügen"; + /* No comment provided by engineer. */ "Paste image" = "Bild einfügen"; @@ -2846,6 +2927,9 @@ /* No comment provided by engineer. */ "Scan QR code" = "QR-Code scannen"; +/* No comment provided by engineer. */ +"Scan QR code from desktop" = "Den QR-Code vom Desktop scannen"; + /* No comment provided by engineer. */ "Scan security code from your contact's app." = "Scannen Sie den Sicherheitscode von der App Ihres Kontakts."; @@ -2990,6 +3074,9 @@ /* No comment provided by engineer. */ "Servers" = "Server"; +/* No comment provided by engineer. */ +"Session code" = "Sitzungscode"; + /* No comment provided by engineer. */ "Set 1 day" = "Einen Tag festlegen"; @@ -3299,6 +3386,9 @@ /* notification title */ "this contact" = "Dieser Kontakt"; +/* No comment provided by engineer. */ +"This device name" = "Dieser Gerätename"; + /* No comment provided by engineer. */ "This group has over %lld members, delivery receipts are not sent." = "Es werden keine Empfangsbestätigungen gesendet, da diese Gruppe über %lld Mitglieder hat."; @@ -3320,6 +3410,9 @@ /* No comment provided by engineer. */ "To connect, your contact can scan QR code or use the link in the app." = "Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden."; +/* No comment provided by engineer. */ +"To hide unwanted messages." = "Um unerwünschte Nachrichten zu verbergen."; + /* No comment provided by engineer. */ "To make a new connection" = "Um eine Verbindung mit einem neuen Kontakt zu erstellen"; @@ -3416,6 +3509,12 @@ /* No comment provided by engineer. */ "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Entweder hat Ihr Kontakt die Verbindung gelöscht, oder dieser Link wurde bereits verwendet, es könnte sich um einen Fehler handeln - Bitte melden Sie es uns.\nBitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um sich neu verbinden zu können und stellen Sie sicher, dass Sie eine stabile Netzwerk-Verbindung haben."; +/* No comment provided by engineer. */ +"Unlink" = "Entkoppeln"; + +/* No comment provided by engineer. */ +"Unlink desktop?" = "Desktop entkoppeln?"; + /* No comment provided by engineer. */ "Unlock" = "Entsperren"; @@ -3470,6 +3569,9 @@ /* No comment provided by engineer. */ "Use for new connections" = "Für neue Verbindungen nutzen"; +/* No comment provided by engineer. */ +"Use from desktop" = "Vom Desktop aus nutzen"; + /* No comment provided by engineer. */ "Use iOS call interface" = "iOS Anrufschnittstelle nutzen"; @@ -3491,12 +3593,24 @@ /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern."; +/* No comment provided by engineer. */ +"v%@" = "v%@"; + /* No comment provided by engineer. */ "v%@ (%@)" = "v%@ (%@)"; +/* No comment provided by engineer. */ +"Verify code with desktop" = "Code mit dem Desktop überprüfen"; + +/* No comment provided by engineer. */ +"Verify connection" = "Verbindung überprüfen"; + /* No comment provided by engineer. */ "Verify connection security" = "Sicherheit der Verbindung überprüfen"; +/* No comment provided by engineer. */ +"Verify connections" = "Verbindungen überprüfen"; + /* No comment provided by engineer. */ "Verify security code" = "Sicherheitscode überprüfen"; @@ -3515,6 +3629,9 @@ /* No comment provided by engineer. */ "via relay" = "über Relais"; +/* No comment provided by engineer. */ +"Via secure quantum resistant protocol." = "Über ein sicheres quantenbeständiges Protokoll."; + /* No comment provided by engineer. */ "Video call" = "Videoanruf"; diff --git a/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings index 6a33e4177..5fe2ef2d0 100644 --- a/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings +++ b/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings @@ -7,6 +7,9 @@ /* Privacy - Face ID Usage Description */ "NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt"; +/* Privacy - Local Network Usage Description */ +"NSLocalNetworkUsageDescription" = "SimpleX nutzt den lokalen Netzwerkzugriff, um die Nutzung von Benutzer-Chatprofilen über eine Desktop-App im gleichen Netzwerk zu erlauben."; + /* Privacy - Microphone Usage Description */ "NSMicrophoneUsageDescription" = "SimpleX benötigt Zugriff auf das Mikrofon, um Audio- und Videoanrufe und die Aufnahme von Sprachnachrichten zu ermöglichen."; diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index 33f571b51..b71d510e7 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- une diffusion plus stable des messages.\n- des groupes un peu plus performants.\n- et bien d'autres choses encore !"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- option pour notifier les contacts supprimés.\n- noms de profil avec espaces.\n- et plus encore !"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messages vocaux pouvant durer jusqu'à 5 minutes.\n- délai personnalisé de disparition.\n- l'historique de modification."; @@ -43,6 +46,12 @@ /* No comment provided by engineer. */ "(" = "("; +/* No comment provided by engineer. */ +"(new)" = "(nouveau)"; + +/* No comment provided by engineer. */ +"(this device v%@)" = "(cet appareil v%@)"; + /* No comment provided by engineer. */ ")" = ")"; @@ -548,6 +557,9 @@ /* No comment provided by engineer. */ "Back" = "Retour"; +/* No comment provided by engineer. */ +"Bad desktop address" = "Mauvaise adresse de bureau"; + /* integrity error chat item */ "bad message hash" = "hash de message incorrect"; @@ -560,12 +572,18 @@ /* No comment provided by engineer. */ "Bad message ID" = "Mauvais ID de message"; +/* No comment provided by engineer. */ +"Better groups" = "Des groupes plus performants"; + /* No comment provided by engineer. */ "Better messages" = "Meilleurs messages"; /* No comment provided by engineer. */ "Block" = "Bloquer"; +/* No comment provided by engineer. */ +"Block group members" = "Bloquer des membres d'un groupe"; + /* No comment provided by engineer. */ "Block member" = "Bloquer ce membre"; @@ -771,6 +789,9 @@ /* No comment provided by engineer. */ "Connect incognito" = "Se connecter incognito"; +/* No comment provided by engineer. */ +"Connect to desktop" = "Se connecter au bureau"; + /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "se connecter aux developpeurs de SimpleX Chat."; @@ -801,9 +822,15 @@ /* No comment provided by engineer. */ "connected" = "connecté"; +/* No comment provided by engineer. */ +"Connected desktop" = "Bureau connecté"; + /* rcv group event chat item */ "connected directly" = "s'est connecté.e de manière directe"; +/* No comment provided by engineer. */ +"Connected to desktop" = "Connecté au bureau"; + /* No comment provided by engineer. */ "connecting" = "connexion"; @@ -828,6 +855,9 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Connexion au serveur… (erreur : %@)"; +/* No comment provided by engineer. */ +"Connecting to desktop" = "Connexion au bureau"; + /* chat list item title */ "connecting…" = "connexion…"; @@ -846,6 +876,9 @@ /* No comment provided by engineer. */ "Connection request sent!" = "Demande de connexion envoyée !"; +/* No comment provided by engineer. */ +"Connection terminated" = "Connexion terminée"; + /* No comment provided by engineer. */ "Connection timeout" = "Délai de connexion"; @@ -900,6 +933,9 @@ /* No comment provided by engineer. */ "Create" = "Créer"; +/* No comment provided by engineer. */ +"Create a group using a random profile." = "Création de groupes via un profil aléatoire."; + /* No comment provided by engineer. */ "Create an address to let people connect with you." = "Créez une adresse pour permettre aux gens de vous contacter."; @@ -1173,6 +1209,15 @@ /* No comment provided by engineer. */ "Description" = "Description"; +/* No comment provided by engineer. */ +"Desktop address" = "Adresse de bureau"; + +/* No comment provided by engineer. */ +"Desktop app version %@ is not compatible with this app." = "La version de l'application de bureau %@ n'est pas compatible avec cette application."; + +/* No comment provided by engineer. */ +"Desktop devices" = "Appareils de bureau"; + /* No comment provided by engineer. */ "Develop" = "Développer"; @@ -1236,6 +1281,9 @@ /* server test step */ "Disconnect" = "Se déconnecter"; +/* No comment provided by engineer. */ +"Disconnect desktop?" = "Déconnecter le bureau ?"; + /* No comment provided by engineer. */ "Discover and join groups" = "Découvrir et rejoindre des groupes"; @@ -1374,6 +1422,12 @@ /* chat item text */ "encryption re-negotiation allowed for %@" = "renégociation de chiffrement autorisée pour %@"; +/* message decrypt error item */ +"Encryption re-negotiation error" = "Erreur lors de la renégociation du chiffrement"; + +/* No comment provided by engineer. */ +"Encryption re-negotiation failed." = "La renégociation du chiffrement a échoué."; + /* chat item text */ "encryption re-negotiation required" = "renégociation de chiffrement requise"; @@ -1404,6 +1458,9 @@ /* No comment provided by engineer. */ "Enter server manually" = "Entrer un serveur manuellement"; +/* No comment provided by engineer. */ +"Enter this device name…" = "Entrez le nom de l'appareil…"; + /* placeholder */ "Enter welcome message…" = "Entrez un message de bienvenue…"; @@ -1605,6 +1662,9 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !"; +/* No comment provided by engineer. */ +"Faster joining and more reliable messages." = "Connexion plus rapide et messages plus fiables."; + /* No comment provided by engineer. */ "Favorite" = "Favoris"; @@ -1866,6 +1926,9 @@ /* No comment provided by engineer. */ "Incognito" = "Incognito"; +/* No comment provided by engineer. */ +"Incognito groups" = "Groupes incognito"; + /* No comment provided by engineer. */ "Incognito mode" = "Mode Incognito"; @@ -1893,6 +1956,9 @@ /* No comment provided by engineer. */ "Incompatible database version" = "Version de la base de données incompatible"; +/* No comment provided by engineer. */ +"Incompatible version" = "Version incompatible"; + /* PIN entry */ "Incorrect passcode" = "Code d'accès erroné"; @@ -2028,6 +2094,9 @@ /* No comment provided by engineer. */ "Joining group" = "Entrain de rejoindre le groupe"; +/* No comment provided by engineer. */ +"Keep the app open to use it from desktop" = "Garder l'application ouverte pour l'utiliser depuis le bureau"; + /* No comment provided by engineer. */ "Keep your connections" = "Conserver vos connexions"; @@ -2064,6 +2133,15 @@ /* No comment provided by engineer. */ "Limitations" = "Limitations"; +/* No comment provided by engineer. */ +"Link mobile and desktop apps! 🔗" = "Liez vos applications mobiles et de bureau ! 🔗"; + +/* No comment provided by engineer. */ +"Linked desktop options" = "Options de bureau lié"; + +/* No comment provided by engineer. */ +"Linked desktops" = "Bureaux liés"; + /* No comment provided by engineer. */ "LIVE" = "LIVE"; @@ -2462,6 +2540,9 @@ /* No comment provided by engineer. */ "Paste" = "Coller"; +/* No comment provided by engineer. */ +"Paste desktop address" = "Coller l'adresse du bureau"; + /* No comment provided by engineer. */ "Paste image" = "Coller l'image"; @@ -2846,6 +2927,9 @@ /* No comment provided by engineer. */ "Scan QR code" = "Scanner un code QR"; +/* No comment provided by engineer. */ +"Scan QR code from desktop" = "Scanner le code QR du bureau"; + /* No comment provided by engineer. */ "Scan security code from your contact's app." = "Scannez le code de sécurité depuis l'application de votre contact."; @@ -2990,6 +3074,9 @@ /* No comment provided by engineer. */ "Servers" = "Serveurs"; +/* No comment provided by engineer. */ +"Session code" = "Code de session"; + /* No comment provided by engineer. */ "Set 1 day" = "Définir 1 jour"; @@ -3299,6 +3386,9 @@ /* notification title */ "this contact" = "ce contact"; +/* No comment provided by engineer. */ +"This device name" = "Ce nom d'appareil"; + /* No comment provided by engineer. */ "This group has over %lld members, delivery receipts are not sent." = "Ce groupe compte plus de %lld membres, les accusés de réception ne sont pas envoyés."; @@ -3320,6 +3410,9 @@ /* No comment provided by engineer. */ "To connect, your contact can scan QR code or use the link in the app." = "Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application."; +/* No comment provided by engineer. */ +"To hide unwanted messages." = "Pour cacher les messages indésirables."; + /* No comment provided by engineer. */ "To make a new connection" = "Pour établir une nouvelle connexion"; @@ -3416,6 +3509,12 @@ /* No comment provided by engineer. */ "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A moins que votre contact ait supprimé la connexion ou que ce lien ait déjà été utilisé, il peut s'agir d'un bug - veuillez le signaler.\nPour vous connecter, veuillez demander à votre contact de créer un autre lien de connexion et vérifiez que vous disposez d'une connexion réseau stable."; +/* No comment provided by engineer. */ +"Unlink" = "Délier"; + +/* No comment provided by engineer. */ +"Unlink desktop?" = "Délier le bureau ?"; + /* No comment provided by engineer. */ "Unlock" = "Déverrouiller"; @@ -3470,6 +3569,9 @@ /* No comment provided by engineer. */ "Use for new connections" = "Utiliser pour les nouvelles connexions"; +/* No comment provided by engineer. */ +"Use from desktop" = "Utilisation depuis le bureau"; + /* No comment provided by engineer. */ "Use iOS call interface" = "Utiliser l'interface d'appel d'iOS"; @@ -3491,12 +3593,24 @@ /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Utilisation des serveurs SimpleX Chat."; +/* No comment provided by engineer. */ +"v%@" = "v%@"; + /* No comment provided by engineer. */ "v%@ (%@)" = "v%@ (%@)"; +/* No comment provided by engineer. */ +"Verify code with desktop" = "Vérifier le code avec le bureau"; + +/* No comment provided by engineer. */ +"Verify connection" = "Vérifier la connexion"; + /* No comment provided by engineer. */ "Verify connection security" = "Vérifier la sécurité de la connexion"; +/* No comment provided by engineer. */ +"Verify connections" = "Vérifier les connexions"; + /* No comment provided by engineer. */ "Verify security code" = "Vérifier le code de sécurité"; @@ -3515,6 +3629,9 @@ /* No comment provided by engineer. */ "via relay" = "via relais"; +/* No comment provided by engineer. */ +"Via secure quantum resistant protocol." = "Via un protocole sécurisé de cryptographie post-quantique."; + /* No comment provided by engineer. */ "Video call" = "Appel vidéo"; diff --git a/apps/ios/fr.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/fr.lproj/SimpleX--iOS--InfoPlist.strings index f04cc09b0..e5bfdf09d 100644 --- a/apps/ios/fr.lproj/SimpleX--iOS--InfoPlist.strings +++ b/apps/ios/fr.lproj/SimpleX--iOS--InfoPlist.strings @@ -7,6 +7,9 @@ /* Privacy - Face ID Usage Description */ "NSFaceIDUsageDescription" = "SimpleGroup not found!X utilise Face ID pour l'authentification locale"; +/* Privacy - Local Network Usage Description */ +"NSLocalNetworkUsageDescription" = "SimpleX utilise un accès au réseau local pour permettre l'utilisation du profil de chat de l'utilisateur via l'application de bureau au sein de ce même réseau."; + /* Privacy - Microphone Usage Description */ "NSMicrophoneUsageDescription" = "SimpleX a besoin d'un accès au microphone pour les appels audio et vidéo ainsi que pour enregistrer des messages vocaux."; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index eb77c2fe1..274b909da 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- recapito dei messaggi più stabile.\n- gruppi un po' migliorati.\n- e altro ancora!"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- avvisa facoltativamente i contatti eliminati.\n- nomi del profilo con spazi.\n- e molto altro!"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messaggi vocali fino a 5 minuti.\n- tempo di scomparsa personalizzato.\n- cronologia delle modifiche."; @@ -569,12 +572,18 @@ /* No comment provided by engineer. */ "Bad message ID" = "ID del messaggio errato"; +/* No comment provided by engineer. */ +"Better groups" = "Gruppi migliorati"; + /* No comment provided by engineer. */ "Better messages" = "Messaggi migliorati"; /* No comment provided by engineer. */ "Block" = "Blocca"; +/* No comment provided by engineer. */ +"Block group members" = "Blocca i membri dei gruppi"; + /* No comment provided by engineer. */ "Block member" = "Blocca membro"; @@ -924,6 +933,9 @@ /* No comment provided by engineer. */ "Create" = "Crea"; +/* No comment provided by engineer. */ +"Create a group using a random profile." = "Crea un gruppo usando un profilo casuale."; + /* No comment provided by engineer. */ "Create an address to let people connect with you." = "Crea un indirizzo per consentire alle persone di connettersi con te."; @@ -1275,9 +1287,6 @@ /* No comment provided by engineer. */ "Discover and join groups" = "Scopri ed unisciti ai gruppi"; -/* No comment provided by engineer. */ -"Discover on network" = "Trova nella rete"; - /* No comment provided by engineer. */ "Do it later" = "Fallo dopo"; @@ -1653,6 +1662,9 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Veloce e senza aspettare che il mittente sia in linea!"; +/* No comment provided by engineer. */ +"Faster joining and more reliable messages." = "Ingresso più veloce e messaggi più affidabili."; + /* No comment provided by engineer. */ "Favorite" = "Preferito"; @@ -1914,6 +1926,9 @@ /* No comment provided by engineer. */ "Incognito" = "Incognito"; +/* No comment provided by engineer. */ +"Incognito groups" = "Gruppi in incognito"; + /* No comment provided by engineer. */ "Incognito mode" = "Modalità incognito"; @@ -2118,6 +2133,9 @@ /* No comment provided by engineer. */ "Limitations" = "Limitazioni"; +/* No comment provided by engineer. */ +"Link mobile and desktop apps! 🔗" = "Collega le app mobile e desktop! 🔗"; + /* No comment provided by engineer. */ "Linked desktop options" = "Opzioni del desktop collegato"; @@ -3392,6 +3410,9 @@ /* No comment provided by engineer. */ "To connect, your contact can scan QR code or use the link in the app." = "Per connettervi, il tuo contatto può scansionare il codice QR o usare il link nell'app."; +/* No comment provided by engineer. */ +"To hide unwanted messages." = "Per nascondere messaggi indesiderati."; + /* No comment provided by engineer. */ "To make a new connection" = "Per creare una nuova connessione"; @@ -3608,6 +3629,9 @@ /* No comment provided by engineer. */ "via relay" = "via relay"; +/* No comment provided by engineer. */ +"Via secure quantum resistant protocol." = "Tramite protocollo sicuro resistente alla quantistica."; + /* No comment provided by engineer. */ "Video call" = "Videochiamata"; diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index 4366d6cb9..ac1f8b03d 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabielere berichtbezorging.\n- een beetje betere groepen.\n- en meer!"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- optioneel verwijderde contacten op de hoogte stellen.\n- profielnamen met spaties.\n- en meer!"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- spraakberichten tot 5 minuten.\n- aangepaste tijd om te verdwijnen.\n- bewerkingsgeschiedenis."; @@ -569,12 +572,18 @@ /* No comment provided by engineer. */ "Bad message ID" = "Onjuiste bericht-ID"; +/* No comment provided by engineer. */ +"Better groups" = "Betere groepen"; + /* No comment provided by engineer. */ "Better messages" = "Betere berichten"; /* No comment provided by engineer. */ "Block" = "Blokkeren"; +/* No comment provided by engineer. */ +"Block group members" = "Groepsleden blokkeren"; + /* No comment provided by engineer. */ "Block member" = "Lid blokkeren"; @@ -924,6 +933,9 @@ /* No comment provided by engineer. */ "Create" = "Maak"; +/* No comment provided by engineer. */ +"Create a group using a random profile." = "Maak een groep met een willekeurig profiel."; + /* No comment provided by engineer. */ "Create an address to let people connect with you." = "Maak een adres aan zodat mensen contact met je kunnen opnemen."; @@ -1275,9 +1287,6 @@ /* No comment provided by engineer. */ "Discover and join groups" = "Ontdek en sluit je aan bij groepen"; -/* No comment provided by engineer. */ -"Discover on network" = "Ontdek via netwerk"; - /* No comment provided by engineer. */ "Do it later" = "Doe het later"; @@ -1653,6 +1662,9 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Snel en niet wachten tot de afzender online is!"; +/* No comment provided by engineer. */ +"Faster joining and more reliable messages." = "Snellere deelname en betrouwbaardere berichten."; + /* No comment provided by engineer. */ "Favorite" = "Favoriet"; @@ -1914,6 +1926,9 @@ /* No comment provided by engineer. */ "Incognito" = "Incognito"; +/* No comment provided by engineer. */ +"Incognito groups" = "Incognitogroepen"; + /* No comment provided by engineer. */ "Incognito mode" = "Incognito modus"; @@ -2118,6 +2133,9 @@ /* No comment provided by engineer. */ "Limitations" = "Beperkingen"; +/* No comment provided by engineer. */ +"Link mobile and desktop apps! 🔗" = "Koppel mobiele en desktop-apps! 🔗"; + /* No comment provided by engineer. */ "Linked desktop options" = "Gekoppelde desktop opties"; @@ -3392,6 +3410,9 @@ /* No comment provided by engineer. */ "To connect, your contact can scan QR code or use the link in the app." = "Om verbinding te maken, kan uw contact de QR-code scannen of de link in de app gebruiken."; +/* No comment provided by engineer. */ +"To hide unwanted messages." = "Om ongewenste berichten te verbergen."; + /* No comment provided by engineer. */ "To make a new connection" = "Om een nieuwe verbinding te maken"; @@ -3608,6 +3629,9 @@ /* No comment provided by engineer. */ "via relay" = "via relais"; +/* No comment provided by engineer. */ +"Via secure quantum resistant protocol." = "Via een beveiligd kwantumbestendig protocol."; + /* No comment provided by engineer. */ "Video call" = "video oproep"; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 73f06afee..7d094fcf2 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- более стабильная доставка сообщений.\n- немного улучшенные группы.\n- и прочее!"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- опционально уведомляйте удалённые контакты.\n- имена профилей с пробелами.\n- и прочее!"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- голосовые сообщения до 5 минут.\n- настройка времени исчезающих сообщений.\n- история редактирования."; @@ -43,6 +46,12 @@ /* No comment provided by engineer. */ "(" = "("; +/* No comment provided by engineer. */ +"(new)" = "(новое)"; + +/* No comment provided by engineer. */ +"(this device v%@)" = "(это устройство v%@)"; + /* No comment provided by engineer. */ ")" = ")"; @@ -118,12 +127,18 @@ /* No comment provided by engineer. */ "%@ %@" = "%@ %@"; +/* No comment provided by engineer. */ +"%@ and %@" = "%@ и %@"; + /* No comment provided by engineer. */ "%@ and %@ connected" = "%@ и %@ соединены"; /* copied message info, at