Skip to content
Snippets Groups Projects
ContactViewModel.swift 7.13 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import HUD
import UIKit
import Models
import Combine
import XXModels
import Defaults
Bruno Muniz's avatar
Bruno Muniz committed
import CombineSchedulers
import DependencyInjection
Bruno Muniz's avatar
Bruno Muniz committed
import XXMessengerClient
Bruno Muniz's avatar
Bruno Muniz committed

import XXClient

Bruno Muniz's avatar
Bruno Muniz committed
struct ContactViewState: Equatable {
    var title: String?
    var email: String?
    var phone: String?
    var photo: UIImage?
    var username: String?
    var nickname: String?
}

final class ContactViewModel {
    @Dependency var database: Database
Bruno Muniz's avatar
Bruno Muniz committed
    @Dependency var messenger: Messenger
    @Dependency var getFactsFromContact: GetFactsFromContact

    @KeyObject(.username, defaultValue: nil) var username: String?
Bruno Muniz's avatar
Bruno Muniz committed
    @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool
    @KeyObject(.sharingPhone, defaultValue: false) var sharingPhone: Bool
Bruno Muniz's avatar
Bruno Muniz committed

Bruno Muniz's avatar
Bruno Muniz committed
    var contact: XXModels.Contact
Bruno Muniz's avatar
Bruno Muniz committed

    var popToRootPublisher: AnyPublisher<Void, Never> { popToRootRelay.eraseToAnyPublisher() }
    var popPublisher: AnyPublisher<Void, Never> { popRelay.eraseToAnyPublisher() }
    var hudPublisher: AnyPublisher<HUDStatus, Never> { hudRelay.eraseToAnyPublisher() }
    var successPublisher: AnyPublisher<Void, Never> { successRelay.eraseToAnyPublisher() }
    var statePublisher: AnyPublisher<ContactViewState, Never> { stateRelay.eraseToAnyPublisher() }

    private let popRelay = PassthroughSubject<Void, Never>()
    private let popToRootRelay = PassthroughSubject<Void, Never>()
    private let successRelay = PassthroughSubject<Void, Never>()
    private let hudRelay = CurrentValueSubject<HUDStatus, Never>(.none)
    private let stateRelay = CurrentValueSubject<ContactViewState, Never>(.init())

    var myId: Data {
Bruno Muniz's avatar
Bruno Muniz committed
        try! messenger.e2e.get()!.getContact().getId()
Bruno Muniz's avatar
Bruno Muniz committed
    var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()

Bruno Muniz's avatar
Bruno Muniz committed
    init(_ contact: XXModels.Contact) {
Bruno Muniz's avatar
Bruno Muniz committed
        self.contact = contact

Bruno Muniz's avatar
Bruno Muniz committed
        let facts = try? getFactsFromContact(contact.marshaled!)
        let email = facts?.first(where: { $0.type == .email })?.value
        let phone = facts?.first(where: { $0.type == .phone })?.value

        stateRelay.value = .init(
            title: contact.nickname ?? contact.username,
            email: email,
            phone: phone,
            photo: contact.photo != nil ? UIImage(data: contact.photo!) : nil,
            username: contact.username,
            nickname: contact.nickname
        )
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func didChoosePhoto(_ photo: UIImage) {
        stateRelay.value.photo = photo
        contact.photo = photo.jpegData(compressionQuality: 0.0)
        _ = try? database.saveContact(contact)
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func didTapDelete() {
Bruno Muniz's avatar
Bruno Muniz committed
        hudRelay.send(.on)
Bruno Muniz's avatar
Bruno Muniz committed

        do {
Bruno Muniz's avatar
Bruno Muniz committed
            try messenger.e2e.get()!.deleteRequest.partnerId(contact.id)
            try database.deleteContact(contact)

Bruno Muniz's avatar
Bruno Muniz committed
            hudRelay.send(.none)
            popToRootRelay.send()
        } catch {
            hudRelay.send(.error(.init(with: error)))
        }
    }

    func didTapReject() {
        // TODO: Reject function on the API?
        _ = try? database.deleteContact(contact)
Bruno Muniz's avatar
Bruno Muniz committed
        popRelay.send()
    }

    func didTapClear() {
        _ = try? database.deleteMessages(.init(chat: .direct(myId, contact.id)))
Bruno Muniz's avatar
Bruno Muniz committed
    }

    func didUpdateNickname(_ string: String) {
        contact.nickname = string.isEmpty ? nil : string
        stateRelay.value.title = string.isEmpty ? contact.username : string
        _ = try? database.saveContact(contact)
Bruno Muniz's avatar
Bruno Muniz committed

        stateRelay.value.nickname = contact.nickname
    }

    func didTapResend() {
Bruno Muniz's avatar
Bruno Muniz committed
        hudRelay.send(.on)
        contact.authStatus = .requesting
Bruno Muniz's avatar
Bruno Muniz committed

        backgroundScheduler.schedule { [weak self] in
            guard let self = self else { return }

            do {
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                var includedFacts: [Fact] = []
                let myFacts = try self.messenger.ud.get()!.getFacts()

                if let fact = myFacts.get(.username) {
                    includedFacts.append(fact)
                }

                if self.sharingEmail, let fact = myFacts.get(.email) {
                    includedFacts.append(fact)
                }

                if self.sharingPhone, let fact = myFacts.get(.phone) {
                    includedFacts.append(fact)
                }
Bruno Muniz's avatar
Bruno Muniz committed
                let _ = try self.messenger.e2e.get()!.requestAuthenticatedChannel(
Bruno Muniz's avatar
Bruno Muniz committed
                    partner: .live(self.contact.marshaled!),
                    myFacts: includedFacts
                )

                self.contact.authStatus = .requested
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.none)
                self.popRelay.send()
            } catch {
                self.contact.authStatus = .requestFailed
                _ = try? self.database.saveContact(self.contact)
Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.error(.init(with: error)))
            }
        }
    }

    func didTapRequest(with nickname: String) {
Bruno Muniz's avatar
Bruno Muniz committed
        hudRelay.send(.on)
Bruno Muniz's avatar
Bruno Muniz committed
        contact.nickname = nickname
        contact.authStatus = .requesting
Bruno Muniz's avatar
Bruno Muniz committed

        backgroundScheduler.schedule { [weak self] in
            guard let self = self else { return }

            do {
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                var includedFacts: [Fact] = []
                let myFacts = try self.messenger.ud.get()!.getFacts()

                if let fact = myFacts.get(.username) {
                    includedFacts.append(fact)
                }

                if self.sharingEmail, let fact = myFacts.get(.email) {
                    includedFacts.append(fact)
                }

                if self.sharingPhone, let fact = myFacts.get(.phone) {
                    includedFacts.append(fact)
                }
Bruno Muniz's avatar
Bruno Muniz committed
                let _ = try self.messenger.e2e.get()!.requestAuthenticatedChannel(
Bruno Muniz's avatar
Bruno Muniz committed
                    partner: .live(self.contact.marshaled!),
                    myFacts: includedFacts
                )

                self.contact.authStatus = .requested
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.none)
                self.successRelay.send()
            } catch {
                self.contact.authStatus = .requestFailed
                _ = try? self.database.saveContact(self.contact)
Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.error(.init(with: error)))
            }
        }
    }

    func didTapAccept(_ nickname: String) {
Bruno Muniz's avatar
Bruno Muniz committed
        hudRelay.send(.on)
Bruno Muniz's avatar
Bruno Muniz committed
        contact.nickname = nickname
        contact.authStatus = .confirming
Bruno Muniz's avatar
Bruno Muniz committed

        backgroundScheduler.schedule { [weak self] in
            guard let self = self else { return }

            do {
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                let _ = try self.messenger.e2e.get()!.confirmReceivedRequest(partner: XXClient.Contact.live(self.contact.marshaled!))

                self.contact.authStatus = .friend
                try self.database.saveContact(self.contact)

Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.none)
                self.popRelay.send()
            } catch {
                self.contact.authStatus = .confirmationFailed
                _ = try? self.database.saveContact(self.contact)
Bruno Muniz's avatar
Bruno Muniz committed
                self.hudRelay.send(.error(.init(with: error)))
            }
        }
    }
}