From c40bfb0f4361dd1228707dfd3b72c5dc8020db20 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Wed, 22 Nov 2023 05:49:39 +0800 Subject: [PATCH] android, desktop: show remote host info (#3423) * android, desktop: show remote host info * hide alerts too --- .../simplex/common/platform/Share.android.kt | 3 - .../chat/simplex/common/model/SimpleXAPI.kt | 10 +- .../views/chatlist/ChatListNavLinkView.kt | 19 +- .../common/views/helpers/AlertManager.kt | 224 +++++++++++------- .../common/views/newchat/ScanToConnectView.kt | 40 +++- .../commonMain/resources/MR/base/strings.xml | 1 + .../kotlin/chat/simplex/common/DesktopApp.kt | 4 +- .../simplex/common/platform/Share.desktop.kt | 1 - .../views/chat/item/ChatItemView.desktop.kt | 1 - 9 files changed, 191 insertions(+), 112 deletions(-) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt index eb6ed0bbf..f0c5ea694 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Share.android.kt @@ -8,15 +8,12 @@ import android.provider.MediaStore import android.webkit.MimeTypeMap import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.UriHandler -import androidx.core.content.FileProvider -import androidx.core.net.toUri import chat.simplex.common.helpers.* import chat.simplex.common.model.* import chat.simplex.common.views.helpers.* import java.io.BufferedOutputStream import java.io.File import chat.simplex.res.MR -import java.io.ByteArrayOutputStream actual fun ClipboardManager.shareText(text: String) { val sendIntent: Intent = Intent().apply { 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 9298388df..5698e8cb0 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 @@ -4,7 +4,6 @@ import chat.simplex.common.views.helpers.* import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter -import chat.simplex.common.model.ChatModel.remoteHostId import chat.simplex.common.model.ChatModel.updatingChatsMutex import dev.icerock.moko.resources.compose.painterResource import chat.simplex.common.platform.* @@ -1845,7 +1844,13 @@ object ChatController { switchUIRemoteHost(r.remoteHost.remoteHostId) } is CR.RemoteHostStopped -> { + val disconnectedHost = chatModel.remoteHosts.firstOrNull { it.remoteHostId == r.remoteHostId_ } chatModel.newRemoteHostPairing.value = null + if (disconnectedHost != null) { + showToast( + generalGetString(MR.strings.remote_host_was_disconnected_toast).format(disconnectedHost.hostDeviceName.ifEmpty { disconnectedHost.remoteHostId.toString() }) + ) + } if (chatModel.currentRemoteHost.value != null) { chatModel.currentRemoteHost.value = null switchUIRemoteHost(null) @@ -1968,6 +1973,9 @@ object ChatController { suspend fun switchUIRemoteHost(rhId: Long?) { // TODO lock the switch so that two switches can't run concurrently? chatModel.chatId.value = null + ModalManager.center.closeModals() + ModalManager.end.closeModals() + AlertManager.shared.alertViews.clear() chatModel.currentRemoteHost.value = switchRemoteHost(rhId) reloadRemoteHosts() val user = apiGetActiveUser(rhId) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index e121f9ee7..9d662758f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -129,7 +129,7 @@ fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) { fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState? = null) { when (groupInfo.membership.memberStatus) { GroupMemberStatus.MemInvited -> acceptGroupInvitationAlertDialog(rhId, groupInfo, chatModel, inProgress) - GroupMemberStatus.MemAccepted -> groupInvitationAcceptedAlert() + GroupMemberStatus.MemAccepted -> groupInvitationAcceptedAlert(rhId) else -> withBGApi { openChat(rhId, ChatInfo.Group(groupInfo), chatModel) } } } @@ -538,7 +538,8 @@ fun contactRequestAlertDialog(rhId: Long?, contactRequest: ChatInfo.ContactReque Text(generalGetString(MR.strings.reject_contact_button), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red) } } - } + }, + hostDevice = hostDevice(rhId), ) } @@ -644,7 +645,8 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress( Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } } - } + }, + hostDevice = hostDevice(rhId), ) } @@ -654,7 +656,8 @@ suspend fun connectContactViaAddress(chatModel: ChatModel, rhId: Long?, contactI chatModel.updateContact(rhId, contact) AlertManager.shared.showAlertMsg( title = generalGetString(MR.strings.connection_request_sent), - text = generalGetString(MR.strings.you_will_be_connected_when_your_connection_request_is_accepted) + text = generalGetString(MR.strings.you_will_be_connected_when_your_connection_request_is_accepted), + hostDevice = hostDevice(rhId), ) return true } @@ -674,7 +677,8 @@ fun acceptGroupInvitationAlertDialog(rhId: Long?, groupInfo: GroupInfo, chatMode } }, dismissText = generalGetString(MR.strings.delete_verb), - onDismiss = { deleteGroup(rhId, groupInfo, chatModel) } + onDismiss = { deleteGroup(rhId, groupInfo, chatModel) }, + hostDevice = hostDevice(rhId), ) } @@ -700,10 +704,11 @@ fun deleteGroup(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) { } } -fun groupInvitationAcceptedAlert() { +fun groupInvitationAcceptedAlert(rhId: Long?) { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.joining_group), - generalGetString(MR.strings.youve_accepted_group_invitation_connecting_to_inviting_group_member) + generalGetString(MR.strings.youve_accepted_group_invitation_connecting_to_inviting_group_member), + hostDevice = hostDevice(rhId), ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt index fa9c89384..287f19ffd 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt @@ -1,6 +1,5 @@ package chat.simplex.common.views.helpers -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape @@ -14,10 +13,12 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.* +import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.res.MR import dev.icerock.moko.resources.StringResource +import dev.icerock.moko.resources.compose.painterResource class AlertManager { var alertViews = mutableStateListOf<(@Composable () -> Unit)>() @@ -40,8 +41,11 @@ class AlertManager { AlertDialog( onDismissRequest = this::hideAlert, title = alertTitle(title), - text = alertText(text), - buttons = buttons, + buttons = { + AlertContent(text, null, extraPadding = true) { + buttons() + } + }, shape = RoundedCornerShape(corner = CornerSize(25.dp)) ) } @@ -51,30 +55,16 @@ class AlertManager { title: String, text: AnnotatedString? = null, onDismissRequest: (() -> Unit)? = null, + hostDevice: Pair? = null, buttons: @Composable () -> Unit, ) { showAlert { AlertDialog( onDismissRequest = { onDismissRequest?.invoke(); hideAlert() }, - title = { - Text( - title, - Modifier.fillMaxWidth().padding(vertical = DEFAULT_PADDING), - textAlign = TextAlign.Center, - fontSize = 20.sp - ) - }, + title = alertTitle(title), buttons = { - Column( - Modifier - .padding(bottom = DEFAULT_PADDING) - ) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { - if (text != null) { - Text(text, Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), fontSize = 16.sp, textAlign = TextAlign.Center, color = MaterialTheme.colors.secondary) - } - buttons() - } + AlertContent(text, hostDevice, extraPadding = true) { + buttons() } }, shape = RoundedCornerShape(corner = CornerSize(25.dp)) @@ -90,30 +80,32 @@ class AlertManager { dismissText: String = generalGetString(MR.strings.cancel_verb), onDismiss: (() -> Unit)? = null, onDismissRequest: (() -> Unit)? = null, - destructive: Boolean = false + destructive: Boolean = false, + hostDevice: Pair? = null, ) { showAlert { AlertDialog( onDismissRequest = { onDismissRequest?.invoke(); hideAlert() }, title = alertTitle(title), - text = alertText(text), buttons = { - Row ( - Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF), - horizontalArrangement = Arrangement.SpaceBetween - ) { - val focusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { - focusRequester.requestFocus() + AlertContent(text, hostDevice) { + Row( + Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING), + horizontalArrangement = Arrangement.SpaceBetween + ) { + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + TextButton(onClick = { + onDismiss?.invoke() + hideAlert() + }) { Text(dismissText) } + TextButton(onClick = { + onConfirm?.invoke() + hideAlert() + }, Modifier.focusRequester(focusRequester)) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) } } - TextButton(onClick = { - onDismiss?.invoke() - hideAlert() - }) { Text(dismissText) } - TextButton(onClick = { - onConfirm?.invoke() - hideAlert() - }, Modifier.focusRequester(focusRequester)) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) } } }, shape = RoundedCornerShape(corner = CornerSize(25.dp)) @@ -135,20 +127,21 @@ class AlertManager { AlertDialog( onDismissRequest = { onDismissRequest?.invoke(); hideAlert() }, title = alertTitle(title), - text = alertText(text), buttons = { - Column( - Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING_HALF).padding(top = DEFAULT_PADDING, bottom = 2.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - TextButton(onClick = { - onDismiss?.invoke() - hideAlert() - }) { Text(dismissText) } - TextButton(onClick = { - onConfirm?.invoke() - hideAlert() - }) { Text(confirmText, color = if (destructive) Color.Red else Color.Unspecified, textAlign = TextAlign.End) } + AlertContent(text, null) { + Column( + Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING_HALF).padding(top = DEFAULT_PADDING, bottom = 2.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + TextButton(onClick = { + onDismiss?.invoke() + hideAlert() + }) { Text(dismissText) } + TextButton(onClick = { + onConfirm?.invoke() + hideAlert() + }) { Text(confirmText, color = if (destructive) Color.Red else Color.Unspecified, textAlign = TextAlign.End) } + } } }, shape = RoundedCornerShape(corner = CornerSize(25.dp)) @@ -158,29 +151,31 @@ class AlertManager { fun showAlertMsg( title: String, text: String? = null, - confirmText: String = generalGetString(MR.strings.ok) + confirmText: String = generalGetString(MR.strings.ok), + hostDevice: Pair? = null, ) { showAlert { AlertDialog( onDismissRequest = this::hideAlert, title = alertTitle(title), - text = alertText(text), buttons = { - val focusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { - focusRequester.requestFocus() - } - Row( - Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF), - horizontalArrangement = Arrangement.Center - ) { - TextButton( - onClick = { - hideAlert() - }, - Modifier.focusRequester(focusRequester) + AlertContent(text, hostDevice, extraPadding = true) { + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + Row( + Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING), + horizontalArrangement = Arrangement.Center ) { - Text(confirmText, color = Color.Unspecified) + TextButton( + onClick = { + hideAlert() + }, + Modifier.focusRequester(focusRequester) + ) { + Text(confirmText, color = Color.Unspecified) + } } } }, @@ -191,16 +186,17 @@ class AlertManager { fun showAlertMsgWithProgress( title: String, - text: String? = null + text: String? = null, ) { showAlert { AlertDialog( onDismissRequest = this::hideAlert, title = alertTitle(title), - text = alertText(text), buttons = { - Box(Modifier.fillMaxWidth().height(72.dp).padding(bottom = DEFAULT_PADDING * 2), contentAlignment = Alignment.Center) { - CircularProgressIndicator(Modifier.size(36.dp).padding(4.dp), color = MaterialTheme.colors.secondary, strokeWidth = 3.dp) + AlertContent(text, null) { + Box(Modifier.fillMaxWidth().height(72.dp).padding(bottom = DEFAULT_PADDING * 2), contentAlignment = Alignment.Center) { + CircularProgressIndicator(Modifier.size(36.dp).padding(4.dp), color = MaterialTheme.colors.secondary, strokeWidth = 3.dp) + } } } ) @@ -211,7 +207,8 @@ class AlertManager { title: StringResource, text: StringResource? = null, confirmText: StringResource = MR.strings.ok, - ) = showAlertMsg(generalGetString(title), if (text != null) generalGetString(text) else null, generalGetString(confirmText)) + hostDevice: Pair? = null, + ) = showAlertMsg(generalGetString(title), if (text != null) generalGetString(text) else null, generalGetString(confirmText), hostDevice) @Composable fun showInView() { @@ -234,18 +231,75 @@ private fun alertTitle(title: String): (@Composable () -> Unit)? { } } -private fun alertText(text: String?): (@Composable () -> Unit)? { - return if (text == null) { - null - } else { - ({ - Text( - escapedHtmlToAnnotatedString(text, LocalDensity.current), - Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - fontSize = 16.sp, - color = MaterialTheme.colors.secondary - ) - }) +@Composable +private fun AlertContent(text: String?, hostDevice: Pair?, extraPadding: Boolean = false, content: @Composable (() -> Unit)) { + Column( + Modifier + .padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF) + ) { + if (appPlatform.isDesktop) { + HostDeviceTitle(hostDevice, extraPadding = extraPadding) + } else { + Spacer(Modifier.size(DEFAULT_PADDING_HALF)) + } + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { + if (text != null) { + Text( + escapedHtmlToAnnotatedString(text, LocalDensity.current), + Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), + fontSize = 16.sp, + textAlign = TextAlign.Center, + color = MaterialTheme.colors.secondary + ) + } + } + content() + } +} + +@Composable +private fun AlertContent(text: AnnotatedString?, hostDevice: Pair?, extraPadding: Boolean = false, content: @Composable (() -> Unit)) { + Column( + Modifier + .padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF) + ) { + if (appPlatform.isDesktop) { + HostDeviceTitle(hostDevice, extraPadding = extraPadding) + } else { + Spacer(Modifier.size(DEFAULT_PADDING_HALF)) + } + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { + if (text != null) { + Text( + text, + Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), + fontSize = 16.sp, + textAlign = TextAlign.Center, + color = MaterialTheme.colors.secondary + ) + } + } + content() + } +} + +fun hostDevice(rhId: Long?): Pair? = if (rhId == null && chatModel.remoteHosts.isNotEmpty()) { + null to ChatModel.controller.appPrefs.deviceNameForRemoteAccess.get()!! +} else if (rhId == null) { + null +} else { + rhId to (chatModel.remoteHosts.firstOrNull { it.remoteHostId == rhId }?.hostDeviceName?.ifEmpty { rhId.toString() } ?: rhId.toString()) +} + +@Composable +private fun HostDeviceTitle(hostDevice: Pair?, extraPadding: Boolean = false) { + if (hostDevice != null) { + Row(Modifier.fillMaxWidth().padding(top = 5.dp, bottom = if (extraPadding) DEFAULT_PADDING * 2 else DEFAULT_PADDING_HALF), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) { + Icon(painterResource(if (hostDevice.first == null) MR.images.ic_desktop else MR.images.ic_smartphone_300), null, Modifier.size(15.dp), tint = MaterialTheme.colors.secondary) + Spacer(Modifier.width(10.dp)) + Text(hostDevice.second, color = MaterialTheme.colors.secondary) + } + } else { + Spacer(Modifier.height(if (extraPadding) DEFAULT_PADDING * 2 else 0.dp)) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt index bd111c9c3..fd3961362 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt @@ -4,7 +4,6 @@ import SectionBottomSpacer import SectionItemView import SectionTextFooter import androidx.compose.desktop.ui.tooling.preview.Preview -import chat.simplex.common.platform.Log import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -17,7 +16,7 @@ import androidx.compose.ui.text.style.TextAlign import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.unit.dp import chat.simplex.common.model.* -import chat.simplex.common.platform.TAG +import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.helpers.* @@ -65,6 +64,7 @@ suspend fun planAndConnect( confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } }, destructive = true, + hostDevice = hostDevice(rhId), ) } else { askCurrentOrIncognitoProfileAlert( @@ -82,12 +82,14 @@ suspend fun planAndConnect( openKnownContact(chatModel, rhId, close, contact) AlertManager.shared.showAlertMsg( generalGetString(MR.strings.contact_already_exists), - String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName) + String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName), + hostDevice = hostDevice(rhId), ) } else { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.connect_plan_already_connecting), - generalGetString(MR.strings.connect_plan_you_are_already_connecting_via_this_one_time_link) + generalGetString(MR.strings.connect_plan_you_are_already_connecting_via_this_one_time_link), + hostDevice = hostDevice(rhId), ) } } @@ -97,7 +99,8 @@ suspend fun planAndConnect( openKnownContact(chatModel, rhId, close, contact) AlertManager.shared.showAlertMsg( generalGetString(MR.strings.contact_already_exists), - String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName) + String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName), + hostDevice = hostDevice(rhId), ) } } @@ -124,6 +127,7 @@ suspend fun planAndConnect( confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } }, destructive = true, + hostDevice = hostDevice(rhId), ) } else { askCurrentOrIncognitoProfileAlert( @@ -143,6 +147,7 @@ suspend fun planAndConnect( confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } }, destructive = true, + hostDevice = hostDevice(rhId), ) } else { askCurrentOrIncognitoProfileAlert( @@ -159,7 +164,8 @@ suspend fun planAndConnect( openKnownContact(chatModel, rhId, close, contact) AlertManager.shared.showAlertMsg( generalGetString(MR.strings.contact_already_exists), - String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName) + String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName), + hostDevice = hostDevice(rhId), ) } is ContactAddressPlan.Known -> { @@ -168,7 +174,8 @@ suspend fun planAndConnect( openKnownContact(chatModel, rhId, close, contact) AlertManager.shared.showAlertMsg( generalGetString(MR.strings.contact_already_exists), - String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName) + String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName), + hostDevice = hostDevice(rhId), ) } is ContactAddressPlan.ContactViaAddress -> { @@ -190,7 +197,8 @@ suspend fun planAndConnect( title = generalGetString(MR.strings.connect_via_group_link), text = generalGetString(MR.strings.you_will_join_group), confirmText = if (incognito) generalGetString(MR.strings.join_group_incognito_button) else generalGetString(MR.strings.join_group_button), - onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } } + onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } }, + hostDevice = hostDevice(rhId), ) } else { askCurrentOrIncognitoProfileAlert( @@ -215,6 +223,7 @@ suspend fun planAndConnect( confirmText = if (incognito) generalGetString(MR.strings.join_group_incognito_button) else generalGetString(MR.strings.join_group_button), onConfirm = { withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } }, destructive = true, + hostDevice = hostDevice(rhId), ) } else { askCurrentOrIncognitoProfileAlert( @@ -236,7 +245,8 @@ suspend fun planAndConnect( } else { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.connect_plan_already_joining_the_group), - generalGetString(MR.strings.connect_plan_you_are_already_joining_the_group_via_this_link) + generalGetString(MR.strings.connect_plan_you_are_already_joining_the_group_via_this_link), + hostDevice = hostDevice(rhId), ) } } @@ -246,7 +256,8 @@ suspend fun planAndConnect( openKnownGroup(chatModel, rhId, close, groupInfo) AlertManager.shared.showAlertMsg( generalGetString(MR.strings.connect_plan_group_already_exists), - String.format(generalGetString(MR.strings.connect_plan_you_are_already_in_group_vName), groupInfo.displayName) + String.format(generalGetString(MR.strings.connect_plan_you_are_already_in_group_vName), groupInfo.displayName), + hostDevice = hostDevice(rhId), ) } } @@ -284,7 +295,8 @@ suspend fun connectViaUri( ConnectionLinkType.CONTACT -> generalGetString(MR.strings.you_will_be_connected_when_your_connection_request_is_accepted) ConnectionLinkType.INVITATION -> generalGetString(MR.strings.you_will_be_connected_when_your_contacts_device_is_online) ConnectionLinkType.GROUP -> generalGetString(MR.strings.you_will_be_connected_when_group_host_device_is_online) - } + }, + hostDevice = hostDevice(rhId), ) } return r @@ -336,7 +348,8 @@ fun askCurrentOrIncognitoProfileAlert( Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } } - } + }, + hostDevice = hostDevice(rhId), ) } @@ -411,7 +424,8 @@ fun ownGroupLinkConfirmConnect( Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } } - } + }, + hostDevice = hostDevice(rhId), ) } 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 177dcb55f..55cf4e6c9 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1668,6 +1668,7 @@ Unlink desktop? Unlink Disconnect + %s]]> Disconnect desktop? Only one device can work at the same time Use from desktop in mobile app and scan QR code]]> diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt index 2931e0e01..1a95317a6 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.key.* +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.window.* import chat.simplex.common.model.ChatController @@ -19,6 +20,7 @@ import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH import chat.simplex.common.ui.theme.SimpleXTheme import chat.simplex.common.views.TerminalView import chat.simplex.common.views.helpers.FileDialogChooser +import chat.simplex.common.views.helpers.escapedHtmlToAnnotatedString import chat.simplex.res.MR import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.* @@ -80,7 +82,7 @@ fun showApp() = application { if (toast != null) { Box(Modifier.fillMaxSize().padding(bottom = 20.dp), contentAlignment = Alignment.BottomCenter) { Text( - toast.first, + escapedHtmlToAnnotatedString(toast.first, LocalDensity.current), Modifier.background(MaterialTheme.colors.primary, RoundedCornerShape(100)).padding(vertical = 5.dp, horizontal = 10.dp), color = MaterialTheme.colors.onPrimary, style = MaterialTheme.typography.body1 diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt index a91bc5a76..f7728f9c6 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Share.desktop.kt @@ -4,7 +4,6 @@ import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.text.AnnotatedString import chat.simplex.common.model.* -import chat.simplex.common.views.helpers.getAppFileUri import chat.simplex.common.views.helpers.withApi import java.io.File import java.net.URI diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt index 91efdf790..905a6e352 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt @@ -19,7 +19,6 @@ import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import java.io.File -import java.util.* @Composable actual fun ReactionIcon(text: String, fontSize: TextUnit) {