Skip to content

Commit

Permalink
Updated Design
Browse files Browse the repository at this point in the history
Simplified UnrecoverableErrorView
Added optional 'supportURL' to be displayed on non recoverable errors
Added Options Tests
Updated UnrecoverableError Tests
  • Loading branch information
cocojoe committed Apr 3, 2017
1 parent 9102442 commit abd8f48
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 61 deletions.
21 changes: 12 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.0)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
aws-sdk (2.8.6)
aws-sdk-resources (= 2.8.6)
Expand Down Expand Up @@ -56,28 +56,31 @@ GEM
netrc (= 0.7.8)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.4.3)
highline (~> 1.7.2)
commander-fastlane (4.4.4)
highline (~> 1.7.2)
domain_name (0.5.20170223)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.2.0)
escape (0.0.4)
excon (0.55.0)
faraday (0.11.0)
faraday (0.12.0.1)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.11.0.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.0)
fastlane (2.19.3)
fastlane (2.24.0)
activesupport (< 5)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.0.0)
colored
commander (>= 4.4.0, < 5.0.0)
commander-fastlane (>= 4.4.0, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
Expand Down Expand Up @@ -173,21 +176,21 @@ GEM
unicode-display_width (~> 1.1.1)
thread_safe (0.3.6)
tty-screen (0.5.0)
tzinfo (1.2.2)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uber (0.0.15)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.1.3)
word_wrap (1.0.0)
xcodeproj (1.4.2)
xcodeproj (1.4.3)
CFPropertyList (~> 2.3.3)
activesupport (>= 3)
claide (>= 1.0.1, < 2.0)
colored (~> 1.2)
colored2 (~> 3.1)
nanaimo (~> 0.2.3)
xcpretty (0.2.4)
xcpretty (0.2.6)
rouge (~> 1.8)
xcpretty-travis-formatter (0.0.4)
xcpretty (~> 0.2, >= 0.0.7)
Expand All @@ -203,4 +206,4 @@ DEPENDENCIES
xcpretty-travis-formatter

BUNDLED WITH
1.13.6
1.14.6
18 changes: 8 additions & 10 deletions Lock/Base.lproj/Lock.strings
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,14 @@
"com.auth0.lock.error.unrecoverable.invalid_options" = "Your options configuration failed with: %1$@";
// No connections
"com.auth0.lock.error.unrecoverable.no_connections" = "No authentication methods found for this client. please check your client setup.";
// Retry action
"com.auth0.lock.error.unrecoverable.retry.action" = "retry.";
// Retry label
"com.auth0.lock.error.unrecoverable.retry.title" = "Please ";
// Support action
"com.auth0.lock.error.unrecoverable.support.action" = "support.";
// Support label
"com.auth0.lock.error.unrecoverable.support.title" = "Please contact ";
// Unrecoverable error title
"com.auth0.lock.error.unrecoverable.title" = "We encountered an error";
// Retry button title
"com.auth0.lock.error.unrecoverable.retry.button" = "Retry request";
// Unrecoverable error retry title
"com.auth0.lock.error.unrecoverable.retry.title" = "There was a problem with the request.";
// Support button title
"com.auth0.lock.error.unrecoverable.support.button" = "Contact support";
// Unrecoverable error support title
"com.auth0.lock.error.unrecoverable.support.title" = "There was an unexpected error, please contact support.";
// Forgot Password message
"com.auth0.lock.forgot.message" = "Please enter your email and the new password. We will send you an email to confirm the password change.";
// Forgot Password title
Expand Down
1 change: 1 addition & 0 deletions Lock/LockOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct LockOptions: OptionBuildable {
var closable: Bool = false
var termsOfServiceURL: URL = URL(string: "https://auth0.com/terms")!
var privacyPolicyURL: URL = URL(string: "https://auth0.com/privacy")!
var supportURL: URL?
var logLevel: LoggerLevel = .off
var loggerOutput: LoggerOutput?
var logHttpRequest: Bool = false
Expand Down
15 changes: 15 additions & 0 deletions Lock/OptionBuildable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public protocol OptionBuildable: Options {
/// Privacy Policy URL. By default is Auth0's.
var privacyPolicyURL: URL { get set }

/// Support URL. By default this is not set.
var supportURL: URL? { get set }

/// Log level for Lock. By default is `Off`.
var logLevel: LoggerLevel { get set }

Expand Down Expand Up @@ -136,4 +139,16 @@ public extension OptionBuildable {
}
}

/// SupportURL. By default is not set.
var support: String {
get {
guard let url = self.supportURL else { return "" }
return url.absoluteString
}
set {
guard let url = URL(string: newValue) else { return } // FIXME: log error
self.supportURL = url
}
}

}
1 change: 1 addition & 0 deletions Lock/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public protocol Options {

var termsOfServiceURL: URL { get }
var privacyPolicyURL: URL { get }
var supportURL: URL? { get }

var logLevel: LoggerLevel { get }
var loggerOutput: LoggerOutput? { get }
Expand Down
3 changes: 2 additions & 1 deletion Lock/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ extension Router {
}

func unrecoverableError(for error: UnrecoverableError) -> Presentable? {
let presenter = UnrecoverableErrorPresenter(error: error, navigator: self)
guard let options = self.controller?.lock.options else { return nil }
let presenter = UnrecoverableErrorPresenter(error: error, navigator: self, options: options)
return presenter
}
}
9 changes: 6 additions & 3 deletions Lock/UnrecoverableErrorPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import Foundation
class UnrecoverableErrorPresenter: Presentable {
let navigator: Navigable
let error: UnrecoverableError
let options: Options

var messagePresenter: MessagePresenter?

init(error: UnrecoverableError, navigator: Navigable) {
init(error: UnrecoverableError, navigator: Navigable, options: Options) {
self.navigator = navigator
self.error = error
self.options = options
}

var view: View {
Expand All @@ -39,10 +41,11 @@ class UnrecoverableErrorPresenter: Presentable {
view.secondaryButton?.onPress = { _ in
self.navigator.navigate(.root)
}
} else {
} else if let supportURL = self.options.supportURL {
view.secondaryButton?.onPress = { _ in
UIApplication.shared.openURL(URL(string: "https://auth0.com/docs/")!)
UIApplication.shared.openURL(supportURL)
}
view.secondaryButton?.isHidden = false
}
return view
}
Expand Down
43 changes: 13 additions & 30 deletions Lock/UnrecoverableErrorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,16 @@ class UnrecoverableErrorView: UIView, View {
let center = UILayoutGuide()
let messageLabel = UILabel()
let imageView = UIImageView()
let actionLabel = UILabel()
let actionButton = SecondaryButton()
let actionView = UIView()
self.secondaryButton = actionButton

super.init(frame: CGRect.zero)

self.addSubview(imageView)
self.addSubview(messageLabel)
self.addSubview(actionView)
self.addSubview(actionButton)
self.addLayoutGuide(center)

actionView.addSubview(actionLabel)
actionView.addSubview(actionButton)

constraintEqual(anchor: center.leftAnchor, toAnchor: self.leftAnchor)
constraintEqual(anchor: center.topAnchor, toAnchor: self.topAnchor)
constraintEqual(anchor: center.rightAnchor, toAnchor: self.rightAnchor)
Expand All @@ -54,41 +49,29 @@ class UnrecoverableErrorView: UIView, View {
constraintEqual(anchor: imageView.centerYAnchor, toAnchor: center.centerYAnchor, constant: -90)
imageView.translatesAutoresizingMaskIntoConstraints = false

constraintEqual(anchor: messageLabel.leftAnchor, toAnchor: self.leftAnchor)
constraintEqual(anchor: messageLabel.rightAnchor, toAnchor: self.rightAnchor)
constraintEqual(anchor: messageLabel.leftAnchor, toAnchor: self.leftAnchor, constant: 20)
constraintEqual(anchor: messageLabel.rightAnchor, toAnchor: self.rightAnchor, constant: -20)
constraintEqual(anchor: messageLabel.centerYAnchor, toAnchor: center.centerYAnchor)
messageLabel.translatesAutoresizingMaskIntoConstraints = false

constraintEqual(anchor: actionView.centerXAnchor, toAnchor: center.centerXAnchor)
constraintEqual(anchor: actionView.centerYAnchor, toAnchor: center.centerYAnchor, constant: 50)
dimension(dimension: actionView.heightAnchor, withValue: 50)
actionView.translatesAutoresizingMaskIntoConstraints = false

constraintEqual(anchor: actionLabel.leftAnchor, toAnchor: actionView.leftAnchor)
constraintEqual(anchor: actionLabel.centerYAnchor, toAnchor: actionView.centerYAnchor)
constraintEqual(anchor: actionLabel.rightAnchor, toAnchor: actionButton.leftAnchor)
actionLabel.translatesAutoresizingMaskIntoConstraints = false

constraintEqual(anchor: actionButton.rightAnchor, toAnchor: actionView.rightAnchor)
constraintEqual(anchor: actionButton.leftAnchor, toAnchor: actionLabel.rightAnchor)
constraintEqual(anchor: actionButton.centerYAnchor, toAnchor: actionView.centerYAnchor)
constraintEqual(anchor: actionButton.centerXAnchor, toAnchor: center.centerXAnchor)
constraintEqual(anchor: actionButton.centerYAnchor, toAnchor: center.centerYAnchor, constant: 70)
actionButton.translatesAutoresizingMaskIntoConstraints = false

imageView.image = LazyImage(name: "ic_connection_error", bundle: bundleForLock()).image(compatibleWithTraits: self.traitCollection)
messageLabel.text = "We encountered an error".i18n(key: "com.auth0.lock.error.unrecoverable.title", comment: "Unrecoverable error title")
messageLabel.textAlignment = .center
messageLabel.font = lightSystemFont(size: 24)
actionLabel.textColor = UIColor.lightGray
actionLabel.font = regularSystemFont(size: 16)
messageLabel.font = lightSystemFont(size: 22)
messageLabel.numberOfLines = 3
actionButton.button?.setTitleColor(UIColor(red:0.04, green:0.53, blue:0.69, alpha:1.0), for: .normal)
actionButton.button?.titleLabel?.font = actionLabel.font
actionButton.button?.titleLabel?.font = regularSystemFont(size: 16)

if canRetry {
actionLabel.text = "Please ".i18n(key: "com.auth0.lock.error.unrecoverable.retry.title", comment: "Retry label")
actionButton.title = "retry.".i18n(key: "com.auth0.lock.error.unrecoverable.retry.action", comment: "Retry action")
messageLabel.text = "There was a problem with the request.".i18n(key: "com.auth0.lock.error.unrecoverable.retry.title", comment: "Unrecoverable error retry title")
actionButton.title = "Retry request".i18n(key: "com.auth0.lock.error.unrecoverable.retry.button", comment: "Retry button title")
} else {
actionLabel.text = "Please contact ".i18n(key: "com.auth0.lock.error.unrecoverable.support.title", comment: "Support label")
actionButton.title = "support.".i18n(key: "com.auth0.lock.error.unrecoverable.support.action", comment: "Support action")
messageLabel.text = "There was an unexpected error, please contact support.".i18n(key: "com.auth0.lock.error.unrecoverable.support.title", comment: "Unrecoverable error support title")
actionButton.title = "Contact support".i18n(key: "com.auth0.lock.error.unrecoverable.support.button", comment: "Support button title")
actionButton.isHidden = true
}
}

Expand Down
18 changes: 18 additions & 0 deletions LockTests/OptionsSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class OptionsSpec: QuickSpec {
expect(options.privacyPolicyURL.absoluteString) == "https://auth0.com/privacy"
}

it("should have Auth0 support as nil") {
expect(options.supportURL).to(beNil())
}

it("should have Auth0 support as empty String") {
expect(options.support) == ""
}

it("should have openid as scope") {
expect(options.scope) == "openid"
}
Expand Down Expand Up @@ -222,6 +230,16 @@ class OptionsSpec: QuickSpec {
options.privacyPolicy = "not a url"
expect(options.privacyPolicyURL.absoluteString) == "https://auth0.com/privacy"
}

it("should set support site") {
options.support = "https://auth0.com/docs"
expect(options.supportURL?.absoluteString) == "https://auth0.com/docs"
}

it("should ignore invalid support site") {
options.support = "not a url"
expect(options.supportURL?.absoluteString) == ""
}
}
}
}
35 changes: 27 additions & 8 deletions LockTests/Presenters/UnrecoverableErrorPresenterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ class UnrecoverableErrorPresenterSpec: QuickSpec {
var navigator: MockNavigator!
var error: UnrecoverableError!
var view: UnrecoverableErrorView!
var options: OptionBuildable!

beforeEach {
options = LockOptions()
error = UnrecoverableError.connectionTimeout
navigator = MockNavigator()
presenter = UnrecoverableErrorPresenter(error: error, navigator: navigator)
presenter = UnrecoverableErrorPresenter(error: error, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

Expand All @@ -51,24 +53,41 @@ class UnrecoverableErrorPresenterSpec: QuickSpec {
context("retry error") {

beforeEach {
presenter = UnrecoverableErrorPresenter(error: .connectionTimeout, navigator: navigator)
presenter = UnrecoverableErrorPresenter(error: .connectionTimeout, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

it("should have relevant retry button title") {
expect(view.secondaryButton?.title?.contains("retry")) == true
expect(view.secondaryButton?.title?.contains("Retry")) == true
}
}

context("support error") {

beforeEach {
presenter = UnrecoverableErrorPresenter(error: .invalidClientOrDomain, navigator: navigator)
presenter = UnrecoverableErrorPresenter(error: .invalidClientOrDomain, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

it("should have relevant support button title") {
expect(view.secondaryButton?.title?.contains("support")) == true
it("should not display support button") {
expect(view.secondaryButton?.isHidden) == true
}
}

context("support error with URL option") {

beforeEach {
options.support = "http://auth0.com/docs"
presenter = UnrecoverableErrorPresenter(error: .invalidClientOrDomain, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

it("should have a support button title") {
expect(view.secondaryButton?.title?.contains("Contact")) == true
}

it("should have a visible support button") {
expect(view.secondaryButton?.isHidden) == false
}
}
}
Expand All @@ -78,7 +97,7 @@ class UnrecoverableErrorPresenterSpec: QuickSpec {
context("retry error") {

beforeEach {
presenter = UnrecoverableErrorPresenter(error: .connectionTimeout, navigator: navigator)
presenter = UnrecoverableErrorPresenter(error: .connectionTimeout, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

Expand All @@ -91,7 +110,7 @@ class UnrecoverableErrorPresenterSpec: QuickSpec {
context("support error") {

beforeEach {
presenter = UnrecoverableErrorPresenter(error: .invalidClientOrDomain, navigator: navigator)
presenter = UnrecoverableErrorPresenter(error: .invalidClientOrDomain, navigator: navigator, options: options)
view = presenter.view as? UnrecoverableErrorView
}

Expand Down

0 comments on commit abd8f48

Please sign in to comment.