From 4ca610157d5e48ea99ebd2fa29fd0ffd52a7e180 Mon Sep 17 00:00:00 2001 From: Bruno Muniz Azevedo Filho <bruno@elixxir.io> Date: Mon, 18 Jul 2022 22:12:17 -0300 Subject: [PATCH] Fixed scrolling only on qr and non-qr --- .../SearchContainerController.swift | 100 ++---------- .../Controllers/SearchEmailController.swift | 9 -- ...oller.swift => SearchLeftController.swift} | 8 +- .../Controllers/SearchPhoneController.swift | 9 -- ...ller.swift => SearchRightController.swift} | 4 +- .../Controllers/SearchTableController.swift | 55 ------- ...wModel.swift => SearchLeftViewModel.swift} | 8 +- .../ViewModels/SearchViewModel.swift | 147 ------------------ Sources/SearchFeature/Views/SearchCell.swift | 82 ---------- .../SearchFeature/Views/SearchEmailView.swift | 26 ---- ....swift => SearchLeftPlaceholderView.swift} | 2 +- ...sernameView.swift => SearchLeftView.swift} | 4 +- .../SearchFeature/Views/SearchPhoneView.swift | 26 ---- .../Views/SearchPlaceholderView.swift | 65 -------- ...archQRView.swift => SearchRightView.swift} | 4 +- .../Views/SearchSegmentedButton.swift | 28 +--- .../Views/SearchSegmentedControl.swift | 49 +++--- 17 files changed, 68 insertions(+), 558 deletions(-) delete mode 100644 Sources/SearchFeature/Controllers/SearchEmailController.swift rename Sources/SearchFeature/Controllers/{SearchUsernameController.swift => SearchLeftController.swift} (98%) delete mode 100644 Sources/SearchFeature/Controllers/SearchPhoneController.swift rename Sources/SearchFeature/Controllers/{SearchQRController.swift => SearchRightController.swift} (97%) delete mode 100644 Sources/SearchFeature/Controllers/SearchTableController.swift rename Sources/SearchFeature/ViewModels/{SearchUsernameViewModel.swift => SearchLeftViewModel.swift} (92%) delete mode 100644 Sources/SearchFeature/ViewModels/SearchViewModel.swift delete mode 100644 Sources/SearchFeature/Views/SearchCell.swift delete mode 100644 Sources/SearchFeature/Views/SearchEmailView.swift rename Sources/SearchFeature/Views/{SearchUsernamePlaceholderView.swift => SearchLeftPlaceholderView.swift} (97%) rename Sources/SearchFeature/Views/{SearchUsernameView.swift => SearchLeftView.swift} (95%) delete mode 100644 Sources/SearchFeature/Views/SearchPhoneView.swift delete mode 100644 Sources/SearchFeature/Views/SearchPlaceholderView.swift rename Sources/SearchFeature/Views/{SearchQRView.swift => SearchRightView.swift} (97%) diff --git a/Sources/SearchFeature/Controllers/SearchContainerController.swift b/Sources/SearchFeature/Controllers/SearchContainerController.swift index 788d47de..efc85dcf 100644 --- a/Sources/SearchFeature/Controllers/SearchContainerController.swift +++ b/Sources/SearchFeature/Controllers/SearchContainerController.swift @@ -33,24 +33,20 @@ public final class SearchContainerController: UIViewController { lazy private var screenView = SearchContainerView() - private let qrController = SearchQRController() private var cancellables = Set<AnyCancellable>() private let viewModel = SearchContainerViewModel() - private let emailController = SearchEmailController() - private let phoneController = SearchPhoneController() + private let leftController = SearchLeftController() + private let rightController = SearchRightController() private var drawerCancellables = Set<AnyCancellable>() - private let usernameController = SearchUsernameController() public override func loadView() { view = screenView - screenView.scrollView.delegate = self embedControllers() } public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) statusBarController.style.send(.darkContent) - navigationController?.navigationBar.customize( backgroundColor: Asset.neutralWhite.color ) @@ -88,16 +84,18 @@ public final class SearchContainerController: UIViewController { .actionPublisher .receive(on: DispatchQueue.main) .sink { [unowned self] in - let page = CGFloat($0.rawValue) - let point: CGPoint = CGPoint(x: screenView.frame.width * page, y: 0.0) - screenView.scrollView.setContentOffset(point, animated: true) + if $0 == .qr { + let point = CGPoint(x: screenView.frame.width, y: 0.0) + screenView.scrollView.setContentOffset(point, animated: true) + } else { + screenView.scrollView.setContentOffset(.zero, animated: true) + } }.store(in: &cancellables) viewModel.coverTrafficPublisher .receive(on: DispatchQueue.main) .sink { [unowned self] in presentCoverTrafficDrawer() } .store(in: &cancellables) - } @objc private func didTapBack() { @@ -105,92 +103,28 @@ public final class SearchContainerController: UIViewController { } private func embedControllers() { - addChild(qrController) - addChild(emailController) - addChild(phoneController) - addChild(usernameController) + addChild(leftController) + addChild(rightController) - screenView.scrollView.addSubview(qrController.view) - screenView.scrollView.addSubview(emailController.view) - screenView.scrollView.addSubview(phoneController.view) - screenView.scrollView.addSubview(usernameController.view) + screenView.scrollView.addSubview(leftController.view) + screenView.scrollView.addSubview(rightController.view) - usernameController.view.snp.makeConstraints { + leftController.view.snp.makeConstraints { $0.top.equalTo(screenView.segmentedControl.snp.bottom) $0.width.equalTo(screenView) $0.bottom.equalTo(screenView) $0.left.equalToSuperview() - $0.right.equalTo(emailController.view.snp.left) - } - - emailController.view.snp.makeConstraints { - $0.top.equalTo(screenView.segmentedControl.snp.bottom) - $0.width.equalTo(screenView) - $0.bottom.equalTo(screenView) - $0.right.equalTo(phoneController.view.snp.left) + $0.right.equalTo(rightController.view.snp.left) } - phoneController.view.snp.makeConstraints { + rightController.view.snp.makeConstraints { $0.top.equalTo(screenView.segmentedControl.snp.bottom) $0.width.equalTo(screenView) $0.bottom.equalTo(screenView) - $0.right.equalTo(qrController.view.snp.left) } - qrController.view.snp.makeConstraints { - $0.top.equalTo(screenView.segmentedControl.snp.bottom) - $0.width.equalTo(screenView) - $0.bottom.equalTo(screenView) - } - - qrController.didMove(toParent: self) - emailController.didMove(toParent: self) - phoneController.didMove(toParent: self) - usernameController.didMove(toParent: self) - } -} - -extension SearchContainerController: UIScrollViewDelegate { - public func scrollViewDidScroll(_ scrollView: UIScrollView) { - let pageOffset = scrollView.contentOffset.x / view.frame.width - scrollSegmentedControlTrack(using: pageOffset) - updateSegmentedControlButtonsColor(using: pageOffset) - } - - private func scrollSegmentedControlTrack(using pageOffset: CGFloat) { - let amountOfTabs = 4.0 - let tabWidth = screenView.bounds.width / amountOfTabs - - if let leftConstraint = screenView.segmentedControl.leftConstraint { - leftConstraint.update(offset: pageOffset * tabWidth) - } - } - - private func updateSegmentedControlButtonsColor(using pageOffset: CGFloat) { - let qrRate = highlightRateFor(page: 3, offset: pageOffset) - let emailRate = highlightRateFor(page: 1, offset: pageOffset) - let phoneRate = highlightRateFor(page: 2, offset: pageOffset) - let usernameRate = highlightRateFor(page: 0, offset: pageOffset) - - screenView.segmentedControl.qrCodeButton.updateHighlighting(rate: qrRate) - screenView.segmentedControl.emailButton.updateHighlighting(rate: emailRate) - screenView.segmentedControl.phoneButton.updateHighlighting(rate: phoneRate) - screenView.segmentedControl.usernameButton.updateHighlighting(rate: usernameRate) - } - - private func highlightRateFor(page: CGFloat, offset: CGFloat) -> CGFloat { - let lowerBound = page - 1 - let upperBound = page + 1 - - if offset > lowerBound && offset < upperBound { - if (offset - lowerBound) > 1 { - return 1 - (offset - page) - } else { - return offset - lowerBound - } - } else { - return 0 - } + leftController.didMove(toParent: self) + rightController.didMove(toParent: self) } } diff --git a/Sources/SearchFeature/Controllers/SearchEmailController.swift b/Sources/SearchFeature/Controllers/SearchEmailController.swift deleted file mode 100644 index 1380e7f1..00000000 --- a/Sources/SearchFeature/Controllers/SearchEmailController.swift +++ /dev/null @@ -1,9 +0,0 @@ -import UIKit - -final class SearchEmailController: UIViewController { - lazy private var screenView = SearchEmailView() - - override func loadView() { - view = screenView - } -} diff --git a/Sources/SearchFeature/Controllers/SearchUsernameController.swift b/Sources/SearchFeature/Controllers/SearchLeftController.swift similarity index 98% rename from Sources/SearchFeature/Controllers/SearchUsernameController.swift rename to Sources/SearchFeature/Controllers/SearchLeftController.swift index 45cfd638..35014bc2 100644 --- a/Sources/SearchFeature/Controllers/SearchUsernameController.swift +++ b/Sources/SearchFeature/Controllers/SearchLeftController.swift @@ -8,7 +8,7 @@ import Countries import DrawerFeature import DependencyInjection -final class SearchUsernameController: UIViewController { +final class SearchLeftController: UIViewController { @Dependency private var hud: HUDType @Dependency private var coordinator: SearchCoordinating @@ -17,10 +17,10 @@ final class SearchUsernameController: UIViewController { @KeyObject(.sharingEmail, defaultValue: false) var isSharingEmail: Bool @KeyObject(.sharingPhone, defaultValue: false) var isSharingPhone: Bool - lazy private var screenView = SearchUsernameView() + lazy private var screenView = SearchLeftView() + private let viewModel = SearchLeftViewModel() private var cancellables = Set<AnyCancellable>() - private let viewModel = SearchUsernameViewModel() private var drawerCancellables = Set<AnyCancellable>() private let adrpURLString = "https://links.xx.network/adrp" private var dataSource: SearchTableViewDiffableDataSource! @@ -339,7 +339,7 @@ final class SearchUsernameController: UIViewController { } -extension SearchUsernameController: UITableViewDelegate { +extension SearchLeftController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let item = dataSource.itemIdentifier(for: indexPath) { switch item { diff --git a/Sources/SearchFeature/Controllers/SearchPhoneController.swift b/Sources/SearchFeature/Controllers/SearchPhoneController.swift deleted file mode 100644 index 63d6f568..00000000 --- a/Sources/SearchFeature/Controllers/SearchPhoneController.swift +++ /dev/null @@ -1,9 +0,0 @@ -import UIKit - -final class SearchPhoneController: UIViewController { - lazy private var screenView = SearchPhoneView() - - override func loadView() { - view = screenView - } -} diff --git a/Sources/SearchFeature/Controllers/SearchQRController.swift b/Sources/SearchFeature/Controllers/SearchRightController.swift similarity index 97% rename from Sources/SearchFeature/Controllers/SearchQRController.swift rename to Sources/SearchFeature/Controllers/SearchRightController.swift index b6941edd..b8468f11 100644 --- a/Sources/SearchFeature/Controllers/SearchQRController.swift +++ b/Sources/SearchFeature/Controllers/SearchRightController.swift @@ -16,10 +16,10 @@ enum SearchQRError: Equatable { case alreadyFriends(String) } -final class SearchQRController: UIViewController { +final class SearchRightController: UIViewController { @Dependency private var permissions: PermissionHandling - lazy private var screenView = SearchQRView() + lazy private var screenView = SearchRightView() private let camera = Camera() private var status: SearchQRStatus? diff --git a/Sources/SearchFeature/Controllers/SearchTableController.swift b/Sources/SearchFeature/Controllers/SearchTableController.swift deleted file mode 100644 index 67459cff..00000000 --- a/Sources/SearchFeature/Controllers/SearchTableController.swift +++ /dev/null @@ -1,55 +0,0 @@ -import UIKit -import Models -import Combine -import XXModels - -final class SearchTableController: UITableViewController { - private let viewModel: SearchViewModel - private var cancellables = Set<AnyCancellable>() - private(set) var dataSource = [Contact]() - - init(_ viewModel: SearchViewModel) { - self.viewModel = viewModel - super.init(style: .grouped) - } - - required init?(coder: NSCoder) { nil } - - override func viewDidLoad() { - super.viewDidLoad() - tableView.backgroundColor = .clear - tableView.separatorStyle = .none - tableView.register(SearchCell.self) - - viewModel.itemsRelay - .receive(on: DispatchQueue.main) - .sink { [unowned self] in - dataSource = $0 - tableView.reloadData() - }.store(in: &cancellables) - } - - override func tableView( - _ tableView: UITableView, - cellForRowAt indexPath: IndexPath - ) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: SearchCell.self) - let username = dataSource[indexPath.row].username! - - cell.setup( - title: username, - subtitle: username, - avatarTitle: username, - avatarImage: nil, - avatarSize: .large - ) - - return cell - } - - override func tableView( - _: UITableView, - numberOfRowsInSection: Int - ) -> Int { dataSource.count } -} - diff --git a/Sources/SearchFeature/ViewModels/SearchUsernameViewModel.swift b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift similarity index 92% rename from Sources/SearchFeature/ViewModels/SearchUsernameViewModel.swift rename to Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift index f64543be..effe59c7 100644 --- a/Sources/SearchFeature/ViewModels/SearchUsernameViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift @@ -7,12 +7,12 @@ import DependencyInjection typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> -struct SearchUsernameViewState { +struct SearchLeftViewState { var input = "" var snapshot: SearchSnapshot? } -final class SearchUsernameViewModel { +final class SearchLeftViewModel { @Dependency var session: SessionType var hudPublisher: AnyPublisher<HUDStatus, Never> { @@ -23,13 +23,13 @@ final class SearchUsernameViewModel { successSubject.eraseToAnyPublisher() } - var statePublisher: AnyPublisher<SearchUsernameViewState, Never> { + var statePublisher: AnyPublisher<SearchLeftViewState, Never> { stateSubject.eraseToAnyPublisher() } private let successSubject = PassthroughSubject<Contact, Never>() private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) - private let stateSubject = CurrentValueSubject<SearchUsernameViewState, Never>(.init()) + private let stateSubject = CurrentValueSubject<SearchLeftViewState, Never>(.init()) func didEnterInput(_ string: String) { stateSubject.value.input = string diff --git a/Sources/SearchFeature/ViewModels/SearchViewModel.swift b/Sources/SearchFeature/ViewModels/SearchViewModel.swift deleted file mode 100644 index e59b7a85..00000000 --- a/Sources/SearchFeature/ViewModels/SearchViewModel.swift +++ /dev/null @@ -1,147 +0,0 @@ -import HUD -import UIKit -import Models -import Combine -import Defaults -import XXModels -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 { - - @Dependency private var session: SessionType - - - var hudPublisher: AnyPublisher<HUDStatus, Never> { - hudSubject.eraseToAnyPublisher() - } - - var placeholderPublisher: AnyPublisher<Bool, Never> { - placeholderSubject.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 hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) - private let placeholderSubject = CurrentValueSubject<Bool, Never>(true) - private let stateSubject = CurrentValueSubject<SearchViewState, Never>(.init()) - - - - 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 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))) - } - } - } - - - func didSet(nickname: String, for contact: Contact) { - if var contact = try? session.dbManager.fetchContacts(.init(id: [contact.id])).first { - contact.nickname = nickname - _ = try? session.dbManager.saveContact(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))) - } - } - - } -} diff --git a/Sources/SearchFeature/Views/SearchCell.swift b/Sources/SearchFeature/Views/SearchCell.swift deleted file mode 100644 index 08efd502..00000000 --- a/Sources/SearchFeature/Views/SearchCell.swift +++ /dev/null @@ -1,82 +0,0 @@ -import UIKit -import Shared - -final class SearchCell: UITableViewCell { - private let titleLabel = UILabel() - private let subtitleLabel = UILabel() - private let separatorView = UIView() - private let avatarView = AvatarView() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - selectionStyle = .none - backgroundColor = Asset.neutralWhite.color - - titleLabel.textColor = Asset.neutralActive.color - subtitleLabel.textColor = Asset.neutralDisabled.color - separatorView.backgroundColor = Asset.neutralLine.color - - titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) - subtitleLabel.font = Fonts.Mulish.regular.font(size: 12.0) - - contentView.addSubview(titleLabel) - contentView.addSubview(avatarView) - contentView.addSubview(subtitleLabel) - contentView.addSubview(separatorView) - - setupConstraints() - } - - required init?(coder: NSCoder) { nil } - - override func prepareForReuse() { - super.prepareForReuse() - titleLabel.text = nil - subtitleLabel.text = nil - avatarView.prepareForReuse() - } - - func setup( - title: String, - subtitle: String, - avatarTitle: String, - avatarImage: Data?, - avatarSize: AvatarView.Size - ) { - titleLabel.text = title - subtitleLabel.text = subtitle - avatarView.setupProfile( - title: avatarTitle, - image: avatarImage, - size: avatarSize - ) - } - - private func setupConstraints() { - titleLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(10) - $0.left.equalTo(avatarView.snp.right).offset(16) - $0.right.lessThanOrEqualToSuperview().offset(-20) - } - - subtitleLabel.snp.makeConstraints { - $0.top.equalTo(titleLabel.snp.bottom).offset(3) - $0.left.equalTo(titleLabel) - $0.bottom.equalToSuperview().offset(-22) - } - - avatarView.snp.makeConstraints { - $0.left.equalToSuperview().offset(28) - $0.width.height.equalTo(48) - $0.bottom.equalToSuperview().offset(-16) - } - - separatorView.snp.makeConstraints { - $0.height.equalTo(1) - $0.left.equalToSuperview().offset(24) - $0.right.equalToSuperview().offset(-24) - $0.bottom.equalToSuperview() - } - } -} diff --git a/Sources/SearchFeature/Views/SearchEmailView.swift b/Sources/SearchFeature/Views/SearchEmailView.swift deleted file mode 100644 index 8110ec4b..00000000 --- a/Sources/SearchFeature/Views/SearchEmailView.swift +++ /dev/null @@ -1,26 +0,0 @@ -import UIKit -import Shared - -final class SearchEmailView: UIView { - let inputField = SearchComponent() - - init() { - super.init(frame: .zero) - - inputField.set( - placeholder: Localized.Ud.Search.Email.input, - imageAtRight: nil - ) - - addSubview(inputField) - - inputField.snp.makeConstraints { - $0.top.equalToSuperview().offset(15) - $0.left.equalToSuperview().offset(15) - $0.right.equalToSuperview().offset(-15) - $0.bottom.lessThanOrEqualToSuperview() - } - } - - required init?(coder: NSCoder) { nil } -} diff --git a/Sources/SearchFeature/Views/SearchUsernamePlaceholderView.swift b/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift similarity index 97% rename from Sources/SearchFeature/Views/SearchUsernamePlaceholderView.swift rename to Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift index aa8843c4..5f6264be 100644 --- a/Sources/SearchFeature/Views/SearchUsernamePlaceholderView.swift +++ b/Sources/SearchFeature/Views/SearchLeftPlaceholderView.swift @@ -2,7 +2,7 @@ import UIKit import Shared import Combine -final class SearchUsernamePlaceholderView: UIView { +final class SearchLeftPlaceholderView: UIView { let titleLabel = UILabel() let subtitleWithInfo = TextWithInfoView() diff --git a/Sources/SearchFeature/Views/SearchUsernameView.swift b/Sources/SearchFeature/Views/SearchLeftView.swift similarity index 95% rename from Sources/SearchFeature/Views/SearchUsernameView.swift rename to Sources/SearchFeature/Views/SearchLeftView.swift index bd979803..f9df64ab 100644 --- a/Sources/SearchFeature/Views/SearchUsernameView.swift +++ b/Sources/SearchFeature/Views/SearchLeftView.swift @@ -1,7 +1,7 @@ import UIKit import Shared -final class SearchUsernameView: UIView { +final class SearchLeftView: UIView { let tableView = UITableView() let inputField = SearchComponent() let emptyView: UIView = { @@ -24,7 +24,7 @@ final class SearchUsernameView: UIView { return view }() - let placeholderView = SearchUsernamePlaceholderView() + let placeholderView = SearchLeftPlaceholderView() init() { super.init(frame: .zero) diff --git a/Sources/SearchFeature/Views/SearchPhoneView.swift b/Sources/SearchFeature/Views/SearchPhoneView.swift deleted file mode 100644 index 9b086bcf..00000000 --- a/Sources/SearchFeature/Views/SearchPhoneView.swift +++ /dev/null @@ -1,26 +0,0 @@ -import UIKit -import Shared - -final class SearchPhoneView: UIView { - let inputField = SearchComponent() - - init() { - super.init(frame: .zero) - - inputField.set( - placeholder: Localized.Ud.Search.Phone.input, - imageAtRight: nil - ) - - addSubview(inputField) - - inputField.snp.makeConstraints { - $0.top.equalToSuperview().offset(15) - $0.left.equalToSuperview().offset(15) - $0.right.equalToSuperview().offset(-15) - $0.bottom.lessThanOrEqualToSuperview() - } - } - - required init?(coder: NSCoder) { nil } -} diff --git a/Sources/SearchFeature/Views/SearchPlaceholderView.swift b/Sources/SearchFeature/Views/SearchPlaceholderView.swift deleted file mode 100644 index 84ae4592..00000000 --- a/Sources/SearchFeature/Views/SearchPlaceholderView.swift +++ /dev/null @@ -1,65 +0,0 @@ -import UIKit -import Shared - -final class SearchPlaceholderView: UIView { - let titleView = TextWithInfoView() - let didTapInfo: () -> Void - - init(didTapInfo: @escaping () -> Void) { - self.didTapInfo = didTapInfo - - super.init(frame: .zero) - - let paragraph = NSMutableParagraphStyle() - paragraph.lineSpacing = 5 - paragraph.lineHeightMultiple = 1.0 - paragraph.alignment = .center - - titleView.setup( - text: "Your searches are anonymous.\nSearch information is never linked to your account or personally identifiable.", - attributes: [ - .foregroundColor: Asset.neutralBody.color, - .font: Fonts.Mulish.regular.font(size: 16.0) as Any, - .paragraphStyle: paragraph - ], - didTapInfo: { didTapInfo() } - ) - - addSubview(titleView) - - titleView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(60) - make.left.equalToSuperview().offset(60) - make.right.equalToSuperview().offset(-60) - } - } - - required init?(coder: NSCoder) { nil } -} - -final class SearchEmptyView: UIView { - private let title = UILabel() - - init() { - super.init(frame: .zero) - - backgroundColor = Asset.neutralWhite.color - - title.textColor = Asset.neutralBody.color - title.font = Fonts.Mulish.regular.font(size: 12.0) - - addSubview(title) - - title.snp.makeConstraints { make in - make.top.equalToSuperview().offset(20) - make.left.equalToSuperview().offset(30) - make.right.equalToSuperview().offset(-30) - } - } - - required init?(coder: NSCoder) { nil } - - func set(filter: String) { - title.text = Localized.Ud.noneFound(filter) - } -} diff --git a/Sources/SearchFeature/Views/SearchQRView.swift b/Sources/SearchFeature/Views/SearchRightView.swift similarity index 97% rename from Sources/SearchFeature/Views/SearchQRView.swift rename to Sources/SearchFeature/Views/SearchRightView.swift index 24278ce7..89dfb813 100644 --- a/Sources/SearchFeature/Views/SearchQRView.swift +++ b/Sources/SearchFeature/Views/SearchRightView.swift @@ -1,12 +1,12 @@ import UIKit import Shared -final class SearchQRView: UIView { +final class SearchRightView: UIView { let statusLabel = UILabel() let imageView = UIImageView() let stackView = UIStackView() - let animationView = DotAnimation() let overlayView = OverlayView() + let animationView = DotAnimation() let actionButton = CapsuleButton() init() { diff --git a/Sources/SearchFeature/Views/SearchSegmentedButton.swift b/Sources/SearchFeature/Views/SearchSegmentedButton.swift index bc4c0f28..3b8e65fb 100644 --- a/Sources/SearchFeature/Views/SearchSegmentedButton.swift +++ b/Sources/SearchFeature/Views/SearchSegmentedButton.swift @@ -12,7 +12,6 @@ final class SearchSegmentedButton: UIControl { imageView.contentMode = .center titleLabel.textAlignment = .center - titleLabel.textColor = Asset.neutralWhite.color titleLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) addSubview(titleLabel) @@ -23,27 +22,16 @@ final class SearchSegmentedButton: UIControl { required init?(coder: NSCoder) { nil } - func setup( - title: String, - icon: UIImage, - iconColor: UIColor = Asset.neutralDisabled.color, - titleColor: UIColor = Asset.neutralDisabled.color - ) { - self.imageView.image = icon - self.titleLabel.text = title - self.imageView.tintColor = iconColor - self.titleLabel.textColor = titleColor + func setup(title: String, icon: UIImage) { + imageView.image = icon + titleLabel.text = title + imageView.tintColor = discreteColor + titleLabel.textColor = discreteColor } - func updateHighlighting(rate: CGFloat) { - let color = UIColor.fade( - from: discreteColor, - to: highlightColor, - pcent: rate - ) - - imageView.tintColor = color - titleLabel.textColor = color + func setSelected(_ bool: Bool) { + imageView.tintColor = bool ? highlightColor : discreteColor + titleLabel.textColor = bool ? highlightColor : discreteColor } private func setupConstraints() { diff --git a/Sources/SearchFeature/Views/SearchSegmentedControl.swift b/Sources/SearchFeature/Views/SearchSegmentedControl.swift index 212c78e6..05eabb36 100644 --- a/Sources/SearchFeature/Views/SearchSegmentedControl.swift +++ b/Sources/SearchFeature/Views/SearchSegmentedControl.swift @@ -13,35 +13,29 @@ final class SearchSegmentedControl: UIView { private let trackView = UIView() private let stackView = UIStackView() + private var leftConstraint: Constraint? private let trackIndicatorView = UIView() - private(set) var leftConstraint: Constraint? - private(set) var usernameButton = SearchSegmentedButton() - private(set) var emailButton = SearchSegmentedButton() - private(set) var phoneButton = SearchSegmentedButton() - private(set) var qrCodeButton = SearchSegmentedButton() + private let emailButton = SearchSegmentedButton() + private let phoneButton = SearchSegmentedButton() + private let qrCodeButton = SearchSegmentedButton() + private let usernameButton = SearchSegmentedButton() var actionPublisher: AnyPublisher<Item, Never> { actionSubject.eraseToAnyPublisher() } private var cancellables = Set<AnyCancellable>() - private let actionSubject = PassthroughSubject<Item, Never>() + private let actionSubject = CurrentValueSubject<Item, Never>(.username) init() { super.init(frame: .zero) trackView.backgroundColor = Asset.neutralLine.color trackIndicatorView.backgroundColor = Asset.brandPrimary.color - usernameButton.setup( - title: Localized.Ud.Tab.username, - icon: Asset.searchTabUsername.image, - iconColor: Asset.brandPrimary.color, - titleColor: Asset.brandPrimary.color - ) - qrCodeButton.setup(title: Localized.Ud.Tab.qr, icon: Asset.searchTabQr.image) emailButton.setup(title: Localized.Ud.Tab.email, icon: Asset.searchTabEmail.image) phoneButton.setup(title: Localized.Ud.Tab.phone, icon: Asset.searchTabPhone.image) + usernameButton.setup(title: Localized.Ud.Tab.username, icon: Asset.searchTabUsername.image) stackView.distribution = .fillEqually stackView.addArrangedSubview(usernameButton) @@ -61,25 +55,38 @@ final class SearchSegmentedControl: UIView { required init?(coder: NSCoder) { nil } private func setupBindings() { - usernameButton - .publisher(for: .touchUpInside) + usernameButton.publisher(for: .touchUpInside) .sink { [unowned self] in actionSubject.send(.username) } .store(in: &cancellables) - emailButton - .publisher(for: .touchUpInside) + emailButton.publisher(for: .touchUpInside) .sink { [unowned self] in actionSubject.send(.email) } .store(in: &cancellables) - phoneButton - .publisher(for: .touchUpInside) + phoneButton.publisher(for: .touchUpInside) .sink { [unowned self] in actionSubject.send(.phone) } .store(in: &cancellables) - qrCodeButton - .publisher(for: .touchUpInside) + qrCodeButton.publisher(for: .touchUpInside) .sink { [unowned self] in actionSubject.send(.qr) } .store(in: &cancellables) + + actionSubject + .removeDuplicates() + .receive(on: DispatchQueue.main) + .sink { [unowned self] in + let tabWidth = bounds.width / 4 + if let leftConstraint = leftConstraint { + leftConstraint.update(offset: tabWidth * CGFloat($0.rawValue)) + setNeedsLayout() + UIView.animate(withDuration: 0.25) { self.layoutIfNeeded() } + } + + qrCodeButton.setSelected($0 == .qr) + emailButton.setSelected($0 == .email) + phoneButton.setSelected($0 == .phone) + usernameButton.setSelected($0 == .username) + }.store(in: &cancellables) } private func setupConstraints() { -- GitLab