From df0453992a4d2697a18c32fcc8283f98f33ff6a4 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Thu, 11 Aug 2022 02:05:55 +0100 Subject: [PATCH] Filter messages by sender's blocked and banned status --- Sources/XXDatabase/Models/Message+GRDB.swift | 20 +++++ Sources/XXModels/Models/Message.swift | 26 ++++++ Tests/XXDatabaseTests/Message+GRDBTests.swift | 88 +++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/Sources/XXDatabase/Models/Message+GRDB.swift b/Sources/XXDatabase/Models/Message+GRDB.swift index bb0196b..d242a67 100644 --- a/Sources/XXDatabase/Models/Message+GRDB.swift +++ b/Sources/XXDatabase/Models/Message+GRDB.swift @@ -17,6 +17,13 @@ extension Message: FetchableRecord, MutablePersistableRecord { case fileTransferId } + enum Association { + static let sender = belongsTo( + Contact.self, + using: .init([Column.senderId], to: [Contact.Column.id]) + ) + } + public static let databaseTableName = "messages" static func request(_ query: Query) -> QueryInterfaceRequest<Message> { @@ -74,6 +81,19 @@ extension Message: FetchableRecord, MutablePersistableRecord { break } + if query.isSenderBlocked != nil || query.isSenderBanned != nil { + let sender = TableAlias(name: "sender") + request = request.joining(required: Association.sender.aliased(sender)) + + if let isSenderBlocked = query.isSenderBlocked { + request = request.filter(sender[Contact.Column.isBlocked] == isSenderBlocked) + } + + if let isSenderBanned = query.isSenderBanned { + request = request.filter(sender[Contact.Column.isBanned] == isSenderBanned) + } + } + switch query.sortBy { case .date(desc: false): request = request.order(Column.date) diff --git a/Sources/XXModels/Models/Message.swift b/Sources/XXModels/Models/Message.swift index 9e75dd7..cc5645d 100644 --- a/Sources/XXModels/Models/Message.swift +++ b/Sources/XXModels/Models/Message.swift @@ -176,6 +176,14 @@ extension Message { /// If `true`, get only unread messages. /// If `false`, get only read messages. /// If `nil` (default), disable the filter. + /// - isBlocked: Filter by sender's contact `isBlocked` status. + /// If `true`, include only messages from blocked senders. + /// If `false`, include only messages from non-blocked senders. + /// If `nil` (default), disable the filter. + /// - isBanned: Filter by sender's contact `isBanned` status. + /// If `true`, include only messages from banned senders. + /// If `false`, include only messages from non-banned senders. + /// If `nil` (default), disable the filter. /// - fileTransferId: Filter by file transfer id. /// If `.some(.some(fileTransferId))`, get messages with provided `fileTransferId`. /// If `.some(.none)`, get messages without `fileTransferId`. @@ -187,6 +195,8 @@ extension Message { chat: Chat? = nil, status: Set<Status>? = nil, isUnread: Bool? = nil, + isSenderBlocked: Bool? = nil, + isSenderBanned: Bool? = nil, fileTransferId: FileTransfer.ID?? = nil, sortBy: SortOrder = .date() ) { @@ -195,6 +205,8 @@ extension Message { self.chat = chat self.status = status self.isUnread = isUnread + self.isSenderBlocked = isSenderBlocked + self.isSenderBanned = isSenderBanned self.fileTransferId = fileTransferId self.sortBy = sortBy } @@ -229,6 +241,20 @@ extension Message { /// If `nil`, disable the filter. public var isUnread: Bool? + /// Filter by sender's contact `isBlocked` status + /// + /// If `true`, include only messages from blocked senders. + /// If `false`, include only messages from non-blocked senders. + /// If `nil`, disable the filter. + public var isSenderBlocked: Bool? + + /// Filter by sender's contact `isBanned` status + /// + /// If `true`, include only messages from banned senders. + /// If `false`, include only messages from non-banned senders. + /// If `nil`, disable the filter. + public var isSenderBanned: Bool? + /// Filter by file transfer id /// /// If `.some(.some(fileTransferId))`, get messages with provided `fileTransferId`. diff --git a/Tests/XXDatabaseTests/Message+GRDBTests.swift b/Tests/XXDatabaseTests/Message+GRDBTests.swift index f77d31c..00a2c99 100644 --- a/Tests/XXDatabaseTests/Message+GRDBTests.swift +++ b/Tests/XXDatabaseTests/Message+GRDBTests.swift @@ -474,6 +474,94 @@ final class MessageGRDBTests: XCTestCase { ) } + func testFetchingBySenderBlockedStatus() 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)) + + // Mock up messages: + + let message_AtoB_at1 = try db.saveMessage(.stub(from: contactA, to: contactB, at: 1)) + let message_AtoC_at2 = try db.saveMessage(.stub(from: contactA, to: contactC, at: 2)) + let message_BtoA_at3 = try db.saveMessage(.stub(from: contactB, to: contactA, at: 3)) + let message_BtoC_at4 = try db.saveMessage(.stub(from: contactB, to: contactC, at: 4)) + let message_CtoA_at5 = try db.saveMessage(.stub(from: contactC, to: contactA, at: 5)) + let message_CtoB_at6 = try db.saveMessage(.stub(from: contactC, to: contactB, at: 6)) + + // Fetch messages from blocked contacts: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBlocked: true)), [ + message_BtoA_at3, + message_BtoC_at4, + ]) + + // Fetch messages from non-blocked contacts: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBlocked: false)), [ + message_AtoB_at1, + message_AtoC_at2, + message_CtoA_at5, + message_CtoB_at6, + ]) + + // Fetch messages regardless sender contact's `isBlocked` status: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBlocked: nil)), [ + message_AtoB_at1, + message_AtoC_at2, + message_BtoA_at3, + message_BtoC_at4, + message_CtoA_at5, + message_CtoB_at6, + ]) + } + + func testFetchingBySenderBannedStatus() 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)) + + // Mock up messages: + + let message_AtoB_at1 = try db.saveMessage(.stub(from: contactA, to: contactB, at: 1)) + let message_AtoC_at2 = try db.saveMessage(.stub(from: contactA, to: contactC, at: 2)) + let message_BtoA_at3 = try db.saveMessage(.stub(from: contactB, to: contactA, at: 3)) + let message_BtoC_at4 = try db.saveMessage(.stub(from: contactB, to: contactC, at: 4)) + let message_CtoA_at5 = try db.saveMessage(.stub(from: contactC, to: contactA, at: 5)) + let message_CtoB_at6 = try db.saveMessage(.stub(from: contactC, to: contactB, at: 6)) + + // Fetch messages from banned contacts: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBanned: true)), [ + message_BtoA_at3, + message_BtoC_at4, + ]) + + // Fetch messages from non-banned contacts: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBanned: false)), [ + message_AtoB_at1, + message_AtoC_at2, + message_CtoA_at5, + message_CtoB_at6, + ]) + + // Fetch messages regardless sender contact's `isBanned` status: + + XCTAssertNoDifference(try db.fetchMessages(.init(isSenderBanned: nil)), [ + message_AtoB_at1, + message_AtoC_at2, + message_BtoA_at3, + message_BtoC_at4, + message_CtoA_at5, + message_CtoB_at6, + ]) + } + func testDeletingMany() throws { // Mock up contacts -- GitLab