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