Newer
Older
import Retry
import Models
import Combine
import Defaults
public class Client {
@KeyObject(.inappnotifications, defaultValue: true) var inappnotifications: Bool
let bindings: BindingsInterface
var dummyManager: DummyTrafficManaging?
var groupManager: GroupManagerInterface?
var userDiscovery: UserDiscoveryInterface?
var transferManager: TransferManagerInterface?
var backup: AnyPublisher<Data, Never> { backupSubject.eraseToAnyPublisher() }
var network: AnyPublisher<Bool, Never> { networkSubject.eraseToAnyPublisher() }
var resets: AnyPublisher<Contact, Never> { resetsSubject.eraseToAnyPublisher() }
var messages: AnyPublisher<Message, Never> { messagesSubject.eraseToAnyPublisher() }
var requests: AnyPublisher<Contact, Never> { requestsSubject.eraseToAnyPublisher() }
var events: AnyPublisher<BackendEvent, Never> { eventsSubject.eraseToAnyPublisher() }
var transfers: AnyPublisher<FileTransfer, Never> { transfersSubject.eraseToAnyPublisher() }
var requestsSent: AnyPublisher<Contact, Never> { requestsSentSubject.eraseToAnyPublisher() }
var confirmations: AnyPublisher<Contact, Never> { confirmationsSubject.eraseToAnyPublisher() }
var groupRequests: AnyPublisher<(Group, [Data], String?), Never> { groupRequestsSubject.eraseToAnyPublisher() }
private let backupSubject = PassthroughSubject<Data, Never>()
private let networkSubject = PassthroughSubject<Bool, Never>()
private let resetsSubject = PassthroughSubject<Contact, Never>()
private let requestsSubject = PassthroughSubject<Contact, Never>()
private let messagesSubject = PassthroughSubject<Message, Never>()
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 groupRequestsSubject = PassthroughSubject<(Group, [Data], String?), Never>()
private var isBackupInitialization = false
private var isBackupInitializationCompleted = false
init(
_ bindings: BindingsInterface,
fromBackup: Bool,
email: String?,
phone: String?
) {
if fromBackup {
try instantiateUserDiscoveryFromBackup(email: email, phone: phone)
} else {
try instantiateUserDiscovery()
}
try instantiateTransferManager()
try instantiateDummyTrafficManager()
updatePreImage()
} catch {
log(string: error.localizedDescription, type: .error)
}
}
backupManager = bindings.initializeBackup(passphrase: passphrase) { [weak backupSubject] in
public func resumeBackup() {
backupManager = nil
backupManager = bindings.resumeBackup { [weak backupSubject] in
backupSubject?.send($0)
}
}
// public func isBackupRunning() -> Bool {
// guard let backupManager = backupManager else { return false }
// return backupManager.isBackupRunning()
// }
guard let backupManager = backupManager else {
fatalError("Trying to add json parameters to backup but no backup manager created yet")
public func stopListeningBackup() {
guard let backupManager = backupManager else { return }
try? backupManager.stop()
self.backupManager = nil
}
public func restoreContacts(fromBackup backup: Data) {
var totalPendingRestoration: Int = 0
let report = bindings.restore(
ids: backup,
using: userDiscovery!) { [weak self] in
guard let self = self else { return }
switch $0 {
case .success(var contact):
self.requestsSentSubject.send(contact)
print(">>> Restored \(contact.username). Setting status as requested")
case .failure(let error):
print(">>> \(error.localizedDescription)")
}
} restoreCallback: { numFound, numRestored, total, errorString in
totalPendingRestoration = total
let results =
"""
>>> Results from within closure of RestoreContacts:
- numFound: \(numFound)
- numRestored: \(numRestored)
- total: \(total)
- errorString: \(errorString)
"""
print(results)
}
guard totalPendingRestoration > 0 else { fatalError("Total is zero, why called restore contacts?") }
guard report.lenRestored() == totalPendingRestoration else {
print(">>> numRestored \(report.lenRestored()) is != than the total (\(totalPendingRestoration)). Going on recursion...\nnumFailed: \(report.lenFailed())\n\(report.getRestoreContactsError())")
restoreContacts(fromBackup: backup)
return
isBackupInitializationCompleted = true
}
private func registerListenersAndStart() throws {
bindings.listenNetworkUpdates { [weak networkSubject] in networkSubject?.send($0) }
bindings.listenRequests { [weak self] in
guard let self = self else { return }
if self.isBackupInitialization {
if self.isBackupInitializationCompleted {
self.requestsSubject.send($0)
}
} else {
self.requestsSubject.send($0)
}
} _: { [weak confirmationsSubject] in
} _: { [weak resetsSubject] in
resetsSubject?.send($0)
}
bindings.listenEvents { [weak eventsSubject] in
eventsSubject?.send($0)
}
groupManager = try bindings.listenGroupRequests { [weak groupRequestsSubject] request, members, welcome in
groupRequestsSubject?.send((request, members, welcome))
} groupMessages: { [weak messagesSubject] in
messagesSubject?.send($0)
}
bindings.listenPreImageUpdates()
try bindings.listenMessages { [weak messagesSubject] in
messagesSubject?.send($0)
}
bindings.startNetwork()
}
private func instantiateTransferManager() throws {
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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 contactId = sender else {
log(string: "Transfer of \(name ?? "nil").\(type ?? "nil") is being dismissed", type: .error)
return
}
transfersSubject?.send(
FileTransfer(
id: tid,
contactId: contactId,
name: name,
type: type,
data: nil,
progress: 0.0,
isIncoming: true,
createdAt: Date()
)
)
}
}
private func instantiateUserDiscovery() throws {
retry(max: 4, retryStrategy: .delay(seconds: 1)) { [weak self] in
guard let self = self else { return }
self.userDiscovery = try self.bindings.generateUD()
}
}
private func instantiateUserDiscoveryFromBackup(email: String?, phone: String?) throws {
retry(max: 4, retryStrategy: .delay(seconds: 1)) { [weak self] in
guard let self = self else { return }
self.userDiscovery = try self.bindings.generateUDFromBackup(email: email, phone: phone)
}
}
private func instantiateDummyTrafficManager() throws {
dummyManager = try bindings.generateDummyTraficManager()
}
private func updatePreImage() {
if let defaults = UserDefaults(suiteName: "group.elixxir.messenger") {
defaults.set(bindings.getPreImages(), forKey: "preImage")
}
}
}