android, desktop: show remote host info (#3423)

* android, desktop: show remote host info

* hide alerts too
This commit is contained in:
Stanislav Dmitrenko 2023-11-22 05:49:39 +08:00 committed by GitHub
parent 64520a4cf4
commit c40bfb0f43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 191 additions and 112 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -129,7 +129,7 @@ fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) {
fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState<Boolean>? = 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),
)
}

View File

@ -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<Long?, String>? = 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<Long?, String>? = 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<Long?, String>? = 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<Long?, String>? = 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<Long?, String>?, 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<Long?, String>?, 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<Long?, String>? = 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<Long?, String>?, 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))
}
}

View File

@ -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),
)
}

View File

@ -1668,6 +1668,7 @@
<string name="unlink_desktop_question">Unlink desktop?</string>
<string name="unlink_desktop">Unlink</string>
<string name="disconnect_remote_host">Disconnect</string>
<string name="remote_host_was_disconnected_toast"><![CDATA[Remote host was disconnected: <b>%s</b>]]></string>
<string name="disconnect_desktop_question">Disconnect desktop?</string>
<string name="only_one_device_can_work_at_the_same_time">Only one device can work at the same time</string>
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Open <i>Use from desktop</i> in mobile app and scan QR code]]></string>

View File

@ -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

View File

@ -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

View File

@ -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) {