Skip to content

Commit

Permalink
[WEB-666] - Require FB Only Users To Set A Password (#1755)
Browse files Browse the repository at this point in the history
* adds  bool to user model

* separate apple and facebook signals to gain more control over facebook flow in LoginToutViewController

* Push SetYourPasswordViewController if fb user needs a password

* Run make strings script to pull in latest string updates from server

* fetch user's email and set context label

* create new password on user account and handle success and error cases

* make strings

* change nav bar back button text

* update unit tests

* wrap in fb login depreciated feature flag

* update view model tests

* adds activity indicator

* update strings

* add tests

* format

* update failing snapshot tests

* needsPassword is not actually an optional

* use kickstarter conventions

* updates uses of now non nil needsPassword property

* move loading indicator from top level view to stackview and replace save button when loading indicator is showing

* update snapshot tests

* update comments

* update snapshot tests

* update saveButton isHidden/isEnabled

* Remove unused User+GraphUser.swift

* Update User+UserFragment.swift

* Removes User+UserGraph releated tests

* Update LoginToutViewModelTests

* Update SetYourPasswordViewModelTests

* formatting

* Refactor needsPassword static func

* put back User+GraphUser because more test files than expected use it

* Revert "Removes User+UserGraph releated tests"

This reverts commit 2026508.

* corrected the api and library tests, was able to remove unused User+GraphUser and tests. Snapshot tests still failing.

* updated snapshot tests, all kickstarter-ios tests recorded and passing on intel. Should pass CI checks.

* revoked a change just being used for testing.

* rerecorded tests due to translation changes

* more re-records on Intel because of translations

* updated set your view model tests

Co-authored-by: Mubarak Sadoon <msadoon@gmail.com>
  • Loading branch information
scottkicks and msadoon committed Nov 28, 2022
1 parent 0054fad commit 611cc91
Show file tree
Hide file tree
Showing 48 changed files with 747 additions and 207 deletions.
Expand Up @@ -187,12 +187,28 @@ public final class LoginToutViewController: UIViewController, MFMailComposeViewC
self?.pushSignupViewController()
}

self.viewModel.outputs.logIntoEnvironment
self.viewModel.outputs.logIntoEnvironmentWithApple
.observeValues { [weak self] accessTokenEnv in
AppEnvironment.login(accessTokenEnv)
self?.viewModel.inputs.environmentLoggedIn()
}

self.viewModel.outputs.logIntoEnvironmentWithFacebook
.observeValues { [weak self] accessTokenEnv in
guard let strongSelf = self else { return }

AppEnvironment.login(accessTokenEnv)

guard featureFacebookLoginDeprecationEnabled(),
accessTokenEnv.user.needsPassword else {
strongSelf.pushSetYourPasswordViewController()

return
}

strongSelf.viewModel.inputs.environmentLoggedIn()
}

self.viewModel.outputs.postNotification
.observeForUI()
.observeValues {
Expand Down Expand Up @@ -241,10 +257,10 @@ public final class LoginToutViewController: UIViewController, MFMailComposeViewC
self.helpViewModel.outputs.showMailCompose
.observeForControllerAction()
.observeValues { [weak self] in
guard let _self = self else { return }
guard let strongSelf = self else { return }
let controller = MFMailComposeViewController.support()
controller.mailComposeDelegate = _self
_self.present(controller, animated: true, completion: nil)
controller.mailComposeDelegate = strongSelf
strongSelf.present(controller, animated: true, completion: nil)
}

self.helpViewModel.outputs.showNoEmailError
Expand Down Expand Up @@ -407,6 +423,14 @@ public final class LoginToutViewController: UIViewController, MFMailComposeViewC
self.navigationItem.backBarButtonItem = UIBarButtonItem.back(nil, selector: nil)
}

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

fileprivate func showHelpSheet(helpTypes: [HelpType]) {
let helpSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

Expand Down Expand Up @@ -573,6 +597,14 @@ extension LoginToutViewController: ASAuthorizationControllerDelegate {
}
}

// MARK: SetYourPasswordViewControllerDelegate

extension LoginToutViewController: SetYourPasswordViewControllerDelegate {
func setPasswordCompleteAndLogUserIn() {
self.viewModel.inputs.environmentLoggedIn()
}
}

// MARK: - ASAuthorizationControllerPresentationContextProviding

@available(iOS 13.0, *)
Expand Down
Expand Up @@ -4,6 +4,10 @@ import Prelude
import ReactiveSwift
import UIKit

protocol SetYourPasswordViewControllerDelegate: AnyObject {
func setPasswordCompleteAndLogUserIn()
}

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

Expand All @@ -13,6 +17,11 @@ public final class SetYourPasswordViewController: UIViewController {
private lazy var confirmPasswordLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var confirmPasswordTextField: UITextField = { UITextField(frame: .zero) |> \.tag .~ 1 }()

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

private lazy var rootStackView = { UIStackView() }()
private lazy var scrollView = {
UIScrollView(frame: .zero)
Expand All @@ -33,16 +42,7 @@ public final class SetYourPasswordViewController: UIViewController {
}()

private let viewModel: SetYourPasswordViewModelType = SetYourPasswordViewModel()

// MARK: - Configuration

public static func configuredWith(
userEmail: String
) -> SetYourPasswordViewController {
let vc = SetYourPasswordViewController.instantiate()
vc.viewModel.inputs.configureWith(userEmail)
return vc
}
weak var delegate: SetYourPasswordViewControllerDelegate?

// MARK: - Lifecycle

Expand All @@ -60,6 +60,12 @@ public final class SetYourPasswordViewController: UIViewController {
self.viewModel.inputs.viewDidLoad()
}

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

self.viewModel.inputs.viewWillAppear()
}

// MARK: - Styles

public override func bindStyles() {
Expand Down Expand Up @@ -96,19 +102,39 @@ public final class SetYourPasswordViewController: UIViewController {
// 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.newPasswordLabel.rac.text = self.viewModel.outputs.newPasswordLabel
self.confirmPasswordLabel.rac.text = self.viewModel.outputs.confirmPasswordLabel
self.saveButton.rac.enabled = self.viewModel.outputs.saveButtonIsEnabled

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] in
self?.delegate?.setPasswordCompleteAndLogUserIn()
}

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

// MARK: - Functions

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

_ = (self.scrollView, self.view)
|> ksr_addSubviewToParent()
Expand All @@ -124,7 +150,8 @@ public final class SetYourPasswordViewController: UIViewController {
self.newPasswordTextField,
self.confirmPasswordLabel,
self.confirmPasswordTextField,
self.saveButton
self.saveButton,
self.loadingIndicator
], self.rootStackView)
|> ksr_addArrangedSubviewsToStackView()

Expand All @@ -135,8 +162,11 @@ public final class SetYourPasswordViewController: UIViewController {
private func setupConstraints() {
NSLayoutConstraint.activate([
self.rootStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor),

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

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

self.saveButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 48)
])
}
Expand All @@ -149,6 +179,13 @@ public final class SetYourPasswordViewController: UIViewController {
self.saveButton.addTarget(self, action: #selector(self.saveButtonPressed), for: .touchUpInside)
}

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

self.saveButton.isHidden = !isEnabled
}

// MARK: - Accessors

@objc private func dismissKeyboard() {
Expand Down
Expand Up @@ -7,16 +7,24 @@ import XCTest
final class SetYourPasswordViewControllerTests: TestCase {
override func setUp() {
super.setUp()

AppEnvironment.pushEnvironment(mainBundle: Bundle.framework)
UIView.setAnimationsEnabled(false)
}

func testView() {
combos([Language.en], [Device.phone4_7inch, Device.pad]).forEach {
override func tearDown() {
AppEnvironment.popEnvironment()
super.tearDown()
}

func testSetYourPasswordViewController_DisabledSave() {
let userTemplate = GraphUser.template |> \.isEmailVerified .~ true
let userEnvelope = UserEnvelope(me: userTemplate)
let service = MockService(fetchGraphUserResult: .success(userEnvelope))

combos(Language.allLanguages, [Device.phone4_7inch, Device.pad]).forEach {
language, device in
withEnvironment(language: language) {
let controller = SetYourPasswordViewController.configuredWith(userEmail: "abc******@gmail.com")
withEnvironment(apiService: service, apiDelayInterval: .seconds(0), language: language) {
let controller = SetYourPasswordViewController.instantiate()
let (parent, _) = traitControllers(device: device, orientation: .portrait, child: controller)

self.scheduler.run()
Expand All @@ -25,10 +33,4 @@ final class SetYourPasswordViewControllerTests: TestCase {
}
}
}

override func tearDown() {
AppEnvironment.popEnvironment()
UIView.setAnimationsEnabled(true)
super.tearDown()
}
}
11 changes: 11 additions & 0 deletions Kickstarter-iOS/Locales/Base.lproj/Localizable.strings
Expand Up @@ -323,6 +323,7 @@
"Kickstarter_is_not_a_store_Its_a_way_to_bring_creative_projects_to_life" = "Kickstarter is not a store. It's a way to bring creative projects to life.";
"Kickstarter_is_not_a_store_Its_a_way_to_bring_creative_projects_to_life_Learn_more_about_accountability" = "Kickstarter is not a store. It's a way to bring creative projects to life.<br/><a href=\"%{trust_link}\">Learn more about accountability</a>";
"Kickstarter_on_Film" = "Kickstarter on Film";
"Kickstarters_community_guidelines" = "Kickstarter’s Community Guidelines.";
"Know_when_creators_and_backers_message_you" = "Know when creators and backers message you by enabling notifications.";
"Learn_about_accountability_on_Kickstarter" = "Learn about accountability on Kickstarter";
"Learn_more_about_accountability" = "Learn more about accountability";
Expand Down Expand Up @@ -499,6 +500,7 @@
"Request_my_personal_data" = "Request my personal data";
"Resend_email" = "Resend email";
"Resend_verification_email" = "Re-send verification email";
"Reset_your_password" = "Reset your password";
"Retry" = "Retry";
"Retry_or_select_another_method" = "Retry or select another method.";
"Reusability_and_recyclability" = "Reusability and recyclability";
Expand Down Expand Up @@ -555,6 +557,8 @@
"Selected_reward" = "Selected reward";
"Send" = "Send";
"Send_verfication_email" = "Send verification email";
"Set_new_password" = "Set new password";
"Set_your_password" = "Set your password";
"Share" = "Share";
"Share_an_update_about_your_project" = "Share an update about your project…";
"Share_this_live_stream" = "Share this live stream.";
Expand Down Expand Up @@ -633,6 +637,9 @@
"These_projects_could_use_your_support" = "These projects could use your support.";
"This_allows_you_to_see_project_goal_and_pledge_amounts_in_your_preferred_currency" = "This allows you to see project goal and pledge amounts in your preferred currency.";
"This_comment_has_been_removed_by_Kickstarter" = "This comment has been removed by Kickstarter.";
"This_comment_is_under_review" = "This comment is under review.";
"This_comment_is_under_review_for_potentially_violating" = "This comment is under review for potentially violating";
"This_comment_is_under_review_for_potentially_violating_kickstarters_community_guidelines" = "This comment is under review for potentially violating <a href=\"%{community_guidelines}\">Kickstarter’s Community Guidelines.</a>";
"This_facebook_account_is_already_linked_to_another_Kickstarter_user" = "This Facebook account is already linked to another Kickstarter user.";
"This_holiday_season_support_a_project_for_no_reward" = "This holiday season, support a project for no reward, just because it speaks to you.";
"This_person_canceled_their_pledge" = "This person has canceled their pledge.";
Expand Down Expand Up @@ -690,6 +697,7 @@
"Visit_our_Environmental_Resources_Center" = "Visit our Environmental Resources Center";
"Visit_our_Environmental_Resources_Center_Alternative" = "<a href=\"%{environment_link}\">Visit our Environmental Resources Center</a>";
"Watch_live" = "Watch live";
"We_can_no_longer_log_you_in_through_Facebook" = "We can no longer log you in through Facebook. Please log in with your Kickstarter password, or set a new password with your Facebook email.";
"We_cant_process_this_pledge_because_of_a_problem_with_the_backers_payment_method" = "We can’t process this pledge because of a problem with the backer’s payment method.";
"We_cant_process_your_pledge" = "We can't process your pledge.";
"We_cant_process_your_pledge_Please_update_your_payment_method" = "We can’t process your pledge. Please update your payment method.";
Expand All @@ -701,11 +709,13 @@
"We_dont_allow_cancelations_that_will_cause_a_project_to_fall_short_of_its_goal_within_the_last_24_hours" = "We don’t allow cancelations that will cause a project to fall short of its goal within the last 24 hours.";
"We_re_processing_this_pledge_pull_to_refresh" = "We're processing this pledge—pull to refresh.";
"We_re_processing_your_pledge_pull_to_refresh" = "We're processing your pledge—pull to refresh.";
"We_re_simplifying_our_login_process_To_log_in" = "We’re simplifying our login process. To access your Kickstarter account, enter the email associated to your Facebook account and we’ll send you a link to set a password";
"We_think_youll_like_these_too" = "We think you’ll like these, too";
"We_use_your_activity_internally_to_make_recommendations_for_you" = "We use your activity internally to make recommendations for you. Turn recommendations off to opt out of this.";
"We_ve_been_unable_to_send_email" = "We've been unable to send email to this address. Please make sure it is typed correctly.";
"We_were_unable_to_connect_to_the_live_stream_chat" = "We were unable to connect to the live stream chat.";
"We_were_unable_to_load_the_shipping_destinations" = "We were unable to load the shipping destinations.\nPlease try again later.";
"We_will_be_discontinuing_the_ability_to_log_in_via_Facebook" = "We will be discontinuing the ability to log in via Facebook. To log in to your account using the email %{email}, please set a password that's at least 6 characters long.";
"We_wont_share_this_with_the_creator" = "We won’t share this with the creator.";
"Websites" = "Websites";
"Welcome_to_our_library_Peruse_the_stacks_with_us" = "Welcome to our library. Peruse the stacks with us.";
Expand Down Expand Up @@ -1313,6 +1323,7 @@
"forgot_password.placeholder_email" = "Email address";
"forgot_password.title" = "Forgot your password?";
"forgot_password.we_sent_an_email_to_email_address_with_instructions_to_reset_your_password" = "We’ve sent an email to %{email} with instructions to reset your password.";
"forgot_password.we_sent_an_email_to_email_address_with_instructions_to_set_your_password" = "We’ve sent an email to %{email} with instructions to set your password.";
"general.accessibility.kickstarter" = "Kickstarter";
"general.alert.buttons.ok" = "OK";
"general.backer_count_backers.few" = "%{backer_count} backers";
Expand Down

0 comments on commit 611cc91

Please sign in to comment.