Skip to content

Commit

Permalink
[WEB-696] CTA Dialog on Facebook Login Error (FB Deprecation) (#1763)
Browse files Browse the repository at this point in the history
* create SetYourPasswordViewController and SetYourPasswordViewModel

* alphabetize SetYourPasswordViewModel & ViewModelTests files

* adds viewmodel tests

* add viewcontroller tests

* present new CTA on facebook login error

* remove TODO comment

* remove snapshot tests until they can be recorded on an intel machine
  • Loading branch information
scottkicks authored Dec 13, 2022
1 parent b936c69 commit cd6fe2b
Show file tree
Hide file tree
Showing 16 changed files with 591 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,26 @@ public final class LoginToutViewController: UIViewController, MFMailComposeViewC
self.viewModel.outputs.showFacebookErrorAlert
.observeForControllerAction()
.observeValues { [weak self] error in
self?.present(
guard let strongSelf = self else { return }

if featureFacebookLoginDeprecationEnabled() {
strongSelf.present(
UIAlertController.facebookDeprecationNewPasswordOptionAlert(
loginHandler: { [weak self] _ in
self?.pushLoginViewController()
},
setNewPasswordHandler: { [weak self] _ in
self?.pushResetYourFacebookPasswordViewController()
}
),
animated: true,
completion: nil
)

return
}

strongSelf.present(
UIAlertController.alertController(forError: error),
animated: true,
completion: nil
Expand Down Expand Up @@ -424,6 +443,13 @@ public final class LoginToutViewController: UIViewController, MFMailComposeViewC
self.navigationItem.backBarButtonItem = UIBarButtonItem.back(nil, selector: nil)
}

private func pushResetYourFacebookPasswordViewController() {
let vc = ResetYourFacebookPasswordViewController.instantiate()
self.navigationController?.pushViewController(vc, animated: true)
self.navigationItem
.backBarButtonItem = UIBarButtonItem(title: "Log in", style: .plain, target: nil, action: nil)
}

private func pushSetYourPasswordViewController() {
let vc = SetYourPasswordViewController.instantiate()
vc.delegate = self
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import Foundation
import Library
import Prelude
import ReactiveSwift
import UIKit

public final class ResetYourFacebookPasswordViewController: UIViewController {
// MARK: - Properties

private lazy var contextLabel = { UILabel(frame: .zero) }()
private lazy var emailLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var emailTextField: UITextField = { UITextField(frame: .zero) |> \.tag .~ 0 }()

private lazy var loadingIndicator: UIActivityIndicatorView = {
UIActivityIndicatorView()
|> \.translatesAutoresizingMaskIntoConstraints .~ false
}()

private lazy var rootStackView = { UIStackView() }()
private lazy var scrollView = {
UIScrollView(frame: .zero)
|> \.alwaysBounceVertical .~ true

}()

private lazy var setPasswordButton = { UIButton(type: .custom)
|> \.translatesAutoresizingMaskIntoConstraints .~ false
}()

fileprivate lazy var keyboardDimissingTapGestureRecognizer: UITapGestureRecognizer = {
UITapGestureRecognizer(
target: self,
action: #selector(ResetYourFacebookPasswordViewController.dismissKeyboard)
)
|> \.cancelsTouchesInView .~ false
}()

private let viewModel: ResetYourFacebookPasswordViewModelType = ResetYourFacebookPasswordViewModel()

// MARK: - Lifecycle

public override func viewDidLoad() {
super.viewDidLoad()

self.title = "Set new password"

self.view.addGestureRecognizer(self.keyboardDimissingTapGestureRecognizer)

self.configureViews()
self.setupConstraints()
self.configureTargets()

self.viewModel.inputs.viewDidLoad()
}

public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

self.viewModel.inputs.viewWillAppear()
}

// MARK: - Styles

public override func bindStyles() {
super.bindStyles()

_ = self.contextLabel
|> contextLabelStyle

_ = self.rootStackView
|> baseStackViewStyle
|> loginRootStackViewStyle

_ = self.emailLabel
|> textFieldLabelStyle

_ = self.emailTextField
|> emailFieldStyle
|> roundedStyle(cornerRadius: Styles.grid(2))
|> \.borderStyle .~ UITextField.BorderStyle.roundedRect
|> \.layer.borderColor .~ UIColor.ksr_support_300.cgColor
|> \.layer.borderWidth .~ 1
|> \.accessibilityLabel .~ self.emailLabel.text
|> \.attributedPlaceholder %~ { _ in settingsAttributedPlaceholder("") }

_ = self.setPasswordButton
|> resetPasswordButtonStyle
|> UIButton.lens.title(for: .normal) %~ { _ in
Strings.Set_new_password()
}
|> \.isEnabled .~ false
}

// MARK: - Bind View Model

public override func bindViewModel() {
super.bindViewModel()

self.loadingIndicator.rac.animating = self.viewModel.outputs.shouldShowActivityIndicator
self.contextLabel.rac.text = self.viewModel.outputs.contextLabelText
self.emailLabel.rac.text = self.viewModel.outputs.emailLabel
self.setPasswordButton.rac.enabled = self.viewModel.outputs.setPasswordButtonIsEnabled

self.viewModel.outputs.setPasswordFailure
.observeForControllerAction()
.observeValues { [weak self] errorMessage in
self?.present(UIAlertController.genericError(errorMessage), animated: true, completion: nil)
self?.enableTextFieldsAndSaveButton(true)
}

self.viewModel.outputs.setPasswordSuccess
.observeForControllerAction()
.observeValues { [weak self] successMessage in
self?.present(UIAlertController.alert(message: successMessage), animated: true)
self?.enableTextFieldsAndSaveButton(true)
}

self.viewModel.outputs.textFieldAndSetPasswordButtonAreEnabled
.observeForUI()
.observeValues { [weak self] isEnabled in
self?.enableTextFieldsAndSaveButton(isEnabled)
}
}

// MARK: - Functions

private func configureViews() {
_ = self.view
|> \.backgroundColor .~ .ksr_white

_ = (self.scrollView, self.view)
|> ksr_addSubviewToParent()
|> ksr_constrainViewToEdgesInParent()

_ = (self.rootStackView, self.scrollView)
|> ksr_addSubviewToParent()
|> ksr_constrainViewToEdgesInParent()

_ = ([
self.contextLabel,
self.emailLabel,
self.emailTextField,
self.setPasswordButton,
self.loadingIndicator
], self.rootStackView)
|> ksr_addArrangedSubviewsToStackView()

self.rootStackView.setCustomSpacing(Styles.grid(7), after: self.contextLabel)
}

private func setupConstraints() {
NSLayoutConstraint.activate([
self.rootStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor),

self.emailTextField.heightAnchor.constraint(greaterThanOrEqualToConstant: 44),

self.setPasswordButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 48)
])
}

private func configureTargets() {
self.emailTextField
.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
self.setPasswordButton
.addTarget(self, action: #selector(self.setPasswordButtonPressed), for: .touchUpInside)
}

private func enableTextFieldsAndSaveButton(_ isEnabled: Bool) {
_ = [self.emailTextField, self.setPasswordButton]
||> \.isUserInteractionEnabled .~ isEnabled

self.setPasswordButton.isHidden = !isEnabled
}

// MARK: - Accessors

@objc private func dismissKeyboard() {
self.view.endEditing(true)
}

@objc private func textFieldDidChange(_ textField: UITextField) {
guard let email = textField.text else { return }

self.viewModel.inputs.emailTextFieldFieldDidChange(email)
}

@objc private func setPasswordButtonPressed() {
self.viewModel.inputs.setPasswordButtonPressed()
}
}

// MARK: - Extensions

extension ResetYourFacebookPasswordViewController: UITextFieldDelegate {
public func textFieldDidEndEditing(_ textField: UITextField) {
guard let email = textField.text else { return }

self.viewModel.inputs.emailTextFieldDidReturn(email: email)
}
}

// MARK: - Styles

private let baseStackViewStyle: StackViewStyle = { stackView in
stackView
|> \.distribution .~ .fill
|> \.alignment .~ .fill
|> \.axis .~ .vertical
|> UIStackView.lens.spacing .~ Styles.grid(2)
}

private let contextLabelStyle: LabelStyle = { label in
label
|> \.textAlignment .~ NSTextAlignment.left
|> \.lineBreakMode .~ NSLineBreakMode.byWordWrapping
|> \.numberOfLines .~ 0
|> UILabel.lens.font %~ { _ in UIFont.ksr_body(size: 16) }
}

private let textFieldLabelStyle: LabelStyle = { label in
label
|> \.textAlignment .~ NSTextAlignment.left
|> \.lineBreakMode .~ NSLineBreakMode.byWordWrapping
|> \.numberOfLines .~ 0
|> \.backgroundColor .~ .ksr_white
|> \.textColor .~ UIColor.ksr_support_700
|> \.font %~ { _ in .ksr_callout(size: 13) }
}

private let textFieldStyle: TextFieldStyle = { textField in
textField
|> settingsNewPasswordFormFieldAutoFillStyle
|> roundedStyle(cornerRadius: Styles.grid(2))
|> UITextField.lens.textColor .~ .ksr_black
|> UITextField.lens.font %~ { _ in UIFont.ksr_body(size: 13) }
|> \.textAlignment .~ .left
|> \.borderStyle .~ UITextField.BorderStyle.roundedRect
|> \.layer.borderColor .~ UIColor.ksr_support_300.cgColor
|> \.layer.borderWidth .~ 1
|> \.returnKeyType .~ .done
}

private let savePasswordButtonStyle: ButtonStyle = { button in
button
|> greenButtonStyle
|> roundedStyle(cornerRadius: Styles.grid(2))
|> UIButton.lens.backgroundColor(for: .disabled) .~ UIColor.ksr_support_300.mixLighter(0.12)
|> UIButton.lens.title(for: .normal) %~ { _ in
Strings.Save()
}
}
32 changes: 30 additions & 2 deletions Kickstarter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@
59E877381DC9419700BCD1F7 /* Newsletter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59E877371DC9419700BCD1F7 /* Newsletter.swift */; };
606754BD28CF91D60033CD5E /* FacebookCore in Frameworks */ = {isa = PBXBuildFile; productRef = 606754BC28CF91D60033CD5E /* FacebookCore */; };
606754BF28CF91DD0033CD5E /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 606754BE28CF91DD0033CD5E /* FacebookLogin */; };
6067BCE9293E49AC0036ABB1 /* ResetYourFacebookPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6067BCE7293E48140036ABB1 /* ResetYourFacebookPasswordViewController.swift */; };
6067BCEC293E49F00036ABB1 /* ResetYourFacebookPasswordViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6067BCEA293E49CB0036ABB1 /* ResetYourFacebookPasswordViewModel.swift */; };
6067BCF2293FC3520036ABB1 /* ResetYourFacebookPasswordViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6067BCEF293FC10E0036ABB1 /* ResetYourFacebookPasswordViewModelTests.swift */; };
608E7A5328ABDBAE00289E92 /* SetYourPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608E7A5128ABD5E700289E92 /* SetYourPasswordViewController.swift */; };
608E7A5628ABE6CD00289E92 /* SetYourPasswordViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608E7A5428ABE27400289E92 /* SetYourPasswordViewModel.swift */; };
60DA50EB28B689A4002E2DF1 /* SetYourPasswordViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60DA50E928B68990002E2DF1 /* SetYourPasswordViewModelTests.swift */; };
Expand Down Expand Up @@ -2063,6 +2066,9 @@
59D1E6241D1865AC00896A4C /* DashboardVideoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashboardVideoCell.swift; sourceTree = "<group>"; };
59D1E6571D1866F800896A4C /* DashboardVideoCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashboardVideoCellViewModel.swift; sourceTree = "<group>"; };
59E877371DC9419700BCD1F7 /* Newsletter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Newsletter.swift; sourceTree = "<group>"; };
6067BCE7293E48140036ABB1 /* ResetYourFacebookPasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetYourFacebookPasswordViewController.swift; sourceTree = "<group>"; };
6067BCEA293E49CB0036ABB1 /* ResetYourFacebookPasswordViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetYourFacebookPasswordViewModel.swift; sourceTree = "<group>"; };
6067BCEF293FC10E0036ABB1 /* ResetYourFacebookPasswordViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetYourFacebookPasswordViewModelTests.swift; sourceTree = "<group>"; };
608E7A5128ABD5E700289E92 /* SetYourPasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetYourPasswordViewController.swift; sourceTree = "<group>"; };
608E7A5428ABE27400289E92 /* SetYourPasswordViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetYourPasswordViewModel.swift; sourceTree = "<group>"; };
60DA50E928B68990002E2DF1 /* SetYourPasswordViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetYourPasswordViewModelTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4842,6 +4848,7 @@
1965437528C812F100457EC6 /* ProjectPamphletContentDataSource_DEPRECATED_09_06_2022 */,
1965437628C8141700457EC6 /* ProjectPage */,
1937A70728C939D100DD732D /* ResetPassword */,
6067BCE5293E47D50036ABB1 /* ResetYourFacebookPassword */,
19450C1A28C81DBC00C60F97 /* RewardAddOnSelection */,
19450C1F28C81FEF00C60F97 /* RewardsCollection */,
1937A70928C93BC800DD732D /* RewardPledgeNavigation */,
Expand Down Expand Up @@ -5817,6 +5824,22 @@
path = Layouts;
sourceTree = "<group>";
};
6067BCE5293E47D50036ABB1 /* ResetYourFacebookPassword */ = {
isa = PBXGroup;
children = (
6067BCE6293E48050036ABB1 /* Controller */,
);
path = ResetYourFacebookPassword;
sourceTree = "<group>";
};
6067BCE6293E48050036ABB1 /* Controller */ = {
isa = PBXGroup;
children = (
6067BCE7293E48140036ABB1 /* ResetYourFacebookPasswordViewController.swift */,
);
path = Controller;
sourceTree = "<group>";
};
775DFADB2162A56D00620CED /* mutations */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -6723,6 +6746,8 @@
A7ED1FA11E831C5C00BFFA01 /* ProjectUpdatesViewModelTests.swift */,
A7F441A81D005A9400FE6FC5 /* ResetPasswordViewModel.swift */,
A7ED1F9B1E831C5C00BFFA01 /* ResetPasswordViewModelTests.swift */,
6067BCEA293E49CB0036ABB1 /* ResetYourFacebookPasswordViewModel.swift */,
6067BCEF293FC10E0036ABB1 /* ResetYourFacebookPasswordViewModelTests.swift */,
8A49396E24B77B3500C3C3CE /* RewardAddOnCardViewModel.swift */,
8A00CCFE24BD439E00E12D49 /* RewardAddOnCardViewModelTests.swift */,
8A6C978F24BFCDED00C4FA71 /* RewardAddOnSelectionContinueCTAViewModel.swift */,
Expand Down Expand Up @@ -6772,6 +6797,8 @@
D72370532118C19B001EA4CA /* SettingsRecommendationsCellViewModelTests.swift */,
D79F0F762107D60800D3B32C /* SettingsRequestDataCellViewModel.swift */,
D723708D2118CD9B001EA4CA /* SettingsRequestDataCellViewModelTests.swift */,
608E7A5428ABE27400289E92 /* SetYourPasswordViewModel.swift */,
60DA50E928B68990002E2DF1 /* SetYourPasswordViewModelTests.swift */,
A75C81161D210BD700B5AD03 /* ShareViewModel.swift */,
A7ED1F811E831C5C00BFFA01 /* ShareViewModelTests.swift */,
37C7B81423187BA400C78278 /* ShippingRuleCellViewModel.swift */,
Expand All @@ -6796,8 +6823,6 @@
A7ED1F9C1E831C5C00BFFA01 /* VideoViewModelTests.swift */,
D08CD1FE21913166009F89F0 /* WatchProjectViewModel.swift */,
D08CD200219216BA009F89F0 /* WatchProjectViewModelTests.swift */,
608E7A5428ABE27400289E92 /* SetYourPasswordViewModel.swift */,
60DA50E928B68990002E2DF1 /* SetYourPasswordViewModelTests.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand Down Expand Up @@ -7934,6 +7959,7 @@
A76126BB1C90C94000EDCCB9 /* UITableView-Extensions.swift in Sources */,
9D9F58191D13243900CE81DE /* ProjectActivitiesViewModel.swift in Sources */,
77AA2B3B233C0A1B008BBCB8 /* CreateBackingInput+Constructor.swift in Sources */,
6067BCEC293E49F00036ABB1 /* ResetYourFacebookPasswordViewModel.swift in Sources */,
77C26957240D711A009AD91E /* CategoryCollectionViewSectionHeaderViewModel.swift in Sources */,
A72C3A8E1D00F6A80075227E /* SelectableRow.swift in Sources */,
3706408222A8A66E00889CBD /* PledgeAmountViewModel.swift in Sources */,
Expand Down Expand Up @@ -8128,6 +8154,7 @@
77C7B654226E0E54001101AC /* RewardsCollectionViewModelTests.swift in Sources */,
A7ED1FBA1E831C5C00BFFA01 /* ProjectActivityCommentCellViewModelTests.swift in Sources */,
D62B14B02212184500AC05C8 /* DeletePaymentMethodEnvelopeTests.swift in Sources */,
6067BCF2293FC3520036ABB1 /* ResetYourFacebookPasswordViewModelTests.swift in Sources */,
D66FB348218212B700A27BCC /* PaymentMethodsViewModelTests.swift in Sources */,
771F388A2422C961009036A0 /* PersonalizationCellViewModelTests.swift in Sources */,
94BA16E926698EF00034CC3F /* CommentTableViewFooterViewModelTests.swift in Sources */,
Expand Down Expand Up @@ -8330,6 +8357,7 @@
777C60A6241FECD800820C59 /* PersonalizationCell.swift in Sources */,
370C8B64234FCC6F00DE75DD /* LoadingButton.swift in Sources */,
A75A292E1CE0B95300D35E5C /* MessagesDataSource.swift in Sources */,
6067BCE9293E49AC0036ABB1 /* ResetYourFacebookPasswordViewController.swift in Sources */,
A74382051D3458C900040A95 /* PaddingCell.swift in Sources */,
0169F9841D6E0B2000C8D5C5 /* DiscoveryFiltersStaticRowCell.swift in Sources */,
8A864ACD24429A5B0026BF13 /* PledgePaymentMethodLoadingCell.swift in Sources */,
Expand Down
Loading

0 comments on commit cd6fe2b

Please sign in to comment.