diff --git a/Sources/XXDatabase/Models/ContactChatInfo+GRDB.swift b/Sources/XXDatabase/Models/ContactChatInfo+GRDB.swift index 47315f4fcb4652ee8ab55eb4f6000eec6534ab70..6f50587b1bd3e7b9301d26a7c0782fff2f38ee8a 100644 --- a/Sources/XXDatabase/Models/ContactChatInfo+GRDB.swift +++ b/Sources/XXDatabase/Models/ContactChatInfo+GRDB.swift @@ -17,6 +17,16 @@ extension ContactChatInfo: FetchableRecord { _ = sqlArguments.append(contentsOf: sqlArgumentsAuthStatus(authStatus)) } + if let isBlocked = query.isBlocked { + sqlWhere.append("AND c2.isBlocked = :isBlocked") + _ = sqlArguments.append(contentsOf: StatementArguments(["isBlocked": isBlocked])) + } + + if let isBanned = query.isBanned { + sqlWhere.append("AND c2.isBanned = :isBanned") + _ = sqlArguments.append(contentsOf: StatementArguments(["isBanned": isBanned])) + } + let sql = """ SELECT -- All contact columns: diff --git a/Sources/XXModels/Models/ContactChatInfo.swift b/Sources/XXModels/Models/ContactChatInfo.swift index b7eb7134a036cf2ff4c269a441b144a83106aa50..37f2f505c5089928d5da827c703e0ede2aba965a 100644 --- a/Sources/XXModels/Models/ContactChatInfo.swift +++ b/Sources/XXModels/Models/ContactChatInfo.swift @@ -51,12 +51,24 @@ extension ContactChatInfo { /// If set, only chats with contacts that have any of the provided /// auth statuses will be included. /// If `nil` (default), the filter is not used. + /// - isBlocked: Filter by other contact's `isBlocked` status. + /// If `true`, only chats with blocked contacts are included. + /// If `false`, only chats with non-blocked contacts are included. + /// If `nil` (default), the filter is not used. + /// - isBanned: Filter by other contact's `isBanned` status + /// If `true`, only chats with banned contacts are included. + /// If `false`, only chats with non-banned contacts are included. + /// If `nil` (default), the filter is not used. public init( userId: Contact.ID, - authStatus: Set<Contact.AuthStatus>? = nil + authStatus: Set<Contact.AuthStatus>? = nil, + isBlocked: Bool? = nil, + isBanned: Bool? = nil ) { self.userId = userId self.authStatus = authStatus + self.isBlocked = isBlocked + self.isBanned = isBanned } /// Current user's contact ID @@ -68,5 +80,19 @@ extension ContactChatInfo { /// auth statuses will be included. /// If `nil`, the filter is not used. public var authStatus: Set<Contact.AuthStatus>? + + /// Filter by other contact's `isBlocked` status + /// + /// If `true`, only chats with blocked contacts are included. + /// If `false`, only chats with non-blocked contacts are included. + /// If `nil`, the filter is not used. + public var isBlocked: Bool? + + /// Filter by other contact's `isBanned` status + /// + /// If `true`, only chats with banned contacts are included. + /// If `false`, only chats with non-banned contacts are included. + /// If `nil`, the filter is not used. + public var isBanned: Bool? } } diff --git a/Tests/XXDatabaseTests/ContactChatInfo+GRDBTests.swift b/Tests/XXDatabaseTests/ContactChatInfo+GRDBTests.swift index 05a1cb0c2294efe6e0a906f7e41786f4eac75940..b142b68e0e4e833598f1da2fcb18e40b1adc1cda 100644 --- a/Tests/XXDatabaseTests/ContactChatInfo+GRDBTests.swift +++ b/Tests/XXDatabaseTests/ContactChatInfo+GRDBTests.swift @@ -259,4 +259,258 @@ final class ContactChatInfoGRDBTests: XCTestCase { ] ) } + + func testFetchingByContactBlockedStatus() throws { + // Mock up contacts: + + let contactA = try db.saveContact(.stub("A").withBlocked(false)) + let contactB = try db.saveContact(.stub("B").withBlocked(true)) + let contactC = try db.saveContact(.stub("C").withBlocked(false)) + let contactD = try db.saveContact(.stub("D").withBlocked(true)) + + // Mock up conversation between contact A and B: + + try db.saveMessage(.stub( + from: contactA, + to: contactB, + at: 1, + isUnread: false + )) + + try db.saveMessage(.stub( + from: contactB, + to: contactA, + at: 2, + isUnread: true + )) + + let lastMessage_betweenAandB_at3 = try db.saveMessage(.stub( + from: contactA, + to: contactB, + at: 3, + isUnread: true + )) + + // Mock up conversation between contact A and C: + + try db.saveMessage(.stub( + from: contactA, + to: contactC, + at: 4, + isUnread: false + )) + + let lastMessage_betweenAandC_at5 = try db.saveMessage(.stub( + from: contactC, + to: contactA, + at: 5, + isUnread: true + )) + + // Mock up conversation between contact A and D: + + try db.saveMessage(.stub( + from: contactA, + to: contactD, + at: 6, + isUnread: false + )) + + let lastMessage_betweenAandD_at7 = try db.saveMessage(.stub( + from: contactD, + to: contactA, + at: 7, + isUnread: false + )) + + // Fetch chats between contact A and blocked contacts: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBlocked: true + )), + [ + ContactChatInfo( + contact: contactD, + lastMessage: lastMessage_betweenAandD_at7, + unreadCount: 0 + ), + ContactChatInfo( + contact: contactB, + lastMessage: lastMessage_betweenAandB_at3, + unreadCount: 2 + ), + ] + ) + + // Fetch chats between contact A and non-blocked contacts: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBlocked: false + )), + [ + ContactChatInfo( + contact: contactC, + lastMessage: lastMessage_betweenAandC_at5, + unreadCount: 1 + ), + ] + ) + + // Fetch chats between contact A and other contacts, regardless its `isBlocked` status: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBlocked: nil + )), + [ + ContactChatInfo( + contact: contactD, + lastMessage: lastMessage_betweenAandD_at7, + unreadCount: 0 + ), + ContactChatInfo( + contact: contactC, + lastMessage: lastMessage_betweenAandC_at5, + unreadCount: 1 + ), + ContactChatInfo( + contact: contactB, + lastMessage: lastMessage_betweenAandB_at3, + unreadCount: 2 + ), + ] + ) + } + + func testFetchingByContactBannedStatus() throws { + // Mock up contacts: + + let contactA = try db.saveContact(.stub("A").withBanned(false)) + let contactB = try db.saveContact(.stub("B").withBanned(true)) + let contactC = try db.saveContact(.stub("C").withBanned(false)) + let contactD = try db.saveContact(.stub("D").withBanned(true)) + + // Mock up conversation between contact A and B: + + try db.saveMessage(.stub( + from: contactA, + to: contactB, + at: 1, + isUnread: false + )) + + try db.saveMessage(.stub( + from: contactB, + to: contactA, + at: 2, + isUnread: true + )) + + let lastMessage_betweenAandB_at3 = try db.saveMessage(.stub( + from: contactA, + to: contactB, + at: 3, + isUnread: true + )) + + // Mock up conversation between contact A and C: + + try db.saveMessage(.stub( + from: contactA, + to: contactC, + at: 4, + isUnread: false + )) + + let lastMessage_betweenAandC_at5 = try db.saveMessage(.stub( + from: contactC, + to: contactA, + at: 5, + isUnread: true + )) + + // Mock up conversation between contact A and D: + + try db.saveMessage(.stub( + from: contactA, + to: contactD, + at: 6, + isUnread: false + )) + + let lastMessage_betweenAandD_at7 = try db.saveMessage(.stub( + from: contactD, + to: contactA, + at: 7, + isUnread: false + )) + + // Fetch chats between contact A and banned contacts: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBanned: true + )), + [ + ContactChatInfo( + contact: contactD, + lastMessage: lastMessage_betweenAandD_at7, + unreadCount: 0 + ), + ContactChatInfo( + contact: contactB, + lastMessage: lastMessage_betweenAandB_at3, + unreadCount: 2 + ), + ] + ) + + // Fetch chats between contact A and non-banned contacts: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBanned: false + )), + [ + ContactChatInfo( + contact: contactC, + lastMessage: lastMessage_betweenAandC_at5, + unreadCount: 1 + ), + ] + ) + + // Fetch chats between contact A and other contacts, regardless its `isBanned` status: + + XCTAssertNoDifference( + try db.fetchContactChatInfos(ContactChatInfo.Query( + userId: contactA.id, + isBanned: nil + )), + [ + ContactChatInfo( + contact: contactD, + lastMessage: lastMessage_betweenAandD_at7, + unreadCount: 0 + ), + ContactChatInfo( + contact: contactC, + lastMessage: lastMessage_betweenAandC_at5, + unreadCount: 1 + ), + ContactChatInfo( + contact: contactB, + lastMessage: lastMessage_betweenAandB_at3, + unreadCount: 2 + ), + ] + ) + } }