diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
index c7bf7635a9..68893ebef6 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
@@ -33,8 +33,7 @@ import chat.simplex.app.views.chat.ChatView
import chat.simplex.app.views.chatlist.*
import chat.simplex.app.views.database.DatabaseErrorView
import chat.simplex.app.views.helpers.*
-import chat.simplex.app.views.newchat.connectViaUri
-import chat.simplex.app.views.newchat.withUriAction
+import chat.simplex.app.views.newchat.*
import chat.simplex.app.views.onboarding.*
import kotlinx.coroutines.delay
@@ -426,23 +425,23 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) {
// TODO open from chat list view
chatModel.appOpenUrl.value = uri
} else {
- withUriAction(uri) { action ->
- val title = when (action) {
- "contact" -> generalGetString(R.string.connect_via_contact_link)
- "invitation" -> generalGetString(R.string.connect_via_invitation_link)
- else -> {
- Log.e(TAG, "URI has unexpected action. Alert shown.")
- action
- }
+ withUriAction(uri) { linkType ->
+ val title = when (linkType) {
+ ConnectionLinkType.CONTACT -> generalGetString(R.string.connect_via_contact_link)
+ ConnectionLinkType.INVITATION -> generalGetString(R.string.connect_via_invitation_link)
+ ConnectionLinkType.GROUP -> generalGetString(R.string.connect_via_group_link)
}
AlertManager.shared.showAlertMsg(
title = title,
- text = generalGetString(R.string.profile_will_be_sent_to_contact_sending_link),
+ text = if (linkType == ConnectionLinkType.GROUP)
+ generalGetString(R.string.you_will_join_group)
+ else
+ generalGetString(R.string.profile_will_be_sent_to_contact_sending_link),
confirmText = generalGetString(R.string.connect_via_link_verb),
onConfirm = {
withApi {
Log.d(TAG, "connectIfOpenedViaUri: connecting")
- connectViaUri(chatModel, action, uri)
+ connectViaUri(chatModel, linkType, uri)
}
}
)
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
index 9554d8a991..f231195b0e 100644
--- 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
@@ -3,6 +3,7 @@ package chat.simplex.app.views.newchat
import android.content.ClipboardManager
import android.content.res.Configuration
import android.net.Uri
+import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -17,6 +18,7 @@ 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.TAG
import chat.simplex.app.model.ChatModel
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
@@ -35,10 +37,21 @@ fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) {
connectViaLink = { connReqUri ->
try {
val uri = Uri.parse(connReqUri)
- withUriAction(uri) { action ->
- if (connectViaUri(chatModel, action, uri)) {
- close()
+ withUriAction(uri) { linkType ->
+ val action = suspend {
+ Log.d(TAG, "connectViaUri: connecting")
+ if (connectViaUri(chatModel, linkType, uri)) {
+ close()
+ }
}
+ if (linkType == ConnectionLinkType.GROUP) {
+ AlertManager.shared.showAlertMsg(
+ title = generalGetString(R.string.connect_via_group_link),
+ text = generalGetString(R.string.you_will_join_group),
+ confirmText = generalGetString(R.string.connect_via_link_verb),
+ onConfirm = { withApi { action() } }
+ )
+ } else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt
index 5d22090c27..eb7558dbf5 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt
@@ -3,6 +3,7 @@ package chat.simplex.app.views.newchat
import android.Manifest
import android.content.res.Configuration
import android.net.Uri
+import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -14,12 +15,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.core.net.toUri
import chat.simplex.app.R
+import chat.simplex.app.TAG
import chat.simplex.app.model.ChatModel
+import chat.simplex.app.model.json
import chat.simplex.app.ui.theme.DEFAULT_PADDING
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.helpers.*
import com.google.accompanist.permissions.rememberPermissionState
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
@Composable
fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
@@ -33,10 +39,21 @@ fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
QRCodeScanner { connReqUri ->
try {
val uri = Uri.parse(connReqUri)
- withUriAction(uri) { action ->
- if (connectViaUri(chatModel, action, uri)) {
- close()
+ withUriAction(uri) { linkType ->
+ val action = suspend {
+ Log.d(TAG, "connectViaUri: connecting")
+ if (connectViaUri(chatModel, linkType, uri)) {
+ close()
+ }
}
+ if (linkType == ConnectionLinkType.GROUP) {
+ AlertManager.shared.showAlertMsg(
+ title = generalGetString(R.string.connect_via_group_link),
+ text = generalGetString(R.string.you_will_join_group),
+ confirmText = generalGetString(R.string.connect_via_link_verb),
+ onConfirm = { withApi { action() } }
+ )
+ } else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
@@ -49,10 +66,34 @@ fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
)
}
-fun withUriAction(uri: Uri, run: suspend (String) -> Unit) {
+enum class ConnectionLinkType {
+ CONTACT, INVITATION, GROUP
+}
+
+@Serializable
+sealed class CReqClientData {
+ @Serializable @SerialName("group") data class Group(val groupLinkId: String): CReqClientData()
+}
+
+fun withUriAction(uri: Uri, run: suspend (ConnectionLinkType) -> Unit) {
val action = uri.path?.drop(1)?.replace("/", "")
- if (action == "contact" || action == "invitation") {
- withApi { run(action) }
+ val data = uri.toString().replaceFirst("#/", "/").toUri().getQueryParameter("data")
+ val type = when {
+ data != null -> {
+ val parsed = runCatching {
+ json.decodeFromString(CReqClientData.serializer(), data)
+ }
+ when {
+ parsed.getOrNull() is CReqClientData.Group -> ConnectionLinkType.GROUP
+ else -> null
+ }
+ }
+ action == "contact" -> ConnectionLinkType.CONTACT
+ action == "invitation" -> ConnectionLinkType.INVITATION
+ else -> null
+ }
+ if (type != null) {
+ withApi { run(type) }
} else {
AlertManager.shared.showAlertMsg(
title = generalGetString(R.string.invalid_contact_link),
@@ -61,14 +102,17 @@ fun withUriAction(uri: Uri, run: suspend (String) -> Unit) {
}
}
-suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri): Boolean {
+suspend fun connectViaUri(chatModel: ChatModel, action: ConnectionLinkType, uri: Uri): Boolean {
val r = chatModel.controller.apiConnect(uri.toString())
if (r) {
AlertManager.shared.showAlertMsg(
title = generalGetString(R.string.connection_request_sent),
text =
- if (action == "contact") generalGetString(R.string.you_will_be_connected_when_your_connection_request_is_accepted)
- else generalGetString(R.string.you_will_be_connected_when_your_contacts_device_is_online)
+ when (action) {
+ ConnectionLinkType.CONTACT -> generalGetString(R.string.you_will_be_connected_when_your_connection_request_is_accepted)
+ ConnectionLinkType.INVITATION -> generalGetString(R.string.you_will_be_connected_when_your_contacts_device_is_online)
+ ConnectionLinkType.GROUP -> generalGetString(R.string.you_will_be_connected_when_group_host_device_is_online)
+ }
)
}
return r
diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml
index ce89176602..5c2011f2bb 100644
--- a/apps/android/app/src/main/res/values-de/strings.xml
+++ b/apps/android/app/src/main/res/values-de/strings.xml
@@ -6,7 +6,9 @@
Über den Kontakt-Link verbinden?
Über den Einladungs-Link verbinden?
+ Über den Gruppen-Link verbinden?
Ihr Profil wird an den Kontakt gesendet von dem Sie diesen Link erhalten haben.
+ Sie werden der Gruppe beitreten, auf die sich dieser Link bezieht und sich mit deren Gruppenmitgliedern verbinden.
Verbinden
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 7c00d2daa7..da0397cc9b 100644
--- a/apps/android/app/src/main/res/values-ru/strings.xml
+++ b/apps/android/app/src/main/res/values-ru/strings.xml
@@ -6,7 +6,9 @@
Соединиться через ссылку-контакт?
Соединиться через ссылку-приглашение?
+ Соединиться через ссылку группы?
Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку.
+ Вы вступите в группу, на которую ссылается эта ссылка.
Соединиться
diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml
index 3cdd934816..b1e492ea7d 100644
--- a/apps/android/app/src/main/res/values/strings.xml
+++ b/apps/android/app/src/main/res/values/strings.xml
@@ -6,7 +6,9 @@
Connect via contact link?
Connect via invitation link?
+ Connect via group link?
Your profile will be sent to the contact that you received this link from.
+ You will join a group this link refers to and connect to its group members.
Connect
diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift
index ff6b94dffd..bdf8f9b02e 100644
--- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift
+++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift
@@ -78,25 +78,25 @@ func connectViaLink(_ connectionLink: String, _ dismiss: DismissAction? = nil) {
}
}
-struct CRData: Decodable {
+struct CReqClientData: Decodable {
var type: String
var groupLinkId: String?
}
-func parseLinkQueryData(_ connectionLink: String) -> CRData? {
+func parseLinkQueryData(_ connectionLink: String) -> CReqClientData? {
if let hashIndex = connectionLink.firstIndex(of: "#"),
let urlQuery = URL(string: String(connectionLink[connectionLink.index(after: hashIndex)...])),
let components = URLComponents(url: urlQuery, resolvingAgainstBaseURL: false),
let data = components.queryItems?.first(where: { $0.name == "data" })?.value,
let d = data.data(using: .utf8),
- let crData = try? getJSONDecoder().decode(CRData.self, from: d) {
+ let crData = try? getJSONDecoder().decode(CReqClientData.self, from: d) {
return crData
} else {
return nil
}
}
-func checkCRDataGroup(_ crData: CRData) -> Bool {
+func checkCRDataGroup(_ crData: CReqClientData) -> Bool {
return crData.type == "group" && crData.groupLinkId != nil
}