Merge branch 'master-ghc8107' into master-android

This commit is contained in:
Evgeny Poberezkin 2023-10-22 15:35:55 +01:00
commit c4d7e5307c
23 changed files with 215 additions and 99 deletions

View File

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

View File

@ -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,14 +448,10 @@ 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")) {
private func deleteContact(notify: Bool? = nil) {
Task {
do {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, notify: notify)
await MainActor.run {
dismiss()
chatModel.chatId = nil
@ -451,9 +465,6 @@ struct ChatInfoView: View {
}
}
}
},
secondaryButton: .cancel()
)
}
private func clearChatAlert() -> Alert {

View File

@ -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?"),

View File

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

View File

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

View File

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

View File

@ -196,13 +196,55 @@ 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 = {
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 {
val r = chatModel.controller.apiDeleteChat(chatInfo.chatType, chatInfo.apiId)
deleteContact(chatInfo, chatModel, close, notify = true)
}
}) {
Text(generalGetString(MR.strings.delete_and_notify_contact), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.error)
}
// 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)
}
}
}
)
}
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) {
@ -213,9 +255,6 @@ fun deleteContactDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() ->
close?.invoke()
}
}
},
destructive = true,
)
}
fun clearChatDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {

View File

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

View File

@ -350,6 +350,7 @@
<!-- Chat Info Actions - ChatInfoView.kt -->
<string name="delete_contact_question">Delete contact?</string>
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Contact and all messages will be deleted - this cannot be undone!</string>
<string name="delete_and_notify_contact">Delete and notify contact</string>
<string name="button_delete_contact">Delete contact</string>
<string name="text_field_set_contact_placeholder">Set contact name…</string>
<string name="icon_descr_server_status_connected">Connected</string>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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