Skip to content

Instantly share code, notes, and snippets.

@ivanbruel
Created April 19, 2016 16:10
Show Gist options
  • Save ivanbruel/ddc0ea5168679fa92755b1562a74943d to your computer and use it in GitHub Desktop.
Save ivanbruel/ddc0ea5168679fa92755b1562a74943d to your computer and use it in GitHub Desktop.
//
// DressDetailsAddToBagViewModel.swift
// ChicByChoice
//
// Created by Francisco Gonçalves on 19/02/16.
// Copyright © 2016 Chic by Choice. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
import RxOptional
import Maya
class DressDetailsAddToBagViewModel: DressDetailsViewModel, ErrorAlertable {
var size: DressDetailsAddToBagCellViewModel!
var freeSecondSize: DressDetailsAddToBagCellViewModel!
var cut: DressDetailsAddToBagCellViewModel?
var rentalPeriod: DressDetailsAddToBagCellViewModel!
var deliveryDate: DressDetailsAddToBagCellViewModel!
var tryOnService: DressDetailsAddToBagCellViewModel!
var requestDeliveryDate: Disposable?
var requestTryOnService: Disposable?
var ctaViewModel: CTAAlertViewModel {
get {
return CTAAlertViewModel(
message: tr(.CTAMessageCall),
image: UIImage(asset: .PhoneIcon),
buttonTitle: tr(.CTAButtonDismiss))
}
}
var dressDeliveryDateViewModel: Variable<DressDetailsAvailabilitiesViewModel?>
var dressTryOnServiceViewModel: Variable<DressDetailsAvailabilitiesViewModel?>
init(user: User, dress: Dress, failedToLoad: (() -> Void)? = nil,
oldGallery: DressPhotoGalleryViewModel? = nil) {
dressDeliveryDateViewModel = Variable(nil)
dressTryOnServiceViewModel = Variable(nil)
super.init(user: user,
confirmButtonTitle: tr(.DressDetailsButtonAddToBag), dress: dress,
failedToLoad: failedToLoad, oldGallery: oldGallery)
}
override func initViewModels(dress: Dress) -> [DressDetailsType] {
return setupViewModels(dress, user: user)
}
func setupViewModels(dress: Dress, user: User) -> [DressDetailsType] {
size = DressDetailsAddToBag.Size(dress, user).viewModel
let freeSecondSizeContent = size.valuePicked.asObservable().distinctUntilChanged()
.map { DressSize.sizeFromString($0, country: user.country) }
.map { (dressSize) -> [String] in
guard let dressSize = dressSize, sizes = dress.sizes else {
return [""]
}
return sizes.filter { $0.rawValue == dressSize.rawValue + 1 }
.flatMap { $0.name(user.country) } ?? [""]
}.doOnNext { (sizes) in
if sizes == [] {
self.freeSecondSize.information.value = (false, tr(.DressDetailsLabelNotAvailable))
} else if sizes == [""] {
self.freeSecondSize.information.value = (false, tr(.DressDetailsLabelSelectSize))
} else {
self.freeSecondSize.information.value = (true, nil)
}
}
freeSecondSize = DressDetailsAddToBag.FreeSecondSize(dress, freeSecondSizeContent).viewModel
rentalPeriod = DressDetailsAddToBag.RentalPeriod(dress).viewModel
deliveryDate = DressDetailsAddToBag
.DeliveryDate(dress, user.session.filters.value.date,
dressDeliveryDateViewModel, { return self.deliveryDateVerification() })
.viewModel
let maxDate = deliveryDate.valuePicked.asObservable().map { self.toDateByScreenSize($0) }
tryOnService = DressDetailsAddToBag.TryOnService(dress, user.tryOnServicePrice,
dressTryOnServiceViewModel,
maxDate, { return self.tryOnVerification() })
.viewModel
setupSecondDressValues()
setupDeliveryDateResetOnSizeChange()
deliveryDate.valuePicked.asObservable()
.map { $0.localizedShortDateValue }
.subscribeNext { (date) in
guard let date = date else { return }
self.dressDeliveryDateViewModel.value?.date = date
self.checkSecondSizeAvailability(date)
self.checkTryOnAvailability(date)
}
.addDisposableTo(rx_disposeBag)
setupNetworking()
setupCutViewModel(dress, user: user)
performInitialAvailabilityRequest()
return [size, freeSecondSize, cut, rentalPeriod, deliveryDate, tryOnService].flatMap { $0 }
}
func setupDeliveryDateResetOnSizeChange() {
size.valuePicked.asObservable().bindNext { _ in
self.deliveryDate.valuePicked.value = ""
self.tryOnService.valuePicked.value = ""
}.addDisposableTo(rx_disposeBag)
}
func setupSecondDressValues() {
if let shoppingBag = user.shoppingBag, orderItem = shoppingBag.items.first
where shoppingBag.tryOnService == true {
rentalPeriod.information.value = (false, orderItem.period.name)
if let deliveryDateString = toStringByScreenSize(orderItem.rentalDeliveryDate) {
deliveryDate.valuePicked.value = deliveryDateString
deliveryDate.information.value = (false, deliveryDateString)
}
if let tryOnServiceString = toStringByScreenSize(orderItem.tryOnDeliveryDate) {
tryOnService.valuePicked.value = tryOnServiceString
tryOnService.information.value = (false, tryOnServiceString)
}
}
}
func setupCutViewModel(dress: Dress, user: User) {
if let items = dress.items, dressLength = dress.length where dressLength == .Floor {
let lengthsContent = size.valuePicked.asObservable()
.map { DressSize.sizeFromString($0, country: user.country) }
.map { (dressSize) -> [String] in
guard let dressSize = dressSize else {
return [""]
}
return items.filter { $0.size == dressSize }.first?.cuts?.map { $0.name } ?? [""]
}.doOnNext { (sizes) in
if sizes == [] {
self.cut?.information.value = (false, tr(.DressDetailsLabelNotAvailable))
} else if sizes == [""] {
self.cut?.information.value = (false, tr(.DressDetailsLabelSelectSize))
} else {
self.cut?.information.value = (true, nil)
}
}
cut = DressDetailsAddToBag.Cut(dress, lengthsContent).viewModel
}
}
func setupNetworking() {
let sizePicked = size.valuePicked.asObservable()
.map { DressSize.sizeFromString($0, country: self.user.country) }
.filterNil()
let secondSizePicked = freeSecondSize.valuePicked.asObservable()
.startWith("")
.map { DressSize.sizeFromString($0, country: self.user.country) }
let rentalPeriodPicked = rentalPeriod.valuePicked.asObservable()
.map { OrderPeriod.orderPeriodFromString($0) }
let deliveryDatePicked = deliveryDate.valuePicked.asObservable()
.map { self.toDateByScreenSize($0) }
setupObservables(sizePicked, secondSizePicked: secondSizePicked,
rentalPeriodPicked: rentalPeriodPicked, deliveryDatePicked: deliveryDatePicked)
}
}
// MARK: Observables
extension DressDetailsAddToBagViewModel {
func setupObservables(sizePicked: Observable<DressSize>, secondSizePicked: Observable<DressSize?>,
rentalPeriodPicked: Observable<OrderPeriod?>,
deliveryDatePicked: Observable<NSDate?>) {
setupDeliveryDateObservable(sizePicked, secondSizePicked: secondSizePicked,
rentalPeriodPicked: rentalPeriodPicked)
setupTryOnDateObservable(sizePicked, deliveryDatePicked: deliveryDatePicked)
}
func setupDeliveryDateObservable(sizePicked: Observable<DressSize>,
secondSizePicked: Observable<DressSize?>,
rentalPeriodPicked: Observable<OrderPeriod?>) {
Observable.combineLatest(sizePicked, secondSizePicked, rentalPeriodPicked) {
($0, $1, $2)
}
.skip(1)
.throttle(0.5, scheduler: MainScheduler.instance)
.distinctUntilChanged { (lhs: (DressSize, DressSize?, OrderPeriod?),
rhs: (DressSize, DressSize?, OrderPeriod?)) -> Bool in
lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}
.subscribeNext { (size, secondSize, rentalPeriodPicked) -> Void in
self.requestDeliveryDate?.dispose()
self.performDressAvailabilityRequest(size, secondSize: secondSize,
rentalPeriodPicked: rentalPeriodPicked)
}
.addDisposableTo(rx_disposeBag)
}
func setupTryOnDateObservable(sizePicked: Observable<DressSize>,
deliveryDatePicked: Observable<NSDate?>) {
Observable.combineLatest(sizePicked, deliveryDatePicked) { ($0, $1) }
.filter { $1 != nil }
.distinctUntilChanged { (lhs: (DressSize, NSDate?), rhs: (DressSize, NSDate?)) -> Bool in
lhs.0 == rhs.0 && lhs.1 == rhs.1
}
.subscribeNext { size, deliveryDatePicked in
guard let deliveryDatePicked = deliveryDatePicked else {
return
}
self.performTryOnAvailabilityRequest(size, deliveryDatePicked: deliveryDatePicked)
}
.addDisposableTo(rx_disposeBag)
}
}
// MARK: Verifications
extension DressDetailsAddToBagViewModel {
func tryOnVerification() -> Bool {
var errorMessage: String?
if DressSize.sizeFromString(self.size.valuePicked.value, country: user.country) == nil {
errorMessage = tr(.DressDetailsErrorSizeNotPicked)
} else if self.toDateByScreenSize(self.deliveryDate.valuePicked.value) == nil {
errorMessage = tr(.DressDetailsErrorDeliveryDateNotPicked)
}
if let errorMessage = errorMessage {
self.presentAlert(errorMessage)
return false
}
return true
}
func deliveryDateVerification() -> Bool {
if DressSize.sizeFromString(self.size.valuePicked.value, country: user.country) == nil {
self.presentAlert(tr(.DressDetailsErrorSizeNotPicked))
return false
}
return true
}
func checkTryOnAvailability(date: NSDate) {
let mayaDate = MayaDate(date: date)
let today = MayaDate.today
var hasAvailableDates = false
self.dressTryOnServiceViewModel.value?.availabilities
.value.forEach({ (availabilityDate, viewModel) in
if mayaDate.compare(availabilityDate) == .OrderedDescending &&
today.compare(availabilityDate) == .OrderedAscending && viewModel.sizeAvailable {
hasAvailableDates = true
}
})
if !hasAvailableDates
&& self.dressTryOnServiceViewModel.value?.availabilities.value.count ?? 0 > 0 {
self.tryOnService.information.value = (false, tr(.DressDetailsLabelNotAvailable))
} else {
self.tryOnService.information.value = (true, self.user.tryOnServicePrice)
}
}
func checkSecondSizeAvailability(date: NSDate) {
guard let secondSizeAvailable = self.dressDeliveryDateViewModel.value?
.availabilities.value[MayaDate(date: date)]?.secondSizeAvailable else {
return
}
if !secondSizeAvailable {
self.freeSecondSize.information.value = (false, tr(.DressDetailsLabelNotAvailable))
} else {
self.freeSecondSize.information.value = (true, nil)
}
}
}
// Helpers
extension DressDetailsAddToBagViewModel {
func toDateByScreenSize(date: String) -> NSDate? {
return UIScreen.mainScreen().bounds.width == 320 ? date.shortDateValue :
date.localizedLongDateValue
}
func toStringByScreenSize(date: NSDate?) -> String? {
return UIScreen.mainScreen().bounds.width == 320 ? date?.dateValue : date?.localizedLongValue
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment