Skip to content
Snippets Groups Projects
Commit 6c74ba6c authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Fix nickname drawer dependency

parent 840e3905
No related branches found
No related tags found
1 merge request!97Fix nickname drawer dependency
...@@ -110,6 +110,9 @@ extension NavigatorKey: DependencyKey { ...@@ -110,6 +110,9 @@ extension NavigatorKey: DependencyKey {
PresentOnboardingEmailNavigator( PresentOnboardingEmailNavigator(
OnboardingEmailController.init OnboardingEmailController.init
), ),
PresentNicknameNavigator(
NicknameController.init(_:_:)
),
PresentOnboardingSuccessNavigator( PresentOnboardingSuccessNavigator(
OnboardingSuccessController.init(_:) OnboardingSuccessController.init(_:)
), ),
......
...@@ -8,7 +8,7 @@ public struct PresentNickname: Action { ...@@ -8,7 +8,7 @@ public struct PresentNickname: Action {
/// - parent: Parent view controller from which presentation should happen /// - parent: Parent view controller from which presentation should happen
/// - animated: Animate the transition /// - animated: Animate the transition
public init( public init(
prefilled: String?, prefilled: String,
completion: @escaping (String) -> Void, completion: @escaping (String) -> Void,
from parent: UIViewController, from parent: UIViewController,
animated: Bool = true animated: Bool = true
...@@ -19,8 +19,8 @@ public struct PresentNickname: Action { ...@@ -19,8 +19,8 @@ public struct PresentNickname: Action {
self.animated = animated self.animated = animated
} }
/// Optional value to be set as placeholder/pre-existent text /// Value to be set as placeholder/pre-existent text
public var prefilled: String? public var prefilled: String
/// Closure that passes the value of the text set /// Closure that passes the value of the text set
public var completion: (String) -> Void public var completion: (String) -> Void
...@@ -38,16 +38,19 @@ public struct PresentNicknameNavigator: TypedNavigator { ...@@ -38,16 +38,19 @@ public struct PresentNicknameNavigator: TypedNavigator {
let transitioningDelegate = BottomTransitioningDelegate() let transitioningDelegate = BottomTransitioningDelegate()
/// View controller which should be opened up /// View controller which should be opened up
var viewController: () -> UIViewController var viewController: (String, @escaping (String) -> Void) -> UIViewController
/// - Parameters: /// - Parameters:
/// - viewController: view controller which should be presented /// - viewController: view controller which should be presented
public init(_ viewController: @escaping () -> UIViewController) { public init(_ viewController: @escaping (
String, @escaping (String) -> Void
) -> UIViewController
) {
self.viewController = viewController self.viewController = viewController
} }
public func perform(_ action: PresentNickname, completion: @escaping () -> Void) { public func perform(_ action: PresentNickname, completion: @escaping () -> Void) {
let controller = viewController() let controller = viewController(action.prefilled, action.completion)
controller.transitioningDelegate = transitioningDelegate controller.transitioningDelegate = transitioningDelegate
controller.modalPresentationStyle = .overFullScreen controller.modalPresentationStyle = .overFullScreen
......
import UIKit import UIKit
import Shared
import Combine import Combine
import InputField
import ScrollViewController
public final class NicknameController: UIViewController { public final class NicknameController: UIViewController {
private lazy var screenView = NicknameView() private lazy var screenView = NicknameView()
private let prefilled: String private let viewModel: NicknameViewModel
private let completion: (String) -> Void private let completion: (String) -> Void
private let viewModel = NicknameViewModel()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default)
public init(_ prefilled: String, _ completion: @escaping (String) -> Void) { public init(
self.prefilled = prefilled _ prefilled: String,
_ completion: @escaping (String) -> Void
) {
self.completion = completion self.completion = completion
self.viewModel = .init(prefilled: prefilled)
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
required init?(coder: NSCoder) { nil } required init?(coder: NSCoder) { nil }
public override func loadView() { public override func loadView() {
let view = UIView() view = screenView
view.addSubview(screenView)
screenView.snp.makeConstraints {
$0.top.equalToSuperview()
$0.left.equalToSuperview()
$0.right.equalToSuperview()
$0.bottom.equalToSuperview().offset(0)
}
self.view = view
} }
public override func viewDidLoad() { public override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
setupKeyboard()
setupBindings()
screenView.inputField.update(content: prefilled)
viewModel.didInput(prefilled)
}
private func setupKeyboard() {
keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in
guard let self else { return }
let inset = self.view.frame.height - self.view.convert(keyboard.frame, from: nil).minY
self.screenView.snp.updateConstraints {
$0.bottom.equalToSuperview().offset(-inset)
}
self.view.setNeedsLayout()
UIView.animate(withDuration: keyboard.animationDuration) {
self.view.layoutIfNeeded()
}
}
}
private func setupBindings() {
viewModel.state
.map(\.status)
.receive(on: DispatchQueue.main)
.sink { [weak screenView] in screenView?.update(status: $0) }
.store(in: &cancellables)
viewModel.done viewModel
.statePublisher
.removeDuplicates()
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] in .sink { [unowned self] in
dismiss(animated: true) screenView.inputField.update(status: $0.status)
completion($0) screenView.inputField.update(content: $0.input)
if case .valid = $0.status {
screenView.saveButton.isEnabled = true
} else {
screenView.saveButton.isEnabled = false
}
}.store(in: &cancellables) }.store(in: &cancellables)
screenView.inputField.textPublisher screenView
.sink { [weak viewModel] in viewModel?.didInput($0) } .inputField
.store(in: &cancellables) .textPublisher
.sink { [unowned self] in
viewModel.didInput($0)
}.store(in: &cancellables)
screenView.saveButton.publisher(for: .touchUpInside) screenView
.sink { [weak viewModel] in viewModel?.didTapSave() } .saveButton
.store(in: &cancellables) .publisher(for: .touchUpInside)
.sink { [unowned self] in
dismiss(animated: true)
completion(viewModel.getInput())
}.store(in: &cancellables)
} }
} }
import Shared
import Combine import Combine
import InputField import InputField
import AppResources import AppResources
struct NicknameViewState {
var nickname: String = ""
var status: InputField.ValidationStatus = .unknown(nil)
}
final class NicknameViewModel { final class NicknameViewModel {
// MARK: Properties struct ViewState: Equatable {
var input: String
var state: AnyPublisher<NicknameViewState, Never> { var status: InputField.ValidationStatus
stateRelay.eraseToAnyPublisher() }
}
var statePublisher: AnyPublisher<ViewState, Never> {
var done: AnyPublisher<String, Never> { stateSubject.eraseToAnyPublisher()
doneRelay.eraseToAnyPublisher() }
}
private let stateSubject: CurrentValueSubject<ViewState, Never>
private let doneRelay = PassthroughSubject<String, Never>()
private let stateRelay = CurrentValueSubject<NicknameViewState, Never>(.init()) init(prefilled: String) {
self.stateSubject = .init(.init(
// MARK: Public input: prefilled,
status: .valid(nil)
func didInput(_ string: String) { ))
stateRelay.value.nickname = string }
validate()
} func getInput() -> String {
stateSubject.value.input
func didTapSave() { }
doneRelay.send(stateRelay.value.nickname.trimmingCharacters(in: .whitespacesAndNewlines))
} func didInput(_ string: String) {
let input = stateSubject.value.input
// MARK: Private .trimmingCharacters(in: .whitespacesAndNewlines)
private func validate() { stateSubject.value.input = input
if stateRelay.value.nickname.trimmingCharacters(in: .whitespacesAndNewlines).count >= 1 { stateSubject.value.status = input.count >= 1 ?
stateRelay.value.status = .valid(nil) .valid(nil) :
} else { .invalid(Localized.Contact.Nickname.minimum)
stateRelay.value.status = .invalid(Localized.Contact.Nickname.minimum) }
}
}
} }
...@@ -4,65 +4,54 @@ import InputField ...@@ -4,65 +4,54 @@ import InputField
import AppResources import AppResources
final class NicknameView: UIView { final class NicknameView: UIView {
let titleLabel = UILabel() let titleLabel = UILabel()
let imageView = UIImageView() let imageView = UIImageView()
let inputField = InputField() let inputField = InputField()
let stackView = UIStackView() let stackView = UIStackView()
let saveButton = CapsuleButton() let saveButton = CapsuleButton()
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
layer.cornerRadius = 40 layer.cornerRadius = 40
backgroundColor = Asset.neutralWhite.color backgroundColor = Asset.neutralWhite.color
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
imageView.contentMode = .center imageView.contentMode = .center
titleLabel.textColor = Asset.neutralDark.color titleLabel.textColor = Asset.neutralDark.color
imageView.image = Asset.personPlaceholder.image imageView.image = Asset.personPlaceholder.image
inputField.setup( inputField.setup(
style: .regular, style: .regular,
title: Localized.Contact.Nickname.input, title: Localized.Contact.Nickname.input,
placeholder: "Jim Morrison", placeholder: "Jim Morrison",
leftView: .image(Asset.personGray.image), leftView: .image(Asset.personGray.image),
subtitleColor: Asset.neutralDisabled.color subtitleColor: Asset.neutralDisabled.color
) )
titleLabel.text = Localized.Contact.Nickname.title titleLabel.text = Localized.Contact.Nickname.title
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
titleLabel.font = Fonts.Mulish.semiBold.font(size: 18.0) titleLabel.font = Fonts.Mulish.semiBold.font(size: 18.0)
saveButton.setStyle(.brandColored) saveButton.setStyle(.brandColored)
saveButton.setTitle(Localized.Contact.Nickname.save, for: .normal) saveButton.setTitle(Localized.Contact.Nickname.save, for: .normal)
stackView.spacing = 20 stackView.spacing = 20
stackView.axis = .vertical stackView.axis = .vertical
stackView.addArrangedSubview(imageView) stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(titleLabel) stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(inputField) stackView.addArrangedSubview(inputField)
stackView.addArrangedSubview(saveButton) stackView.addArrangedSubview(saveButton)
addSubview(stackView) addSubview(stackView)
stackView.snp.makeConstraints { stackView.snp.makeConstraints {
$0.top.equalToSuperview().offset(32) $0.top.equalToSuperview().offset(32)
$0.left.equalToSuperview().offset(30) $0.left.equalToSuperview().offset(30)
$0.right.equalToSuperview().offset(-30) $0.right.equalToSuperview().offset(-30)
$0.bottom.equalToSuperview().offset(-40) $0.bottom.equalToSuperview().offset(-40)
}
} }
}
required init?(coder: NSCoder) { nil } required init?(coder: NSCoder) { nil }
func update(status: InputField.ValidationStatus) {
inputField.update(status: status)
switch status {
case .valid:
saveButton.isEnabled = true
case .invalid, .unknown:
saveButton.isEnabled = false
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment