Skip to content

Commit

Permalink
FEAT: Add ability to fill print stream URL at login
Browse files Browse the repository at this point in the history
  • Loading branch information
josefdolezal committed May 7, 2017
1 parent 8c4b640 commit 28e227a
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 62 deletions.
4 changes: 4 additions & 0 deletions OctoPhone/Generated/Localizable.Generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ enum L10n {
case storedLogsCouldNotBeLoaded
/// Stored printers could not be loaded.
case storedPrintersCouldNotBeLoaded
/// Stream URL (optional)
case streamURL
/// Target temeprature
case targetTemperature
/// Terminal
Expand Down Expand Up @@ -625,6 +627,8 @@ extension L10n: CustomStringConvertible {
return L10n.tr(key: "Stored logs could not be loaded")
case .storedPrintersCouldNotBeLoaded:
return L10n.tr(key: "Stored printers could not be loaded")
case .streamURL:
return L10n.tr(key: "Stream URL")
case .targetTemperature:
return L10n.tr(key: "Target temperature")
case .terminal:
Expand Down
27 changes: 20 additions & 7 deletions OctoPhone/Model/Printer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import RealmSwift
final class Printer: Object {

// MARK: - Stored properties

/// Stored representation of printer URL
private dynamic var _url = ""

/// Stored representation of stream URL
private dynamic var _streamUrl: String?

/// User's access token for authorization to printer
dynamic var accessToken = ""

Expand All @@ -25,7 +29,7 @@ final class Printer: Object {
// MARK: - Computed properties

/// Printer URL based on stored property
dynamic var url: URL {
var url: URL {
get {
return URL(string: _url)!
}
Expand All @@ -34,23 +38,32 @@ final class Printer: Object {
}
}

/// Print stream URL
var streamUrl: URL? {
get {
guard let url = _streamUrl else { return nil }

return URL(string: url)
}
set {
_streamUrl = newValue?.absoluteString
}
}

// MARK: - Public API

/// Creates new Printer instance
///
/// - Parameters:
/// - url: String representation of printer URL
/// - accessToken: User's access token
convenience init(
url: URL,
accessToken: String,
name: String
) {
convenience init(url: URL, accessToken: String, name: String, streamUrl: URL?) {
self.init()

self.url = url
self.accessToken = accessToken
self.name = name
self.streamUrl = streamUrl
}

// MARK: - Realm API
Expand All @@ -60,6 +73,6 @@ final class Printer: Object {
}

override static func ignoredProperties() -> [String] {
return ["url"]
return ["url", "streamUrl"]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
//

import UIKit
import RealmSwift
import ReactiveSwift
import ReactiveCocoa
import SnapKit

/// Interface for flow delegate
protocol PrinterLoginViewControllerDelegate: class {
Expand All @@ -23,13 +23,16 @@ protocol PrinterLoginViewControllerDelegate: class {
/// Gives user ability to add new printer
final class PrinterLoginViewController: BaseViewController {
/// User-friendly name of printer
private let printerNameField = UITextField()
private let printerNameField = PrinterLoginViewController.inputField(placeholder: .printerName)

/// Printer URL text field
private let urlField = UITextField()
private let urlField = PrinterLoginViewController.inputField(placeholder: .printerURL)

/// User's access token for printer
private let tokenField = UITextField()
private let tokenField = PrinterLoginViewController.inputField(placeholder: .printerAccessToken)

/// Print sream URL
private let printStreamField = PrinterLoginViewController.inputField(placeholder: .streamURL)

/// Login button
private let loginButton = UIButton(type: .system)
Expand Down Expand Up @@ -96,42 +99,19 @@ final class PrinterLoginViewController: BaseViewController {
target: self,
action: #selector(didTapCancel))

let constraints = [
printerNameField.topAnchor.constraint(
equalTo: view.topAnchor,
constant: Sizes.groupTopSpacing
),
printerNameField.widthAnchor.constraint(equalToConstant: Sizes.textFieldWidth),
printerNameField.heightAnchor.constraint(equalToConstant: Sizes.fieldheight),
printerNameField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
urlField.topAnchor.constraint(
equalTo: printerNameField.bottomAnchor,
constant: Sizes.fieldSpacing
),
urlField.widthAnchor.constraint(equalToConstant: Sizes.textFieldWidth),
urlField.heightAnchor.constraint(equalTo: printerNameField.heightAnchor),
urlField.centerXAnchor.constraint(equalTo: view.centerXAnchor),

tokenField.topAnchor.constraint(
equalTo: urlField.bottomAnchor,
constant: Sizes.fieldSpacing
),
tokenField.widthAnchor.constraint(equalTo: urlField.widthAnchor),
tokenField.heightAnchor.constraint(equalTo: printerNameField.heightAnchor),
tokenField.centerXAnchor.constraint(equalTo: urlField.centerXAnchor),

loginButton.topAnchor.constraint(
equalTo: tokenField.bottomAnchor,
constant: Sizes.fieldSpacing
),
loginButton.centerXAnchor.constraint(equalTo: urlField.centerXAnchor)
]

constraints.forEach { $0.isActive = true }

printerNameField.placeholder = tr(.printerName)
urlField.placeholder = tr(.printerURL)
tokenField.placeholder = tr(.printerAccessToken)
let stackView = UIStackView(arrangedSubviews: [printerNameField, urlField, tokenField,
printStreamField, loginButton],
axis: .vertical)

stackView.distribution = .equalCentering

view.addSubview(stackView)

stackView.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview().inset(15)
make.width.equalToSuperview()
}

loginButton.setTitle(tr(.login), for: .normal)

edgesForExtendedLayout = []
Expand All @@ -148,21 +128,35 @@ final class PrinterLoginViewController: BaseViewController {
self?.viewModel.inputs.tokenChanged(token)
}

printStreamField.reactive.continuousTextValues.observeValues { [weak self] streamUrl in
self?.viewModel.inputs.streamUrlChanged(streamUrl)
}

loginButton.reactive.controlEvents(.touchUpInside).observeValues { [weak self] _ in
self?.viewModel.inputs.loginButtonPressed()
}

loginButton.reactive.isEnabled <~ viewModel.outputs.isFormValid

viewModel.outputs.displayError
.observe(on: UIScheduler())
.observeValues { [weak self] title, message in
let alertController = UIAlertController(title: title,
message: message, preferredStyle: .alert)
reactive.displayableError <~ viewModel.outputs.displayError
}
// swiftlint:enable function_body_length

/// Preconfigured input field
///
/// - Parameter placeholder: Input placeholder
/// - Returns: Preconfigured field
private static func inputField(placeholder: L10n) -> UITextField {
let field = UITextField()

alertController.addAction(UIAlertAction(title: tr(.ok), style: .default, handler: nil))
self?.present(alertController, animated: true, completion: nil)
field.snp.makeConstraints { make in
make.height.equalTo(44)
}

field.placeholder = tr(placeholder)
field.autocorrectionType = .no
field.autocapitalizationType = .none

return field
}
// swiftlint:enable function_body_length
}
25 changes: 21 additions & 4 deletions OctoPhone/View Related/Printer Login/PrinterLoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ protocol PrinterLoginViewModelInputs {
/// - Parameter token: Entered login token
func tokenChanged(_ token: String?)

/// Call when user changed print stream URL
///
/// - Parameter url: Entered stream URL
func streamUrlChanged(_ url: String?)

/// Call when login button is pressed
func loginButtonPressed()

Expand Down Expand Up @@ -82,6 +87,9 @@ PrinterLoginViewModelOutputs {
/// Model property for printer login token
private let tokenProperty = MutableProperty<String?>(nil)

/// Actual value of stream URL
private let streamUrlProperty = MutableProperty<String?>(nil)

/// Model property for button press action
private let loginButtonPressedProperty = MutableProperty(())

Expand Down Expand Up @@ -111,9 +119,13 @@ PrinterLoginViewModelOutputs {
let formValues = Signal.combineLatest(
printerNameProperty.signal.skipNil(),
printerUrlProperty.signal.skipNil(),
tokenProperty.signal.skipNil()
tokenProperty.signal.skipNil(),
streamUrlProperty.signal
)

// Dirty hack to make stream URL optional
streamUrlProperty.value = nil

displayError = displayErrorProperty.signal.skipNil()

isFormValid = Signal.merge([
Expand All @@ -123,8 +135,9 @@ PrinterLoginViewModelOutputs {

let loginEvent = formValues
.sample(on: loginButtonPressedProperty.signal)
.map({ name, url, token -> (Printer, OctoPrintProvider) in
let printer = Printer(url: URL(string: url)!, accessToken: token, name: name)
.map({ name, url, token, stream -> (Printer, OctoPrintProvider) in
let printer = Printer(url: URL(string: url)!, accessToken: token, name: name,
streamUrl: URL(string: stream ?? ""))
let tokenPlugin = TokenPlugin(token: token)
let provider = OctoPrintProvider(baseURL: printer.url, plugins: [tokenPlugin])

Expand Down Expand Up @@ -191,6 +204,10 @@ PrinterLoginViewModelOutputs {
tokenProperty.value = token
}

func streamUrlChanged(_ url: String?) {
streamUrlProperty.value = url
}

func loginButtonPressed() {
loginButtonPressedProperty.value = ()
}
Expand All @@ -206,7 +223,7 @@ PrinterLoginViewModelOutputs {
/// - url: Printer base URL path
/// - token: Access token
/// - Returns: True if form values are valid
private static func isValid(_ name: String, _ url: String, token: String) -> Bool {
private static func isValid(_ name: String, _ url: String, token: String, stream: String?) -> Bool {
guard let url = URL(string: url) else { return false }

return !name.characters.isEmpty && !url.absoluteString.characters.isEmpty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class PrinterListCellViewModelTests: QuickSpec {
let printerPath = "http://localhost"
let printerName = "My Printer"

let printer = Printer(url: URL(string: printerPath)!, accessToken: "Secret Token", name: printerName)
let printer = Printer(url: URL(string: printerPath)!, accessToken: "Secret Token", name: printerName, streamUrl: nil)
let subject = PrinterListCellViewModel(printer: printer)

expect(subject.printerName) == printerName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class PrinterListViewModelTests: QuickSpec {
/// - Parameter index: Object identifier in collection
/// - Returns: New Printer with given index identifier
func createPrinter(index: Int) -> Printer {
return Printer(url: URL(string: "http://localhost\(index)")!, accessToken: "Secret Token \(index)", name: "My Printer \(index)")
return Printer(url: URL(string: "http://localhost\(index)")!, accessToken: "Secret Token \(index)", name: "My Printer \(index)", streamUrl: nil)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class PrinterLoginViewModelSpec: QuickSpec {
expect(buttonEnabled) == false
}

it("should require all fields filled") {
it("should require all fields filled but stream url") {
subject.inputs.printerNameChanged("My Printer")
expect(buttonEnabled) == false
subject.inputs.printerUrlChanged("http://localhost")
Expand Down
1 change: 1 addition & 0 deletions en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"Login Error" = "Login Error";
"Incorrect Credentials" = "The credentials you entered are incorrect.";
"Could not connect to printer" = "Can not connect to the printer.";
"Stream URL" = "Stream URL (optional)";


// *
Expand Down

0 comments on commit 28e227a

Please sign in to comment.