From e3e9ae2ffded462680283ff4e8198e8e533d9871 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 17 Jun 2025 12:34:51 +0100 Subject: [PATCH] core, ui: create all links with short links, config parameter to use large link data, use short link as address in user profile (#5991) Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> --- apps/ios/Shared/Model/AppAPITypes.swift | 16 +- apps/ios/Shared/Model/SimpleXAPI.swift | 12 +- .../Views/Chat/Group/GroupLinkView.swift | 24 +- .../Onboarding/CreateSimpleXAddress.swift | 5 +- .../Views/UserSettings/PrivacySettings.swift | 6 - .../Views/UserSettings/SettingsView.swift | 2 - .../Views/UserSettings/UserAddressView.swift | 15 +- apps/ios/de.lproj/Localizable.strings | 3 - apps/ios/es.lproj/Localizable.strings | 3 - apps/ios/hu.lproj/Localizable.strings | 3 - apps/ios/it.lproj/Localizable.strings | 3 - apps/ios/nl.lproj/Localizable.strings | 3 - apps/ios/ru.lproj/Localizable.strings | 3 - .../chat/simplex/common/model/SimpleXAPI.kt | 30 ++- .../common/views/chat/group/GroupLinkView.kt | 2 +- .../views/usersettings/PrivacySettings.kt | 7 - .../views/usersettings/UserAddressView.kt | 5 +- .../commonMain/resources/MR/ar/strings.xml | 1 - .../commonMain/resources/MR/base/strings.xml | 1 - .../commonMain/resources/MR/ca/strings.xml | 1 - .../commonMain/resources/MR/cs/strings.xml | 1 - .../commonMain/resources/MR/de/strings.xml | 1 - .../commonMain/resources/MR/es/strings.xml | 1 - .../commonMain/resources/MR/hu/strings.xml | 1 - .../commonMain/resources/MR/in/strings.xml | 3 +- .../commonMain/resources/MR/it/strings.xml | 1 - .../commonMain/resources/MR/nl/strings.xml | 1 - .../commonMain/resources/MR/ro/strings.xml | 1 - .../commonMain/resources/MR/ru/strings.xml | 1 - .../commonMain/resources/MR/uk/strings.xml | 1 - .../commonMain/resources/MR/vi/strings.xml | 1 - .../resources/MR/zh-rCN/strings.xml | 1 - .../src/Directory/Service.hs | 6 +- src/Simplex/Chat.hs | 4 +- src/Simplex/Chat/Bot.hs | 7 +- src/Simplex/Chat/Controller.hs | 17 +- src/Simplex/Chat/Library/Commands.hs | 113 +++++----- src/Simplex/Chat/Store/Groups.hs | 8 +- src/Simplex/Chat/Store/Profiles.hs | 35 ++- .../SQLite/Migrations/chat_query_plans.txt | 12 + src/Simplex/Chat/Types.hs | 2 - tests/ChatClient.hs | 12 +- tests/ChatTests/Direct.hs | 44 ++-- tests/ChatTests/Groups.hs | 14 +- tests/ChatTests/Profiles.hs | 210 ++++++++++++------ tests/ChatTests/Utils.hs | 93 +++++--- 46 files changed, 413 insertions(+), 323 deletions(-) diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift index 209cf6c1e0..c0ba72b1fc 100644 --- a/apps/ios/Shared/Model/AppAPITypes.swift +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -23,7 +23,7 @@ enum ChatCommand: ChatCmdProtocol { case apiMuteUser(userId: Int64) case apiUnmuteUser(userId: Int64) case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?) - case startChat(mainApp: Bool, enableSndFiles: Bool) + case startChat(mainApp: Bool, enableSndFiles: Bool, largeLinkData: Bool) case checkChatRunning case apiStopChat case apiActivateChat(restoreChat: Bool) @@ -76,7 +76,7 @@ enum ChatCommand: ChatCmdProtocol { case apiLeaveGroup(groupId: Int64) case apiListMembers(groupId: Int64) case apiUpdateGroupProfile(groupId: Int64, groupProfile: GroupProfile) - case apiCreateGroupLink(groupId: Int64, memberRole: GroupMemberRole, short: Bool) + case apiCreateGroupLink(groupId: Int64, memberRole: GroupMemberRole) case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole) case apiDeleteGroupLink(groupId: Int64) case apiGetGroupLink(groupId: Int64) @@ -116,7 +116,7 @@ enum ChatCommand: ChatCmdProtocol { case apiGetGroupMemberCode(groupId: Int64, groupMemberId: Int64) case apiVerifyContact(contactId: Int64, connectionCode: String?) case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?) - case apiAddContact(userId: Int64, short: Bool, incognito: Bool) + case apiAddContact(userId: Int64, incognito: Bool) case apiSetConnectionIncognito(connId: Int64, incognito: Bool) case apiChangeConnectionUser(connId: Int64, userId: Int64) case apiConnectPlan(userId: Int64, connLink: String) @@ -138,7 +138,7 @@ enum ChatCommand: ChatCmdProtocol { case apiSetConnectionAlias(connId: Int64, localAlias: String) case apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) case apiSetChatUIThemes(chatId: String, themes: ThemeModeOverrides?) - case apiCreateMyAddress(userId: Int64, short: Bool) + case apiCreateMyAddress(userId: Int64) case apiDeleteMyAddress(userId: Int64) case apiShowMyAddress(userId: Int64) case apiAddMyAddressShortLink(userId: Int64) @@ -203,7 +203,7 @@ enum ChatCommand: ChatCmdProtocol { case let .apiMuteUser(userId): return "/_mute user \(userId)" case let .apiUnmuteUser(userId): return "/_unmute user \(userId)" case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))" - case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))" + case let .startChat(mainApp, enableSndFiles, largeLinkData): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles)) large_link_data=\(onOff(largeLinkData))" case .checkChatRunning: return "/_check running" case .apiStopChat: return "/_stop" case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" @@ -266,7 +266,7 @@ enum ChatCommand: ChatCmdProtocol { case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)" case let .apiListMembers(groupId): return "/_members #\(groupId)" case let .apiUpdateGroupProfile(groupId, groupProfile): return "/_group_profile #\(groupId) \(encodeJSON(groupProfile))" - case let .apiCreateGroupLink(groupId, memberRole, short): return "/_create link #\(groupId) \(memberRole) short=\(onOff(short))" + case let .apiCreateGroupLink(groupId, memberRole): return "/_create link #\(groupId) \(memberRole)" case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)" case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)" case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" @@ -316,7 +316,7 @@ enum ChatCommand: ChatCmdProtocol { case let .apiVerifyContact(contactId, .none): return "/_verify code @\(contactId)" case let .apiVerifyGroupMember(groupId, groupMemberId, .some(connectionCode)): return "/_verify code #\(groupId) \(groupMemberId) \(connectionCode)" case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)" - case let .apiAddContact(userId, short, incognito): return "/_connect \(userId) short=\(onOff(short)) incognito=\(onOff(incognito))" + case let .apiAddContact(userId, incognito): return "/_connect \(userId) incognito=\(onOff(incognito))" case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))" case let .apiChangeConnectionUser(connId, userId): return "/_set conn user :\(connId) \(userId)" case let .apiConnectPlan(userId, connLink): return "/_connect plan \(userId) \(connLink)" @@ -338,7 +338,7 @@ enum ChatCommand: ChatCmdProtocol { case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))" case let .apiSetUserUIThemes(userId, themes): return "/_set theme user \(userId) \(themes != nil ? encodeJSON(themes) : "")" case let .apiSetChatUIThemes(chatId, themes): return "/_set theme \(chatId) \(themes != nil ? encodeJSON(themes) : "")" - case let .apiCreateMyAddress(userId, short): return "/_address \(userId) short=\(onOff(short))" + case let .apiCreateMyAddress(userId): return "/_address \(userId)" case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)" case let .apiShowMyAddress(userId): return "/_show_address \(userId)" case let .apiAddMyAddressShortLink(userId): return "/_short_link_address \(userId)" diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index ca2c77804d..7ad62c900c 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -227,7 +227,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn } func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool { - let r: ChatResponse0 = try chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl: ctrl) + let r: ChatResponse0 = try chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true, largeLinkData: false), ctrl: ctrl) switch r { case .chatStarted: return true case .chatRunning: return false @@ -874,8 +874,7 @@ func apiAddContact(incognito: Bool) async -> ((CreatedConnLink, PendingContactCo logger.error("apiAddContact: no current user") return (nil, nil) } - let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) - let r: APIResult = await chatApiSendCmd(.apiAddContact(userId: userId, short: short, incognito: incognito), bgTask: false) + let r: APIResult = await chatApiSendCmd(.apiAddContact(userId: userId, incognito: incognito), bgTask: false) if case let .result(.invitation(_, connLinkInv, connection)) = r { return ((connLinkInv, connection), nil) } let alert = connectionErrorAlert(r) return (nil, alert) @@ -1215,9 +1214,9 @@ func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) async -> Bo } -func apiCreateUserAddress(short: Bool) async throws -> CreatedConnLink { +func apiCreateUserAddress() async throws -> CreatedConnLink { let userId = try currentUserId("apiCreateUserAddress") - let r: ChatResponse1 = try await chatSendCmd(.apiCreateMyAddress(userId: userId, short: short)) + let r: ChatResponse1 = try await chatSendCmd(.apiCreateMyAddress(userId: userId)) if case let .userContactLinkCreated(_, connLink) = r { return connLink } throw r.unexpected } @@ -1770,8 +1769,7 @@ func apiUpdateGroup(_ groupId: Int64, _ groupProfile: GroupProfile) async throws } func apiCreateGroupLink(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> GroupLink { - let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) - let r: ChatResponse2 = try await chatSendCmd(.apiCreateGroupLink(groupId: groupId, memberRole: memberRole, short: short)) + let r: ChatResponse2 = try await chatSendCmd(.apiCreateGroupLink(groupId: groupId, memberRole: memberRole)) if case let .groupLinkCreated(_, _, groupLink) = r { return groupLink } throw r.unexpected } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift index ce52c70cab..7834d67abb 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift @@ -86,19 +86,17 @@ struct GroupLinkView: View { Label("Share link", systemImage: "square.and.arrow.up") } - if UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) { - if groupLink.connLinkContact.connShortLink == nil { - Button { - addShortLink() - } label: { - Label("Add short link", systemImage: "plus") - } - } else if !groupLink.shortLinkDataSet { - Button { - addShortLink() - } label: { - Label("Share group profile via link", systemImage: "plus") - } + if groupLink.connLinkContact.connShortLink == nil { + Button { + addShortLink() + } label: { + Label("Add short link", systemImage: "plus") + } + } else if !groupLink.shortLinkDataSet { + Button { + addShortLink() + } label: { + Label("Share group profile via link", systemImage: "plus") } } diff --git a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift index 3a58826371..a34015dd8f 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift @@ -77,10 +77,9 @@ struct CreateSimpleXAddress: View { progressIndicator = true Task { do { - let short = false - let connLinkContact = try await apiCreateUserAddress(short: short) + let connLinkContact = try await apiCreateUserAddress() DispatchQueue.main.async { - m.userAddress = UserContactLink(connLinkContact: connLinkContact, shortLinkDataSet: short) + m.userAddress = UserContactLink(connLinkContact: connLinkContact, shortLinkDataSet: connLinkContact.connShortLink != nil) } await MainActor.run { progressIndicator = false } } catch let error { diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift index eba7f8066a..0d34242569 100644 --- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift +++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift @@ -20,7 +20,6 @@ struct PrivacySettings: View { @AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true @State private var simplexLinkMode = privacySimplexLinkModeDefault.get() @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false - @AppStorage(DEFAULT_PRIVACY_SHORT_LINKS) private var shortSimplexLinks = false @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @State private var currentLAMode = privacyLocalAuthModeDefault.get() @@ -101,11 +100,6 @@ struct PrivacySettings: View { .onChange(of: simplexLinkMode) { mode in privacySimplexLinkModeDefault.set(mode) } - if developerTools { - settingsRow("link.badge.plus", color: theme.colors.secondary) { - Toggle("Use short links (BETA)", isOn: $shortSimplexLinks) - } - } } header: { Text("Chats") .foregroundColor(theme.colors.secondary) diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 50a012f4f8..cb6fdf8597 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -32,7 +32,6 @@ let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews" // deprecated, moved t let DEFAULT_PRIVACY_SIMPLEX_LINK_MODE = "privacySimplexLinkMode" let DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS = "privacyShowChatPreviews" let DEFAULT_PRIVACY_SAVE_LAST_DRAFT = "privacySaveLastDraft" -let DEFAULT_PRIVACY_SHORT_LINKS = "privacyShortLinks" let DEFAULT_PRIVACY_PROTECT_SCREEN = "privacyProtectScreen" let DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET = "privacyDeliveryReceiptsSet" let DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS = "privacyMediaBlurRadius" @@ -100,7 +99,6 @@ let appDefaults: [String: Any] = [ DEFAULT_PRIVACY_SIMPLEX_LINK_MODE: SimpleXLinkMode.description.rawValue, DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS: true, DEFAULT_PRIVACY_SAVE_LAST_DRAFT: true, - DEFAULT_PRIVACY_SHORT_LINKS: false, DEFAULT_PRIVACY_PROTECT_SCREEN: false, DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET: false, DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS: 0, diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index 1b0b11dd23..0535421f9f 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -153,12 +153,10 @@ struct UserAddressView: View { } } addressSettingsButton(userAddress) - if UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) { - if userAddress.connLinkContact.connShortLink == nil { - addShortLinkButton() - } else if !userAddress.shortLinkDataSet { - addProfileToShortLinkButton() - } + if userAddress.connLinkContact.connShortLink == nil { + addShortLinkButton() + } else if !userAddress.shortLinkDataSet { + addProfileToShortLinkButton() } } header: { ToggleShortLinkHeader(text: Text("For social media"), link: userAddress.connLinkContact, short: $showShortLink) @@ -200,10 +198,9 @@ struct UserAddressView: View { progressIndicator = true Task { do { - let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) - let connLinkContact = try await apiCreateUserAddress(short: short) + let connLinkContact = try await apiCreateUserAddress() DispatchQueue.main.async { - chatModel.userAddress = UserContactLink(connLinkContact: connLinkContact, shortLinkDataSet: short) + chatModel.userAddress = UserContactLink(connLinkContact: connLinkContact, shortLinkDataSet: connLinkContact.connShortLink != nil) alert = .shareOnCreate progressIndicator = false } diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 0cddd855ea..c6f6212fee 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Verwende Server"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Kurze Links verwenden (BETA)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Verwenden Sie SimpleX-Chat-Server?"; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index 205642b64d..404d11f65c 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Usar servidores"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Usar enlaces cortos (BETA)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "¿Usar servidores SimpleX Chat?"; diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index 691099731d..510fd3b79c 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Kiszolgálók használata"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Rövid hivatkozások használata (béta)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "SimpleX Chat kiszolgálók használata?"; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index eb02c9463d..6ba668de1e 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Usa i server"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Usa link brevi (BETA)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Usare i server di SimpleX Chat?"; diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index 01977dc899..412d2f84d8 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Gebruik servers"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Gebruik korte links (BETA)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "SimpleX Chat servers gebruiken?"; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 0619722c5a..82b86c6a9f 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -5551,9 +5551,6 @@ report reason */ /* No comment provided by engineer. */ "Use servers" = "Использовать серверы"; -/* No comment provided by engineer. */ -"Use short links (BETA)" = "Короткие ссылки (БЕТА)"; - /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?"; 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 0f65783152..b0e5e9896a 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 @@ -107,7 +107,6 @@ class AppPreferences { val simplexLinkMode: SharedPreference = mkSafeEnumPreference(SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE, SimplexLinkMode.default) val privacyShowChatPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_SHOW_CHAT_PREVIEWS, true) val privacySaveLastDraft = mkBoolPreference(SHARED_PREFS_PRIVACY_SAVE_LAST_DRAFT, true) - val privacyShortLinks = mkBoolPreference(SHARED_PREFS_PRIVACY_SHORT_LINKS, false) val privacyDeliveryReceiptsSet = mkBoolPreference(SHARED_PREFS_PRIVACY_DELIVERY_RECEIPTS_SET, false) val privacyEncryptLocalFiles = mkBoolPreference(SHARED_PREFS_PRIVACY_ENCRYPT_LOCAL_FILES, true) val privacyAskToApproveRelays = mkBoolPreference(SHARED_PREFS_PRIVACY_ASK_TO_APPROVE_RELAYS, true) @@ -370,7 +369,6 @@ class AppPreferences { private const val SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE = "PrivacySimplexLinkMode" private const val SHARED_PREFS_PRIVACY_SHOW_CHAT_PREVIEWS = "PrivacyShowChatPreviews" private const val SHARED_PREFS_PRIVACY_SAVE_LAST_DRAFT = "PrivacySaveLastDraft" - private const val SHARED_PREFS_PRIVACY_SHORT_LINKS = "PrivacyShortLinks" private const val SHARED_PREFS_PRIVACY_DELIVERY_RECEIPTS_SET = "PrivacyDeliveryReceiptsSet" private const val SHARED_PREFS_PRIVACY_ENCRYPT_LOCAL_FILES = "PrivacyEncryptLocalFiles" private const val SHARED_PREFS_PRIVACY_ASK_TO_APPROVE_RELAYS = "PrivacyAskToApproveRelays" @@ -795,7 +793,7 @@ object ChatController { } suspend fun apiStartChat(ctrl: ChatCtrl? = null): Boolean { - val r = sendCmd(null, CC.StartChat(mainApp = true), ctrl) + val r = sendCmd(null, CC.StartChat(mainApp = true, largeLinkData = false), ctrl) when (r.result) { is CR.ChatStarted -> return true is CR.ChatRunning -> return false @@ -1295,8 +1293,7 @@ object ChatController { suspend fun apiAddContact(rh: Long?, incognito: Boolean): Pair?, (() -> Unit)?> { val userId = try { currentUserId("apiAddContact") } catch (e: Exception) { return null to null } - val short = appPrefs.privacyShortLinks.get() - val r = sendCmd(rh, CC.APIAddContact(userId, short = short, incognito = incognito)) + val r = sendCmd(rh, CC.APIAddContact(userId, incognito = incognito)) return when { r is API.Result && r.res is CR.Invitation -> (r.res.connLinkInvitation to r.res.connection) to null !(networkErrorAlert(r)) -> null to { apiErrorAlert("apiAddContact", generalGetString(MR.strings.connection_error), r) } @@ -1539,9 +1536,9 @@ object ChatController { return false } - suspend fun apiCreateUserAddress(rh: Long?, short: Boolean): CreatedConnLink? { + suspend fun apiCreateUserAddress(rh: Long?): CreatedConnLink? { val userId = kotlin.runCatching { currentUserId("apiCreateUserAddress") }.getOrElse { return null } - val r = sendCmd(rh, CC.ApiCreateMyAddress(userId, short)) + val r = sendCmd(rh, CC.ApiCreateMyAddress(userId)) if (r is API.Result && r.res is CR.UserContactLinkCreated) return r.res.connLinkContact if (!(networkErrorAlert(r))) { apiErrorAlert("apiCreateUserAddress", generalGetString(MR.strings.error_creating_address), r) @@ -1992,8 +1989,7 @@ object ChatController { } suspend fun apiCreateGroupLink(rh: Long?, groupId: Long, memberRole: GroupMemberRole = GroupMemberRole.Member): Pair? { - val short = appPrefs.privacyShortLinks.get() - val r = sendCmd(rh, CC.APICreateGroupLink(groupId, memberRole, short)) + val r = sendCmd(rh, CC.APICreateGroupLink(groupId, memberRole)) if (r is API.Result && r.res is CR.GroupLinkCreated) return r.res.connLinkContact to r.res.memberRole if (!(networkErrorAlert(r))) { apiErrorAlert("apiCreateGroupLink", generalGetString(MR.strings.error_creating_link_for_group), r) @@ -3338,7 +3334,7 @@ sealed class CC { class ApiMuteUser(val userId: Long): CC() class ApiUnmuteUser(val userId: Long): CC() class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean, val viewPwd: String?): CC() - class StartChat(val mainApp: Boolean): CC() + class StartChat(val mainApp: Boolean, val largeLinkData: Boolean): CC() class CheckChatRunning: CC() class ApiStopChat: CC() @Serializable @@ -3383,7 +3379,7 @@ sealed class CC { class ApiLeaveGroup(val groupId: Long): CC() class ApiListMembers(val groupId: Long): CC() class ApiUpdateGroupProfile(val groupId: Long, val groupProfile: GroupProfile): CC() - class APICreateGroupLink(val groupId: Long, val memberRole: GroupMemberRole, val short: Boolean): CC() + class APICreateGroupLink(val groupId: Long, val memberRole: GroupMemberRole): CC() class APIGroupLinkMemberRole(val groupId: Long, val memberRole: GroupMemberRole): CC() class APIDeleteGroupLink(val groupId: Long): CC() class APIGetGroupLink(val groupId: Long): CC() @@ -3423,7 +3419,7 @@ sealed class CC { class APIGetGroupMemberCode(val groupId: Long, val groupMemberId: Long): CC() class APIVerifyContact(val contactId: Long, val connectionCode: String?): CC() class APIVerifyGroupMember(val groupId: Long, val groupMemberId: Long, val connectionCode: String?): CC() - class APIAddContact(val userId: Long, val short: Boolean, val incognito: Boolean): CC() + class APIAddContact(val userId: Long, val incognito: Boolean): CC() class ApiSetConnectionIncognito(val connId: Long, val incognito: Boolean): CC() class ApiChangeConnectionUser(val connId: Long, val userId: Long): CC() class APIConnectPlan(val userId: Long, val connLink: String): CC() @@ -3439,7 +3435,7 @@ sealed class CC { class ApiSetConnectionAlias(val connId: Long, val localAlias: String): CC() class ApiSetUserUIThemes(val userId: Long, val themes: ThemeModeOverrides?): CC() class ApiSetChatUIThemes(val chatId: String, val themes: ThemeModeOverrides?): CC() - class ApiCreateMyAddress(val userId: Long, val short: Boolean): CC() + class ApiCreateMyAddress(val userId: Long): CC() class ApiDeleteMyAddress(val userId: Long): CC() class ApiShowMyAddress(val userId: Long): CC() class ApiAddMyAddressShortLink(val userId: Long): CC() @@ -3509,7 +3505,7 @@ sealed class CC { is ApiMuteUser -> "/_mute user $userId" is ApiUnmuteUser -> "/_unmute user $userId" is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}${maybePwd(viewPwd)}" - is StartChat -> "/_start main=${onOff(mainApp)}" + is StartChat -> "/_start main=${onOff(mainApp)} large_link_data=${onOff(largeLinkData)}" is CheckChatRunning -> "/_check running" is ApiStopChat -> "/_stop" is ApiSetAppFilePaths -> "/set file paths ${json.encodeToString(this)}" @@ -3572,7 +3568,7 @@ sealed class CC { is ApiLeaveGroup -> "/_leave #$groupId" is ApiListMembers -> "/_members #$groupId" is ApiUpdateGroupProfile -> "/_group_profile #$groupId ${json.encodeToString(groupProfile)}" - is APICreateGroupLink -> "/_create link #$groupId ${memberRole.name.lowercase()} short=${onOff(short)}" + is APICreateGroupLink -> "/_create link #$groupId ${memberRole.name.lowercase()}" is APIGroupLinkMemberRole -> "/_set link role #$groupId ${memberRole.name.lowercase()}" is APIDeleteGroupLink -> "/_delete link #$groupId" is APIGetGroupLink -> "/_get link #$groupId" @@ -3612,7 +3608,7 @@ sealed class CC { is APIGetGroupMemberCode -> "/_get code #$groupId $groupMemberId" is APIVerifyContact -> "/_verify code @$contactId" + if (connectionCode != null) " $connectionCode" else "" is APIVerifyGroupMember -> "/_verify code #$groupId $groupMemberId" + if (connectionCode != null) " $connectionCode" else "" - is APIAddContact -> "/_connect $userId short=${onOff(short)} incognito=${onOff(incognito)}" + is APIAddContact -> "/_connect $userId incognito=${onOff(incognito)}" is ApiSetConnectionIncognito -> "/_set incognito :$connId ${onOff(incognito)}" is ApiChangeConnectionUser -> "/_set conn user :$connId $userId" is APIConnectPlan -> "/_connect plan $userId $connLink" @@ -3628,7 +3624,7 @@ sealed class CC { is ApiSetConnectionAlias -> "/_set alias :$connId ${localAlias.trim()}" is ApiSetUserUIThemes -> "/_set theme user $userId ${if (themes != null) json.encodeToString(themes) else ""}" is ApiSetChatUIThemes -> "/_set theme $chatId ${if (themes != null) json.encodeToString(themes) else ""}" - is ApiCreateMyAddress -> "/_address $userId short=${onOff(short)}" + is ApiCreateMyAddress -> "/_address $userId" is ApiDeleteMyAddress -> "/_delete_address $userId" is ApiShowMyAddress -> "/_show_address $userId" is ApiAddMyAddressShortLink -> "/_short_link_address $userId" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupLinkView.kt index 30c16db6a4..25f8a861bb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupLinkView.kt @@ -196,7 +196,7 @@ fun GroupLinkLayout( ) } } - if (groupLink.connShortLink == null && appPreferences.privacyShortLinks.get()) { + if (groupLink.connShortLink == null) { AddShortLinkButton(addShortLink) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt index 569f4ff5f8..ab4c41ca93 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt @@ -88,13 +88,6 @@ fun PrivacySettingsView( simplexLinkMode.set(it) chatModel.simplexLinkMode.value = it }) - if (appPrefs.developerTools.get()) { - SettingsPreferenceItem( - null, - stringResource(MR.strings.privacy_short_links), - chatModel.controller.appPrefs.privacyShortLinks - ) - } } SectionDividerSpaced() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt index d77c4bc7f6..bdb8e385e1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt @@ -63,8 +63,7 @@ fun UserAddressView( fun createAddress() { withBGApi { progressIndicator = true - val short = appPreferences.privacyShortLinks.get() - val connReqContact = chatModel.controller.apiCreateUserAddress(user.value?.remoteHostId, short = short) + val connReqContact = chatModel.controller.apiCreateUserAddress(user.value?.remoteHostId) if (connReqContact != null) { chatModel.userAddress.value = UserContactLinkRec(connReqContact) @@ -225,7 +224,7 @@ private fun UserAddressLayout( // ShareViaEmailButton { sendEmail(userAddress) } BusinessAddressToggle(autoAcceptState) { saveAas(autoAcceptState.value, autoAcceptStateSaved) } AddressSettingsButton(user, userAddress, shareViaProfile, setProfileAddress, saveAas) - if (userAddress.connLinkContact.connShortLink == null && appPreferences.privacyShortLinks.get()) { + if (userAddress.connLinkContact.connShortLink == null) { AddShortLinkButton(addShortLink) } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml index ab6973d141..85a089ab48 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml @@ -2362,7 +2362,6 @@ لا يمكن الوصول إلى الدردشات الخاصة والمجموعات وجهات اتصالك لمشغلي الخادم. باستخدام SimpleX Chat، توافق على:\n- إرسال المحتوى القانوني فقط في المجموعات العامة.\n- احترام المستخدمين الآخرين – لا سبام. اقبل - استخدم روابط قصيرة (تجريبي) يتطلب هذا الرابط إصدار تطبيق أحدث. يُرجى ترقية التطبيق أو اطلب من جهة اتصالك إرسال رابط متوافق. رابط كامل رابط قصير 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 1804d7c644..501a4050d5 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -98,7 +98,6 @@ Full link Via browser Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red. - Use short links (BETA) Spam diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml index 30376c43b7..274778a2f2 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml @@ -2346,7 +2346,6 @@ Enllaç al canal SimpleX Aquest enllaç requereix una versió de l\'aplicació més recent. Actualitzeu l\'aplicació o demaneu al vostre contacte que enviï un enllaç compatible. Enllaç de connexió no compatible - Emprar enllaços curts (BETA) Enllaç complet Enllaç curt Tots els servidors diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml index fdc0266f2d..2aa2e9b761 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml @@ -2371,7 +2371,6 @@ Zásady ochrany soukromí a podmínky používání. Soukromé konverzace, skupiny a kontakty nejsou přístupné provozovatelům serverů. Nepodporovaný odkaz k připojení - Používejte krátké odkazy (BETA) Tento odkaz vyžaduje novější verzi aplikace. Prosím aktualizujte aplikaci nebo požádejte kontakt o odeslání kompatibilního odkazu. odkaz SimpleX kanálu Úplný odkaz diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml index 13b0bc95b6..47fa31274d 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml @@ -2453,7 +2453,6 @@ Server-Betreiber konfigurieren Private Chats, Gruppen und Ihre Kontakte sind für Server-Betreiber nicht zugänglich. Verbindungs-Link wird nicht unterstützt - Kurze Links verwenden (BETA) Verkürzter Link Vollständiger Link SimpleX-Kanal-Link diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml index ae782eb09c..cbc07aad20 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml @@ -2382,7 +2382,6 @@ Enlace corto Este enlace requiere una versión más reciente de la aplicación. Por favor, actualiza la aplicación o pide a tu contacto un enlace compatible. Enlace de conexión no compatible - Usar enlaces cortos (BETA) Usar puerto TCP 443 solo en servidores predefinidos. Todos los servidores Servidores predefinidos diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml index ec91c3040a..6bba8acc88 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml @@ -2345,7 +2345,6 @@ A privát csevegések, a csoportok és a partnerek nem érhetők el a szerver üzemeltetői számára. Kiszolgálóüzemeltetők beállítása Nem támogatott kapcsolattartási hivatkozás - Rövid hivatkozások használata (béta) Rövid hivatkozás Teljes hivatkozás Ez a hivatkozás újabb alkalmazásverziót igényel. Frissítse az alkalmazást vagy kérjen egy kompatibilis hivatkozást a partnerétől. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml index a9819b32d0..29627eb5c1 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml @@ -105,7 +105,7 @@ Periksa apakah tautan SimpleX sudah benar. Anda terhubung ke server yang digunakan untuk menerima pesan dari kontak ini. Mencoba menyambung ke server yang digunakan untuk menerima pesan dari kontak ini (error: %1$s). - Migrasi basis data sedang berlangsung, + Migrasi basis data sedang berlangsung, \nmemerlukan waktu beberapa menit. menghubungkan Lokasi file tidak valid @@ -2356,7 +2356,6 @@ Tautan lengkap Tautan ini perlu versi aplikasi yang baru. Harap perbarui aplikasi atau minta kontak untuk kirim tautan kompatibel. Tautan saluran SimpleX - Gunakan tautan singkat (BETA) Tautan koneksi tidak didukung Tautan singkat gagal mengirim pesan diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index 1dbb0617af..e29b4fa3e8 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -2386,7 +2386,6 @@ Link breve Link del canale SimpleX Link di connessione non supportato - Usa link brevi (BETA) Tutti i server Off Server preimpostati diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml index ff90fb7783..b5be8b9773 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml @@ -2379,7 +2379,6 @@ Voor deze link is een nieuwere app-versie vereist. Werk de app bij of vraag je contactpersoon om een compatibele link te sturen. Volledige link Niet-ondersteunde verbindingslink - Gebruik korte links (BETA) Korte link Serveroperators configureren Privacybeleid en gebruiksvoorwaarden. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml index 9cf3e3ac6a..da12b51839 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml @@ -2215,7 +2215,6 @@ Spam Acest link necesită o versiune mai nouă a aplicației. Vă rugăm să actualizați aplicația sau să solicitați persoanei de contact să vă trimită un link compatibil. Legătură de conexiune neacceptată - Utilizați linkuri scurte (BETA) Utilizați portul web %s.]]> %s]]> diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml index 189bd7e07e..d190441ec3 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml @@ -2520,6 +2520,5 @@ Вы приняты 1 чат с членом группы SimpleX ссылка канала - Короткие ссылки (БЕТА) добавить короткую ссылку diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml index 011eec76fc..4c342076ba 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml @@ -2378,7 +2378,6 @@ Використовуючи SimpleX Chat, ви погоджуєтесь на:\n- надсилати тільки легальний контент у публічних групах.\n- поважати інших користувачів – без спаму. Налаштувати операторів сервера Політика конфіденційності та умови використання - Використовувати короткі посилання (BETA) Це посилання вимагає новішої версії додатку. Будь ласка, оновіть додаток або попросіть вашого контакту надіслати сумісне посилання. Повне посилання Коротке посилання diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml index 3cbc54f652..2904d2d8ca 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml @@ -2355,7 +2355,6 @@ Đường dẫn này yêu cầu một phiên bản ứng dụng mới hơn. Vui lòng nâng cấp ứng dụng hoặc yêu cầu liên hệ của một gửi cho một đường dẫn tương thích. Đường dẫn kênh SimpleX Đường dẫn kết nối không được hỗ trợ - Sử dụng đường dẫn ngắn (BETA) Toàn bộ đường dẫn Đường dẫn ngắn Tắt diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml index f71a61f895..bdcb89b161 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -2366,7 +2366,6 @@ 服务器运营方无法访问私密聊天、群组和你的联系人。 配置服务器运营方 不支持的连接链接 - 使用短链接(测试) SimpleX 频道链接 短链接 此链接需要更新的应用版本。请升级应用或请求你的联系人发送相容的链接。 diff --git a/apps/simplex-directory-service/src/Directory/Service.hs b/apps/simplex-directory-service/src/Directory/Service.hs index 7cd1d2c757..9db9e4a019 100644 --- a/apps/simplex-directory-service/src/Directory/Service.hs +++ b/apps/simplex-directory-service/src/Directory/Service.hs @@ -189,7 +189,7 @@ useMemberFilter img_ = \case readBlockedWordsConfig :: DirectoryOpts -> IO BlockedWordsConfig readBlockedWordsConfig DirectoryOpts {blockedFragmentsFile, blockedWordsFile, nameSpellingFile, blockedExtensionRules, testing} = do - extensionRules <- maybe (pure []) (fmap read . readFile) blockedExtensionRules + extensionRules <- maybe (pure []) (fmap read . readFile) blockedExtensionRules spelling <- maybe (pure M.empty) (fmap (M.fromList . read) . readFile) nameSpellingFile blockedFragments <- S.fromList <$> maybe (pure []) (fmap T.lines . T.readFile) blockedFragmentsFile bws <- maybe (pure []) (fmap lines . readFile) blockedWordsFile @@ -348,7 +348,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName setGroupRegOwner st gr owner let GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = g notifyOwner gr $ "Joined the group " <> displayName <> ", creating the link…" - sendChatCmd cc (APICreateGroupLink groupId GRMember False) >>= \case + sendChatCmd cc (APICreateGroupLink groupId GRMember) >>= \case Right CRGroupLinkCreated {groupLink = GroupLink {connLinkContact = CCLink gLink _}} -> do setGroupStatus st gr GRSPendingUpdate notifyOwner @@ -489,7 +489,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName Nothing -> pure textMsg Just script -> content <$> readProcess script [s] "" where - textMsg = MCText $ T.pack s + textMsg = MCText $ T.pack s content r = case T.lines $ T.pack r of [] -> textMsg "" : _ -> textMsg diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index e14275b75c..94fd789100 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -173,6 +173,7 @@ newChatController tempDirectory <- newTVarIO optTempDirectory assetsDirectory <- newTVarIO Nothing contactMergeEnabled <- newTVarIO True + useLargeLinkData <- newTVarIO True pure ChatController { firstTime, @@ -213,7 +214,8 @@ newChatController tempDirectory, assetsDirectory, logFilePath = logFile, - contactMergeEnabled + contactMergeEnabled, + useLargeLinkData } where presetServers' :: PresetServers diff --git a/src/Simplex/Chat/Bot.hs b/src/Simplex/Chat/Bot.hs index 9b92c8b800..374180d688 100644 --- a/src/Simplex/Chat/Bot.hs +++ b/src/Simplex/Chat/Bot.hs @@ -55,14 +55,15 @@ initializeBotAddress' logAddress cc = do Left (ChatErrorStore SEUserContactLinkNotFound) -> do when logAddress $ putStrLn "No bot address, creating..." -- TODO [short links] create short link by default - sendChatCmd cc (CreateMyAddress False) >>= \case + sendChatCmd cc CreateMyAddress >>= \case Right (CRUserContactLinkCreated _ ccLink) -> showBotAddress ccLink _ -> putStrLn "can't create bot address" >> exitFailure _ -> putStrLn "unexpected response" >> exitFailure where showBotAddress (CCLink uri shortUri) = do - when logAddress $ putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri) - when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri) + when logAddress $ do + putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri) + when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri) void $ sendChatCmd cc $ AddressAutoAccept $ Just AutoAccept {businessAddress = False, acceptIncognito = False, autoReply = Nothing} sendMessage :: ChatController -> Contact -> Text -> IO () diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 37d70475a4..c4db16e7ef 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -249,7 +249,8 @@ data ChatController = ChatController tempDirectory :: TVar (Maybe FilePath), assetsDirectory :: TVar (Maybe FilePath), logFilePath :: Maybe FilePath, - contactMergeEnabled :: TVar Bool + contactMergeEnabled :: TVar Bool, + useLargeLinkData :: TVar Bool } data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase @@ -276,7 +277,7 @@ data ChatCommand | UnmuteUser | APIDeleteUser UserId Bool (Maybe UserPwd) | DeleteUser UserName Bool (Maybe UserPwd) - | StartChat {mainApp :: Bool, enableSndFiles :: Bool} -- enableSndFiles has no effect when mainApp is True + | StartChat {mainApp :: Bool, enableSndFiles :: Bool, largeLinkData :: Bool} -- enableSndFiles has no effect when mainApp is True | CheckChatRunning | APIStopChat | APIActivateChat {restoreChat :: Bool} @@ -369,7 +370,7 @@ data ChatCommand -- | APIDeleteGroupConversations GroupId (NonEmpty GroupConversationId) -- | APIArchiveGroupConversations GroupId (NonEmpty GroupConversationId) | APIUpdateGroupProfile GroupId GroupProfile - | APICreateGroupLink GroupId GroupMemberRole CreateShortLink + | APICreateGroupLink GroupId GroupMemberRole | APIGroupLinkMemberRole GroupId GroupMemberRole | APIDeleteGroupLink GroupId | APIGetGroupLink GroupId @@ -443,8 +444,8 @@ data ChatCommand | EnableGroupMember GroupName ContactName | ChatHelp HelpSection | Welcome - | APIAddContact UserId CreateShortLink IncognitoEnabled - | AddContact CreateShortLink IncognitoEnabled + | APIAddContact UserId IncognitoEnabled + | AddContact IncognitoEnabled | APISetConnectionIncognito Int64 IncognitoEnabled | APIChangeConnectionUser Int64 UserId -- new user id to switch connection to | APIConnectPlan UserId AConnectionLink @@ -462,8 +463,8 @@ data ChatCommand | ClearContact ContactName | APIListContacts UserId | ListContacts - | APICreateMyAddress UserId CreateShortLink - | CreateMyAddress CreateShortLink + | APICreateMyAddress UserId + | CreateMyAddress | APIDeleteMyAddress UserId | DeleteMyAddress | APIShowMyAddress UserId @@ -507,7 +508,7 @@ data ChatCommand | ShowGroupProfile GroupName | UpdateGroupDescription GroupName (Maybe Text) | ShowGroupDescription GroupName - | CreateGroupLink GroupName GroupMemberRole CreateShortLink + | CreateGroupLink GroupName GroupMemberRole | GroupLinkMemberRole GroupName GroupMemberRole | DeleteGroupLink GroupName | ShowGroupLink GroupName diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 91e36375ab..1268455e4b 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -413,7 +413,8 @@ processChatCommand' vr = \case checkDeleteChatUser user' withChatLock "deleteUser" . procCmd $ deleteChatUser user' delSMPQueues DeleteUser uName delSMPQueues viewPwd_ -> withUserName uName $ \userId -> APIDeleteUser userId delSMPQueues viewPwd_ - StartChat {mainApp, enableSndFiles} -> withUser' $ \_ -> + StartChat {mainApp, enableSndFiles, largeLinkData} -> withUser' $ \_ -> do + chatWriteVar useLargeLinkData largeLinkData asks agentAsync >>= readTVarIO >>= \case Just _ -> pure CRChatRunning _ -> checkStoreNotChanged . lift $ startChatController mainApp enableSndFiles $> CRChatStarted @@ -1671,21 +1672,19 @@ processChatCommand' vr = \case EnableGroupMember gName mName -> withMemberName gName mName $ \gId mId -> APIEnableGroupMember gId mId ChatHelp section -> pure $ CRChatHelp section Welcome -> withUser $ pure . CRWelcome - APIAddContact userId short incognito -> withUserId userId $ \user -> procCmd $ do + APIAddContact userId incognito -> withUserId userId $ \user -> procCmd $ do -- [incognito] generate profile for connection incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing subMode <- chatReadVar subscriptionMode - let userData - | short = Just $ shortLinkUserData $ userProfileToSend user incognitoProfile Nothing False - | otherwise = Nothing + userData <- contactShortLinkData (userProfileToSend user incognitoProfile Nothing False) Nothing -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation userData Nothing IKPQOn subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation (Just userData) Nothing IKPQOn subMode ccLink' <- shortenCreatedLink ccLink -- TODO PQ pass minVersion from the current range conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' Nothing ConnNew incognitoProfile subMode initialChatVersion PQSupportOn pure $ CRInvitation user ccLink' conn - AddContact short incognito -> withUser $ \User {userId} -> - processChatCommand $ APIAddContact userId short incognito + AddContact incognito -> withUser $ \User {userId} -> + processChatCommand $ APIAddContact userId incognito APISetConnectionIncognito connId incognito -> withUser $ \user@User {userId} -> do conn <- withFastStore $ \db -> getPendingContactConnection db userId connId let PendingContactConnection {pccConnStatus, customUserProfileId} = conn @@ -1717,11 +1716,12 @@ processChatCommand' vr = \case recreateConn user conn@PendingContactConnection {customUserProfileId, connLinkInv} newUser = do subMode <- chatReadVar subscriptionMode let short = isJust $ connShortLink =<< connLinkInv - userData - | short = Just $ shortLinkUserData $ userProfileToSend newUser Nothing Nothing False - | otherwise = Nothing + userData_ <- + if short + then Just <$> contactShortLinkData (userProfileToSend newUser Nothing Nothing False) Nothing + else pure Nothing -- TODO [certs rcv] - (agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation userData Nothing IKPQOn subMode + (agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation userData_ Nothing IKPQOn subMode ccLink' <- shortenCreatedLink ccLink conn' <- withFastStore' $ \db -> do deleteConnectionRecord db user connId @@ -1824,18 +1824,16 @@ processChatCommand' vr = \case CRContactsList user <$> withFastStore' (\db -> getUserContacts db vr user) ListContacts -> withUser $ \User {userId} -> processChatCommand $ APIListContacts userId - APICreateMyAddress userId short -> withUserId userId $ \user -> procCmd $ do + APICreateMyAddress userId -> withUserId userId $ \user -> procCmd $ do subMode <- chatReadVar subscriptionMode - let userData - | short = Just $ shortLinkUserData $ userProfileToSend user Nothing Nothing False - | otherwise = Nothing + userData <- contactShortLinkData (userProfileToSend user Nothing Nothing False) Nothing -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData Nothing IKPQOn subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact (Just userData) Nothing IKPQOn subMode ccLink' <- shortenCreatedLink ccLink - withFastStore $ \db -> createUserContactLink db user connId ccLink' short subMode + withFastStore $ \db -> createUserContactLink db user connId ccLink' subMode pure $ CRUserContactLinkCreated user ccLink' - CreateMyAddress short -> withUser $ \User {userId} -> - processChatCommand $ APICreateMyAddress userId short + CreateMyAddress -> withUser $ \User {userId} -> + processChatCommand $ APICreateMyAddress userId APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do conn <- withFastStore $ \db -> getUserAddressConnection db vr user withChatLock "deleteMyAddress" $ do @@ -1860,9 +1858,9 @@ processChatCommand' vr = \case let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing} updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing APISetProfileAddress userId True -> withUserId userId $ \user@User {profile = p} -> do - ucl@UserContactLink {connLinkContact = CCLink cReq _} <- withFastStore (`getUserAddress` user) + ucl <- withFastStore (`getUserAddress` user) -- TODO [short links] replace with short links - let p' = (fromLocalProfile p :: Profile) {contactLink = Just $ CLFull cReq} + let p' = (fromLocalProfile p :: Profile) {contactLink = Just $ profileContactLink ucl} updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl SetProfileAddress onOff -> withUser $ \User {userId} -> processChatCommand $ APISetProfileAddress userId onOff @@ -2435,20 +2433,18 @@ processChatCommand' vr = \case updateGroupProfileByName gName $ \p -> p {description} ShowGroupDescription gName -> withUser $ \user -> CRGroupDescription user <$> withFastStore (\db -> getGroupInfoByName db vr user gName) - APICreateGroupLink groupId mRole short -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do + APICreateGroupLink groupId mRole -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do gInfo@GroupInfo {groupProfile} <- withFastStore $ \db -> getGroupInfo db vr user groupId assertUserGroupRole gInfo GRAdmin when (mRole > GRMember) $ throwChatError $ CEGroupMemberInitialRole gInfo mRole groupLinkId <- GroupLinkId <$> drgRandomBytes 16 subMode <- chatReadVar subscriptionMode - let userData - | short = Just $ UserLinkData $ LB.toStrict $ J.encode $ GroupShortLinkData groupProfile - | otherwise = Nothing - crClientData = encodeJSON $ CRDataGroup groupLinkId + userData <- groupShortLinkData groupProfile + let crClientData = encodeJSON $ CRDataGroup groupLinkId -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData (Just crClientData) IKPQOff subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact (Just userData) (Just crClientData) IKPQOff subMode ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink - gLink <- withFastStore $ \db -> createGroupLink db user gInfo connId ccLink' groupLinkId mRole short subMode + gLink <- withFastStore $ \db -> createGroupLink db user gInfo connId ccLink' groupLinkId mRole subMode pure $ CRGroupLinkCreated user gInfo gLink APIGroupLinkMemberRole groupId mRole' -> withUser $ \user -> withGroupLock "groupLinkMemberRole" groupId $ do gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId @@ -2507,9 +2503,9 @@ processChatCommand' vr = \case toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci] pure $ CRNewMemberContactSentInv user ct' g m _ -> throwChatError CEGroupMemberNotActive - CreateGroupLink gName mRole short -> withUser $ \user -> do + CreateGroupLink gName mRole -> withUser $ \user -> do groupId <- withFastStore $ \db -> getGroupIdByName db user gName - processChatCommand $ APICreateGroupLink groupId mRole short + processChatCommand $ APICreateGroupLink groupId mRole GroupLinkMemberRole gName mRole -> withUser $ \user -> do groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIGroupLinkMemberRole groupId mRole @@ -3030,7 +3026,7 @@ processChatCommand' vr = \case conn <- withFastStore $ \db -> getUserAddressConnection db vr user let shortLinkProfile = userProfileToSend user Nothing Nothing False shortLinkMsg = autoAccept >>= autoReply >>= (Just . msgContentText) - userData = UserLinkData $ LB.toStrict $ J.encode $ ContactShortLinkData shortLinkProfile shortLinkMsg + userData <- contactShortLinkData shortLinkProfile shortLinkMsg sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a (aConnId conn) SCMContact userData Nothing) withFastStore' $ \db -> setUserContactLinkShortLink db userContactLinkId sLnk let autoAccept' = autoAccept >>= \aa -> Just aa {acceptIncognito = False} @@ -3089,8 +3085,8 @@ processChatCommand' vr = \case setGroupLinkData :: User -> GroupInfo -> GroupLink -> CM ChatResponse setGroupLinkData user gInfo@GroupInfo {groupProfile} gLink@GroupLink {groupLinkId} = do conn <- withFastStore $ \db -> getGroupLinkConnection db vr user gInfo - let userData = UserLinkData $ LB.toStrict $ J.encode $ GroupShortLinkData groupProfile - crClientData = encodeJSON $ CRDataGroup groupLinkId + userData <- groupShortLinkData groupProfile + let crClientData = encodeJSON $ CRDataGroup groupLinkId sLnk <- shortenShortLink' . toShortGroupLink =<< withAgent (\a -> setConnShortLink a (aConnId conn) SCMContact userData (Just crClientData)) gLink' <- withFastStore' $ \db -> setGroupLinkShortLink db gLink sLnk pure $ CRGroupLink user gInfo gLink' @@ -3316,9 +3312,13 @@ processChatCommand' vr = \case Just UserContactLink {connLinkContact = CCLink cReq _} -> pure (ACCL SCMContact $ CCLink cReq (Just l'), CPContactAddress CAPOwnLink) Nothing -> do (cReq, cData) <- getShortLinkConnReq user l' - let contactSLinkData_ = J.decodeStrict $ linkUserData' cData - plan <- contactRequestPlan user cReq contactSLinkData_ - pure (ACCL SCMContact $ CCLink cReq (Just l'), plan) + let cl = ACCL SCMContact $ CCLink cReq (Just l') + withFastStore' (\db -> getContactWithoutConnViaShortAddress db vr user l') >>= \case + Just ct -> pure (cl, CPContactAddress (CAPContactViaAddress ct)) + Nothing -> do + let contactSLinkData_ = J.decodeStrict $ linkUserData' cData + plan <- contactRequestPlan user cReq contactSLinkData_ + pure (cl, plan) CCTGroup -> withFastStore' (\db -> getGroupInfoViaUserShortLink db vr user l') >>= \case Just (cReq, g) -> pure (ACCL SCMContact $ CCLink cReq (Just l'), CPGroupLink (GLPOwnLink g)) @@ -3434,12 +3434,26 @@ processChatCommand' vr = \case CSLInvitation _ srv lnkId linkKey -> CSLInvitation SLSServer srv lnkId linkKey CSLContact _ ct srv linkKey -> CSLContact SLSServer ct srv linkKey restoreShortLink' l = (`restoreShortLink` l) <$> asks (shortLinkPresetServers . config) - shortLinkUserData :: Profile -> UserLinkData - shortLinkUserData profile = UserLinkData $ LB.toStrict $ J.encode $ ContactShortLinkData profile Nothing + contactShortLinkData :: Profile -> Maybe Text -> CM UserLinkData + contactShortLinkData p msg = do + largeLinkData <- chatReadVar useLargeLinkData + let contactData + | largeLinkData = ContactShortLinkData p msg + | otherwise = ContactShortLinkData p {fullName = "", image = Nothing, contactLink = Nothing} Nothing + -- TODO [short links] compress + pure $ UserLinkData $ LB.toStrict $ J.encode contactData + groupShortLinkData :: GroupProfile -> CM UserLinkData + groupShortLinkData gp = do + largeLinkData <- chatReadVar useLargeLinkData + let gp' + | largeLinkData = gp + | otherwise = gp {fullName = "", description = Nothing, image = Nothing, memberAdmission = Nothing} + -- TODO [short links] compress + pure $ UserLinkData $ LB.toStrict $ J.encode $ GroupShortLinkData gp' updatePCCShortLinkData :: PendingContactConnection -> Profile -> CM (Maybe ShortLinkInvitation) updatePCCShortLinkData conn@PendingContactConnection {connLinkInv} profile = forM (connShortLink =<< connLinkInv) $ \_ -> do - let userData = UserLinkData $ LB.toStrict $ J.encode $ ContactShortLinkData profile Nothing + userData <- contactShortLinkData profile Nothing shortenShortLink' =<< withAgent (\a -> setConnShortLink a (aConnId' conn) SCMInvitation userData Nothing) shortenShortLink' :: ConnShortLink m -> CM (ConnShortLink m) shortenShortLink' l = (`shortenShortLink` l) <$> asks (shortLinkPresetServers . config) @@ -4180,8 +4194,9 @@ chatCommandP = "/_start " *> do mainApp <- "main=" *> onOffP enableSndFiles <- " snd_files=" *> onOffP <|> pure mainApp - pure StartChat {mainApp, enableSndFiles}, - "/_start" $> StartChat True True, + largeLinkData <- " large_link_data=" *> onOffP <|> pure False + pure StartChat {mainApp, enableSndFiles, largeLinkData}, + "/_start" $> StartChat {mainApp = True, enableSndFiles = True, largeLinkData = False}, "/_check running" $> CheckChatRunning, "/_stop" $> APIStopChat, "/_app activate restore=" *> (APIActivateChat <$> onOffP), @@ -4388,12 +4403,12 @@ chatCommandP = "/set welcome " *> char_ '#' *> (UpdateGroupDescription <$> displayNameP <* A.space <*> (Just <$> msgTextP)), "/delete welcome " *> char_ '#' *> (UpdateGroupDescription <$> displayNameP <*> pure Nothing), "/show welcome " *> char_ '#' *> (ShowGroupDescription <$> displayNameP), - "/_create link #" *> (APICreateGroupLink <$> A.decimal <*> (memberRole <|> pure GRMember) <*> shortOnOffP), + "/_create link #" *> (APICreateGroupLink <$> A.decimal <*> (memberRole <|> pure GRMember)), "/_set link role #" *> (APIGroupLinkMemberRole <$> A.decimal <*> memberRole), "/_delete link #" *> (APIDeleteGroupLink <$> A.decimal), "/_get link #" *> (APIGetGroupLink <$> A.decimal), "/_short link #" *> (APIAddGroupShortLink <$> A.decimal), - "/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember) <*> shortP), + "/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember)), "/set link role #" *> (GroupLinkMemberRole <$> displayNameP <*> memberRole), "/delete link #" *> (DeleteGroupLink <$> displayNameP), "/show link #" *> (ShowGroupLink <$> displayNameP), @@ -4410,11 +4425,11 @@ chatCommandP = "/_set group user #" *> (APIChangePreparedGroupUser <$> A.decimal <* A.space <*> A.decimal), "/_connect contact @" *> (APIConnectPreparedContact <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)), "/_connect group #" *> (APIConnectPreparedGroup <$> A.decimal <*> incognitoOnOffP), - "/_connect " *> (APIAddContact <$> A.decimal <*> shortOnOffP <*> incognitoOnOffP), + "/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP), "/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> connLinkP_ <*> optional (A.space *> msgContentP)), "/_set incognito :" *> (APISetConnectionIncognito <$> A.decimal <* A.space <*> onOffP), "/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal), - ("/connect" <|> "/c") *> (AddContact <$> shortP <*> incognitoP), + ("/connect" <|> "/c") *> (AddContact <$> incognitoP), ("/connect" <|> "/c") *> (Connect <$> incognitoP <* A.space <*> ((Just <$> strP) <|> A.takeTill isSpace $> Nothing)), ForwardMessage <$> chatNameP <* " <- @" <*> displayNameP <* A.space <*> msgTextP, ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <* A.space <* A.char '@' <*> (Just <$> displayNameP) <* A.space <*> msgTextP, @@ -4448,8 +4463,8 @@ chatCommandP = ("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal), "/_connect contact " *> (APIConnectContactViaAddress <$> A.decimal <*> incognitoOnOffP <* A.space <*> A.decimal), "/simplex" *> (ConnectSimplex <$> incognitoP), - "/_address " *> (APICreateMyAddress <$> A.decimal <*> shortOnOffP), - ("/address" <|> "/ad") *> (CreateMyAddress <$> shortP), + "/_address " *> (APICreateMyAddress <$> A.decimal), + ("/address" <|> "/ad") $> CreateMyAddress, "/_delete_address " *> (APIDeleteMyAddress <$> A.decimal), ("/delete_address" <|> "/da") $> DeleteMyAddress, "/_show_address " *> (APIShowMyAddress <$> A.decimal), @@ -4527,9 +4542,7 @@ chatCommandP = pure $ ACCL m (CCLink cReq sLink_) connLinkP_ = ((Just <$> connLinkP) <|> A.takeTill (== ' ') $> Nothing) - shortP = (A.space *> ("short" <|> "s")) $> True <|> pure False incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False - shortOnOffP = (A.space *> "short=" *> onOffP) <|> pure False incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,") imageP = safeDecodeUtf8 <$> ((<>) <$> imagePrefix <*> (B64.encode <$> base64P)) diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index d6596bbe49..7cdc265735 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -200,14 +200,14 @@ toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just member Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs)) toMaybeGroupMember _ _ = Nothing -createGroupLink :: DB.Connection -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> Bool -> SubscriptionMode -> ExceptT StoreError IO GroupLink -createGroupLink db user@User {userId} groupInfo@GroupInfo {groupId, localDisplayName} agentConnId (CCLink cReq shortLink) groupLinkId memberRole shortLinkDataSet subMode = do +createGroupLink :: DB.Connection -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink +createGroupLink db user@User {userId} groupInfo@GroupInfo {groupId, localDisplayName} agentConnId (CCLink cReq shortLink) groupLinkId memberRole subMode = do checkConstraint (SEDuplicateGroupLink groupInfo) . liftIO $ do currentTs <- getCurrentTime DB.execute db "INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, short_link_contact, short_link_data_set, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)" - ((userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, shortLink, BI shortLinkDataSet) :. (memberRole, BI True, currentTs, currentTs)) + ((userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, shortLink, BI (isJust shortLink)) :. (memberRole, BI True, currentTs, currentTs)) userContactLinkId <- insertedRowId db void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode PQSupportOff getGroupLink db user groupInfo @@ -2032,7 +2032,7 @@ getGroupInfoViaUserShortLink db vr user@User {userId} shortLink = fmap eitherToM (cReq, groupId) <- ExceptT getConnReqGroup (cReq,) <$> getGroupInfo db vr user groupId where - getConnReqGroup = + getConnReqGroup = firstRow' toConnReqGroupId (SEInternalError "group link not found") $ DB.query db diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index 5e5cdf8cb5..1d2b188a57 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -53,6 +53,7 @@ module Simplex.Chat.Store.Profiles getUserContactLinkViaShortLink, setUserContactLinkShortLink, getContactWithoutConnViaAddress, + getContactWithoutConnViaShortAddress, updateUserAddressAutoAccept, getProtocolServers, insertProtocolServer, @@ -75,6 +76,7 @@ module Simplex.Chat.Store.Profiles updateCommandStatus, getCommandDataByCorrId, setUserUIThemes, + profileContactLink, ) where @@ -86,7 +88,7 @@ import Data.Functor (($>)) import Data.Int (Int64) import Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as L -import Data.Maybe (catMaybes, fromMaybe) +import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (decodeLatin1, encodeUtf8) @@ -332,11 +334,7 @@ setUserProfileContactLink db user@User {userId, profile = p@LocalProfile {profil (contactLink, ts, userId, profileId) pure (user :: User) {profile = p {contactLink}} where - -- TODO [short links] this should be replaced with short links once they are supported by all clients. - -- Or, maybe, we want to allow both, when both are optional. - contactLink = case ucl_ of - Just UserContactLink {connLinkContact = CCLink cReq _} -> Just $ CLFull cReq - _ -> Nothing + contactLink = profileContactLink <$> ucl_ -- only used in tests getUserContactProfiles :: DB.Connection -> User -> IO [Profile] @@ -354,14 +352,14 @@ getUserContactProfiles db User {userId} = toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) -> Profile toContactProfile (displayName, fullName, image, contactLink, preferences) = Profile {displayName, fullName, image, contactLink, preferences} -createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> Bool -> SubscriptionMode -> ExceptT StoreError IO () -createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) shortLinkDataSet subMode = +createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> ExceptT StoreError IO () +createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode = checkConstraint SEDuplicateContactLink . liftIO $ do currentTs <- getCurrentTime DB.execute db "INSERT INTO user_contact_links (user_id, conn_req_contact, short_link_contact, short_link_data_set, created_at, updated_at) VALUES (?,?,?,?,?,?)" - (userId, cReq, shortLink, BI shortLinkDataSet, currentTs, currentTs) + (userId, cReq, shortLink, BI (isJust shortLink), currentTs, currentTs) userContactLinkId <- insertedRowId db void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff @@ -457,6 +455,9 @@ data UserContactLink = UserContactLink } deriving (Show) +profileContactLink :: UserContactLink -> ConnLinkContact +profileContactLink UserContactLink {connLinkContact = CCLink cReq sLink} = maybe (CLFull cReq) CLShort sLink + data GroupLinkInfo = GroupLinkInfo { groupId :: GroupId, memberRole :: GroupMemberRole @@ -559,6 +560,22 @@ getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchem (userId, cReqSchema1, cReqSchema2) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) ctId_ +getContactWithoutConnViaShortAddress :: DB.Connection -> VersionRangeChat -> User -> ShortLinkContact -> IO (Maybe Contact) +getContactWithoutConnViaShortAddress db vr user@User {userId} shortLink = do + ctId_ <- + maybeFirstRow fromOnly $ + DB.query + db + [sql| + SELECT ct.contact_id + FROM contacts ct + JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id + LEFT JOIN connections c ON c.contact_id = ct.contact_id + WHERE cp.user_id = ? AND cp.contact_link = ? AND c.connection_id IS NULL + |] + (userId, shortLink) + maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) ctId_ + updateUserAddressAutoAccept :: DB.Connection -> Int64 -> Maybe AutoAccept -> IO () updateUserAddressAutoAccept db userContactLinkId autoAccept = DB.execute diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index 54f73be851..734a83ab68 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -1178,6 +1178,18 @@ Plan: SEARCH g USING INTEGER PRIMARY KEY (rowid=?) SEARCH m USING INDEX sqlite_autoindex_group_members_1 (group_id=?) +Query: + SELECT ct.contact_id + FROM contacts ct + JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id + LEFT JOIN connections c ON c.contact_id = ct.contact_id + WHERE cp.user_id = ? AND cp.contact_link = ? AND c.connection_id IS NULL + +Plan: +SEARCH cp USING COVERING INDEX idx_contact_profiles_contact_link (user_id=? AND contact_link=?) +SEARCH ct USING COVERING INDEX idx_contacts_contact_profile_id (contact_profile_id=?) +SEARCH c USING COVERING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN + Query: SELECT ct.contact_id FROM contacts ct diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index c3192455c3..aaddabefef 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -222,8 +222,6 @@ contactConnId c = aConnId <$> contactConn c type IncognitoEnabled = Bool -type CreateShortLink = Bool - contactConnIncognito :: Contact -> IncognitoEnabled contactConnIncognito = maybe False connIncognito . contactConn diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 491fc0c5ea..bcc8b30885 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -53,7 +53,7 @@ import Simplex.Messaging.Client (ProtocolClientConfig (..)) import Simplex.Messaging.Client.Agent (defaultSMPClientAgentConfig) import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange) import qualified Simplex.Messaging.Crypto.Ratchet as CR -import Simplex.Messaging.Protocol (srvHostnamesSMPClientVersion) +import Simplex.Messaging.Protocol (srvHostnamesSMPClientVersion, sndAuthKeySMPClientVersion) import Simplex.Messaging.Server (runSMPServerBlocking) import Simplex.Messaging.Server.Env.STM (ServerConfig (..), ServerStoreCfg (..), StartOptions (..), StorePaths (..), defaultMessageExpiration, defaultIdleQueueInterval, defaultNtfExpiration, defaultInactiveClientExpiration) import Simplex.Messaging.Server.MsgStore.STM (STMMsgStore) @@ -182,6 +182,13 @@ testAgentCfgSlow = smpCfg = (smpCfg testAgentCfg) {serverVRange = mkVersionRange minClientSMPRelayVersion sendingProxySMPVersion} -- v8 } +testAgentCfgNoShortLinks :: AgentConfig +testAgentCfgNoShortLinks = + testAgentCfg + { smpClientVRange = mkVersionRange (Version 1) sndAuthKeySMPClientVersion, -- v3 + smpCfg = (smpCfg testAgentCfg) {serverVRange = mkVersionRange minClientSMPRelayVersion (Version 14)} -- before shortLinksSMPVersion + } + testCfg :: ChatConfig testCfg = defaultChatConfig @@ -195,6 +202,9 @@ testCfg = testCfgSlow :: ChatConfig testCfgSlow = testCfg {agentConfig = testAgentCfgSlow} +testCfgNoShortLinks :: ChatConfig +testCfgNoShortLinks = testCfg {agentConfig = testAgentCfgNoShortLinks} + testAgentCfgVPrev :: AgentConfig testAgentCfgVPrev = testAgentCfg diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 56052d2833..cd52128b4d 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -94,21 +94,21 @@ chatDirectTests = do it "get and enable operators, accept conditions" testOperators describe "async connection handshake" $ do describe "connect when initiating client goes offline" $ do - it "curr" $ testAsyncInitiatingOffline testCfg testCfg - it "v5" $ testAsyncInitiatingOffline testCfgSlow testCfgSlow - it "v5/curr" $ testAsyncInitiatingOffline testCfgSlow testCfg - it "curr/v5" $ testAsyncInitiatingOffline testCfg testCfgSlow + it "curr" $ testAsyncInitiatingOffline True testCfg testCfg + it "v5" $ testAsyncInitiatingOffline False testCfgSlow testCfgSlow + it "v5/curr" $ testAsyncInitiatingOffline False testCfgSlow testCfg + it "curr/v5" $ testAsyncInitiatingOffline True testCfg testCfgSlow describe "connect when accepting client goes offline" $ do - it "curr" $ testAsyncAcceptingOffline testCfg testCfg - it "v5" $ testAsyncAcceptingOffline testCfgSlow testCfgSlow - it "v5/curr" $ testAsyncAcceptingOffline testCfgSlow testCfg - it "curr/v5" $ testAsyncAcceptingOffline testCfg testCfgSlow + it "curr" $ testAsyncAcceptingOffline True testCfg testCfg + it "v5" $ testAsyncAcceptingOffline False testCfgSlow testCfgSlow + it "v5/curr" $ testAsyncAcceptingOffline False testCfgSlow testCfg + it "curr/v5" $ testAsyncAcceptingOffline True testCfg testCfgSlow describe "connect, fully asynchronous (when clients are never simultaneously online)" $ do it "curr" testFullAsyncFast -- fails in CI - xit'' "v5" $ testFullAsyncSlow testCfgSlow testCfgSlow - xit'' "v5/curr" $ testFullAsyncSlow testCfgSlow testCfg - xit'' "curr/v5" $ testFullAsyncSlow testCfg testCfgSlow + xit'' "v5" $ testFullAsyncSlow False testCfgSlow testCfgSlow + xit'' "v5/curr" $ testFullAsyncSlow False testCfgSlow testCfg + xit'' "curr/v5" $ testFullAsyncSlow True testCfg testCfgSlow describe "webrtc calls api" $ do it "negotiate call" testNegotiateCall #if !defined(dbPostgres) @@ -183,9 +183,9 @@ chatDirectTests = do testAddContact :: HasCallStack => SpecWith TestParams testAddContact = versionTestMatrix2 runTestAddContact where - runTestAddContact pqExpected alice bob = do + runTestAddContact pqExpected withShortLink alice bob = do alice ##> "/_connect 1" - inv <- getInvitation alice + inv <- (if withShortLink then getInvitation else getInvitationNoShortLink) alice bob ##> ("/_connect 1 " <> inv) bob <## "confirmation sent!" concurrently_ @@ -1237,12 +1237,12 @@ testOperators = where opts' = testOpts {coreOptions = testCoreOpts {smpServers = [], xftpServers = []}} -testAsyncInitiatingOffline :: HasCallStack => ChatConfig -> ChatConfig -> TestParams -> IO () -testAsyncInitiatingOffline aliceCfg bobCfg ps = do +testAsyncInitiatingOffline :: HasCallStack => Bool -> ChatConfig -> ChatConfig -> TestParams -> IO () +testAsyncInitiatingOffline withShortLink aliceCfg bobCfg ps = do inv <- withNewTestChatCfg ps aliceCfg "alice" aliceProfile $ \alice -> do threadDelay 250000 alice ##> "/c" - getInvitation alice + (if withShortLink then getInvitation else getInvitationNoShortLink) alice withNewTestChatCfg ps bobCfg "bob" bobProfile $ \bob -> do threadDelay 250000 bob ##> ("/c " <> inv) @@ -1252,11 +1252,11 @@ testAsyncInitiatingOffline aliceCfg bobCfg ps = do (bob <## "alice (Alice): contact is connected") (alice <## "bob (Bob): contact is connected") -testAsyncAcceptingOffline :: HasCallStack => ChatConfig -> ChatConfig -> TestParams -> IO () -testAsyncAcceptingOffline aliceCfg bobCfg ps = do +testAsyncAcceptingOffline :: HasCallStack => Bool -> ChatConfig -> ChatConfig -> TestParams -> IO () +testAsyncAcceptingOffline withShortLink aliceCfg bobCfg ps = do inv <- withNewTestChatCfg ps aliceCfg "alice" aliceProfile $ \alice -> do alice ##> "/c" - getInvitation alice + (if withShortLink then getInvitation else getInvitationNoShortLink) alice withNewTestChatCfg ps bobCfg "bob" bobProfile $ \bob -> do threadDelay 250000 bob ##> ("/c " <> inv) @@ -1283,12 +1283,12 @@ testFullAsyncFast ps = do withTestChat ps "bob" $ \bob -> bob <## "alice (Alice): contact is connected" -testFullAsyncSlow :: HasCallStack => ChatConfig -> ChatConfig -> TestParams -> IO () -testFullAsyncSlow aliceCfg bobCfg ps = do +testFullAsyncSlow :: HasCallStack => Bool -> ChatConfig -> ChatConfig -> TestParams -> IO () +testFullAsyncSlow withShortLink aliceCfg bobCfg ps = do inv <- withNewTestChatCfg ps aliceCfg "alice" aliceProfile $ \alice -> do threadDelay 250000 alice ##> "/c" - getInvitation alice + (if withShortLink then getInvitation else getInvitationNoShortLink) alice withNewTestChatCfg ps bobCfg "bob" bobProfile $ \bob -> do threadDelay 250000 bob ##> ("/c " <> inv) diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 436e77f50a..9d69b4fc27 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -3442,7 +3442,7 @@ testPlanGroupLinkConnectingSlow ps = do alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" alice ##> "/create link #team" - getGroupLink alice "team" GRMember True + getGroupLinkNoShortLink alice "team" GRMember True withNewTestChatCfg ps testCfgSlow "bob" bobProfile $ \bob -> do threadDelay 100000 @@ -5865,13 +5865,13 @@ testMembershipProfileUpdateContactActive = checkItems bob alice ##> "/ad" - cLink <- getContactLink alice True + (sLink, _cLink) <- getContactLinks alice True alice ##> "/pa on" alice <## "new contact address set" bob <## "alisa set new contact address, use /info alisa to view" bob `hasContactProfiles` ["alisa", "bob"] - checkAliceProfileLink bob "alisa" cLink + checkAliceProfileLink bob "alisa" sLink -- profile update does not remove contact address from profile alice ##> "/p 'Alice Smith'" @@ -5880,14 +5880,14 @@ testMembershipProfileUpdateContactActive = bob <## "use @'Alice Smith' to send messages" bob `hasContactProfiles` ["Alice Smith", "bob"] - checkAliceProfileLink bob "'Alice Smith'" cLink + checkAliceProfileLink bob "'Alice Smith'" sLink -- receiving group message does not remove contact address from profile alice #> "#team team 2" bob <# "#team 'Alice Smith'> team 2" bob `hasContactProfiles` ["Alice Smith", "bob"] - checkAliceProfileLink bob "'Alice Smith'" cLink + checkAliceProfileLink bob "'Alice Smith'" sLink checkItems bob where @@ -5899,13 +5899,13 @@ testMembershipProfileUpdateContactActive = bob ##> "/_get chat #1 count=100" rGrp <- chat <$> getTermLine bob rGrp `shouldNotContain` [(0, "updated profile")] - checkAliceProfileLink bob name cLink = do + checkAliceProfileLink bob name sLink = do bob ##> ("/info #team " <> name) bob <## "group ID: 1" bob <## "member ID: 1" bob <##. "receiving messages via" bob <##. "sending messages via" - bob <## ("contact address: " <> cLink) + bob <## ("contact address: " <> sLink) bob <## "connection not verified, use /code command to see security code" bob <## currentChatVRangeInfo diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index 8a6f91204e..7e04468421 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -48,10 +48,12 @@ chatProfileTests = do it "deduplicate contact requests with profile change" testDeduplicateContactRequestsProfileChange it "reject contact and delete contact link" testRejectContactAndDeleteUserContact it "delete connection requests when contact link deleted" testDeleteConnectionRequests + -- TODO [short links] test auto-reply with current version, with connecting client not preparing contact it "auto-reply message" testAutoReplyMessage it "auto-reply message in incognito" testAutoReplyMessageInIncognito describe "business address" $ do it "create and connect via business address" testBusinessAddress + -- TODO [short links] test business auto-reply with current version, with connecting client not preparing contact it "update profiles with business address" testBusinessUpdateProfiles describe "contact address connection plan" $ do it "contact address ok to connect; known contact" testPlanAddressOkKnown @@ -60,6 +62,7 @@ chatProfileTests = do it "connecting via contact address (slow handshake)" testPlanAddressConnectingSlow it "re-connect with deleted contact" testPlanAddressContactDeletedReconnected it "contact via address" testPlanAddressContactViaAddress + it "contact via short address" testPlanAddressContactViaShortAddress describe "incognito" $ do it "connect incognito via invitation link" testConnectIncognitoInvitationLink it "connect incognito via contact address" testConnectIncognitoContactAddress @@ -357,9 +360,9 @@ testProfileLink = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do alice ##> "/ad" - cLink <- getContactLink alice True + (sLink, _cLink) <- getContactLinks alice True - bob ##> ("/c " <> cLink) + bob ##> ("/c " <> sLink) alice <#? bob alice ##> "/ac bob" alice <## "bob (Bob): accepting contact request, you can send messages to contact" @@ -372,9 +375,9 @@ testProfileLink = alice <## "new contact address set" bob <## "alice set new contact address, use /info alice to view" - checkAliceProfileLink bob cLink + checkAliceProfileLink bob sLink - cath ##> ("/c " <> cLink) + cath ##> ("/c " <> sLink) alice <#? cath alice ##> "/ac cath" alice <## "cath (Catherine): accepting contact request, you can send messages to contact" @@ -383,7 +386,7 @@ testProfileLink = (alice <## "cath (Catherine): contact is connected") alice <##> cath - checkAliceProfileLink cath cLink + checkAliceProfileLink cath sLink alice ##> "/pa off" alice <## "contact address removed" @@ -398,10 +401,10 @@ testProfileLink = alice <## "new contact address set" bob <## "alice set new contact address, use /info alice to view" - checkAliceProfileLink bob cLink + checkAliceProfileLink bob sLink cath <## "alice set new contact address, use /info alice to view" - checkAliceProfileLink cath cLink + checkAliceProfileLink cath sLink alice ##> "/da" alice <## "Your chat address is deleted - accepted contacts will remain connected." @@ -413,12 +416,12 @@ testProfileLink = cath <## "alice removed contact address" checkAliceNoProfileLink cath where - checkAliceProfileLink cc cLink = do + checkAliceProfileLink cc sLink = do cc ##> "/info alice" cc <## "contact ID: 2" cc <##. "receiving messages via" cc <##. "sending messages via" - cc <## ("contact address: " <> cLink) + cc <## ("contact address: " <> sLink) cc <## "you've shared main profile with this contact" cc <## "connection not verified, use /code command to see security code" cc <## "quantum resistant end-to-end encryption" @@ -666,10 +669,10 @@ testDeleteConnectionRequests = testChat3 aliceProfile bobProfile cathProfile $ alice <#? cath testAutoReplyMessage :: HasCallStack => TestParams -> IO () -testAutoReplyMessage = testChat2 aliceProfile bobProfile $ +testAutoReplyMessage = testChatCfg2 testCfgNoShortLinks aliceProfile bobProfile $ \alice bob -> do alice ##> "/ad" - cLink <- getContactLink alice True + cLink <- getContactLinkNoShortLink alice True alice ##> "/_auto_accept 1 on incognito=off text hello!" alice <## "auto_accept on" alice <## "auto reply:" @@ -688,10 +691,10 @@ testAutoReplyMessage = testChat2 aliceProfile bobProfile $ ] testAutoReplyMessageInIncognito :: HasCallStack => TestParams -> IO () -testAutoReplyMessageInIncognito = testChat2 aliceProfile bobProfile $ +testAutoReplyMessageInIncognito = testChatCfg2 testCfgNoShortLinks aliceProfile bobProfile $ \alice bob -> do alice ##> "/ad" - cLink <- getContactLink alice True + cLink <- getContactLinkNoShortLink alice True alice ##> "/auto_accept on incognito=on text hello!" alice <## "auto_accept on, incognito" alice <## "auto reply:" @@ -767,10 +770,10 @@ testBusinessAddress = testChat3 businessProfile aliceProfile {fullName = "Alice (biz <# "#bob bob_1> hey there") testBusinessUpdateProfiles :: HasCallStack => TestParams -> IO () -testBusinessUpdateProfiles = testChat4 businessProfile aliceProfile bobProfile cathProfile $ +testBusinessUpdateProfiles = testChatCfg4 testCfgNoShortLinks businessProfile aliceProfile bobProfile cathProfile $ \biz alice bob cath -> do biz ##> "/ad" - cLink <- getContactLink biz True + cLink <- getContactLinkNoShortLink biz True biz ##> "/auto_accept on business text Welcome" biz <## "auto_accept on, business" biz <## "auto reply:" @@ -800,7 +803,7 @@ testBusinessUpdateProfiles = testChat4 businessProfile aliceProfile bobProfile c biz ##> "/mr alisa alisa_1 admin" biz <## "#alisa: you changed the role of alisa_1 to admin" alice <## "#biz: biz_1 changed your role from member to admin" - connectUsers alice bob + connectUsersNoShortLink alice bob alice ##> "/a #biz bob" alice <## "invitation to join the group #biz sent to bob" bob <## "#biz (Biz Inc): alisa invites you to join the group as member" @@ -831,7 +834,7 @@ testBusinessUpdateProfiles = testChat4 businessProfile aliceProfile bobProfile c alice <# "#biz robert> hi there" biz <# "#alisa robert> hi there" -- add business team member - connectUsers biz cath + connectUsersNoShortLink biz cath biz ##> "/a #alisa cath" biz <## "invitation to join the group #alisa sent to cath" cath <## "#alisa: biz invites you to join the group as member" @@ -1025,7 +1028,7 @@ testPlanAddressConnectingSlow :: HasCallStack => TestParams -> IO () testPlanAddressConnectingSlow ps = do cLink <- withNewTestChatCfg ps testCfgSlow "alice" aliceProfile $ \alice -> do alice ##> "/ad" - getContactLink alice True + getContactLinkNoShortLink alice True withNewTestChatCfg ps testCfgSlow "bob" bobProfile $ \bob -> do threadDelay 100000 @@ -1130,7 +1133,7 @@ testPlanAddressContactViaAddress = testChat2 aliceProfile bobProfile $ \alice bob -> do alice ##> "/ad" - cLink <- getContactLink alice True + (_sLink, cLink) <- getContactLinks alice True alice ##> "/pa on" -- not necessary, without it bob would receive profile update removing contact link alice <## "new contact address set" @@ -1151,10 +1154,6 @@ testPlanAddressContactViaAddress = bob ##> ("/_connect plan 1 " <> cLink) bob <## "contact address: known contact without connection alice" - let cLinkSchema2 = linkAnotherSchema cLink - bob ##> ("/_connect plan 1 " <> cLinkSchema2) - bob <## "contact address: known contact without connection alice" - -- terminal api bob ##> ("/c " <> cLink) connecting alice bob @@ -1175,6 +1174,68 @@ testPlanAddressContactViaAddress = #endif connecting alice bob where + connecting alice bob = do + bob <## "connection request sent!" + alice <## "bob (Bob) wants to connect to you!" + alice <## "to accept: /ac bob" + alice <## "to reject: /rc bob (the sender will NOT be notified)" + alice ##> "/ac bob" + alice <## "bob (Bob): accepting contact request, you can send messages to contact" + concurrentlyN_ + [ do + bob <## "alice set new contact address, use /info alice to view" + bob <## "alice (Alice): contact is connected", + alice <## "bob (Bob): contact is connected" + ] + alice <##> bob + bob @@@ [("@alice", "hey")] + +testPlanAddressContactViaShortAddress :: HasCallStack => TestParams -> IO () +testPlanAddressContactViaShortAddress = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + alice ##> "/ad" + (sLink, _) <- getContactLinks alice True + + alice ##> "/pa on" -- not necessary, without it bob would receive profile update removing contact link + alice <## "new contact address set" + + case A.parseOnly strP (B.pack sLink) of + Left _ -> error "error parsing contact link" + Right shortLink -> do + let profile = aliceProfile {contactLink = Just shortLink} + void $ withCCUser bob $ \user -> withCCTransaction bob $ \db -> runExceptT $ createContact db user profile + bob @@@ [("@alice", "")] + + bob ##> "/delete @alice" + bob <## "alice: contact is deleted" + + void $ withCCUser bob $ \user -> withCCTransaction bob $ \db -> runExceptT $ createContact db user profile + bob @@@ [("@alice", "")] + + bob ##> ("/_connect plan 1 " <> sLink) + bob <## "contact address: known contact without connection alice" + + -- terminal api + bob ##> ("/c " <> sLink) + connecting alice bob + + bob ##> "/delete @alice" + bob <## "alice: contact is deleted" + alice ##> "/delete @bob" + alice <## "bob: contact is deleted" + + void $ withCCUser bob $ \user -> withCCTransaction bob $ \db -> runExceptT $ createContact db user profile + bob @@@ [("@alice", "")] + + -- GUI api +#if defined(dbPostgres) + bob ##> "/_connect contact 1 4" +#else + bob ##> "/_connect contact 1 2" +#endif + connecting alice bob + where connecting alice bob = do bob <## "connection request sent!" alice <## "bob (Bob) wants to connect to you!" @@ -1185,7 +1246,6 @@ testPlanAddressContactViaAddress = concurrently_ (bob <## "alice (Alice): contact is connected") (alice <## "bob (Bob): contact is connected") - alice <##> bob bob @@@ [("@alice", "hey")] @@ -1302,10 +1362,10 @@ testConnectIncognitoContactAddress = testChat2 aliceProfile bobProfile $ bob `hasContactProfiles` ["bob"] testAcceptContactRequestIncognito :: HasCallStack => TestParams -> IO () -testAcceptContactRequestIncognito = testChat3 aliceProfile bobProfile cathProfile $ +testAcceptContactRequestIncognito = testChatCfg3 testCfgNoShortLinks aliceProfile bobProfile cathProfile $ \alice bob cath -> do alice ##> "/ad" - cLink <- getContactLink alice True + cLink <- getContactLinkNoShortLink alice True -- GUI /_accept api bob ##> ("/c " <> cLink) alice <#? bob @@ -1415,7 +1475,7 @@ testSetConnectionIncognitoProhibitedDuringNegotiationSlow ps = do inv <- withNewTestChatCfg ps testCfgSlow "alice" aliceProfile $ \alice -> do threadDelay 250000 alice ##> "/connect" - getInvitation alice + getInvitationNoShortLink alice withNewTestChatCfg ps testCfgSlow "bob" bobProfile $ \bob -> do threadDelay 250000 bob ##> ("/c " <> inv) @@ -1869,14 +1929,18 @@ testChangePCCUser = testChat2 aliceProfile bobProfile $ alice <## "" _ <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + _ <- getTermLine alice alice ##> "/user alisa" showActiveUser alice "alisa" -- Change connection back to other user alice ##> "/_set conn user :1 3" alice <## "connection 1 changed from user alisa to user alisa2, new link:" alice <## "" - inv <- getTermLine alice + _shortInv <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + inv <- getTermLine alice alice ##> "/user alisa2" showActiveUser alice "alisa2" -- Connect @@ -1907,6 +1971,8 @@ testChangePCCUserFromIncognito = testChat2 aliceProfile bobProfile $ alice <## "" _ <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + _ <- getTermLine alice alice `hasContactProfiles` ["alice"] alice ##> "/user alisa" showActiveUser alice "alisa" @@ -1914,8 +1980,10 @@ testChangePCCUserFromIncognito = testChat2 aliceProfile bobProfile $ alice ##> "/_set conn user :1 1" alice <## "connection 1 changed from user alisa to user alice, new link:" alice <## "" - inv <- getTermLine alice + _shortInv <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + inv <- getTermLine alice alice ##> "/user alice" showActiveUser alice "alice (Alice)" -- Connect @@ -1941,8 +2009,10 @@ testChangePCCUserAndThenIncognito = testChat2 aliceProfile bobProfile $ alice ##> "/_set conn user :1 2" alice <## "connection 1 changed from user alice to user alisa, new link:" alice <## "" - inv <- getTermLine alice + _shortInv <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + inv <- getTermLine alice alice ##> "/user alisa" showActiveUser alice "alisa" -- Change connection to incognito and make sure it's attached to the newly created user profile @@ -1992,8 +2062,10 @@ testChangePCCUserDiffSrv ps = do alice ##> "/_set conn user :1 2" alice <## "connection 1 changed from user alice to user alisa, new link:" alice <## "" - inv <- getTermLine alice + _shortInv <- getTermLine alice alice <## "" + alice <## "The invitation link for old clients:" + inv <- getTermLine alice alice `hasContactProfiles` ["alice"] alice ##> "/user alisa" showActiveUser alice "alisa" @@ -2645,8 +2717,8 @@ testSetUITheme = testShortLinkInvitation :: HasCallStack => TestParams -> IO () testShortLinkInvitation = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/c short" - (inv, _) <- getShortInvitation alice + alice ##> "/c" + (inv, _) <- getInvitations alice bob ##> ("/c " <> inv) bob <## "confirmation sent!" concurrently_ @@ -2660,8 +2732,8 @@ testShortLinkInvitation = testPlanShortLinkInvitation :: HasCallStack => TestParams -> IO () testPlanShortLinkInvitation = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do - alice ##> "/c short" - (inv, _) <- getShortInvitation alice + alice ##> "/c" + (inv, _) <- getInvitations alice alice ##> ("/_connect plan 1 " <> inv) alice <## "invitation link: own link" alice ##> ("/_connect plan 1 " <> slSimplexScheme inv) @@ -2702,8 +2774,8 @@ slSimplexScheme sl = T.unpack $ T.replace "https://localhost/" "simplex:/" (T.pa testShortLinkContactAddress :: HasCallStack => TestParams -> IO () testShortLinkContactAddress = testChat4 aliceProfile bobProfile cathProfile danProfile $ \alice bob cath dan -> do - alice ##> "/ad short" - (shortLink, fullLink) <- getShortContactLink alice True + alice ##> "/ad" + (shortLink, fullLink) <- getContactLinks alice True alice ##> ("/_connect plan 1 " <> shortLink) alice <## "contact address: own address" alice ##> ("/_connect plan 1 " <> slSimplexScheme shortLink) @@ -2741,13 +2813,13 @@ testShortLinkJoinGroup :: HasCallStack => TestParams -> IO () testShortLinkJoinGroup = testChat4 aliceProfile bobProfile cathProfile danProfile $ \alice bob cath dan -> do threadDelay 100000 - alice ##> "/ad short" -- create the address to test that it can co-exist with group link - _ <- getShortContactLink alice True + alice ##> "/ad" -- create the address to test that it can co-exist with group link + _ <- getContactLinks alice True alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True alice ##> ("/_connect plan 1 " <> shortLink) alice <## "group link: own link for group #team" alice ##> ("/_connect plan 1 " <> slSimplexScheme shortLink) @@ -2810,8 +2882,8 @@ testShortLinkInvitationPrepareContact :: HasCallStack => TestParams -> IO () testShortLinkInvitationPrepareContact = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/_connect 1 short=on" - (shortLink, fullLink) <- getShortInvitation alice + alice ##> "/_connect 1" + (shortLink, fullLink) <- getInvitations alice bob ##> ("/_connect plan 1 " <> shortLink) bob <## "invitation link: ok to connect" contactSLinkData <- getTermLine bob @@ -2830,8 +2902,8 @@ testShortLinkInvitationPrepareContact = testShortLinkInvitationImage :: HasCallStack => TestParams -> IO () testShortLinkInvitationImage = testChat2 aliceProfile bobProfile $ \alice bob -> do - bob ##> "/_connect 1 short=on" - (shortLink, fullLink) <- getShortInvitation bob + bob ##> "/_connect 1" + (shortLink, fullLink) <- getInvitations bob alice ##> ("/_connect plan 1 " <> shortLink) alice <## "invitation link: ok to connect" contactSLinkData <- getTermLine alice @@ -2852,8 +2924,8 @@ testShortLinkAddressPrepareContact :: HasCallStack => TestParams -> IO () testShortLinkAddressPrepareContact = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/ad short" - (shortLink, fullLink) <- getShortContactLink alice True + alice ##> "/ad" + (shortLink, fullLink) <- getContactLinks alice True bob ##> ("/_connect plan 1 " <> shortLink) bob <## "contact address: ok to connect" contactSLinkData <- getTermLine bob @@ -2884,8 +2956,8 @@ testShortLinkPrepareGroup = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup2 "team" alice cath - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True bob ##> ("/_connect plan 1 " <> shortLink) bob <## "group link: ok to connect" groupSLinkData <- getTermLine bob @@ -2916,8 +2988,8 @@ testShortLinkPrepareGroupReject = testChatCfg3 cfg aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup2 "team" alice cath - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True bob ##> ("/_connect plan 1 " <> shortLink) bob <## "group link: ok to connect" groupSLinkData <- getTermLine bob @@ -2949,8 +3021,8 @@ testShortLinkChangePreparedContactUser = bob ##> "/user bob" showActiveUser bob "bob (Bob)" - alice ##> "/_connect 1 short=on" - (shortLink, fullLink) <- getShortInvitation alice + alice ##> "/_connect 1" + (shortLink, fullLink) <- getInvitations alice bob ##> ("/_connect plan 1 " <> shortLink) bob <## "invitation link: ok to connect" contactSLinkData <- getTermLine bob @@ -2998,8 +3070,8 @@ testShortLinkChangePreparedContactUserDuplicate = bob ##> "/user bob" showActiveUser bob "bob (Bob)" - alice ##> "/_connect 1 short=on" - (shortLink, fullLink) <- getShortInvitation alice + alice ##> "/_connect 1" + (shortLink, fullLink) <- getInvitations alice bob ##> ("/_connect plan 1 " <> shortLink) bob <## "invitation link: ok to connect" contactSLinkData <- getTermLine bob @@ -3047,8 +3119,8 @@ testShortLinkChangePreparedGroupUser = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup2 "team" alice cath - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True bob ##> "/create user robert" showActiveUser bob "robert" @@ -3105,8 +3177,8 @@ testShortLinkChangePreparedGroupUserDuplicate = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup2 "team" alice cath - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True bob ##> "/create user robert" showActiveUser bob "robert" @@ -3217,8 +3289,8 @@ testShortLinkInvitationSetIncognito :: HasCallStack => TestParams -> IO () testShortLinkInvitationSetIncognito = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/_connect 1 short=on" - (shortLink, fullLink) <- getShortInvitation alice + alice ##> "/_connect 1" + (shortLink, fullLink) <- getInvitations alice alice ##> "/_set incognito :1 on" aliceIncognito <- getTermLine alice @@ -3256,8 +3328,8 @@ testShortLinkInvitationChangeUser = alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice ##> "/_connect 1 short=on" - _ <- getShortInvitation alice + alice ##> "/_connect 1" + _ <- getInvitations alice alice ##> "/_set conn user :1 2" alice <## "connection 1 changed from user alice to user alisa, new link:" @@ -3289,8 +3361,8 @@ testShortLinkAddressChangeProfile :: HasCallStack => TestParams -> IO () testShortLinkAddressChangeProfile = testChat2 aliceProfile bobProfile $ \alice bob -> do - alice ##> "/ad short" - (shortLink, fullLink) <- getShortContactLink alice True + alice ##> "/ad" + (shortLink, fullLink) <- getContactLinks alice True alice ##> "/p alisa" alice <## "user profile is changed to alisa (your 0 contacts are notified)" @@ -3324,8 +3396,8 @@ testShortLinkAddressChangeAutoReply :: HasCallStack => TestParams -> IO () testShortLinkAddressChangeAutoReply = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do - alice ##> "/ad short" - (shortLink, fullLink) <- getShortContactLink alice True + alice ##> "/ad" + (shortLink, fullLink) <- getContactLinks alice True alice ##> "/_auto_accept 1 on incognito=off text welcome!" alice <## "auto_accept on" @@ -3378,8 +3450,8 @@ testShortLinkGroupChangeProfile = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup2 "team" alice cath - alice ##> "/create link #team short" - (shortLink, fullLink) <- getShortGroupLink alice "team" GRMember True + alice ##> "/create link #team" + (shortLink, fullLink) <- getGroupLinks alice "team" GRMember True alice ##> "/gp team club" alice <## "changed to #club" diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index bb42c265e8..c3856069ac 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -101,15 +101,15 @@ skip :: String -> SpecWith a -> SpecWith a skip = before_ . pendingWith -- Bool is pqExpected - see testAddContact -versionTestMatrix2 :: (HasCallStack => Bool -> TestCC -> TestCC -> IO ()) -> SpecWith TestParams +versionTestMatrix2 :: (HasCallStack => Bool -> Bool -> TestCC -> TestCC -> IO ()) -> SpecWith TestParams versionTestMatrix2 runTest = do - it "current" $ testChat2 aliceProfile bobProfile (runTest True) - it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest False) - it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest False) - it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest False) - it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest False) - it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest False) - it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest False) + it "current" $ testChat2 aliceProfile bobProfile (runTest True True) + it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest False True) + it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest False True) + it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest False True) + it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest False False) + it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest False True) + it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest False False) versionTestMatrix3 :: (HasCallStack => TestCC -> TestCC -> TestCC -> IO ()) -> SpecWith TestParams versionTestMatrix3 runTest = do @@ -506,35 +506,44 @@ dropPartialReceipt_ msg = case splitAt 2 msg of getInvitation :: HasCallStack => TestCC -> IO String getInvitation cc = do - (inv, _) <- getInvitation_ False cc - pure inv + (_, fullInv) <- getInvitations cc + pure fullInv -getShortInvitation :: HasCallStack => TestCC -> IO (String, String) -getShortInvitation = getInvitation_ True +getInvitations :: HasCallStack => TestCC -> IO (String, String) +getInvitations cc = do + shortInv <- getInvitation_ cc + cc <##. "The invitation link for old clients:" + fullInv <- getTermLine cc + pure (shortInv, fullInv) -getInvitation_ :: HasCallStack => Bool -> TestCC -> IO (String, String) -getInvitation_ short cc = do +getInvitationNoShortLink :: HasCallStack => TestCC -> IO String +getInvitationNoShortLink = getInvitation_ + +getInvitation_ :: HasCallStack => TestCC -> IO String +getInvitation_ cc = do cc <## "pass this invitation link to your contact (via another channel):" cc <## "" inv <- getTermLine cc cc <## "" cc <## "and ask them to connect: /c " - fullLink <- - if short - then do - cc <##. "The invitation link for old clients:" - getTermLine cc - else pure "" - pure (inv, fullLink) - -getShortContactLink :: HasCallStack => TestCC -> Bool -> IO (String, String) -getShortContactLink cc created = do - shortLink <- getContactLink cc created - fullLink <- dropLinePrefix "The contact link for old clients: " =<< getTermLine cc - pure (shortLink, fullLink) + pure inv getContactLink :: HasCallStack => TestCC -> Bool -> IO String getContactLink cc created = do + (_shortLink, fullLink) <- getContactLinks cc created + pure fullLink + +getContactLinks :: HasCallStack => TestCC -> Bool -> IO (String, String) +getContactLinks cc created = do + shortLink <- getContactLink_ cc created + fullLink <- dropLinePrefix "The contact link for old clients: " =<< getTermLine cc + pure (shortLink, fullLink) + +getContactLinkNoShortLink :: HasCallStack => TestCC -> Bool -> IO String +getContactLinkNoShortLink = getContactLink_ + +getContactLink_ :: HasCallStack => TestCC -> Bool -> IO String +getContactLink_ cc created = do cc <## if created then "Your new chat address is created!" else "Your chat address:" cc <## "" link <- getTermLine cc @@ -550,14 +559,22 @@ dropLinePrefix line s | line `isPrefixOf` s = pure $ drop (length line) s | otherwise = error $ "expected to start from: " <> line <> ", got: " <> s -getShortGroupLink :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO (String, String) -getShortGroupLink cc gName mRole created = do - shortLink <- getGroupLink cc gName mRole created +getGroupLink :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO String +getGroupLink cc gName mRole created = do + (_shortLink, fullLink) <- getGroupLinks cc gName mRole created + pure fullLink + +getGroupLinks :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO (String, String) +getGroupLinks cc gName mRole created = do + shortLink <- getGroupLink_ cc gName mRole created fullLink <- dropLinePrefix "The group link for old clients: " =<< getTermLine cc pure (shortLink, fullLink) -getGroupLink :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO String -getGroupLink cc gName mRole created = do +getGroupLinkNoShortLink :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO String +getGroupLinkNoShortLink = getGroupLink_ + +getGroupLink_ :: HasCallStack => TestCC -> String -> GroupMemberRole -> Bool -> IO String +getGroupLink_ cc gName mRole created = do cc <## if created then "Group link is created!" else "Group link:" cc <## "" link <- getTermLine cc @@ -646,12 +663,20 @@ showActiveUser cc name = do cc <## "use /p to change it" cc <## "(the updated profile will be sent to all your contacts)" +connectUsersNoShortLink :: HasCallStack => TestCC -> TestCC -> IO () +connectUsersNoShortLink cc1 cc2 = connectUsers_ cc1 cc2 True + connectUsers :: HasCallStack => TestCC -> TestCC -> IO () -connectUsers cc1 cc2 = do +connectUsers cc1 cc2 = connectUsers_ cc1 cc2 False + +connectUsers_ :: HasCallStack => TestCC -> TestCC -> Bool -> IO () +connectUsers_ cc1 cc2 noShortLink = do name1 <- showName cc1 name2 <- showName cc2 cc1 ##> "/c" - inv <- getInvitation cc1 + inv <- if noShortLink + then getInvitationNoShortLink cc1 + else getInvitation cc1 cc2 ##> ("/c " <> inv) cc2 <## "confirmation sent!" concurrently_