import UIKit import Shared import Combine import AppCore import AppResources import AppNavigation import DrawerFeature import ComposableArchitecture public final class ProfileController: UIViewController { @Dependency(\.navigator) var navigator @Dependency(\.app.statusBar) var statusBar private lazy var screenView = ProfileView() private let viewModel = ProfileViewModel() private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() public override func loadView() { view = screenView } public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) statusBar.set(.lightContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralBody.color) viewModel.refresh() } public override func viewDidLoad() { super.viewDidLoad() screenView.cardComponent.nameLabel.text = viewModel.username! setupNavigationBar() setupBindings() } private func setupNavigationBar() { navigationItem.backButtonTitle = "" let menuButton = UIButton() menuButton.tintColor = Asset.neutralWhite.color menuButton.setImage(Asset.chatListMenu.image, for: .normal) menuButton.addTarget(self, action: #selector(didTapMenu), for: .touchUpInside) menuButton.snp.makeConstraints { $0.width.equalTo(50) } navigationItem.leftBarButtonItem = UIBarButtonItem(customView: menuButton) } private func setupBindings() { screenView.emailView.actionButton .publisher(for: .touchUpInside) .receive(on: DispatchQueue.main) .sink { [unowned self] in if screenView.emailView.currentValue != nil { presentDrawer( title: Localized.Profile.Delete.title( Localized.Profile.Email.title.capitalized ), subtitle: Localized.Profile.Delete.subtitle( Localized.Profile.Email.title.lowercased(), Localized.Profile.Email.title.lowercased() ), actionTitle: Localized.Profile.Delete.action( Localized.Profile.Email.title )) { self.viewModel.didTapDelete(isEmail: true) } } else { navigator.perform(PresentProfileEmail(on: navigationController!)) } }.store(in: &cancellables) screenView.phoneView.actionButton .publisher(for: .touchUpInside) .receive(on: DispatchQueue.main) .sink { [unowned self] in if screenView.phoneView.currentValue != nil { presentDrawer( title: Localized.Profile.Delete.title( Localized.Profile.Phone.title.capitalized ), subtitle: Localized.Profile.Delete.subtitle( Localized.Profile.Phone.title.lowercased(), Localized.Profile.Phone.title.lowercased() ), actionTitle: Localized.Profile.Delete.action( Localized.Profile.Phone.title )) { self.viewModel.didTapDelete(isEmail: false) } } else { navigator.perform(PresentProfilePhone(on: navigationController!)) } }.store(in: &cancellables) screenView .cardComponent .avatarView .editButton .publisher(for: .touchUpInside) .receive(on: DispatchQueue.main) .sink { [unowned self] in viewModel.didRequestLibraryAccess() }.store(in: &cancellables) viewModel .navigation .receive(on: DispatchQueue.main) .removeDuplicates() .sink { [unowned self] in switch $0 { case .library: presentDrawer( title: Localized.Profile.Photo.title, subtitle: Localized.Profile.Photo.subtitle, actionTitle: Localized.Profile.Photo.continue) { self.navigator.perform(PresentPhotoLibrary(from: self)) } case .libraryPermission: self.navigator.perform(PresentPermissionRequest(type: .library, from: self)) case .none: break } viewModel.didNavigateSomewhere() }.store(in: &cancellables) viewModel .state .map(\.email) .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [unowned self] in screenView.emailView.set(value: $0) }.store(in: &cancellables) viewModel .state .map(\.phone) .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [unowned self] in screenView.phoneView.set(value: $0) }.store(in: &cancellables) viewModel .state .map(\.photo) .compactMap { $0 } .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [unowned self] in screenView.cardComponent.image = $0 }.store(in: &cancellables) } private func presentDrawer( title: String, subtitle: String, actionTitle: String, action: @escaping () -> Void ) { let actionButton = DrawerCapsuleButton(model: .init( title: actionTitle, style: .red )) actionButton .action .receive(on: DispatchQueue.main) .sink { [unowned self] in navigator.perform(DismissModal(from: self)) { [weak self] in guard let self else { return } self.drawerCancellables.removeAll() action() } }.store(in: &drawerCancellables) navigator.perform(PresentDrawer(items: [ DrawerText( font: Fonts.Mulish.bold.font(size: 26.0), text: title, color: Asset.neutralActive.color, alignment: .left, spacingAfter: 19 ), DrawerText( font: Fonts.Mulish.regular.font(size: 16.0), text: subtitle, color: Asset.neutralBody.color, alignment: .left, lineHeightMultiple: 1.1, spacingAfter: 37 ), actionButton ], isDismissable: true, from: self)) } @objc private func didTapMenu() { navigator.perform(PresentMenu(currentItem: .profile, from: self)) } } extension ProfileController: UIImagePickerControllerDelegate { public func imagePickerController( _ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any] ) { var image: UIImage? if let originalImage = info[.originalImage] as? UIImage { image = originalImage } if let croppedImage = info[.editedImage] as? UIImage { image = croppedImage } guard let image = image else { picker.dismiss(animated: true) return } picker.dismiss(animated: true) viewModel.didChoosePhoto(image) } } extension ProfileController: UINavigationControllerDelegate {}