import HUD import UIKit import Models import Combine import Defaults import Countries import Foundation import Integration import PushFeature import CombineSchedulers import DependencyInjection enum SelectedFilter { case username case email case phone var prefix: String { switch self { case .username: return "U" case .phone: return "P" case .email: return "E" } } } struct SearchViewState: Equatable { var input: String = "" var phoneInput: String = "" var selectedFilter: SelectedFilter = .username var country: Country = .fromMyPhone() } final class SearchViewModel { @KeyObject(.dummyTrafficOn, defaultValue: false) var isCoverTrafficEnabled: Bool @KeyObject(.pushNotifications, defaultValue: false) private var pushNotifications @KeyObject(.askedDummyTrafficOnce, defaultValue: false) var offeredCoverTraffic: Bool @Dependency private var session: SessionType @Dependency private var pushHandler: PushHandling var hudPublisher: AnyPublisher<HUDStatus, Never> { hudSubject.eraseToAnyPublisher() } var placeholderPublisher: AnyPublisher<Bool, Never> { placeholderSubject.eraseToAnyPublisher() } var coverTrafficPublisher: AnyPublisher<Void, Never> { coverTrafficSubject.eraseToAnyPublisher() } var statePublisher: AnyPublisher<SearchViewState, Never> { stateSubject.eraseToAnyPublisher() } var successPublisher: AnyPublisher<Contact, Never> { successSubject.eraseToAnyPublisher() } var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler() let itemsRelay = CurrentValueSubject<[Contact], Never>([]) private let successSubject = PassthroughSubject<Contact, Never>() private let coverTrafficSubject = PassthroughSubject<Void, Never>() private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) private let placeholderSubject = CurrentValueSubject<Bool, Never>(true) private let stateSubject = CurrentValueSubject<SearchViewState, Never>(.init()) func didAppear() { verifyCoverTraffic() verifyNotifications() } func didSelect(filter: SelectedFilter) { stateSubject.value.selectedFilter = filter } func didInput(_ string: String) { stateSubject.value.input = string.trimmingCharacters(in: .whitespacesAndNewlines) } func didInputPhone(_ string: String) { stateSubject.value.phoneInput = string.trimmingCharacters(in: .whitespacesAndNewlines) } func didChooseCountry(_ country: Country) { stateSubject.value.country = country } func didEnableCoverTraffic() { isCoverTrafficEnabled = true session.setDummyTraffic(status: true) } func didTapSearch() { hudSubject.send(.on(nil)) backgroundScheduler.schedule { [weak self] in guard let self = self else { return } do { var content = self.stateSubject.value.selectedFilter.prefix if self.stateSubject.value.selectedFilter == .phone { content += self.stateSubject.value.phoneInput + self.stateSubject.value.country.code } else { content += self.stateSubject.value.input } try self.session.search(fact: content) { result in self.placeholderSubject.send(false) switch result { case .success(let searched): self.hudSubject.send(.none) self.itemsRelay.send([searched]) case .failure(let error): self.hudSubject.send(.error(.init(with: error))) self.itemsRelay.send([]) } } } catch { self.hudSubject.send(.error(.init(with: error))) } } } private func verifyCoverTraffic() { guard offeredCoverTraffic == false else { return } offeredCoverTraffic = true coverTrafficSubject.send() } private func verifyNotifications() { guard pushNotifications == false else { return } pushHandler.requestAuthorization { [weak self] result in guard let self = self else { return } switch result { case .success(let granted): if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } self.pushNotifications = granted case .failure: self.pushNotifications = false } } } func didSet(nickname: String, for contact: Contact) { var contact = contact contact.nickname = nickname backgroundScheduler.schedule { [weak self] in guard let self = self else { return } self.session.update(contact) } } func didTapRequest(contact: Contact) { hudSubject.send(.on(nil)) var contact = contact contact.nickname = contact.username backgroundScheduler.schedule { [weak self] in guard let self = self else { return } do { try self.session.add(contact) self.hudSubject.send(.none) self.successSubject.send(contact) } catch { self.hudSubject.send(.error(.init(with: error))) } } } }