diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index acc57003de..6734b30891 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -1,24 +1,13 @@ package chat.simplex.app.views.chat import ComposeImageView -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.AddCircleOutline -import androidx.compose.foundation.layout.Column import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.views.helpers.ComposeLinkView -import chat.simplex.app.views.helpers.generalGetString // TODO ComposeState diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt index d0196336c7..cc6aae2ab1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt @@ -7,9 +7,9 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.CIFile import chat.simplex.app.views.helpers.* -import chat.simplex.app.R @Composable fun CIImageView( diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt index f2a96f2418..aa03a7d0cf 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt @@ -1,7 +1,7 @@ package chat.simplex.app.views.chatlist import android.content.res.Configuration -import androidx.compose.foundation.* +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt index c507d8ae2e..23906df768 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt @@ -3,6 +3,7 @@ package chat.simplex.app.ui.theme import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Share @@ -18,14 +19,19 @@ import androidx.compose.ui.unit.dp fun SimpleButton(text: String, icon: ImageVector, color: Color = MaterialTheme.colors.primary, click: () -> Unit) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.clickable { click() } - ) { - Icon(icon, text, tint = color, - modifier = Modifier.padding(horizontal = 10.dp) - ) - Text(text, style = MaterialTheme.typography.caption, color = color) + Surface(shape = RoundedCornerShape(20.dp)) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable { click() } + .padding(8.dp) + ) { + Icon( + icon, text, tint = color, + modifier = Modifier.padding(end = 8.dp) + ) + Text(text, style = MaterialTheme.typography.caption, color = color) + } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt new file mode 100644 index 0000000000..c02bdfb6a4 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt @@ -0,0 +1,58 @@ +package chat.simplex.app.views.helpers + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.app.ui.theme.HighOrLowlight + +@Composable +fun TextEditor(modifier: Modifier, text: MutableState) { + BasicTextField( + value = text.value, + onValueChange = { text.value = it }, + textStyle = TextStyle( + fontFamily = FontFamily.Monospace, fontSize = 14.sp, + color = MaterialTheme.colors.onBackground + ), + keyboardOptions = KeyboardOptions.Default.copy( + capitalization = KeyboardCapitalization.None, + autoCorrect = false + ), + modifier = modifier, + cursorBrush = SolidColor(HighOrLowlight), + decorationBox = { innerTextField -> + Surface( + shape = RoundedCornerShape(10.dp), + border = BorderStroke(1.dp, MaterialTheme.colors.secondary) + ) { + Row( + Modifier.background(MaterialTheme.colors.background), + verticalAlignment = Alignment.Top + ) { + Box( + Modifier + .weight(1f) + .padding(vertical = 5.dp, horizontal = 7.dp) + ) { + innerTextField() + } + } + } + } + ) +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt index 4e668e3580..c23c6521ca 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt @@ -63,7 +63,7 @@ fun AddContactLayout(connReq: String, share: () -> Unit) { lineHeight = 22.sp, modifier = Modifier .padding(horizontal = 16.dp) - .padding(bottom = if(screenHeight > 600.dp) 16.dp else 8.dp) + .padding(bottom = if (screenHeight > 600.dp) 16.dp else 8.dp) ) SimpleButton(generalGetString(R.string.share_invitation_link), icon = Icons.Outlined.Share, click = share) Spacer(Modifier.height(10.dp)) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt index 2d3ff4924a..b89c91fcff 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt @@ -3,6 +3,7 @@ package chat.simplex.app.views.newchat import android.Manifest import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* @@ -39,52 +40,65 @@ fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController) { }, scanCode = { newChatCtrl.collapse() - ModalManager.shared.showCustomModal { close -> ConnectContactView(chatModel, close) } + ModalManager.shared.showCustomModal { close -> ScanToConnectView(chatModel, close) } cameraPermissionState.launchPermissionRequest() + }, + pasteLink = { + newChatCtrl.collapse() + ModalManager.shared.showCustomModal { close -> PasteToConnectView(chatModel, close) } } ) } @Composable -fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { - Row( - Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 48.dp), - horizontalArrangement = Arrangement.SpaceEvenly +fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit, pasteLink: () -> Unit) { + Column( + horizontalAlignment = Alignment.CenterHorizontally ) { - Box( + Text( + generalGetString(R.string.add_contact_to_start_new_chat), + modifier = Modifier.padding(horizontal = 8.dp).padding(top = 32.dp) + ) + Row( Modifier - .weight(1F) - .fillMaxWidth()) { - ActionButton( - generalGetString(R.string.add_contact), - generalGetString(R.string.create_QR_code_or_link__bracketed__multiline), - Icons.Outlined.PersonAdd, - click = addContact - ) - } - Box( - Modifier - .weight(1F) - .fillMaxWidth()) { - ActionButton( - generalGetString(R.string.scan_QR_code), - generalGetString(R.string.in_person_or_in_video_call__bracketed), - Icons.Outlined.QrCode, - click = scanCode - ) - } - Box( - Modifier - .weight(1F) - .fillMaxWidth()) { - ActionButton( - generalGetString(R.string.create_group), - generalGetString(R.string.coming_soon__bracketed), - Icons.Outlined.GroupAdd, - disabled = true - ) + .fillMaxWidth() + .padding(horizontal = 8.dp) + .padding(top = 24.dp, bottom = 40.dp), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { + ActionButton( + generalGetString(R.string.create_one_time_link), + generalGetString(R.string.to_share_with_your_contact), + Icons.Outlined.PersonAdd, + click = addContact + ) + } + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { + ActionButton( + generalGetString(R.string.paste_received_link), + generalGetString(R.string.paste_received_link_from_clipboard), + Icons.Outlined.Link, + click = pasteLink + ) + } + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { + ActionButton( + generalGetString(R.string.scan_QR_code), + generalGetString(R.string.in_person_or_in_video_call__bracketed), + Icons.Outlined.QrCode, + click = scanCode + ) + } } } } @@ -92,33 +106,35 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { @Composable fun ActionButton(text: String?, comment: String?, icon: ImageVector, disabled: Boolean = false, click: () -> Unit = {}) { - Column( - Modifier - .clickable(onClick = click) - .padding(horizontal = 8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - val tint = if (disabled) HighOrLowlight else MaterialTheme.colors.primary - Icon(icon, text, - tint = tint, - modifier = Modifier - .size(40.dp) - .padding(bottom = 8.dp)) - if (text != null) { - Text( - text, - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - color = tint, - modifier = Modifier.padding(bottom = 4.dp) - ) - } - if (comment != null) { - Text( - comment, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.body2 - ) + Surface(shape = RoundedCornerShape(18.dp)) { + Column( + Modifier + .clickable(onClick = click) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val tint = if (disabled) HighOrLowlight else MaterialTheme.colors.primary + Icon(icon, text, + tint = tint, + modifier = Modifier + .size(40.dp) + .padding(bottom = 8.dp)) + if (text != null) { + Text( + text, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + color = tint, + modifier = Modifier.padding(bottom = 4.dp) + ) + } + if (comment != null) { + Text( + comment, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.body2 + ) + } } } } @@ -129,7 +145,8 @@ fun PreviewNewChatSheet() { SimpleXTheme { NewChatSheetLayout( addContact = {}, - scanCode = {} + scanCode = {}, + pasteLink = {} ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt new file mode 100644 index 0000000000..78775ef1e5 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt @@ -0,0 +1,126 @@ +package chat.simplex.app.views.newchat + +import android.content.ClipboardManager +import android.content.res.Configuration +import android.net.Uri +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat.getSystemService +import chat.simplex.app.R +import chat.simplex.app.model.ChatModel +import chat.simplex.app.ui.theme.SimpleButton +import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.* + +@Composable +fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) { + val connectionLink = remember { mutableStateOf("")} + val context = LocalContext.current + val clipboard = getSystemService(context, ClipboardManager::class.java) + BackHandler(onBack = close) + PasteToConnectLayout( + connectionLink = connectionLink, + pasteFromClipboard = { + connectionLink.value = clipboard?.primaryClip?.getItemAt(0)?.coerceToText(context) as String + }, + connectViaLink = { connReqUri -> + try { + val uri = Uri.parse(connReqUri) + withUriAction(uri) { action -> + connectViaUri(chatModel, action, uri) + } + } catch (e: RuntimeException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.invalid_connection_link), + text = generalGetString(R.string.this_string_is_not_a_connection_link) + ) + } + close() + }, + close = close + ) +} + +@Composable +fun PasteToConnectLayout( + connectionLink: MutableState, + pasteFromClipboard: () -> Unit, + connectViaLink: (String) -> Unit, + close: () -> Unit +) { + ModalView(close) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween, + ) { + Text( + generalGetString(R.string.connect_via_link), + style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal), + modifier = Modifier.padding(bottom = 16.dp) + ) + Text(generalGetString(R.string.paste_connection_link_below_to_connect)) + Text(generalGetString(R.string.profile_will_be_sent_to_contact_sending_link)) + + Box(Modifier.padding(top = 16.dp, bottom = 6.dp)) { + TextEditor(Modifier.height(180.dp), text = connectionLink) + } + + Row( + Modifier.fillMaxWidth().padding(bottom = 6.dp), + horizontalArrangement = Arrangement.Start, + ) { + if (connectionLink.value == "") { + SimpleButton(text = "Paste", icon = Icons.Outlined.ContentPaste) { + pasteFromClipboard() + } + } else { + SimpleButton(text = "Clear", icon = Icons.Outlined.Clear) { + connectionLink.value = "" + } + } + Spacer(Modifier.weight(1f).fillMaxWidth()) + SimpleButton(text = "Connect", icon = Icons.Outlined.Link) { + connectViaLink(connectionLink.value) + } + } + + Text(annotatedStringResource(R.string.you_can_also_connect_by_clicking_the_link)) + } + } +} + + +@Preview(showBackground = true) +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_YES, + name = "Dark Mode" +) +@Composable +fun PreviewPasteToConnectTextbox() { + SimpleXTheme { + PasteToConnectLayout( + connectionLink = remember { mutableStateOf("") }, + pasteFromClipboard = {}, + connectViaLink = { link -> + try { + println(link) + // withApi { chatModel.controller.apiConnect(link) } + } catch (e: Exception) { + e.printStackTrace() + } + }, + close = {} + ) + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt rename to apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt index f65b000987..11c67a409a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt @@ -19,7 +19,7 @@ import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.helpers.* @Composable -fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) { +fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) { BackHandler(onBack = close) ConnectContactLayout( qrCodeScanner = { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt index 6668018792..899e2191f6 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt @@ -3,8 +3,6 @@ package chat.simplex.app.views.usersettings import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.* import androidx.compose.material.icons.Icons @@ -12,11 +10,9 @@ import androidx.compose.material.icons.outlined.OpenInNew import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -32,7 +28,7 @@ fun SMPServersView(chatModel: ChatModel) { if (userSMPServers != null) { var isUserSMPServers by remember { mutableStateOf(userSMPServers.isNotEmpty()) } var editSMPServers by remember { mutableStateOf(!isUserSMPServers) } - var userSMPServersStr by remember { mutableStateOf(if (isUserSMPServers) userSMPServers.joinToString(separator = "\n") else "") } + var userSMPServersStr = remember { mutableStateOf(if (isUserSMPServers) userSMPServers.joinToString(separator = "\n") else "") } fun saveSMPServers(smpServers: List) { withApi { val r = chatModel.controller.setUserSMPServers(smpServers = smpServers) @@ -66,23 +62,22 @@ fun SMPServersView(chatModel: ChatModel) { onConfirm = { saveSMPServers(listOf()) isUserSMPServers = false - userSMPServersStr = "" + userSMPServersStr.value = "" } ) } else { isUserSMPServers = false - userSMPServersStr = "" + userSMPServersStr.value = "" } } } }, - editUserSMPServersStr = { userSMPServersStr = it }, cancelEdit = { val userSMPServers = chatModel.userSMPServers.value if (userSMPServers != null) { isUserSMPServers = userSMPServers.isNotEmpty() editSMPServers = !isUserSMPServers - userSMPServersStr = if (isUserSMPServers) userSMPServers.joinToString(separator = "\n") else "" + userSMPServersStr.value = if (isUserSMPServers) userSMPServers.joinToString(separator = "\n") else "" } }, saveSMPServers = { saveSMPServers(it) }, @@ -95,9 +90,8 @@ fun SMPServersView(chatModel: ChatModel) { fun SMPServersLayout( isUserSMPServers: Boolean, editSMPServers: Boolean, - userSMPServersStr: String, + userSMPServersStr: MutableState, isUserSMPServersOnOff: (Boolean) -> Unit, - editUserSMPServersStr: (String) -> Unit, cancelEdit: () -> Unit, saveSMPServers: (List) -> Unit, editOn: () -> Unit, @@ -131,39 +125,7 @@ fun SMPServersLayout( } else { Text(generalGetString(R.string.enter_one_SMP_server_per_line)) if (editSMPServers) { - BasicTextField( - value = userSMPServersStr, - onValueChange = editUserSMPServersStr, - textStyle = TextStyle( - fontFamily = FontFamily.Monospace, fontSize = 14.sp, - color = MaterialTheme.colors.onBackground - ), - keyboardOptions = KeyboardOptions.Default.copy( - capitalization = KeyboardCapitalization.None, - autoCorrect = false - ), - modifier = Modifier.height(160.dp), - cursorBrush = SolidColor(HighOrLowlight), - decorationBox = { innerTextField -> - Surface( - shape = RoundedCornerShape(10.dp), - border = BorderStroke(1.dp, MaterialTheme.colors.secondary) - ) { - Row( - Modifier.background(MaterialTheme.colors.background), - verticalAlignment = Alignment.Top - ) { - Box( - Modifier - .weight(1f) - .padding(vertical = 5.dp, horizontal = 7.dp) - ) { - innerTextField() - } - } - } - } - ) + TextEditor(Modifier.height(160.dp), text = userSMPServersStr) Row( Modifier.fillMaxWidth(), @@ -183,7 +145,7 @@ fun SMPServersLayout( generalGetString(R.string.save_servers_button), color = MaterialTheme.colors.primary, modifier = Modifier.clickable(onClick = { - val servers = userSMPServersStr.split("\n") + val servers = userSMPServersStr.value.split("\n") saveSMPServers(servers) }) ) @@ -205,7 +167,7 @@ fun SMPServersLayout( Modifier.verticalScroll(rememberScrollState()) ) { Text( - userSMPServersStr, + userSMPServersStr.value, Modifier .padding(vertical = 5.dp, horizontal = 7.dp), style = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 14.sp), @@ -256,9 +218,8 @@ fun PreviewSMPServersLayoutDefaultServers() { SMPServersLayout( isUserSMPServers = false, editSMPServers = true, - userSMPServersStr = "", + userSMPServersStr = remember { mutableStateOf("") }, isUserSMPServersOnOff = {}, - editUserSMPServersStr = {}, cancelEdit = {}, saveSMPServers = {}, editOn = {}, @@ -273,9 +234,8 @@ fun PreviewSMPServersLayoutUserServersEditOn() { SMPServersLayout( isUserSMPServers = true, editSMPServers = true, - userSMPServersStr = "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im\nsmp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im", + userSMPServersStr = remember { mutableStateOf("smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im\nsmp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im") }, isUserSMPServersOnOff = {}, - editUserSMPServersStr = {}, cancelEdit = {}, saveSMPServers = {}, editOn = {}, @@ -290,9 +250,8 @@ fun PreviewSMPServersLayoutUserServersEditOff() { SMPServersLayout( isUserSMPServers = true, editSMPServers = false, - userSMPServersStr = "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im\nsmp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im", + userSMPServersStr = remember { mutableStateOf("smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im\nsmp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im") }, isUserSMPServersOnOff = {}, - editUserSMPServersStr = {}, cancelEdit = {}, saveSMPServers = {}, editOn = {}, diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml index f4a36949b7..d5031bfd54 100644 --- a/apps/android/app/src/main/res/values-ru/strings.xml +++ b/apps/android/app/src/main/res/values-ru/strings.xml @@ -98,13 +98,14 @@ нет описания Добавить контакт - Сканировать QR код - (создать QR код или ссылку) + Создать одноразовую ссылку + Вставить полученную ссылку + Сканировать QR код + (чтобы отправить вашему контакту) (при встрече или через видеозвонок) - Создать группу - (скоро!) + (вставить ссылку из буфера обмена) Разрешение не получено! @@ -166,6 +167,8 @@ Ваш профиль будет отправлен\nвашему контакту Если вы не можете встретиться лично, вы можете сосканировать QR код во время видеозвонка, или ваш контакт может отправить вам ссылку. Поделиться ссылкой + Чтобы соединиться, вставьте в это поле ссылку, полученную от вашего контакта.. + Настройки @@ -227,5 +230,9 @@ a + b цвет секрет + Соединиться через ссылку + Эта строка не является ссылкой-приглашением! + Вы также можете соединиться, открыв ссылку там, где вы её получили. Если ссылка откроется в браузере, нажмите кнопку Open in mobile app. + Добавьте контакт, чтобы начать разговор: diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index cbc0bd7183..4307d757f5 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -98,13 +98,15 @@ Ok no details Add contact - Scan QR code - (create QR code\nor link) + Add contact to start a new chat: + Create link / QR code + Connect via received link + Scan QR code + (to share with your contact) (in person or in video call) - Create Group - (coming soon!) + (paste link from clipboard) Permission Denied! @@ -166,6 +168,12 @@ Your chat profile will be sent\nto your contact If you cannot meet in person, you can scan QR code in the video call, or your contact can share an invitation link. Share invitation link + Paste the link you received into the box below to connect with your contact. + + + Connect via link + This string is not a connection link! + You can also connect by clicking the link. If it opens in the browser, click Open in mobile app button. Your settings diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 85438dd285..01c9c68b05 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -496,42 +496,42 @@ func apiAddContact() throws -> String { throw r } -func apiConnect(connReq: String) async throws -> Bool { +func apiConnect(connReq: String) async throws -> ConnReqType? { let r = await chatSendCmd(.connect(connReq: connReq)) let am = AlertManager.shared switch r { - case .sentConfirmation: return true - case .sentInvitation: return true + case .sentConfirmation: return .invitation + case .sentInvitation: return .contact case let .contactAlreadyExists(contact): am.showAlertMsg( title: "Contact already exists", message: "You are already connected to \(contact.displayName) via this link." ) - return false + return nil case .chatCmdError(.error(.invalidConnReq)): am.showAlertMsg( title: "Invalid connection link", message: "Please check that you used the correct link or ask your contact to send you another one." ) - return false + return nil case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))): am.showAlertMsg( title: "Connection timeout", message: "Please check your network connection and try again." ) - return false + return nil case .chatCmdError(.errorAgent(.BROKER(.NETWORK))): am.showAlertMsg( title: "Connection error", message: "Please check your network connection and try again." ) - return false + return nil case .chatCmdError(.errorAgent(.SMP(.AUTH))): am.showAlertMsg( title: "Connection error (AUTH)", message: "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." ) - return false + return nil default: throw r } } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index e220882494..20b513315a 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -78,23 +78,12 @@ struct ChatListView: View { let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)") let title: LocalizedStringKey if case .contact = action { title = "Connect via contact link?" } - else { title = "Connect via invitation link?" } + else { title = "Connect via one-time link?" } return Alert( title: Text(title), message: Text("Your profile will be sent to the contact that you received this link from"), primaryButton: .default(Text("Connect")) { - DispatchQueue.main.async { - Task { - do { - let ok = try await apiConnect(connReq: link) - if ok { connectionReqSentAlert(action) } - } catch { - let err = error.localizedDescription - AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(err)") - logger.debug("ChatListView.connectViaUrlAlert: apiConnect error: \(err)") - } - } - } + connectViaLink(link) }, secondaryButton: .cancel() ) diff --git a/apps/ios/Shared/Views/NewChat/AddContactView.swift b/apps/ios/Shared/Views/NewChat/AddContactView.swift index 9101e893f8..ecd3815fc4 100644 --- a/apps/ios/Shared/Views/NewChat/AddContactView.swift +++ b/apps/ios/Shared/Views/NewChat/AddContactView.swift @@ -11,7 +11,6 @@ import CoreImage.CIFilterBuiltins struct AddContactView: View { var connReqInvitation: String - var body: some View { VStack { Text("Add contact") diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift index 83f7fd85fe..ad0a133a56 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift @@ -12,26 +12,27 @@ struct NewChatButton: View { @State private var showAddChat = false @State private var addContact = false @State private var connReqInvitation: String = "" - @State private var connectContact = false - @State private var createGroup = false + @State private var scanToConnect = false + @State private var pasteToConnect = false var body: some View { Button { showAddChat = true } label: { Image(systemName: "person.crop.circle.badge.plus") } - .confirmationDialog("Start new chat", isPresented: $showAddChat, titleVisibility: .visible) { - Button("Add contact") { addContactAction() } - Button("Scan QR code") { connectContact = true } - Button("Create group") { createGroup = true } - .disabled(true) + .confirmationDialog("Add contact to start a new chat", isPresented: $showAddChat, titleVisibility: .visible) { + Button("Create link / QR code") { addContactAction() } + Button("Paste received link") { pasteToConnect = true } + Button("Scan QR code") { scanToConnect = true } } .sheet(isPresented: $addContact, content: { AddContactView(connReqInvitation: connReqInvitation) }) - .sheet(isPresented: $connectContact, content: { - connectContactSheet() + .sheet(isPresented: $scanToConnect, content: { + ScanToConnectView(openedSheet: $scanToConnect) + }) + .sheet(isPresented: $pasteToConnect, content: { + PasteToConnectView(openedSheet: $pasteToConnect) }) - .sheet(isPresented: $createGroup, content: { CreateGroupView() }) } func addContactAction() { @@ -45,28 +46,6 @@ struct NewChatButton: View { logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)") } } - - func addContactSheet() -> some View { - AddContactView(connReqInvitation: connReqInvitation) - } - - func connectContactSheet() -> some View { - ConnectContactView(completed: { err in - connectContact = false - DispatchQueue.global().async { - switch (err) { - case let .success(ok): - if ok { connectionReqSentAlert(.invitation) } - case let .failure(error): - connectionErrorAlert(error) - } - } - }) - } - - func connectionErrorAlert(_ error: Error) { - AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)") - } } enum ConnReqType: Equatable { @@ -74,6 +53,30 @@ enum ConnReqType: Equatable { case invitation } +func connectViaLink(_ connectionLink: String, _ openedSheet: Binding? = nil) { + Task { + do { + let res = try await apiConnect(connReq: connectionLink) + DispatchQueue.main.async { + openedSheet?.wrappedValue = false + if let connReqType = res { + connectionReqSentAlert(connReqType) + } + } + } catch { + logger.error("connectViaLink apiConnect error: \(responseError(error))") + DispatchQueue.main.async { + openedSheet?.wrappedValue = false + connectionErrorAlert(error) + } + } + } +} + +func connectionErrorAlert(_ error: Error) { + AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)") +} + func connectionReqSentAlert(_ type: ConnReqType) { AlertManager.shared.showAlertMsg( title: "Connection request sent!", diff --git a/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift b/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift new file mode 100644 index 0000000000..e3f9f354ab --- /dev/null +++ b/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift @@ -0,0 +1,78 @@ +// +// PasteToConnectView.swift +// SimpleX (iOS) +// +// Created by Ian Davies on 22/04/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct PasteToConnectView: View { + @Binding var openedSheet: Bool + @State private var connectionLink: String = "" + + var body: some View { + VStack(alignment: .leading) { + Text("Connect via link") + .font(.title) + .padding([.bottom]) + .frame(maxWidth: .infinity, alignment: .center) + Text("Paste the link you received into the box below to connect with your contact.") + .multilineTextAlignment(.leading) + Text("Your profile will be sent to the contact that you received this link from") + .multilineTextAlignment(.leading) + .padding(.bottom) + TextEditor(text: $connectionLink) + .onSubmit(connect) + .font(.body) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .allowsTightening(false) + .frame(height: 180) + .overlay( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(.secondary, lineWidth: 0.3, antialiased: true) + ) + + HStack(spacing: 20) { + if connectionLink == "" { + Button { + connectionLink = UIPasteboard.general.string ?? "" + } label: { + Label("Paste", systemImage: "doc.on.clipboard") + } + } else { + Button { + connectionLink = "" + } label: { + Label("Clear", systemImage: "multiply") + } + + } + Spacer() + Button(action: connect, label: { + Label("Connect", systemImage: "link") + }) + .disabled(connectionLink == "" || connectionLink.trimmingCharacters(in: .whitespaces).firstIndex(of: " ") != nil) + } + .frame(height: 48) + .padding(.bottom) + + Text("You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button") + .multilineTextAlignment(.leading) + } + .padding() + } + + private func connect() { + connectViaLink(connectionLink.trimmingCharacters(in: .whitespaces), $openedSheet) + } +} + +struct PasteToConnectView_Previews: PreviewProvider { + static var previews: some View { + @State var openedSheet: Bool = true + return PasteToConnectView(openedSheet: $openedSheet) + } +} diff --git a/apps/ios/Shared/Views/NewChat/ConnectContactView.swift b/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift similarity index 71% rename from apps/ios/Shared/Views/NewChat/ConnectContactView.swift rename to apps/ios/Shared/Views/NewChat/ScanToConnectView.swift index 9944dc21fb..aed53a2d5b 100644 --- a/apps/ios/Shared/Views/NewChat/ConnectContactView.swift +++ b/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift @@ -9,8 +9,8 @@ import SwiftUI import CodeScanner -struct ConnectContactView: View { - var completed: ((Result) -> Void) +struct ScanToConnectView: View { + @Binding var openedSheet: Bool var body: some View { VStack { @@ -37,24 +37,17 @@ struct ConnectContactView: View { func processQRCode(_ resp: Result) { switch resp { case let .success(r): - Task { - do { - let ok = try await apiConnect(connReq: r.string) - completed(.success(ok)) - } catch { - logger.error("ConnectContactView.processQRCode apiConnect error: \(error.localizedDescription)") - completed(.failure(error)) - } - } + Task { connectViaLink(r.string, $openedSheet) } case let .failure(e): logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)") - completed(.failure(e)) + openedSheet = false } } } struct ConnectContactView_Previews: PreviewProvider { static var previews: some View { - return ConnectContactView(completed: {_ in }) + @State var openedSheet: Bool = true + return ScanToConnectView(openedSheet: $openedSheet) } } 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 fc792ef930..2744ebdc2c 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -115,6 +115,11 @@ Add contact No comment provided by engineer. + + Add contact to start a new chat + Add contact to start a new chat + No comment provided by engineer. + All your contacts will remain connected All your contacts will remain connected @@ -160,6 +165,11 @@ Choose from library No comment provided by engineer. + + Clear + Clear + No comment provided by engineer. + Configure SMP servers Configure SMP servers @@ -180,9 +190,14 @@ Connect via contact link? No comment provided by engineer. - - Connect via invitation link? - Connect via invitation link? + + Connect via link + Connect via link + No comment provided by engineer. + + + Connect via one-time link? + Connect via one-time link? No comment provided by engineer. @@ -260,9 +275,9 @@ Create address No comment provided by engineer. - - Create group - Create group + + Create link / QR code + Create link / QR code No comment provided by engineer. @@ -470,6 +485,21 @@ Open Settings No comment provided by engineer. + + Paste + Paste + No comment provided by engineer. + + + Paste received link + Paste received link + No comment provided by engineer. + + + Paste the link you received into the box below to connect with your contact. + Paste the link you received into the box below to connect with your contact. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Please check that you used the correct link or ask your contact to send you another one. @@ -567,11 +597,6 @@ to scan from the app to scan from the app No comment provided by engineer. - - Start new chat - Start new chat - No comment provided by engineer. - Take picture Take picture @@ -676,6 +701,11 @@ To connect, please ask your contact to create another connection link and check You are connected to the server used to receive messages from this contact. No comment provided by engineer. + + You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button + You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button + No comment provided by engineer. + You can now send messages to %@ You can now send messages to %@ 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 e90ec57cfb..643507ec7d 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -115,6 +115,11 @@ Добавить контакт No comment provided by engineer. + + Add contact to start a new chat + Добавьте контакт, чтобы начать разговор + No comment provided by engineer. + All your contacts will remain connected Все контакты, которые соединились через этот адрес, сохранятся. @@ -160,6 +165,11 @@ Выбрать из библиотеки No comment provided by engineer. + + Clear + Очистить + No comment provided by engineer. + Configure SMP servers Настройка SMP серверов @@ -180,9 +190,14 @@ Соединиться через ссылку-контакт? No comment provided by engineer. - - Connect via invitation link? - Соединиться через ссылку-приглашение? + + Connect via link + Соединиться через ссылку + No comment provided by engineer. + + + Connect via one-time link? + Соединиться через одноразовую ссылку? No comment provided by engineer. @@ -260,9 +275,9 @@ Создать адрес No comment provided by engineer. - - Create group - Создать группу + + Create link / QR code + Создать ссылку / QR код No comment provided by engineer. @@ -470,6 +485,21 @@ Открыть Настройки No comment provided by engineer. + + Paste + Вставить + No comment provided by engineer. + + + Paste received link + Вставить полученную ссылку + No comment provided by engineer. + + + Paste the link you received into the box below to connect with your contact. + Чтобы соединиться, вставьте в это поле ссылку, полученную от вашего контакта. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку. @@ -566,11 +596,6 @@ to scan from the app Покажите QR код вашему контакту для сканирования в приложении No comment provided by engineer. - - Start new chat - Начать новый разговор - No comment provided by engineer. - Take picture Сделать фото @@ -675,6 +700,11 @@ To connect, please ask your contact to create another connection link and check Установлено соединение с сервером, через который вы получаете сообщения от этого контакта. No comment provided by engineer. + + You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button + Вы также можете соединиться, открыв ссылку там, где вы её получили. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**. + No comment provided by engineer. + You can now send messages to %@ Вы теперь можете отправлять сообщения %@ diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index d8ee08ef96..953e7abd55 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3C8C548928133C84000A3EC7 /* PasteToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */; }; 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; }; 3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; }; 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; }; @@ -59,7 +60,7 @@ 5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; }; 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; }; 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; - 5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; }; + 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; }; 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; 5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407127ADB1D0007B033A /* Emoji.swift */; }; 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; }; @@ -84,6 +85,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteToConnectView.swift; sourceTree = ""; }; 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = ""; }; 3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = ""; }; 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = ""; }; @@ -139,7 +141,7 @@ 5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; - 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectContactView.swift; sourceTree = ""; }; + 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = ""; }; 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = ""; }; 5CE4407127ADB1D0007B033A /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = ""; }; 5CE4407827ADB701007B033A /* EmojiItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItemView.swift; sourceTree = ""; }; @@ -311,7 +313,8 @@ children = ( 5C6AD81227A834E300348BD7 /* NewChatButton.swift */, 5CCD403327A5F6DF00368C90 /* AddContactView.swift */, - 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */, + 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */, + 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */, 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */, 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */, ); @@ -491,6 +494,7 @@ 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */, 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */, 5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */, + 3C8C548928133C84000A3EC7 /* PasteToConnectView.swift in Sources */, 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, @@ -511,7 +515,7 @@ 5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */, 5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */, 5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */, - 5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */, + 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */, 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */, diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 422d019a1f..1752f798ea 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -85,6 +85,9 @@ /* No comment provided by engineer. */ "Add contact" = "Добавить контакт"; +/* No comment provided by engineer. */ +"Add contact to start a new chat" = "Добавьте контакт, чтобы начать разговор"; + /* No comment provided by engineer. */ "All your contacts will remain connected" = "Все контакты, которые соединились через этот адрес, сохранятся."; @@ -115,6 +118,9 @@ /* No comment provided by engineer. */ "Choose from library" = "Выбрать из библиотеки"; +/* No comment provided by engineer. */ +"Clear" = "Очистить"; + /* No comment provided by engineer. */ "colored" = "цвет"; @@ -134,7 +140,10 @@ "Connect via contact link?" = "Соединиться через ссылку-контакт?"; /* No comment provided by engineer. */ -"Connect via invitation link?" = "Соединиться через ссылку-приглашение?"; +"Connect via link" = "Соединиться через ссылку"; + +/* No comment provided by engineer. */ +"Connect via one-time link?" = "Соединиться через одноразовую ссылку?"; /* No comment provided by engineer. */ "Connecting server…" = "Устанавливается соединение с сервером…"; @@ -182,7 +191,7 @@ "Create address" = "Создать адрес"; /* No comment provided by engineer. */ -"Create group" = "Создать группу"; +"Create link / QR code" = "Создать ссылку / QR код"; /* No comment provided by engineer. */ "Create profile" = "Создать профиль"; @@ -313,6 +322,15 @@ /* No comment provided by engineer. */ "Open Settings" = "Открыть Настройки"; +/* No comment provided by engineer. */ +"Paste" = "Вставить"; + +/* No comment provided by engineer. */ +"Paste received link" = "Вставить полученную ссылку"; + +/* No comment provided by engineer. */ +"Paste the link you received into the box below to connect with your contact." = "Чтобы соединиться, вставьте в это поле ссылку, полученную от вашего контакта."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку."; @@ -373,9 +391,6 @@ /* No comment provided by engineer. */ "SMP servers" = "SMP серверы"; -/* No comment provided by engineer. */ -"Start new chat" = "Начать новый разговор"; - /* No comment provided by engineer. */ "strike" = "зачеркнуть"; @@ -445,6 +460,9 @@ /* No comment provided by engineer. */ "You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который вы получаете сообщения от этого контакта."; +/* No comment provided by engineer. */ +"You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button" = "Вы также можете соединиться, открыв ссылку там, где вы её получили. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**."; + /* notification body */ "You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@";