From 5e9b0438f25b4aba2e2f3a6da27e1e0080ca51b4 Mon Sep 17 00:00:00 2001
From: Bruno Muniz Azevedo Filho <bruno@elixxir.io>
Date: Wed, 22 Jun 2022 01:12:08 -0300
Subject: [PATCH] Cleaned Session.swift except for FT

---
 Sources/Integration/Client.swift              |  63 ++++----
 Sources/Integration/Extensions.swift          |   5 +-
 .../Implementations/Bindings.swift            |   3 +-
 .../Implementations/GroupManager.swift        |   1 +
 .../Implementations/UserDiscovery.swift       |   1 +
 .../Interfaces/GroupManagerInterface.swift    |   1 +
 Sources/Integration/Listeners.swift           |  11 +-
 .../Integration/Mocks/GroupManagerMock.swift  |   1 +
 .../Integration/Session/Session+Chat.swift    |  79 ++--------
 .../Session/Session+Contacts.swift            |  94 +++++-------
 .../Integration/Session/Session+Group.swift   |  73 ++++-----
 Sources/Integration/Session/Session+UD.swift  |   1 +
 Sources/Integration/Session/Session.swift     |  34 ++---
 Sources/Integration/Session/SessionType.swift |   8 +-
 Sources/Models/GroupChatInfo.swift            |  44 +++---
 Sources/Models/GroupMember.swift              |  84 +++++------
 Sources/Models/GroupMessage.swift             | 112 +++++++-------
 Sources/Models/Message.swift                  | 140 +++++++++---------
 18 files changed, 323 insertions(+), 432 deletions(-)

diff --git a/Sources/Integration/Client.swift b/Sources/Integration/Client.swift
index bdd6784d..34b4cc4f 100644
--- a/Sources/Integration/Client.swift
+++ b/Sources/Integration/Client.swift
@@ -2,8 +2,9 @@ import Retry
 import Models
 import Combine
 import Defaults
-import Foundation
 import Bindings
+import XXModels
+import Foundation
 
 public class Client {
     @KeyObject(.inappnotifications, defaultValue: true) var inappnotifications: Bool
@@ -23,8 +24,6 @@ public class Client {
     var events: AnyPublisher<BackendEvent, Never> { eventsSubject.eraseToAnyPublisher() }
     var requestsSent: AnyPublisher<Contact, Never> { requestsSentSubject.eraseToAnyPublisher() }
     var confirmations: AnyPublisher<Contact, Never> { confirmationsSubject.eraseToAnyPublisher() }
-    var groupMessages: AnyPublisher<GroupMessage, Never> { groupMessagesSubject.eraseToAnyPublisher() }
-    var incomingTransfers: AnyPublisher<FileTransfer, Never> { transfersSubject.eraseToAnyPublisher() }
     var groupRequests: AnyPublisher<(Group, [Data], String?), Never> { groupRequestsSubject.eraseToAnyPublisher() }
 
     private let backupSubject = PassthroughSubject<Data, Never>()
@@ -35,8 +34,6 @@ public class Client {
     private let eventsSubject = PassthroughSubject<BackendEvent, Never>()
     private let requestsSentSubject = PassthroughSubject<Contact, Never>()
     private let confirmationsSubject = PassthroughSubject<Contact, Never>()
-    private let transfersSubject = PassthroughSubject<FileTransfer, Never>()
-    private let groupMessagesSubject = PassthroughSubject<GroupMessage, Never>()
     private let groupRequestsSubject = PassthroughSubject<(Group, [Data], String?), Never>()
 
     private var isBackupInitialization = false
@@ -110,7 +107,7 @@ public class Client {
 
                 switch $0 {
                 case .success(var contact):
-                    contact.status = .requested
+                    contact.authStatus = .requested
                     self.requestsSentSubject.send(contact)
                     print(">>> Restored \(contact.username). Setting status as requested")
                 case .failure(let error):
@@ -165,8 +162,8 @@ public class Client {
 
         groupManager = try bindings.listenGroupRequests { [weak groupRequestsSubject] request, members, welcome in
             groupRequestsSubject?.send((request, members, welcome))
-        } groupMessages: { [weak groupMessagesSubject] in
-            groupMessagesSubject?.send($0)
+        } groupMessages: { [weak messagesSubject] in
+            messagesSubject?.send($0)
         }
 
         bindings.listenPreImageUpdates()
@@ -179,31 +176,31 @@ public class Client {
     }
 
     private func instantiateTransferManager() throws {
-        transferManager = try bindings.generateTransferManager { [weak transfersSubject] tid, name, type, sender in
-
-            /// Someone transfered something to me
-            /// but I haven't received yet. I'll store an
-            /// IncomingTransfer object so later on I can
-            /// pull up whatever this contact has sent me.
-            ///
-            guard let name = name,
-                  let type = type,
-                  let contact = sender,
-                  let _extension = Attachment.Extension.from(type) else {
-                      log(string: "Transfer of \(name ?? "nil").\(type ?? "nil") is being dismissed", type: .error)
-                      return
-                  }
-
-            transfersSubject?.send(
-                FileTransfer(
-                    tid: tid,
-                    contact: contact,
-                    fileName: name,
-                    fileType: _extension.written,
-                    isIncoming: true
-                )
-            )
-        }
+//        transferManager = try bindings.generateTransferManager { [weak transfersSubject] tid, name, type, sender in
+//
+//            /// Someone transfered something to me
+//            /// but I haven't received yet. I'll store an
+//            /// IncomingTransfer object so later on I can
+//            /// pull up whatever this contact has sent me.
+//            ///
+//            guard let name = name,
+//                  let type = type,
+//                  let contact = sender,
+//                  let _extension = Attachment.Extension.from(type) else {
+//                      log(string: "Transfer of \(name ?? "nil").\(type ?? "nil") is being dismissed", type: .error)
+//                      return
+//                  }
+//
+//            transfersSubject?.send(
+//                FileTransfer(
+//                    tid: tid,
+//                    contact: contact,
+//                    fileName: name,
+//                    fileType: _extension.written,
+//                    isIncoming: true
+//                )
+//            )
+//        }
     }
 
     private func instantiateUserDiscovery() throws {
diff --git a/Sources/Integration/Extensions.swift b/Sources/Integration/Extensions.swift
index 93cf186a..74835b71 100644
--- a/Sources/Integration/Extensions.swift
+++ b/Sources/Integration/Extensions.swift
@@ -1,8 +1,9 @@
 import Models
+import XXModels
 import Bindings
 
 extension Contact {
-    init(with contact: BindingsContact, status: Contact.Status) {
+    init(with contact: BindingsContact, status: Contact.AuthStatus) {
         self.init(
             photo: nil,
             userId: contact.getID()!,
@@ -33,9 +34,7 @@ extension Message {
             roundURL: message.getRoundURL()
         )
     }
-}
 
-extension GroupMessage {
     init(with message: BindingsGroupMessageReceive) {
         guard let payload = try? Payload(with: message.getPayload()!) else { fatalError() }
 
diff --git a/Sources/Integration/Implementations/Bindings.swift b/Sources/Integration/Implementations/Bindings.swift
index ce843f4a..6aac71d4 100644
--- a/Sources/Integration/Implementations/Bindings.swift
+++ b/Sources/Integration/Implementations/Bindings.swift
@@ -1,8 +1,9 @@
 import Shared
 import Models
 import Bindings
-import DependencyInjection
+import XXModels
 import Foundation
+import DependencyInjection
 
 public let evaluateNotification: NotificationEvaluation = BindingsNotificationsForMe
 
diff --git a/Sources/Integration/Implementations/GroupManager.swift b/Sources/Integration/Implementations/GroupManager.swift
index c51522be..1fcdbccf 100644
--- a/Sources/Integration/Implementations/GroupManager.swift
+++ b/Sources/Integration/Implementations/GroupManager.swift
@@ -1,4 +1,5 @@
 import Models
+import XXModels
 import Bindings
 
 extension BindingsGroupChat: GroupManagerInterface {
diff --git a/Sources/Integration/Implementations/UserDiscovery.swift b/Sources/Integration/Implementations/UserDiscovery.swift
index b88afa0d..56c9b4de 100644
--- a/Sources/Integration/Implementations/UserDiscovery.swift
+++ b/Sources/Integration/Implementations/UserDiscovery.swift
@@ -1,5 +1,6 @@
 import Retry
 import Models
+import XXModels
 import Bindings
 import Foundation
 
diff --git a/Sources/Integration/Interfaces/GroupManagerInterface.swift b/Sources/Integration/Interfaces/GroupManagerInterface.swift
index c1c5523c..dcddfa9e 100644
--- a/Sources/Integration/Interfaces/GroupManagerInterface.swift
+++ b/Sources/Integration/Interfaces/GroupManagerInterface.swift
@@ -1,4 +1,5 @@
 import Models
+import XXModels
 import Foundation
 
 public protocol GroupManagerInterface {
diff --git a/Sources/Integration/Listeners.swift b/Sources/Integration/Listeners.swift
index 23f9f223..3859566e 100644
--- a/Sources/Integration/Listeners.swift
+++ b/Sources/Integration/Listeners.swift
@@ -1,13 +1,10 @@
 import Models
 import Shared
-import Bindings
-import Foundation
 import os.log
 import Combine
-
-import Combine
-
-import Combine
+import XXModels
+import Bindings
+import Foundation
 
 public extension BindingsClient {
     static func listenLogs() {
@@ -111,7 +108,7 @@ public extension BindingsClient {
 
     func listenGroupRequests(
         _ groupRequests: @escaping (Group, [Data], String?) -> Void,
-        groupMessages: @escaping (GroupMessage) -> Void
+        groupMessages: @escaping (Message) -> Void
     ) throws -> GroupManagerInterface? {
         var error: NSError?
 
diff --git a/Sources/Integration/Mocks/GroupManagerMock.swift b/Sources/Integration/Mocks/GroupManagerMock.swift
index ecec61c7..137cfca6 100644
--- a/Sources/Integration/Mocks/GroupManagerMock.swift
+++ b/Sources/Integration/Mocks/GroupManagerMock.swift
@@ -1,4 +1,5 @@
 import Models
+import XXModels
 import Foundation
 
 final class GroupManagerMock: GroupManagerInterface {
diff --git a/Sources/Integration/Session/Session+Chat.swift b/Sources/Integration/Session/Session+Chat.swift
index 5bdf71c6..814df83d 100644
--- a/Sources/Integration/Session/Session+Chat.swift
+++ b/Sources/Integration/Session/Session+Chat.swift
@@ -1,49 +1,10 @@
-import Models
-import Foundation
 import UIKit
+import Models
 import Shared
+import XXModels
+import Foundation
 
 extension Session {
-    public func readAll(from contact: Contact) {
-        do {
-            try dbManager.updateAll(
-                Message.self,
-                Message.Request.unreadsFromContactId(contact.userId),
-                with: [Message.Column.unread.set(to: false)]
-            )
-        } catch {
-            log(string: error.localizedDescription, type: .error)
-        }
-    }
-
-    public func readAll(from group: Group) {
-        do {
-            try dbManager.updateAll(
-                GroupMessage.self,
-                GroupMessage.Request.unreadsFromGroup(group.groupId),
-                with: [GroupMessage.Column.unread.set(to: false)]
-            )
-        } catch {
-            log(string: error.localizedDescription, type: .error)
-        }
-    }
-
-    public func deleteAll(from contact: Contact) {
-        do {
-            try dbManager.delete(Message.self, .withContact(contact.userId))
-        } catch {
-            log(string: error.localizedDescription, type: .error)
-        }
-    }
-
-    public func deleteAll(from group: Group) {
-        do {
-            try dbManager.delete(GroupMessage.self, .fromGroup(group.groupId))
-        } catch {
-            log(string: error.localizedDescription, type: .error)
-        }
-    }
-
     public func send(imageData: Data, to contact: Contact, completion: @escaping (Result<Void, Error>) -> Void) {
         client.bindings.compress(image: imageData) { [weak self] in
             guard let self = self else {
@@ -68,39 +29,29 @@ extension Session {
     public func send(_ payload: Payload, toContact contact: Contact) {
         var message = Message(
             sender: client.bindings.meMarshalled,
-            receiver: contact.userId,
+            receiver: contact.id,
             payload: payload,
             unread: false,
             timestamp: Date.asTimestamp,
             uniqueId: nil,
-            status: payload.attachment == nil ? .sending : .sendingAttachment
+            status: .sending
         )
 
         do {
-            message = try dbManager.save(message)
+            message = try dbManager.saveMessage(message)
             send(message: message)
         } catch {
             log(string: error.localizedDescription, type: .error)
         }
     }
 
-    public func delete(messages: [Int64]) {
-        messages.forEach {
-            do {
-                try dbManager.delete(Message.self, .withId($0))
-            } catch {
-                log(string: error.localizedDescription, type: .error)
-            }
-        }
-    }
-
     public func retryMessage(_ id: Int64) {
         guard var message: Message = try? dbManager.fetch(withId: id) else { return }
         message.timestamp = Date.asTimestamp
-        message.status = message.payload.attachment == nil ? .sending : .sendingAttachment
+        message.status = .sending
 
         do {
-            message = try dbManager.save(message)
+            message = try dbManager.saveMessage(message)
             send(message: message)
         } catch {
             log(string: error.localizedDescription, type: .error)
@@ -147,13 +98,13 @@ extension Session {
                     }
                 }
             case .failure(let error):
-                message.status = .failedToSend
+                message.status = .sendingFailed
                 log(string: error.localizedDescription, type: .error)
             }
 
             DispatchQueue.main.async {
                 do {
-                    _ = try self.dbManager.save(message)
+                    _ = try self.dbManager.saveMessage(message)
                 } catch {
                     log(string: error.localizedDescription, type: .error)
                 }
@@ -204,20 +155,20 @@ extension Session {
                 )
 
                 message.payload.attachment?.transferId = tid
-                message.status = .sendingAttachment
+                message.status = .sending
 
                 do {
-                    _ = try self.dbManager.save(message)
+                    _ = try self.dbManager.saveMessage(message)
                     _ = try self.dbManager.save(transfer)
                 } catch {
                     log(string: error.localizedDescription, type: .error)
                 }
             } catch {
-                message.status = .failedToSend
+                message.status = .sendingFailed
                 log(string: error.localizedDescription, type: .error)
 
                 do {
-                    _ = try self.dbManager.save(message)
+                    _ = try self.dbManager.saveMessage(message)
                 } catch let otherError {
                     log(string: otherError.localizedDescription, type: .error)
                 }
@@ -267,7 +218,7 @@ extension Session {
         )
 
         do {
-            message = try self.dbManager.save(message)
+            message = try self.dbManager.saveMessage(message)
             try self.dbManager.save(transfer)
         } catch {
             log(string: "Failed to save message/transfer to the database. Will not start listening to transfer... \(error.localizedDescription)", type: .info)
diff --git a/Sources/Integration/Session/Session+Contacts.swift b/Sources/Integration/Session/Session+Contacts.swift
index af09d609..1752883c 100644
--- a/Sources/Integration/Session/Session+Contacts.swift
+++ b/Sources/Integration/Session/Session+Contacts.swift
@@ -1,6 +1,7 @@
 import Retry
 import Models
 import Shared
+import XXModels
 import Foundation
 
 extension Session {
@@ -12,10 +13,10 @@ extension Session {
         log(string: "Requested verification of \(contact.username)", type: .crumbs)
 
         var contact = contact
-        contact.status = .verificationInProgress
+        contact.authStatus = .verificationInProgress
 
         do {
-            contact = try dbManager.save(contact)
+            contact = try dbManager.saveContact(contact)
         } catch {
             log(string: "Failed to store contact request upon verification. Returning, request will be abandoned to not crash", type: .error)
         }
@@ -35,11 +36,11 @@ extension Session {
         let resultClosure: (Result<Contact, Error>) -> Void = { result in
             switch result {
             case .success(let mightBe):
-                guard try! self.client.bindings.verify(marshaled: contact.marshaled, verifiedMarshaled: mightBe.marshaled) else {
+                guard try! self.client.bindings.verify(marshaled: contact.marshaled!, verifiedMarshaled: mightBe.marshaled!) else {
                     log(string: "\(contact.username) is fake. Deleted!", type: .info)
 
                     do {
-                        try self.dbManager.delete(contact)
+                        try self.dbManager.deleteContact(contact)
                     } catch {
                         log(string: error.localizedDescription, type: .error)
                     }
@@ -47,7 +48,7 @@ extension Session {
                     return
                 }
 
-                contact.status = .verified
+                contact.authStatus = .verified
                 log(string: "Verified \(contact.username)", type: .info)
 
                 do {
@@ -58,7 +59,7 @@ extension Session {
 
             case .failure(let error):
                 log(string: "Verification of \(contact.username) failed: \(error.localizedDescription)", type: .error)
-                contact.status = .verificationFailed
+                contact.authStatus = .verificationFailed
 
                 do {
                     try self.dbManager.saveContact(contact)
@@ -74,7 +75,7 @@ extension Session {
         let hasPhone = contact.phone != nil
 
         guard hasEmail || hasPhone else {
-            ud.lookup(forUserId: contact.userId, resultClosure)
+            ud.lookup(forUserId: contact.id, resultClosure)
             return
         }
 
@@ -90,10 +91,10 @@ extension Session {
             try ud.search(fact: fact, resultClosure)
         } catch {
             log(string: error.localizedDescription, type: .error)
-            contact.status = .verificationFailed
+            contact.authStatus = .verificationFailed
 
             do {
-                try self.dbManager.save(contact)
+                try self.dbManager.saveContact(contact)
             } catch {
                 log(string: error.localizedDescription, type: .error)
             }
@@ -103,7 +104,7 @@ extension Session {
     public func retryRequest(_ contact: Contact) throws {
         log(string: "Retrying to request a contact", type: .info)
 
-        client.bindings.add(contact.marshaled, from: myQR) { [weak self, contact] in
+        client.bindings.add(contact.marshaled!, from: myQR) { [weak self, contact] in
             var contact = contact
             guard let self = self else { return }
 
@@ -111,13 +112,13 @@ extension Session {
                 switch $0 {
                 case .success:
                     log(string: "Retrying to request a contact -- Success", type: .info)
-                    contact.status = .requested
+                    contact.authStatus = .requested
                 case .failure(let error):
                     log(string: "Retrying to request a contact -- Failed: \(error.localizedDescription)", type: .error)
                     contact.createdAt = Date()
                 }
 
-                _ = try self.dbManager.save(contact)
+                _ = try self.dbManager.saveContact(contact)
             } catch {
                 log(string: error.localizedDescription, type: .error)
             }
@@ -131,23 +132,23 @@ extension Session {
 
         var contactToOperate: Contact!
 
-        if contact.status == .requestFailed || contact.status == .confirmationFailed {
+        if contact.authStatus == .requestFailed || contact.authStatus == .confirmationFailed {
             contactToOperate = contact
         } else {
             guard (try? dbManager.fetch(.withUsername(contact.username)).first as Contact?) == nil else {
                 throw NSError.create("This user has already been requested")
             }
 
-            contactToOperate = try dbManager.save(contact)
+            contactToOperate = try dbManager.saveContact(contact)
         }
 
-        guard contactToOperate.status != .confirmationFailed else {
+        guard contactToOperate.authStatus != .confirmationFailed else {
             contactToOperate.createdAt = Date()
             try confirm(contact)
             return
         }
 
-        contactToOperate.status = .requesting
+        contactToOperate.authStatus = .requesting
 
         let myself = client.bindings.meMarshalled(
             username!,
@@ -155,77 +156,50 @@ extension Session {
             phone: isSharingPhone ? phone : nil
         )
 
-        client.bindings.add(contactToOperate.marshaled, from: myself) { [weak self, contactToOperate] in
+        client.bindings.add(contactToOperate.marshaled!, from: myself) { [weak self, contactToOperate] in
             guard let self = self, var contactToOperate = contactToOperate else { return }
-            let safeName = contactToOperate.nickname ?? contactToOperate.username
-            let title = "\(safeName.prefix(2))...\(safeName.suffix(3))"
 
             do {
                 switch $0 {
                 case .success(let success):
-                    contactToOperate.status = success ? .requested : .requestFailed
-                    contactToOperate = try self.dbManager.save(contactToOperate)
+                    contactToOperate.authStatus = success ? .requested : .requestFailed
+                    contactToOperate = try self.dbManager.saveContact(contactToOperate)
 
-                    log(string: "Successfully added \(title)", type: .info)
                 case .failure(let error):
-                    contactToOperate.status = .requestFailed
+                    contactToOperate.authStatus = .requestFailed
                     contactToOperate.createdAt = Date()
-                    contactToOperate = try self.dbManager.save(contactToOperate)
-
-                    log(string: "Failed when adding \(title):\n\(error.localizedDescription)", type: .error)
+                    contactToOperate = try self.dbManager.saveContact(contactToOperate)
 
                     self.toastController.enqueueToast(model: .init(
-                        title: Localized.Requests.Failed.toast(contactToOperate.nickname ?? contact.username),
+                        title: Localized.Requests.Failed.toast(contactToOperate.nickname ?? contact.username!),
                         color: Asset.accentDanger.color,
                         leftImage: Asset.requestFailedToaster.image
                     ))
                 }
             } catch {
-                log(string: "Error adding \(title):\n\(error.localizedDescription)", type: .error)
+                print(error.localizedDescription)
             }
         }
     }
 
     public func confirm(_ contact: Contact) throws {
         var contact = contact
-        contact.status = .confirming
-        contact = try dbManager.save(contact)
-
-        client.bindings.confirm(contact.marshaled) { [weak self] in
-            let safeName = contact.nickname ?? contact.username
-            let title = "\(safeName.prefix(2))...\(safeName.suffix(3))"
+        contact.authStatus = .confirming
+        contact = try dbManager.saveContact(contact)
 
+        client.bindings.confirm(contact.marshaled!) { [weak self] in
             switch $0 {
             case .success(let confirmed):
                 contact.isRecent = true
                 contact.createdAt = Date()
-                contact.status = confirmed ? .friend : .confirmationFailed
-                log(string: "Confirming request from \(title) = \(confirmed)", type: confirmed ? .info : .error)
-            case .failure(let error):
-                contact.status = .confirmationFailed
-                log(string: "Error confirming request from \(title):\n\(error.localizedDescription)", type: .error)
-            }
-
-            _ = try? self?.dbManager.save(contact)
-        }
-    }
-
-    public func find(by username: String) -> Contact? {
-        log(string: "Trying to find contact with username: \(username)", type: .info)
+                contact.authStatus = confirmed ? .friend : .confirmationFailed
 
-        do {
-            if let contact: Contact = try dbManager.fetch(.withUsername(username)).first {
-                log(string: "Found \(username)!", type: .info)
-                return contact
-            } else {
-                log(string: "No such contact with username: \(username)", type: .info)
-                return nil
+            case .failure:
+                contact.authStatus = .confirmationFailed
             }
-        } catch {
-            log(string: "Error trying to find a contact: \(error.localizedDescription)", type: .error)
-        }
 
-        return nil
+            _ = try? self?.dbManager.saveContact(contact)
+        }
     }
 
     public func deleteContact(_ contact: Contact) throws {
@@ -235,7 +209,7 @@ extension Session {
             print("No pending transfer with this contact. Free to delete")
         }
 
-        try client.bindings.removeContact(contact.marshaled)
-        try dbManager.delete(contact)
+        try client.bindings.removeContact(contact.marshaled!)
+        try dbManager.deleteContact(contact)
     }
 }
diff --git a/Sources/Integration/Session/Session+Group.swift b/Sources/Integration/Session/Session+Group.swift
index 78670c9b..423c976c 100644
--- a/Sources/Integration/Session/Session+Group.swift
+++ b/Sources/Integration/Session/Session+Group.swift
@@ -1,4 +1,5 @@
 import Models
+import XXModels
 import Foundation
 
 public typealias GroupCompletion = (Result<(Group, [GroupMember]), Error>) -> Void
@@ -7,24 +8,24 @@ extension Session {
     public func join(group: Group) throws {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
 
-        try manager.join(group.serialize)
+        try manager.join(group.serialized)
         var group = group
-        group.status = .participating
+        group.authStatus = .participating
         scanStrangers {}
-        try dbManager.save(group)
+        try dbManager.saveGroup(group)
     }
 
     public func leave(group: Group) throws {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
-        try manager.leave(group.groupId)
-        try dbManager.delete(group)
+        try manager.leave(group.id)
+        try dbManager.deleteGroup(group)
     }
 
     public func createGroup(name: String, welcome: String?, members: [Contact], _ completion: @escaping GroupCompletion) {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
 
         let me = client.bindings.meMarshalled
-        let memberIds = members.map { $0.userId }
+        let memberIds = members.map { $0.id }
 
         manager.create(me: me, name: name, welcome: welcome, with: memberIds) { [weak self] in
             guard let self = self else { return }
@@ -41,10 +42,10 @@ extension Session {
 
     @discardableResult
     func processGroupCreation(_ group: Group, memberIds: [Data], welcome: String?) -> [GroupMember] {
-        try! dbManager.save(group)
+        try! dbManager.saveGroup(group)
 
         if let welcome = welcome {
-            try! dbManager.save(GroupMessage(group: group, text: welcome, me: client.bindings.meMarshalled))
+            try! dbManager.save(Message(group: group, text: welcome, me: client.bindings.meMarshalled))
         }
 
         var members: [GroupMember] = []
@@ -54,7 +55,7 @@ extension Session {
         }
 
         let strangersOnGroup = memberIds
-            .filter { !members.map { $0.userId }.contains($0) }
+            .filter { !members.map { $0.contactId }.contains($0) }
             .filter { $0 != client.bindings.myId }
 
         if !strangersOnGroup.isEmpty {
@@ -69,7 +70,7 @@ extension Session {
             }
         }
 
-        members.forEach { try! dbManager.save($0) }
+        members.forEach { try! dbManager.saveGroupMember($0) }
 
         if group.leader != client.bindings.meMarshalled, inappnotifications {
             DeviceFeedback.sound(.contactAdded)
@@ -84,18 +85,8 @@ extension Session {
 // MARK: - GroupMessages
 
 extension Session {
-    public func delete(groupMessages: [Int64]) {
-        groupMessages.forEach {
-            do {
-                try dbManager.delete(GroupMessage.self, .id($0))
-            } catch {
-                log(string: error.localizedDescription, type: .error)
-            }
-        }
-    }
-
     public func send(_ payload: Payload, toGroup group: Group) {
-        var groupMessage = GroupMessage(
+        var message = Message(
             sender: client.bindings.meMarshalled,
             groupId: group.groupId,
             payload: payload,
@@ -106,8 +97,8 @@ extension Session {
         )
 
         do {
-            groupMessage = try dbManager.save(groupMessage)
-            send(groupMessage: groupMessage)
+            message = try dbManager.saveMessage(message)
+            send(message: message)
         } catch {
             log(string: error.localizedDescription, type: .error)
         }
@@ -117,41 +108,41 @@ extension Session {
         guard var message: GroupMessage = try? dbManager.fetch(withId: id) else { return }
         message.timestamp = Date.asTimestamp
         message.status = .sending
-        send(groupMessage: try! dbManager.save(message))
+        send(groupMessage: try! dbManager.saveMessage(message))
     }
 
-    private func send(groupMessage: GroupMessage) {
+    private func send(message: Message) {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
-        var groupMessage = groupMessage
+        var message = message
 
         DispatchQueue.global().async { [weak self] in
             guard let self = self else { return }
 
-            switch manager.send(groupMessage.payload.asData(), to: groupMessage.groupId) {
+            switch manager.send(message.payload.asData(), to: message.groupId) {
             case .success((let roundId, let uniqueId, let roundURL)):
-                groupMessage.roundURL = roundURL
+                message.roundURL = roundURL
 
                 self.client.bindings.listenRound(id: Int(roundId)) { result in
                     switch result {
                     case .success(let succeeded):
-                        groupMessage.uniqueId = uniqueId
-                        groupMessage.status = succeeded ? .sent : .failed
+                        message.uniqueId = uniqueId
+                        message.status = succeeded ? .sent : .failed
                     case .failure:
-                        groupMessage.status = .failed
+                        message.status = .failed
                     }
 
                     do {
-                        try self.dbManager.save(groupMessage)
+                        try self.dbManager.saveMessage(message)
                     } catch {
                         log(string: error.localizedDescription, type: .error)
                     }
                 }
             case .failure:
-                groupMessage.status = .failed
+                message.status = .sendingFailed
             }
 
             do {
-                try self.dbManager.save(groupMessage)
+                try self.dbManager.saveMessage(message)
             } catch {
                 log(string: error.localizedDescription, type: .error)
             }
@@ -209,20 +200,6 @@ extension Session {
     }
 }
 
-private extension GroupMessage {
-    init(group: Group, text: String, me: Data) {
-        self.init(
-            sender: group.leader,
-            groupId: group.groupId,
-            payload: .init(text: text, reply: nil, attachment: nil),
-            unread: false,
-            timestamp: Date.asTimestamp,
-            uniqueId: nil,
-            status: group.leader == me ? .sent : .received
-        )
-    }
-}
-
 private extension GroupMember {
     init(contact: Contact, group: Group) {
         self.init(
diff --git a/Sources/Integration/Session/Session+UD.swift b/Sources/Integration/Session/Session+UD.swift
index 82c1e427..57422a7d 100644
--- a/Sources/Integration/Session/Session+UD.swift
+++ b/Sources/Integration/Session/Session+UD.swift
@@ -1,4 +1,5 @@
 import Models
+import XXModels
 import Foundation
 
 extension Session {
diff --git a/Sources/Integration/Session/Session.swift b/Sources/Integration/Session/Session.swift
index 6c48b77c..0bad0077 100644
--- a/Sources/Integration/Session/Session.swift
+++ b/Sources/Integration/Session/Session.swift
@@ -229,15 +229,17 @@ public final class Session: SessionType {
 
     private func setupBindings() {
         client.requests
-            .sink { [unowned self] request in
-                if let _: Contact = try? dbManager.fetch(.withUserId(request.userId)).first { return }
+            .sink { [unowned self] in
+                if let _ = try? dbManager.fetchContacts(.init(id: [$0.id])).first {
+                    return
+                }
 
                 if self.inappnotifications {
                     DeviceFeedback.sound(.contactAdded)
                     DeviceFeedback.shake(.notification)
                 }
 
-                verify(contact: request)
+                verify(contact: $0)
             }.store(in: &cancellables)
 
         client.requestsSent
@@ -255,7 +257,7 @@ public final class Session: SessionType {
                 /// TODO: Hold a record on the chat that this contact restored.
                 ///
                 var contact = $0
-                contact.status = .friend
+                contact.authStatus = .friend
                 _ = try? dbManager.saveContact(contact)
             }.store(in: &cancellables)
 
@@ -284,31 +286,25 @@ public final class Session: SessionType {
             .sink { print($0) }
             .store(in: &cancellables)
 
-        client.groupMessages
-            .sink { [unowned self] in _ = try? dbManager.saveMessage($0) }
-            .store(in: &cancellables)
-
         client.messages
             .sink { [unowned self] in
-                if var contact: Contact = try? dbManager.fetch(.withUserId($0.sender)).first {
+                if var contact = try? dbManager.fetchContacts(.init(id: [$0.senderId])).first {
                     contact.isRecent = false
-                    _ = try? dbManager.save(contact)
+                    _ = try? dbManager.saveContact(contact)
                 }
 
-                _ = try? dbManager.save($0)
+                _ = try? dbManager.saveMessage($0)
             }.store(in: &cancellables)
 
         client.network
             .sink { [unowned self] in networkMonitor.update($0) }
             .store(in: &cancellables)
 
-        client.incomingTransfers
-            .sink { [unowned self] in handle(incomingTransfer: $0) }
-            .store(in: &cancellables)
-
         client.groupRequests
             .sink { [unowned self] request in
-                if let _: Group = try? dbManager.fetch(.withGroupId(request.0.groupId)).first { return }
+                if let _ = try? dbManager.fetchGroups(.init(id: [request.0.id])).first {
+                    return
+                }
 
                 DispatchQueue.global().async { [weak self] in
                     self?.processGroupCreation(request.0, memberIds: request.1, welcome: request.2)
@@ -317,14 +313,14 @@ public final class Session: SessionType {
 
         client.confirmations
             .sink { [unowned self] in
-                if var contact: Contact = try? dbManager.fetch(.withUserId($0.userId)).first {
-                    contact.status = .friend
+                if var contact = try? dbManager.fetchContacts(.init(id: [$0.id])).first {
+                    contact.authStatus = .friend
                     contact.isRecent = true
                     contact.createdAt = Date()
                     _ = try? dbManager.saveContact(contact)
 
                     toastController.enqueueToast(model: .init(
-                        title: contact.nickname ?? contact.username,
+                        title: contact.nickname ?? contact.username!,
                         subtitle: Localized.Requests.Confirmations.toaster,
                         leftImage: Asset.sharedSuccess.image
                     ))
diff --git a/Sources/Integration/Session/SessionType.swift b/Sources/Integration/Session/SessionType.swift
index 0c5d6782..f2badbfa 100644
--- a/Sources/Integration/Session/SessionType.swift
+++ b/Sources/Integration/Session/SessionType.swift
@@ -1,5 +1,6 @@
 import Models
 import Combine
+import XXModels
 import Foundation
 
 public protocol SessionType {
@@ -38,21 +39,14 @@ public protocol SessionType {
 
     // Messages
 
-    func readAll(from: Group)
-    func readAll(from: Contact)
     func retryMessage(_: Int64)
     func retryGroupMessage(_: Int64)
-    func deleteAll(from: Group)
-    func deleteAll(from: Contact)
-    func delete(messages: [Int64])
-    func delete(groupMessages: [Int64])
     func send(_: Payload, toContact: Contact)
 
     // Contacts
 
     func add(_: Contact) throws
     func confirm(_: Contact) throws
-    func find(by: String) -> Contact?
     func deleteContact(_: Contact) throws
 
     func retryRequest(_: Contact) throws
diff --git a/Sources/Models/GroupChatInfo.swift b/Sources/Models/GroupChatInfo.swift
index 9b39ff6d..e2d56675 100644
--- a/Sources/Models/GroupChatInfo.swift
+++ b/Sources/Models/GroupChatInfo.swift
@@ -1,22 +1,22 @@
-import Foundation
-
-public struct GroupChatInfo: Codable, Equatable, Hashable {
-    public enum Request {
-        case accepted
-        case fromGroup(Data)
-    }
-
-    public var group: Group
-    public var members: [GroupMember]
-    public var lastMessage: GroupMessage?
-
-    public init(
-        group: Group,
-        members: [GroupMember],
-        lastMessage: GroupMessage? = nil
-    ) {
-        self.group = group
-        self.members = members
-        self.lastMessage = lastMessage
-    }
-}
+//import Foundation
+//
+//public struct GroupChatInfo: Codable, Equatable, Hashable {
+//    public enum Request {
+//        case accepted
+//        case fromGroup(Data)
+//    }
+//
+//    public var group: Group
+//    public var members: [GroupMember]
+//    public var lastMessage: GroupMessage?
+//
+//    public init(
+//        group: Group,
+//        members: [GroupMember],
+//        lastMessage: GroupMessage? = nil
+//    ) {
+//        self.group = group
+//        self.members = members
+//        self.lastMessage = lastMessage
+//    }
+//}
diff --git a/Sources/Models/GroupMember.swift b/Sources/Models/GroupMember.swift
index 25c619f3..c0f7123f 100644
--- a/Sources/Models/GroupMember.swift
+++ b/Sources/Models/GroupMember.swift
@@ -1,42 +1,42 @@
-import Foundation
-
-public struct GroupMember {
-    public enum Request {
-        case all
-        case strangers
-        case fromGroup(Data)
-        case withUserId(Data)
-    }
-
-    public enum Status: Int64, Codable {
-        case usernameSet
-        case pendingUsername
-    }
-
-    public var id: Int64?
-    public var userId: Data
-    public var groupId: Data
-    public var status: Status
-    public var username: String
-    public var photo: Data?
-
-    public init(
-        id: Int64? = nil,
-        userId: Data,
-        groupId: Data,
-        status: Status,
-        username: String,
-        photo: Data? = nil
-    ) {
-        self.id = id
-        self.userId = userId
-        self.groupId = groupId
-        self.username = username
-        self.status = status
-        self.photo = photo
-    }
-}
-
-extension GroupMember: Codable {}
-extension GroupMember: Hashable {}
-extension GroupMember: Equatable {}
+//import Foundation
+//
+//public struct GroupMember {
+//    public enum Request {
+//        case all
+//        case strangers
+//        case fromGroup(Data)
+//        case withUserId(Data)
+//    }
+//
+//    public enum Status: Int64, Codable {
+//        case usernameSet
+//        case pendingUsername
+//    }
+//
+//    public var id: Int64?
+//    public var userId: Data
+//    public var groupId: Data
+//    public var status: Status
+//    public var username: String
+//    public var photo: Data?
+//
+//    public init(
+//        id: Int64? = nil,
+//        userId: Data,
+//        groupId: Data,
+//        status: Status,
+//        username: String,
+//        photo: Data? = nil
+//    ) {
+//        self.id = id
+//        self.userId = userId
+//        self.groupId = groupId
+//        self.username = username
+//        self.status = status
+//        self.photo = photo
+//    }
+//}
+//
+//extension GroupMember: Codable {}
+//extension GroupMember: Hashable {}
+//extension GroupMember: Equatable {}
diff --git a/Sources/Models/GroupMessage.swift b/Sources/Models/GroupMessage.swift
index ffdb6df4..771d4ceb 100644
--- a/Sources/Models/GroupMessage.swift
+++ b/Sources/Models/GroupMessage.swift
@@ -1,56 +1,56 @@
-import Foundation
-
-public struct GroupMessage: Codable, Equatable, Hashable {
-    public enum Request {
-        case withUniqueId(Data)
-        case id(Int64)
-        case sending
-        case fromGroup(Data)
-        case unreadsFromGroup(Data)
-    }
-
-    public static var databaseTableName: String { "groupMessages" }
-
-    public enum Status: Int64, Codable {
-        case sent
-        case read
-        case failed
-        case sending
-        case received
-    }
-
-    public var id: Int64?
-    public var uniqueId: Data?
-    public var groupId: Data
-    public var sender: Data
-    public var roundId: Int64?
-    public var payload: Payload
-    public var status: Status
-    public var roundURL: String?
-    public var unread: Bool
-    public var timestamp: Int
-
-    public init(
-        id: Int64? = nil,
-        sender: Data,
-        groupId: Data,
-        payload: Payload,
-        unread: Bool,
-        timestamp: Int = 0,
-        uniqueId: Data?,
-        status: Status,
-        roundId: Int64? = nil,
-        roundURL: String? = nil
-    ) {
-        self.id = id
-        self.sender = sender
-        self.groupId = groupId
-        self.payload = payload
-        self.unread = unread
-        self.timestamp = timestamp
-        self.uniqueId = uniqueId
-        self.status = status
-        self.roundId = roundId
-        self.roundURL = roundURL
-    }
-}
+//import Foundation
+//
+//public struct GroupMessage: Codable, Equatable, Hashable {
+//    public enum Request {
+//        case withUniqueId(Data)
+//        case id(Int64)
+//        case sending
+//        case fromGroup(Data)
+//        case unreadsFromGroup(Data)
+//    }
+//
+//    public static var databaseTableName: String { "groupMessages" }
+//
+//    public enum Status: Int64, Codable {
+//        case sent
+//        case read
+//        case failed
+//        case sending
+//        case received
+//    }
+//
+//    public var id: Int64?
+//    public var uniqueId: Data?
+//    public var groupId: Data
+//    public var sender: Data
+//    public var roundId: Int64?
+//    public var payload: Payload
+//    public var status: Status
+//    public var roundURL: String?
+//    public var unread: Bool
+//    public var timestamp: Int
+//
+//    public init(
+//        id: Int64? = nil,
+//        sender: Data,
+//        groupId: Data,
+//        payload: Payload,
+//        unread: Bool,
+//        timestamp: Int = 0,
+//        uniqueId: Data?,
+//        status: Status,
+//        roundId: Int64? = nil,
+//        roundURL: String? = nil
+//    ) {
+//        self.id = id
+//        self.sender = sender
+//        self.groupId = groupId
+//        self.payload = payload
+//        self.unread = unread
+//        self.timestamp = timestamp
+//        self.uniqueId = uniqueId
+//        self.status = status
+//        self.roundId = roundId
+//        self.roundURL = roundURL
+//    }
+//}
diff --git a/Sources/Models/Message.swift b/Sources/Models/Message.swift
index fec18587..77ae89db 100644
--- a/Sources/Models/Message.swift
+++ b/Sources/Models/Message.swift
@@ -1,70 +1,70 @@
-import Foundation
-import DifferenceKit
-
-public struct Message: Codable, Equatable, Hashable {
-    public enum Request {
-        case sending
-        case withUniqueId(Data)
-        case withId(Int64)
-        case sendingAttachment
-        case withContact(Data)
-        case unreadsFromContactId(Data)
-        case latestOnesFromContactIds([Data])
-    }
-
-    public enum Status: Int64, Codable {
-        case read
-        case sent
-        case sending
-        case sendingAttachment
-        case receivingAttachment
-        case received
-        case failedToSend
-        case timedOut
-    }
-
-    public var id: Int64?
-    public var unread: Bool
-    public let sender: Data
-    public var roundURL: String?
-    public var report: Data?
-    public var status: Status
-    public let receiver: Data
-    public var timestamp: Int
-    public var uniqueId: Data?
-    public var payload: Payload
-    public static var databaseTableName: String { "messages" }
-
-    public init (
-        sender: Data,
-        receiver: Data,
-        payload: Payload,
-        unread: Bool,
-        timestamp: Int,
-        uniqueId: Data?,
-        status: Status,
-        roundURL: String? = nil
-    ) {
-        self.sender = sender
-        self.unread = unread
-        self.status = status
-        self.payload = payload
-        self.receiver = receiver
-        self.uniqueId = uniqueId
-        self.timestamp = timestamp
-        self.roundURL = roundURL
-    }
-}
-
-public extension Message.Status {
-    var canReply: Bool {
-        switch self {
-        case .sent, .received, .read:
-            return true
-        default:
-            return false
-        }
-    }
-}
-
-extension Message: Differentiable {}
+//import Foundation
+//import DifferenceKit
+//
+//public struct Message: Codable, Equatable, Hashable {
+//    public enum Request {
+//        case sending
+//        case withUniqueId(Data)
+//        case withId(Int64)
+//        case sendingAttachment
+//        case withContact(Data)
+//        case unreadsFromContactId(Data)
+//        case latestOnesFromContactIds([Data])
+//    }
+//
+//    public enum Status: Int64, Codable {
+//        case read
+//        case sent
+//        case sending
+//        case sendingAttachment
+//        case receivingAttachment
+//        case received
+//        case failedToSend
+//        case timedOut
+//    }
+//
+//    public var id: Int64?
+//    public var unread: Bool
+//    public let sender: Data
+//    public var roundURL: String?
+//    public var report: Data?
+//    public var status: Status
+//    public let receiver: Data
+//    public var timestamp: Int
+//    public var uniqueId: Data?
+//    public var payload: Payload
+//    public static var databaseTableName: String { "messages" }
+//
+//    public init (
+//        sender: Data,
+//        receiver: Data,
+//        payload: Payload,
+//        unread: Bool,
+//        timestamp: Int,
+//        uniqueId: Data?,
+//        status: Status,
+//        roundURL: String? = nil
+//    ) {
+//        self.sender = sender
+//        self.unread = unread
+//        self.status = status
+//        self.payload = payload
+//        self.receiver = receiver
+//        self.uniqueId = uniqueId
+//        self.timestamp = timestamp
+//        self.roundURL = roundURL
+//    }
+//}
+//
+//public extension Message.Status {
+//    var canReply: Bool {
+//        switch self {
+//        case .sent, .received, .read:
+//            return true
+//        default:
+//            return false
+//        }
+//    }
+//}
+//
+//extension Message: Differentiable {}
-- 
GitLab