diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index bad15ad52..99e8c0284 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -671,18 +671,18 @@ private func connectionErrorAlert(_ r: ChatResponse) -> Alert {
}
}
-func apiDeleteChat(type: ChatType, id: Int64) async throws {
- let r = await chatSendCmd(.apiDeleteChat(type: type, id: id), bgTask: false)
+func apiDeleteChat(type: ChatType, id: Int64, notify: Bool? = nil) async throws {
+ let r = await chatSendCmd(.apiDeleteChat(type: type, id: id, notify: notify), bgTask: false)
if case .direct = type, case .contactDeleted = r { return }
if case .contactConnection = type, case .contactConnectionDeleted = r { return }
if case .group = type, case .groupDeletedUser = r { return }
throw r
}
-func deleteChat(_ chat: Chat) async {
+func deleteChat(_ chat: Chat, notify: Bool? = nil) async {
do {
let cInfo = chat.chatInfo
- try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId)
+ try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId, notify: notify)
DispatchQueue.main.async { ChatModel.shared.removeChat(cInfo.id) }
} catch let error {
logger.error("deleteChat apiDeleteChat error: \(responseError(error))")
diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift
index 5438eb13b..ec4cc0fc4 100644
--- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift
+++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift
@@ -99,12 +99,12 @@ struct ChatInfoView: View {
@Binding var connectionCode: String?
@FocusState private var aliasTextFieldFocused: Bool
@State private var alert: ChatInfoViewAlert? = nil
+ @State private var showDeleteContactActionSheet = false
@State private var sendReceipts = SendReceipts.userDefault(true)
@State private var sendReceiptsUserDefault = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
enum ChatInfoViewAlert: Identifiable {
- case deleteContactAlert
case clearChatAlert
case networkStatusAlert
case switchAddressAlert
@@ -114,7 +114,6 @@ struct ChatInfoView: View {
var id: String {
switch self {
- case .deleteContactAlert: return "deleteContactAlert"
case .clearChatAlert: return "clearChatAlert"
case .networkStatusAlert: return "networkStatusAlert"
case .switchAddressAlert: return "switchAddressAlert"
@@ -233,7 +232,6 @@ struct ChatInfoView: View {
}
.alert(item: $alert) { alertItem in
switch(alertItem) {
- case .deleteContactAlert: return deleteContactAlert()
case .clearChatAlert: return clearChatAlert()
case .networkStatusAlert: return networkStatusAlert()
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
@@ -242,6 +240,26 @@ struct ChatInfoView: View {
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
+ .actionSheet(isPresented: $showDeleteContactActionSheet) {
+ if contact.ready && contact.active {
+ ActionSheet(
+ title: Text("Delete contact?\nThis cannot be undone!"),
+ buttons: [
+ .destructive(Text("Delete and notify contact")) { deleteContact(notify: true) },
+ .destructive(Text("Delete")) { deleteContact(notify: false) },
+ .cancel()
+ ]
+ )
+ } else {
+ ActionSheet(
+ title: Text("Delete contact?\nThis cannot be undone!"),
+ buttons: [
+ .destructive(Text("Delete")) { deleteContact() },
+ .cancel()
+ ]
+ )
+ }
+ }
}
private func contactInfoHeader() -> some View {
@@ -414,7 +432,7 @@ struct ChatInfoView: View {
private func deleteContactButton() -> some View {
Button(role: .destructive) {
- alert = .deleteContactAlert
+ showDeleteContactActionSheet = true
} label: {
Label("Delete contact", systemImage: "trash")
.foregroundColor(Color.red)
@@ -430,30 +448,23 @@ struct ChatInfoView: View {
}
}
- private func deleteContactAlert() -> Alert {
- Alert(
- title: Text("Delete contact?"),
- message: Text("Contact and all messages will be deleted - this cannot be undone!"),
- primaryButton: .destructive(Text("Delete")) {
- Task {
- do {
- try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
- await MainActor.run {
- dismiss()
- chatModel.chatId = nil
- chatModel.removeChat(chat.chatInfo.id)
- }
- } catch let error {
- logger.error("deleteContactAlert apiDeleteChat error: \(responseError(error))")
- let a = getErrorAlert(error, "Error deleting contact")
- await MainActor.run {
- alert = .error(title: a.title, error: a.message)
- }
- }
+ private func deleteContact(notify: Bool? = nil) {
+ Task {
+ do {
+ try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, notify: notify)
+ await MainActor.run {
+ dismiss()
+ chatModel.chatId = nil
+ chatModel.removeChat(chat.chatInfo.id)
}
- },
- secondaryButton: .cancel()
- )
+ } catch let error {
+ logger.error("deleteContactAlert apiDeleteChat error: \(responseError(error))")
+ let a = getErrorAlert(error, "Error deleting contact")
+ await MainActor.run {
+ alert = .error(title: a.title, error: a.message)
+ }
+ }
+ }
}
private func clearChatAlert() -> Alert {
diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
index f445ae4b5..be912d666 100644
--- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
+++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
@@ -32,6 +32,7 @@ struct ChatListNavLink: View {
@State private var showJoinGroupDialog = false
@State private var showContactConnectionInfo = false
@State private var showInvalidJSON = false
+ @State private var showDeleteContactActionSheet = false
var body: some View {
switch chat.chatInfo {
@@ -64,17 +65,37 @@ struct ChatListNavLink: View {
clearChatButton()
}
Button {
- AlertManager.shared.showAlert(
- contact.ready || !contact.active
- ? deleteContactAlert(chat.chatInfo)
- : deletePendingContactAlert(chat, contact)
- )
+ if contact.ready || !contact.active {
+ showDeleteContactActionSheet = true
+ } else {
+ AlertManager.shared.showAlert(deletePendingContactAlert(chat, contact))
+ }
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
.frame(height: rowHeights[dynamicTypeSize])
+ .actionSheet(isPresented: $showDeleteContactActionSheet) {
+ if contact.ready && contact.active {
+ ActionSheet(
+ title: Text("Delete contact?\nThis cannot be undone!"),
+ buttons: [
+ .destructive(Text("Delete and notify contact")) { Task { await deleteChat(chat, notify: true) } },
+ .destructive(Text("Delete")) { Task { await deleteChat(chat, notify: false) } },
+ .cancel()
+ ]
+ )
+ } else {
+ ActionSheet(
+ title: Text("Delete contact?\nThis cannot be undone!"),
+ buttons: [
+ .destructive(Text("Delete")) { Task { await deleteChat(chat) } },
+ .cancel()
+ ]
+ )
+ }
+ }
}
@ViewBuilder private func groupNavLink(_ groupInfo: GroupInfo) -> some View {
@@ -269,17 +290,6 @@ struct ChatListNavLink: View {
}
}
- private func deleteContactAlert(_ chatInfo: ChatInfo) -> Alert {
- Alert(
- title: Text("Delete contact?"),
- message: Text("Contact and all messages will be deleted - this cannot be undone!"),
- primaryButton: .destructive(Text("Delete")) {
- Task { await deleteChat(chat) }
- },
- secondaryButton: .cancel()
- )
- }
-
private func deleteGroupAlert(_ groupInfo: GroupInfo) -> Alert {
Alert(
title: Text("Delete group?"),
diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
index 71ff7b88b..51bfb9694 100644
--- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
@@ -356,7 +356,7 @@ struct SimplexLockView: View {
var id: Self { self }
}
- let laDelays: [Int] = [10, 30, 60, 180, 0]
+ let laDelays: [Int] = [10, 30, 60, 180, 600, 0]
func laDelayText(_ t: Int) -> LocalizedStringKey {
let m = t / 60
@@ -378,6 +378,7 @@ struct SimplexLockView: View {
Text(mode.text)
}
}
+ .frame(height: 36)
if performLA {
Picker("Lock after", selection: $laLockDelay) {
let delays = laDelays.contains(laLockDelay) ? laDelays : [laLockDelay] + laDelays
@@ -385,6 +386,7 @@ struct SimplexLockView: View {
Text(laDelayText(t))
}
}
+ .frame(height: 36)
if showChangePassword && laMode == .passcode {
Button("Change passcode") {
changeLAPassword()
diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift
index 53da91b04..5c7220f37 100644
--- a/apps/ios/SimpleXChat/APITypes.swift
+++ b/apps/ios/SimpleXChat/APITypes.swift
@@ -89,7 +89,7 @@ public enum ChatCommand {
case apiSetConnectionIncognito(connId: Int64, incognito: Bool)
case apiConnectPlan(userId: Int64, connReq: String)
case apiConnect(userId: Int64, incognito: Bool, connReq: String)
- case apiDeleteChat(type: ChatType, id: Int64)
+ case apiDeleteChat(type: ChatType, id: Int64, notify: Bool?)
case apiClearChat(type: ChatType, id: Int64)
case apiListContacts(userId: Int64)
case apiUpdateProfile(userId: Int64, profile: Profile)
@@ -224,7 +224,11 @@ public enum ChatCommand {
case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))"
case let .apiConnectPlan(userId, connReq): return "/_connect plan \(userId) \(connReq)"
case let .apiConnect(userId, incognito, connReq): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connReq)"
- case let .apiDeleteChat(type, id): return "/_delete \(ref(type, id))"
+ case let .apiDeleteChat(type, id, notify): if let notify = notify {
+ return "/_delete \(ref(type, id)) notify=\(onOff(notify))"
+ } else {
+ return "/_delete \(ref(type, id))"
+ }
case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))"
case let .apiListContacts(userId): return "/_contacts \(userId)"
case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))"
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 8397d2edb..da09ea132 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
@@ -904,8 +904,8 @@ object ChatController {
}
}
- suspend fun apiDeleteChat(type: ChatType, id: Long): Boolean {
- val r = sendCmd(CC.ApiDeleteChat(type, id))
+ suspend fun apiDeleteChat(type: ChatType, id: Long, notify: Boolean? = null): Boolean {
+ val r = sendCmd(CC.ApiDeleteChat(type, id, notify))
when {
r is CR.ContactDeleted && type == ChatType.Direct -> return true
r is CR.ContactConnectionDeleted && type == ChatType.ContactConnection -> return true
@@ -1924,7 +1924,7 @@ sealed class CC {
class ApiSetConnectionIncognito(val connId: Long, val incognito: Boolean): CC()
class APIConnectPlan(val userId: Long, val connReq: String): CC()
class APIConnect(val userId: Long, val incognito: Boolean, val connReq: String): CC()
- class ApiDeleteChat(val type: ChatType, val id: Long): CC()
+ class ApiDeleteChat(val type: ChatType, val id: Long, val notify: Boolean?): CC()
class ApiClearChat(val type: ChatType, val id: Long): CC()
class ApiListContacts(val userId: Long): CC()
class ApiUpdateProfile(val userId: Long, val profile: Profile): CC()
@@ -2034,7 +2034,11 @@ sealed class CC {
is ApiSetConnectionIncognito -> "/_set incognito :$connId ${onOff(incognito)}"
is APIConnectPlan -> "/_connect plan $userId $connReq"
is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} $connReq"
- is ApiDeleteChat -> "/_delete ${chatRef(type, id)}"
+ is ApiDeleteChat -> if (notify != null) {
+ "/_delete ${chatRef(type, id)} notify=${onOff(notify)}"
+ } else {
+ "/_delete ${chatRef(type, id)}"
+ }
is ApiClearChat -> "/_clear chat ${chatRef(type, id)}"
is ApiListContacts -> "/_contacts $userId"
is ApiUpdateProfile -> "/_profile $userId ${json.encodeToString(profile)}"
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt
index 01173157a..4564a4a6e 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt
@@ -196,28 +196,67 @@ sealed class SendReceipts {
}
fun deleteContactDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
- AlertManager.shared.showAlertDialog(
+ AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.delete_contact_question),
- text = generalGetString(MR.strings.delete_contact_all_messages_deleted_cannot_undo_warning),
- confirmText = generalGetString(MR.strings.delete_verb),
- onConfirm = {
- withApi {
- val r = chatModel.controller.apiDeleteChat(chatInfo.chatType, chatInfo.apiId)
- if (r) {
- chatModel.removeChat(chatInfo.id)
- if (chatModel.chatId.value == chatInfo.id) {
- chatModel.chatId.value = null
- ModalManager.end.closeModals()
+ text = AnnotatedString(generalGetString(MR.strings.delete_contact_all_messages_deleted_cannot_undo_warning)),
+ buttons = {
+ Column {
+ if (chatInfo is ChatInfo.Direct && chatInfo.contact.ready && chatInfo.contact.active) {
+ // Delete and notify contact
+ SectionItemView({
+ AlertManager.shared.hideAlert()
+ withApi {
+ deleteContact(chatInfo, chatModel, close, notify = true)
+ }
+ }) {
+ Text(generalGetString(MR.strings.delete_and_notify_contact), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.error)
}
- ntfManager.cancelNotificationsForChat(chatInfo.id)
- close?.invoke()
+ // Delete
+ SectionItemView({
+ AlertManager.shared.hideAlert()
+ withApi {
+ deleteContact(chatInfo, chatModel, close, notify = false)
+ }
+ }) {
+ Text(generalGetString(MR.strings.delete_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.error)
+ }
+ } else {
+ // Delete
+ SectionItemView({
+ AlertManager.shared.hideAlert()
+ withApi {
+ deleteContact(chatInfo, chatModel, close)
+ }
+ }) {
+ Text(generalGetString(MR.strings.delete_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.error)
+ }
+ }
+ // Cancel
+ SectionItemView({
+ AlertManager.shared.hideAlert()
+ }) {
+ Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
- },
- destructive = true,
+ }
)
}
+fun deleteContact(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)?, notify: Boolean? = null) {
+ withApi {
+ val r = chatModel.controller.apiDeleteChat(chatInfo.chatType, chatInfo.apiId, notify)
+ if (r) {
+ chatModel.removeChat(chatInfo.id)
+ if (chatModel.chatId.value == chatInfo.id) {
+ chatModel.chatId.value = null
+ ModalManager.end.closeModals()
+ }
+ ntfManager.cancelNotificationsForChat(chatInfo.id)
+ close?.invoke()
+ }
+ }
+}
+
fun clearChatDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.clear_chat_question),
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 211fe5967..84ab87c65 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
@@ -317,7 +317,7 @@ private fun showUserGroupsReceiptsAlert(
)
}
-private val laDelays = listOf(10, 30, 60, 180, 0)
+private val laDelays = listOf(10, 30, 60, 180, 600, 0)
@Composable
fun SimplexLockView(
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 bd59d236d..1ae85cd05 100644
--- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
+++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
@@ -350,6 +350,7 @@
Delete contact?
Contact and all messages will be deleted - this cannot be undone!
+ Delete and notify contact
Delete contact
Set contact nameā¦
Connected
diff --git a/cabal.project b/cabal.project
index 8816c4c88..8ced5d96e 100644
--- a/cabal.project
+++ b/cabal.project
@@ -9,7 +9,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
- tag: 2a43717738eaf7a1457f70b48586df19d73a10f3
+ tag: 9dfc0e0e924721cf9c4092887b1fe5c954519cb1
source-repository-package
type: git
@@ -19,7 +19,7 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/kazu-yamamoto/http2.git
- tag: b5a1b7200cf5bc7044af34ba325284271f6dff25
+ tag: 804fa283f067bd3fd89b8c5f8d25b3047813a517
source-repository-package
type: git
diff --git a/package.yaml b/package.yaml
index 150797a12..3d4786f83 100644
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
name: simplex-chat
-version: 5.4.0.1
+version: 5.4.0.2
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix
index 98ed779d6..d87a1427e 100644
--- a/scripts/nix/sha256map.nix
+++ b/scripts/nix/sha256map.nix
@@ -1,7 +1,7 @@
{
- "https://github.com/simplex-chat/simplexmq.git"."2a43717738eaf7a1457f70b48586df19d73a10f3" = "1iqaiw48zvk0s9s2dgpmsmi78nw1q6nkmfi0n3xiv6q23l40sf6c";
+ "https://github.com/simplex-chat/simplexmq.git"."9dfc0e0e924721cf9c4092887b1fe5c954519cb1" = "0c7402727nq0gal64n6jjk3038wqiyw3yczzg0q1b1qkd9nc8r0m";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
- "https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
+ "https://github.com/kazu-yamamoto/http2.git"."804fa283f067bd3fd89b8c5f8d25b3047813a517" = "1j67wp7rfybfx3ryx08z6gqmzj85j51hmzhgx47ihgmgr47sl895";
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
"https://github.com/simplex-chat/aeson.git"."aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b" = "0jz7kda8gai893vyvj96fy962ncv8dcsx71fbddyy8zrvc88jfrr";
diff --git a/simplex-chat.cabal b/simplex-chat.cabal
index 6e0832824..e2cdf0d75 100644
--- a/simplex-chat.cabal
+++ b/simplex-chat.cabal
@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
-version: 5.4.0.1
+version: 5.4.0.2
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
@@ -117,6 +117,7 @@ library
Simplex.Chat.Migrations.M20231002_conn_initiated
Simplex.Chat.Migrations.M20231009_via_group_link_uri_hash
Simplex.Chat.Migrations.M20231010_member_settings
+ Simplex.Chat.Migrations.M20231019_indexes
Simplex.Chat.Mobile
Simplex.Chat.Mobile.File
Simplex.Chat.Mobile.Shared
diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs
index 0dafa4bd0..8b28e9c14 100644
--- a/src/Simplex/Chat.hs
+++ b/src/Simplex/Chat.hs
@@ -146,7 +146,10 @@ defaultChatConfig =
_defaultSMPServers :: NonEmpty SMPServerWithAuth
_defaultSMPServers =
L.fromList
- [ "smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
+ [ "smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
+ "smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
+ "smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion",
+ "smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
"smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion",
"smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion",
"smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion",
@@ -2242,7 +2245,7 @@ processChatCommand = \case
case groupLinkId of
-- contact address
Nothing ->
- withStore' (`getUserContactLinkByConnReq` cReqSchemas) >>= \case
+ withStore' (\db -> getUserContactLinkByConnReq db user cReqSchemas) >>= \case
Just _ -> pure $ CPContactAddress CAPOwnLink
Nothing -> do
withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHashes) >>= \case
diff --git a/src/Simplex/Chat/Migrations/M20231019_indexes.hs b/src/Simplex/Chat/Migrations/M20231019_indexes.hs
new file mode 100644
index 000000000..40412e177
--- /dev/null
+++ b/src/Simplex/Chat/Migrations/M20231019_indexes.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Simplex.Chat.Migrations.M20231019_indexes where
+
+import Database.SQLite.Simple (Query)
+import Database.SQLite.Simple.QQ (sql)
+
+m20231019_indexes :: Query
+m20231019_indexes =
+ [sql|
+DROP INDEX idx_connections_conn_req_inv;
+CREATE INDEX idx_connections_conn_req_inv ON connections(user_id, conn_req_inv);
+
+DROP INDEX idx_groups_via_group_link_uri_hash;
+CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(user_id, via_group_link_uri_hash);
+
+DROP INDEX idx_connections_via_contact_uri_hash;
+CREATE INDEX idx_connections_via_contact_uri_hash ON connections(user_id, via_contact_uri_hash);
+|]
+
+down_m20231019_indexes :: Query
+down_m20231019_indexes =
+ [sql|
+DROP INDEX idx_connections_conn_req_inv;
+CREATE INDEX idx_connections_conn_req_inv ON connections(conn_req_inv);
+
+DROP INDEX idx_groups_via_group_link_uri_hash;
+CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(via_group_link_uri_hash);
+
+DROP INDEX idx_connections_via_contact_uri_hash;
+CREATE INDEX idx_connections_via_contact_uri_hash ON connections(via_contact_uri_hash);
+|]
diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql
index 7308ef89f..cadb7caf4 100644
--- a/src/Simplex/Chat/Migrations/chat_schema.sql
+++ b/src/Simplex/Chat/Migrations/chat_schema.sql
@@ -524,9 +524,6 @@ CREATE INDEX contact_profiles_index ON contact_profiles(
full_name
);
CREATE INDEX idx_groups_inv_queue_info ON groups(inv_queue_info);
-CREATE INDEX idx_connections_via_contact_uri_hash ON connections(
- via_contact_uri_hash
-);
CREATE INDEX idx_contact_requests_xcontact_id ON contact_requests(xcontact_id);
CREATE INDEX idx_contacts_xcontact_id ON contacts(xcontact_id);
CREATE INDEX idx_messages_shared_msg_id ON messages(shared_msg_id);
@@ -738,7 +735,15 @@ CREATE INDEX idx_received_probes_probe_hash ON received_probes(probe_hash);
CREATE INDEX idx_sent_probes_created_at ON sent_probes(created_at);
CREATE INDEX idx_sent_probe_hashes_created_at ON sent_probe_hashes(created_at);
CREATE INDEX idx_received_probes_created_at ON received_probes(created_at);
-CREATE INDEX idx_connections_conn_req_inv ON connections(conn_req_inv);
+CREATE INDEX idx_connections_conn_req_inv ON connections(
+ user_id,
+ conn_req_inv
+);
CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(
+ user_id,
via_group_link_uri_hash
);
+CREATE INDEX idx_connections_via_contact_uri_hash ON connections(
+ user_id,
+ via_contact_uri_hash
+);
diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs
index 4f468b0d6..8677364ea 100644
--- a/src/Simplex/Chat/Store/Connections.hs
+++ b/src/Simplex/Chat/Store/Connections.hs
@@ -152,9 +152,9 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do
userContact_ _ = Left SEUserContactLinkNotFound
getConnectionEntityByConnReq :: DB.Connection -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity)
-getConnectionEntityByConnReq db user (cReqSchema1, cReqSchema2) = do
+getConnectionEntityByConnReq db user@User {userId} (cReqSchema1, cReqSchema2) = do
connId_ <- maybeFirstRow fromOnly $
- DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv IN (?,?) LIMIT 1" (cReqSchema1, cReqSchema2)
+ DB.query db "SELECT agent_conn_id FROM connections WHERE user_id = ? AND conn_req_inv IN (?,?) LIMIT 1" (userId, cReqSchema1, cReqSchema2)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_
-- search connection for connection plan:
@@ -162,7 +162,7 @@ getConnectionEntityByConnReq db user (cReqSchema1, cReqSchema2) = do
-- this function searches for latest connection with contact so that "known contact" plan would be chosen;
-- deleted connections are filtered out to allow re-connecting via same contact address
getContactConnEntityByConnReqHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity)
-getContactConnEntityByConnReqHash db user (cReqHash1, cReqHash2) = do
+getContactConnEntityByConnReqHash db user@User {userId} (cReqHash1, cReqHash2) = do
connId_ <- maybeFirstRow fromOnly $
DB.query
db
@@ -172,12 +172,12 @@ getContactConnEntityByConnReqHash db user (cReqHash1, cReqHash2) = do
agent_conn_id,
(CASE WHEN contact_id IS NOT NULL THEN 1 ELSE 0 END) AS conn_ord
FROM connections
- WHERE via_contact_uri_hash IN (?,?) AND conn_status != ?
+ WHERE user_id = ? AND via_contact_uri_hash IN (?,?) AND conn_status != ?
ORDER BY conn_ord DESC, created_at DESC
LIMIT 1
)
|]
- (cReqHash1, cReqHash2, ConnDeleted)
+ (userId, cReqHash1, cReqHash2, ConnDeleted)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_
getConnectionsToSubscribe :: DB.Connection -> IO ([ConnId], [ConnectionEntity])
diff --git a/src/Simplex/Chat/Store/Direct.hs b/src/Simplex/Chat/Store/Direct.hs
index 6620bc3e4..ea0b2abe9 100644
--- a/src/Simplex/Chat/Store/Direct.hs
+++ b/src/Simplex/Chat/Store/Direct.hs
@@ -162,7 +162,7 @@ getContactByConnReqHash db user@User {userId} cReqHash =
FROM contacts ct
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
JOIN connections c ON c.contact_id = ct.contact_id
- WHERE ct.user_id = ? AND c.via_contact_uri_hash = ? AND ct.contact_status = ? AND ct.deleted = 0
+ WHERE c.user_id = ? AND c.via_contact_uri_hash = ? AND ct.contact_status = ? AND ct.deleted = 0
ORDER BY c.created_at DESC
LIMIT 1
|]
diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs
index c5fd442ce..61aae28f2 100644
--- a/src/Simplex/Chat/Store/Groups.hs
+++ b/src/Simplex/Chat/Store/Groups.hs
@@ -1117,16 +1117,16 @@ getGroupInfo db User {userId, userContactId} groupId =
(groupId, userId, userContactId)
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
-getGroupInfoByUserContactLinkConnReq db user (cReqSchema1, cReqSchema2) = do
+getGroupInfoByUserContactLinkConnReq db user@User {userId} (cReqSchema1, cReqSchema2) = do
groupId_ <- maybeFirstRow fromOnly $
DB.query
db
[sql|
SELECT group_id
FROM user_contact_links
- WHERE conn_req_contact IN (?,?)
+ WHERE user_id = ? AND conn_req_contact IN (?,?)
|]
- (cReqSchema1, cReqSchema2)
+ (userId, cReqSchema1, cReqSchema2)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_
getGroupInfoByGroupLinkHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
diff --git a/src/Simplex/Chat/Store/Migrations.hs b/src/Simplex/Chat/Store/Migrations.hs
index 60783f366..357bfd9a2 100644
--- a/src/Simplex/Chat/Store/Migrations.hs
+++ b/src/Simplex/Chat/Store/Migrations.hs
@@ -85,6 +85,7 @@ import Simplex.Chat.Migrations.M20230926_contact_status
import Simplex.Chat.Migrations.M20231002_conn_initiated
import Simplex.Chat.Migrations.M20231009_via_group_link_uri_hash
import Simplex.Chat.Migrations.M20231010_member_settings
+import Simplex.Chat.Migrations.M20231019_indexes
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..))
schemaMigrations :: [(String, Query, Maybe Query)]
@@ -169,7 +170,8 @@ schemaMigrations =
("20230926_contact_status", m20230926_contact_status, Just down_m20230926_contact_status),
("20231002_conn_initiated", m20231002_conn_initiated, Just down_m20231002_conn_initiated),
("20231009_via_group_link_uri_hash", m20231009_via_group_link_uri_hash, Just down_m20231009_via_group_link_uri_hash),
- ("20231010_member_settings", m20231010_member_settings, Just down_m20231010_member_settings)
+ ("20231010_member_settings", m20231010_member_settings, Just down_m20231010_member_settings),
+ ("20231019_indexes", m20231019_indexes, Just down_m20231019_indexes)
]
-- | The list of migrations in ascending order by date
diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs
index 6eb210bd5..1bf6b251d 100644
--- a/src/Simplex/Chat/Store/Profiles.hs
+++ b/src/Simplex/Chat/Store/Profiles.hs
@@ -437,17 +437,17 @@ getUserContactLinkById db userId userContactLinkId =
|]
(userId, userContactLinkId)
-getUserContactLinkByConnReq :: DB.Connection -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
-getUserContactLinkByConnReq db (cReqSchema1, cReqSchema2) =
+getUserContactLinkByConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
+getUserContactLinkByConnReq db User {userId} (cReqSchema1, cReqSchema2) =
maybeFirstRow toUserContactLink $
DB.query
db
[sql|
SELECT conn_req_contact, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links
- WHERE conn_req_contact IN (?,?)
+ WHERE user_id = ? AND conn_req_contact IN (?,?)
|]
- (cReqSchema1, cReqSchema2)
+ (userId, cReqSchema1, cReqSchema2)
updateUserAddressAutoAccept :: DB.Connection -> User -> Maybe AutoAccept -> ExceptT StoreError IO UserContactLink
updateUserAddressAutoAccept db user@User {userId} autoAccept = do
diff --git a/stack.yaml b/stack.yaml
index 92489001d..8ea8455d4 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -49,9 +49,9 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
- commit: 2a43717738eaf7a1457f70b48586df19d73a10f3
+ commit: 9dfc0e0e924721cf9c4092887b1fe5c954519cb1
- github: kazu-yamamoto/http2
- commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
+ commit: 804fa283f067bd3fd89b8c5f8d25b3047813a517
# - ../direct-sqlcipher
- github: simplex-chat/direct-sqlcipher
commit: 34309410eb2069b029b8fc1872deb1e0db123294
diff --git a/tests/SchemaDump.hs b/tests/SchemaDump.hs
index f4538e4b3..f517d13df 100644
--- a/tests/SchemaDump.hs
+++ b/tests/SchemaDump.hs
@@ -71,7 +71,9 @@ skipComparisonForDownMigrations =
-- on down migration idx_chat_items_timed_delete_at index moves down to the end of the file
"20230529_indexes",
-- table and index definitions move down the file, so fields are re-created as not unique
- "20230914_member_probes"
+ "20230914_member_probes",
+ -- on down migration idx_connections_via_contact_uri_hash index moves down to the end of the file
+ "20231019_indexes"
]
getSchema :: FilePath -> FilePath -> IO String