mvcgen 0.1.14 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mvcgen/filemanager.rb +1 -1
- data/lib/mvcgen/generator.rb +36 -33
- data/lib/mvcgen/version.rb +1 -1
- data/lib/templates/default/mvcspec.yml +1 -1
- data/lib/templates/default/podfile/Podfile +73 -0
- data/lib/templates/default/swift/AppDelegate.swift +181 -0
- data/lib/templates/default/swift/Assets.xcassets/AppIcon.appiconset/Contents.json +133 -0
- data/lib/templates/default/swift/Assets.xcassets/Back.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/Back.imageset/icons8-back-filled-100-2.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Contents.json +6 -0
- data/lib/templates/default/swift/Assets.xcassets/Down.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/Down.imageset/icons8-expand-arrow-filled-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Forward.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/Forward.imageset/icons8-forward-filled-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Key.imageset/Contents.json +1 -0
- data/lib/templates/default/swift/Assets.xcassets/Key.imageset/universal_Key.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Shopping-cart.imageset/Contents.json +1 -0
- data/lib/templates/default/swift/Assets.xcassets/Shopping-cart.imageset/universal_Shopping-cart.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Tick.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/Tick.imageset/icons8-Checkmark-96 (1).png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Up.imageset/Contents.json +22 -0
- data/lib/templates/default/swift/Assets.xcassets/Up.imageset/icons8-collapse-arrow-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Up.imageset/icons8-collapse-arrow-50.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Uruguay.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/Uruguay.imageset/icons8-uruguay-40.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Wallet.imageset/Contents.json +1 -0
- data/lib/templates/default/swift/Assets.xcassets/Wallet.imageset/universal_Wallet.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_001.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_001.imageset/Icons_NotATour_Welcome_001.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_002.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_002.imageset/Icons_NotATour_Welcome_002.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_003.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_003.imageset/Icons_NotATour_Welcome_003.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_004.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/Welcome_004.imageset/Icons_NotATour_Welcome_004.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_beach.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_beach.imageset/icons8-beach-64.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_nobaggage.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_nobaggage.imageset/icons8-no-baggage-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_signpost.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_signpost.imageset/icons8-signpost-64.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_sunbed.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/alert_sunbed.imageset/sunbed.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/arrow.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/arrow.imageset/arrow.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/baggage.imageset/Contents.json +22 -0
- data/lib/templates/default/swift/Assets.xcassets/baggage.imageset/baggage.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/baggage.imageset/baggage@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/burger.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/burger.imageset/burger.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/calendar-white.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/calendar-white.imageset/icons8-calendar.pdf +70 -0
- data/lib/templates/default/swift/Assets.xcassets/calendar.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/calendar.imageset/calendar.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/dots.imageset/Contents.json +24 -0
- data/lib/templates/default/swift/Assets.xcassets/dots.imageset/dots.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/edit.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/edit.imageset/edit.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/emptylogin.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/emptylogin.imageset/empty state loging.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/envelope.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/envelope.imageset/icons8-envelope.pdf +70 -0
- data/lib/templates/default/swift/Assets.xcassets/expandSection.imageset/Contents.json +22 -0
- data/lib/templates/default/swift/Assets.xcassets/expandSection.imageset/icons8-expand-arrow-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/expandSection.imageset/icons8-expand-arrow-50.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/facebook.imageset/Contents.json +24 -0
- data/lib/templates/default/swift/Assets.xcassets/facebook.imageset/icons8-facebook-f.pdf +68 -0
- data/lib/templates/default/swift/Assets.xcassets/filter-white.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/filter-white.imageset/icons8-adjust (3).pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/filter.imageset/Contents.json +15 -0
- data/lib/templates/default/swift/Assets.xcassets/filter.imageset/filter.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/food.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/food.imageset/food.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/forgot-password.imageset/Contents.json +23 -0
- data/lib/templates/default/swift/Assets.xcassets/forgot-password.imageset/page-1.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/forgot-password.imageset/page-1@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/forgot-password.imageset/page-1@3x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/heart-active.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/heart-active.imageset/favortio activo.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/heart.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/heart.imageset/favorito inactivo.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/icons8-delete.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/icons8-delete.imageset/icons8-delete (3).pdf +69 -0
- data/lib/templates/default/swift/Assets.xcassets/lock.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/lock.imageset/icons8-lock.pdf +69 -0
- data/lib/templates/default/swift/Assets.xcassets/man-avatar.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/man-avatar.imageset/man-avatar.pdf +1149 -1
- data/lib/templates/default/swift/Assets.xcassets/new-notification.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/new-notification.imageset/icons8-filled-circle-filled-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/no-fav.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/no-fav.imageset/page-1@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/notificaciones.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/notificaciones.imageset/notificaciones.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/notification.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/notification.imageset/icons8-notification-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/other.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/other.imageset/other.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/page-1.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/page-1.imageset/page-1@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/page-3.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/page-3.imageset/page-1@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/photo.imageset/Contents.json +24 -0
- data/lib/templates/default/swift/Assets.xcassets/photo.imageset/photo.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/plus-white.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/plus-white.imageset/icons8-plus-math-26.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/plus.imageset/+.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/plus.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/register.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/register.imageset/registro.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/search.imageset/Contents.json +15 -0
- data/lib/templates/default/swift/Assets.xcassets/search.imageset/search.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/settings.imageset/Contents.json +23 -0
- data/lib/templates/default/swift/Assets.xcassets/settings.imageset/icons8-settings-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/settings.imageset/icons8-settings-50.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/settings.imageset/icons8-settings-500.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/shape.imageset/Contents.json +23 -0
- data/lib/templates/default/swift/Assets.xcassets/shape.imageset/shape.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/shape.imageset/shape@2x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/shape.imageset/shape@3x.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/signpost.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/signpost.imageset/icons8-signpost-50.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/stars.imageset/Contents.json +24 -0
- data/lib/templates/default/swift/Assets.xcassets/stars.imageset/stars.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/terms.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/terms.imageset/terminos y condiciones.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/tickets.imageset/Contents.json +12 -0
- data/lib/templates/default/swift/Assets.xcassets/tickets.imageset/tickets.pdf +0 -0
- data/lib/templates/default/swift/Assets.xcassets/user.imageset/Contents.json +22 -0
- data/lib/templates/default/swift/Assets.xcassets/user.imageset/icons8-account-100.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/user.imageset/icons8-account-50.png +0 -0
- data/lib/templates/default/swift/Assets.xcassets/visa.imageset/Contents.json +21 -0
- data/lib/templates/default/swift/Assets.xcassets/visa.imageset/visa.png +0 -0
- data/lib/templates/default/swift/Config/Config.swift +1 -1
- data/lib/templates/default/swift/Controllers/Login/ForgetPasswordVC.swift +125 -0
- data/lib/templates/default/swift/Controllers/Login/LoginVC.swift +244 -0
- data/lib/templates/default/swift/Controllers/Login/RegisterVC.swift +213 -0
- data/lib/templates/default/swift/Controllers/Notifications/Cells/NotificationCell.swift +51 -0
- data/lib/templates/default/swift/Controllers/Notifications/NotificationsVC.swift +164 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/ForgetPasswordTVC.swift +98 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/LoginTVC.swift +245 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/OtherRegisterVC.swift +299 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/SelectLanguageCell.swift +17 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/SelectLanguageVC.swift +116 -0
- data/lib/templates/default/swift/Controllers/OtherLogin/TermsAndConditionsVC.swift +76 -0
- data/lib/templates/default/swift/Controllers/Profile/AboutUsVC.swift +93 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/AboutUsImageCell.swift +66 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/DescriptionCell.swift +16 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/DestinationCell.swift +35 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/DestinationCellView.xib +84 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/FavoriteCollectionCell.swift +24 -0
- data/lib/templates/default/swift/Controllers/Profile/Cells/ImagesCollectionViewCell.swift +15 -0
- data/lib/templates/default/swift/Controllers/Profile/ProfileContainerVC.swift +72 -0
- data/lib/templates/default/swift/Controllers/Profile/ProfileTVC.swift +581 -0
- data/lib/templates/default/swift/Controllers/Tutorial/TutorialVC.swift +121 -0
- data/lib/templates/default/swift/Extensions/ArrayDuplicates.swift +35 -0
- data/lib/templates/default/swift/Extensions/Buttons.swift +1 -1
- data/lib/templates/default/swift/Extensions/CustomCamera.swift +65 -0
- data/lib/templates/default/swift/Extensions/GradientView.swift +121 -0
- data/lib/templates/default/swift/Extensions/HideKeyboard.swift +22 -0
- data/lib/templates/default/swift/Extensions/Images.swift +82 -0
- data/lib/templates/default/swift/Extensions/InnerShadowExtension.swift +83 -0
- data/lib/templates/default/swift/Extensions/TableViewEmptyView.swift +30 -0
- data/lib/templates/default/swift/Extensions/TapEffectExtension.swift +48 -0
- data/lib/templates/default/swift/Extensions/{ColorHex.swift → UIColorExtensions.swift} +2 -2
- data/lib/templates/default/swift/Extensions/UnderlinedTextView.swift +61 -0
- data/lib/templates/default/swift/Extensions/UnderlinedWithIconTextField.swift +55 -0
- data/lib/templates/default/swift/Helper/APIHelper.swift +4 -4
- data/lib/templates/default/swift/Helper/APIManager.swift +81 -14
- data/lib/templates/default/swift/Helper/APIRequestBody.swift +1 -1
- data/lib/templates/default/swift/Helper/AWSManager.swift +1 -1
- data/lib/templates/default/swift/Helper/FilesManager.swift +1 -1
- data/lib/templates/default/swift/Helper/S3Manager.swift +1 -1
- data/lib/templates/default/swift/Helper/Utils.swift +3 -1
- data/lib/templates/default/swift/Info.plist +53 -0
- data/lib/templates/default/swift/Models/Country.swift +28 -0
- data/lib/templates/default/swift/Models/Managers/UserManager.swift +1 -1
- data/lib/templates/default/swift/Models/Notif.swift +71 -0
- data/lib/templates/default/swift/Models/Responses/BaseResponse.swift +1 -1
- data/lib/templates/default/swift/Models/Responses/NotificationResponse.swift +24 -0
- data/lib/templates/default/swift/Models/Responses/UserResponse.swift +1 -1
- data/lib/templates/default/swift/Models/Responses/UserSingupResponse.swift +1 -1
- data/lib/templates/default/swift/Models/User.swift +2 -2
- data/lib/templates/default/swift/UI/Storyboards/Home.storyboard +2113 -0
- data/lib/templates/default/swift/UI/Storyboards/Login.storyboard +814 -0
- data/lib/templates/default/swift/UI/Storyboards/OtherLogin.storyboard +1031 -0
- data/lib/templates/default/swift/UI/Views/EmptyTableView/EmptyTableLabelView.swift +18 -0
- data/lib/templates/default/swift/UI/Views/EmptyTableView/NoNotificationView.swift +16 -0
- data/lib/templates/default/swift/UI/Views/EmptyTableView/Xibs/EmptyTableLabelView.xib +72 -0
- data/lib/templates/default/swift/UI/Views/EmptyTableView/Xibs/NoNotificationsView.xib +61 -0
- metadata +176 -4
Binary file
|
@@ -0,0 +1,125 @@
|
|
1
|
+
//
|
2
|
+
// ForgetPasswordVC.swift
|
3
|
+
// MVCGEN
|
4
|
+
//
|
5
|
+
// Created by Daniel Martinez on 23/7/18.
|
6
|
+
// Copyright © 2018 Houlak. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
import Foundation
|
10
|
+
|
11
|
+
import UIKit
|
12
|
+
|
13
|
+
protocol ForgetPasswordVCDelegate: class {
|
14
|
+
func removeBlurredBackgroundView()
|
15
|
+
}
|
16
|
+
class ForgetPasswordVC: UIViewController, UIGestureRecognizerDelegate {
|
17
|
+
|
18
|
+
weak var delegate: ForgetPasswordVCDelegate?
|
19
|
+
private var keybaordIsShowing: Bool = false
|
20
|
+
|
21
|
+
@IBOutlet weak var cancelButton: UIButton!
|
22
|
+
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
|
23
|
+
@IBOutlet weak var topConstraint: NSLayoutConstraint!
|
24
|
+
@IBOutlet weak var backgroundView: UIView!
|
25
|
+
@IBOutlet weak var recoverView: UIView!
|
26
|
+
@IBOutlet weak var emailTextField: UITextField!
|
27
|
+
@IBOutlet weak var sendButton: LoadingButton!
|
28
|
+
|
29
|
+
override func viewDidLoad() {
|
30
|
+
super.viewDidLoad()
|
31
|
+
|
32
|
+
hideKeyboardWhenTappedAround()
|
33
|
+
|
34
|
+
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissView))
|
35
|
+
|
36
|
+
tap.delegate = self
|
37
|
+
|
38
|
+
self.backgroundView.addGestureRecognizer(tap)
|
39
|
+
|
40
|
+
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown), name: .UIKeyboardWillShow, object: nil);
|
41
|
+
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasHidden), name: .UIKeyboardWillHide, object: nil);
|
42
|
+
|
43
|
+
|
44
|
+
}
|
45
|
+
|
46
|
+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
47
|
+
if keybaordIsShowing {
|
48
|
+
return false
|
49
|
+
}
|
50
|
+
if touch.view?.restorationIdentifier == "backViewID" {
|
51
|
+
return true
|
52
|
+
}
|
53
|
+
return false
|
54
|
+
}
|
55
|
+
|
56
|
+
@objc func dismissView(){
|
57
|
+
dismiss(animated: true, completion: nil)
|
58
|
+
delegate?.removeBlurredBackgroundView()
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
@objc func keyboardWasShown(notification: NSNotification) {
|
63
|
+
|
64
|
+
self.keybaordIsShowing = true
|
65
|
+
|
66
|
+
UIView.animate(withDuration: 0.5, animations: { () -> Void in
|
67
|
+
self.bottomConstraint.constant = 287
|
68
|
+
self.topConstraint.constant = 10
|
69
|
+
})
|
70
|
+
}
|
71
|
+
|
72
|
+
@objc func keyboardWasHidden(notification: NSNotification) {
|
73
|
+
|
74
|
+
self.keybaordIsShowing = false
|
75
|
+
|
76
|
+
UIView.animate(withDuration: 0.5, animations: { () -> Void in
|
77
|
+
self.topConstraint.constant = 148.5
|
78
|
+
self.bottomConstraint.constant = 148.5
|
79
|
+
})
|
80
|
+
}
|
81
|
+
|
82
|
+
override func viewDidLayoutSubviews() {
|
83
|
+
view.backgroundColor = UIColor.clear
|
84
|
+
|
85
|
+
//ensure that the icon embeded in the cancel button fits in nicely
|
86
|
+
cancelButton.imageView?.contentMode = .scaleAspectFit
|
87
|
+
|
88
|
+
//add a white tint color for the Cancel button image
|
89
|
+
// let cancelImage = UIImage(named: "Cancel")
|
90
|
+
|
91
|
+
// let tintedCancelImage = cancelImage?.withRenderingMode(.alwaysTemplate)
|
92
|
+
// cancelButton.setImage(tintedCancelImage, for: .normal)
|
93
|
+
// cancelButton.tintColor = .white
|
94
|
+
}
|
95
|
+
|
96
|
+
// MARK: - Actions
|
97
|
+
@IBAction func cancelTapped(_ sender: UIButton) {
|
98
|
+
self.dismissView()
|
99
|
+
}
|
100
|
+
|
101
|
+
@IBAction func sendTapped(_ sender: UIButton) {
|
102
|
+
if let email = self.emailTextField.text, isValidEmail(email) {
|
103
|
+
self.sendButton.showLoading()
|
104
|
+
APIManager.sharedInstance.forgotPwd(withParameters: APIRequestBody.forgotPwd(withEmail: email), completion: {
|
105
|
+
result in
|
106
|
+
self.sendButton.hideLoading()
|
107
|
+
if result == .success {
|
108
|
+
APIHelper.sharedInstance.showSuccesMessage(with: NSLocalizedString("Email sent!", comment: ""), and: "")
|
109
|
+
}
|
110
|
+
})
|
111
|
+
} else {
|
112
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Invalid email", comment: ""), and: "")
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
// MARK: - Private
|
117
|
+
|
118
|
+
private func isValidEmail(_ testStr:String) -> Bool {
|
119
|
+
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
|
120
|
+
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
|
121
|
+
return emailTest.evaluate(with: testStr)
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
}
|
@@ -0,0 +1,244 @@
|
|
1
|
+
//
|
2
|
+
// LoginVC.swift
|
3
|
+
// MVCGEN
|
4
|
+
//
|
5
|
+
// Created by Daniel Martinez on 23/7/18.
|
6
|
+
// Copyright © 2018 Houlak. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
import UIKit
|
10
|
+
import Pastel
|
11
|
+
import FacebookLogin
|
12
|
+
|
13
|
+
class LoginVC: UITableViewController, UITextFieldDelegate, ForgetPasswordVCDelegate {
|
14
|
+
|
15
|
+
// MARK: - Outlets
|
16
|
+
|
17
|
+
@IBOutlet weak var emailTextField: UITextField!
|
18
|
+
@IBOutlet weak var passwordTextField: UITextField!
|
19
|
+
@IBOutlet weak var forgetPasswordLabel: UILabel!
|
20
|
+
@IBOutlet weak var loginButton: LoadingButton!{
|
21
|
+
didSet{
|
22
|
+
loginButton.addShadow()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
private let homeStoryboard = UIStoryboard(name: "Home", bundle: Bundle.main)
|
27
|
+
|
28
|
+
@IBOutlet weak var registerButton: UIButton!{
|
29
|
+
didSet{
|
30
|
+
registerButton.addShadow()
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
@IBOutlet weak var facebookLoginButton: LoadingButton!{
|
35
|
+
didSet{
|
36
|
+
facebookLoginButton.addShadow()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
private var pastelView: PastelView!
|
41
|
+
|
42
|
+
override func viewDidLoad() {
|
43
|
+
super.viewDidLoad()
|
44
|
+
|
45
|
+
|
46
|
+
self.emailTextField.delegate = self
|
47
|
+
self.passwordTextField.delegate = self
|
48
|
+
|
49
|
+
configureText()
|
50
|
+
|
51
|
+
configureViews()
|
52
|
+
|
53
|
+
hideKeyboardWhenTappedAround()
|
54
|
+
|
55
|
+
}
|
56
|
+
|
57
|
+
override func viewWillAppear(_ animated: Bool) {
|
58
|
+
super.viewWillAppear(animated)
|
59
|
+
|
60
|
+
self.pastelView.startAnimation()
|
61
|
+
}
|
62
|
+
|
63
|
+
// MARK: - Actions
|
64
|
+
|
65
|
+
@IBAction func loginTapped(_ sender: UIButton) {
|
66
|
+
self.emailLoginTapped()
|
67
|
+
}
|
68
|
+
|
69
|
+
@IBAction func facebookLoginTapped(_ sender: UIButton) {
|
70
|
+
self.facebookLoginButton.showLoading()
|
71
|
+
let fbLoginManager = LoginManager()
|
72
|
+
// TODO: Comment next line after logout is done
|
73
|
+
fbLoginManager.logOut()
|
74
|
+
fbLoginManager.logIn(readPermissions: [.email, .userBirthday, .publicProfile], viewController: self, completion: { loginResult in
|
75
|
+
switch loginResult {
|
76
|
+
case .failed(let error):
|
77
|
+
self.facebookLoginButton.hideLoading()
|
78
|
+
print(error)
|
79
|
+
break
|
80
|
+
case .cancelled:
|
81
|
+
self.facebookLoginButton.hideLoading()
|
82
|
+
print("User cancelled login.")
|
83
|
+
break
|
84
|
+
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
|
85
|
+
// let photoURL = URL(string: "https://graph.facebook.com/\(accessToken.userId ?? "")/picture?type=large&return_ssl_resources=1")
|
86
|
+
APIManager.sharedInstance.loginfb(withParameters: APIRequestBody.getFbLoginBody(withAccesToken: accessToken.authenticationToken), completion: {
|
87
|
+
result in
|
88
|
+
self.facebookLoginButton.hideLoading()
|
89
|
+
if result == .success {
|
90
|
+
self.performSegue(withIdentifier: "GoHome", sender: nil)
|
91
|
+
}
|
92
|
+
})
|
93
|
+
}
|
94
|
+
})
|
95
|
+
}
|
96
|
+
|
97
|
+
@IBAction func forgetPasswordTapped(_ sender: UIButton) {
|
98
|
+
self.definesPresentationContext = true
|
99
|
+
self.providesPresentationContextTransitionStyle = true
|
100
|
+
|
101
|
+
self.overlayBlurredBackgroundView()
|
102
|
+
}
|
103
|
+
|
104
|
+
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
105
|
+
if let identifier = segue.identifier {
|
106
|
+
if identifier == "ShowForgetPassword" {
|
107
|
+
if let viewController = segue.destination as? ForgetPasswordVC {
|
108
|
+
viewController.delegate = self
|
109
|
+
viewController.modalPresentationStyle = .overFullScreen
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
// MARK: - UITextFieldDelegate
|
116
|
+
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
117
|
+
|
118
|
+
if textField.tag == 1 {
|
119
|
+
textField.resignFirstResponder()
|
120
|
+
emailLoginTapped()
|
121
|
+
return true
|
122
|
+
}
|
123
|
+
|
124
|
+
let nextTage = textField.tag+1
|
125
|
+
|
126
|
+
// Try to find next responder
|
127
|
+
guard let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTage) as UIResponder? else {
|
128
|
+
textField.resignFirstResponder()
|
129
|
+
return false
|
130
|
+
}
|
131
|
+
|
132
|
+
nextResponder.becomeFirstResponder()
|
133
|
+
|
134
|
+
return false // We do not want UITextField to insert line-breaks.
|
135
|
+
}
|
136
|
+
|
137
|
+
// MARK: - Private
|
138
|
+
|
139
|
+
private func configureText(){
|
140
|
+
|
141
|
+
self.emailTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Email", comment: ""),
|
142
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
143
|
+
self.passwordTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Password", comment: ""),
|
144
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
145
|
+
|
146
|
+
let normalText = NSLocalizedString("Forget your password?", comment: "")
|
147
|
+
let normalAttrs = [NSAttributedStringKey.font : Fonts.roboto(type: 0, fontSize: 15)]
|
148
|
+
let normalString = NSMutableAttributedString(string:normalText,attributes: normalAttrs)
|
149
|
+
|
150
|
+
let boldText = NSLocalizedString(" Restore", comment: "")
|
151
|
+
let attrs = [NSAttributedStringKey.font : Fonts.roboto(type: 4, fontSize: 15)]
|
152
|
+
let attributedString = NSMutableAttributedString(string:boldText, attributes:attrs)
|
153
|
+
|
154
|
+
normalString.append(attributedString)
|
155
|
+
|
156
|
+
self.forgetPasswordLabel.attributedText = normalString
|
157
|
+
|
158
|
+
}
|
159
|
+
|
160
|
+
private func configureViews(){
|
161
|
+
self.pastelView = PastelView(frame: self.tableView.bounds)
|
162
|
+
|
163
|
+
// Custom Direction
|
164
|
+
pastelView.startPastelPoint = .bottomLeft
|
165
|
+
pastelView.endPastelPoint = .topRight
|
166
|
+
|
167
|
+
// Custom Duration
|
168
|
+
pastelView.animationDuration = 3.0
|
169
|
+
|
170
|
+
// Custom Color
|
171
|
+
pastelView.setColors([Colors.firstGradientColor, Colors.textColor, Colors.secondGradientColor, Colors.thirdGradientColor])
|
172
|
+
|
173
|
+
self.tableView.backgroundView = pastelView
|
174
|
+
|
175
|
+
}
|
176
|
+
|
177
|
+
private func emailLoginTapped(){
|
178
|
+
if self.validateForm(){
|
179
|
+
if let email = self.emailTextField.text, let password = self.passwordTextField.text{
|
180
|
+
self.loginButton.showLoading()
|
181
|
+
APIManager.sharedInstance.login(withParameters: APIRequestBody.getLoginBody(withEmail: email, withPassword: password), completion: {
|
182
|
+
result in
|
183
|
+
self.loginButton.hideLoading()
|
184
|
+
if result == .success {
|
185
|
+
let rootController = self.homeStoryboard.instantiateViewController(withIdentifier: "Home")
|
186
|
+
UIApplication.shared.keyWindow?.rootViewController = rootController
|
187
|
+
self.performSegue(withIdentifier: "GoHome", sender: nil)
|
188
|
+
}
|
189
|
+
})
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
private func validateForm() -> Bool {
|
195
|
+
|
196
|
+
if self.emailTextField.text == "" {
|
197
|
+
errorOnLogin(causeOfFailure: "email")
|
198
|
+
return false
|
199
|
+
}
|
200
|
+
|
201
|
+
if self.passwordTextField.text == "" {
|
202
|
+
errorOnLogin(causeOfFailure: "password")
|
203
|
+
return false
|
204
|
+
}
|
205
|
+
|
206
|
+
return true
|
207
|
+
}
|
208
|
+
|
209
|
+
private func errorOnLogin(causeOfFailure error: String){
|
210
|
+
|
211
|
+
switch error {
|
212
|
+
case "email":
|
213
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("You must enter an email", comment: ""), and: "")
|
214
|
+
break
|
215
|
+
case "password":
|
216
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("You must enter a password", comment: ""), and: "")
|
217
|
+
break
|
218
|
+
default:
|
219
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Check the entered data", comment: ""), and: "")
|
220
|
+
break
|
221
|
+
}
|
222
|
+
|
223
|
+
}
|
224
|
+
|
225
|
+
private func overlayBlurredBackgroundView() {
|
226
|
+
|
227
|
+
let blurredBackgroundView = UIVisualEffectView()
|
228
|
+
|
229
|
+
blurredBackgroundView.frame = self.tableView.bounds
|
230
|
+
blurredBackgroundView.effect = UIBlurEffect(style: .dark)
|
231
|
+
|
232
|
+
self.tableView.addSubview(blurredBackgroundView)
|
233
|
+
|
234
|
+
}
|
235
|
+
|
236
|
+
@objc func removeBlurredBackgroundView() {
|
237
|
+
|
238
|
+
for subview in self.tableView.subviews {
|
239
|
+
if subview.isKind(of: UIVisualEffectView.self) {
|
240
|
+
subview.removeFromSuperview()
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
@@ -0,0 +1,213 @@
|
|
1
|
+
//
|
2
|
+
// RegisterVC.swift
|
3
|
+
// MVCGEN
|
4
|
+
//
|
5
|
+
// Created by Daniel Martinez on 23/7/18.
|
6
|
+
// Copyright © 2018 Houlak. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
import UIKit
|
10
|
+
import Pastel
|
11
|
+
|
12
|
+
class RegisterVC: UITableViewController, UITextFieldDelegate {
|
13
|
+
|
14
|
+
@IBOutlet weak var nameTextField: UITextField!
|
15
|
+
@IBOutlet weak var lastnameTextField: UITextField!
|
16
|
+
@IBOutlet weak var cellphoneTextField: UITextField!
|
17
|
+
@IBOutlet weak var emailTextField: UITextField!
|
18
|
+
@IBOutlet weak var passwordTextField: UITextField!
|
19
|
+
@IBOutlet weak var confirmPasswordTextField: UITextField!
|
20
|
+
@IBOutlet weak var createAccountButton: LoadingButton!{
|
21
|
+
didSet{
|
22
|
+
createAccountButton.addShadow()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
@IBOutlet weak var acceptTermsButton: UIButton!
|
26
|
+
|
27
|
+
private var pastelView: PastelView!
|
28
|
+
|
29
|
+
private var termsAndConditionsAccepted: Bool = false
|
30
|
+
|
31
|
+
override func viewDidLoad() {
|
32
|
+
super.viewDidLoad()
|
33
|
+
|
34
|
+
self.nameTextField.delegate = self
|
35
|
+
self.lastnameTextField.delegate = self
|
36
|
+
self.cellphoneTextField.delegate = self
|
37
|
+
self.emailTextField.delegate = self
|
38
|
+
self.passwordTextField.delegate = self
|
39
|
+
self.confirmPasswordTextField.delegate = self
|
40
|
+
|
41
|
+
configureText()
|
42
|
+
|
43
|
+
configureViews()
|
44
|
+
|
45
|
+
hideKeyboardWhenTappedAround()
|
46
|
+
|
47
|
+
}
|
48
|
+
|
49
|
+
override func viewWillAppear(_ animated: Bool) {
|
50
|
+
super.viewWillAppear(animated)
|
51
|
+
|
52
|
+
self.pastelView.startAnimation()
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
// MARK: - UITextFieldDelegate
|
57
|
+
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
58
|
+
|
59
|
+
let nextTage = textField.tag+1
|
60
|
+
|
61
|
+
// Try to find next responder
|
62
|
+
guard let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTage) as UIResponder? else {
|
63
|
+
textField.resignFirstResponder()
|
64
|
+
return false
|
65
|
+
}
|
66
|
+
|
67
|
+
nextResponder.becomeFirstResponder()
|
68
|
+
|
69
|
+
return false // We do not want UITextField to insert line-breaks.
|
70
|
+
}
|
71
|
+
|
72
|
+
// MARK: - Actions
|
73
|
+
|
74
|
+
@IBAction func createTapped(_ sender: UIButton) {
|
75
|
+
if self.validateForm(){
|
76
|
+
if let email = self.emailTextField.text, let password = self.passwordTextField.text, let name = self.nameTextField.text, let lastname = self.lastnameTextField.text, let cellphone = self.cellphoneTextField.text {
|
77
|
+
self.createAccountButton.showLoading()
|
78
|
+
APIManager.sharedInstance.signup(withParameters: APIRequestBody.getSignupBody(withEmail: email, withPassword: password, withFirstName: name, withLastname: lastname, withPhone: cellphone, withProfilePic: "", withStudies: "", withCertifications: "", withAbout: ""), completion: {
|
79
|
+
result, user in
|
80
|
+
if result == .success {
|
81
|
+
APIManager.sharedInstance.login(withParameters: APIRequestBody.getLoginBody(withEmail: email, withPassword: password), completion: {
|
82
|
+
result in
|
83
|
+
self.createAccountButton.hideLoading()
|
84
|
+
if result == .success {
|
85
|
+
self.performSegue(withIdentifier: "SuccesfullySignup", sender: nil)
|
86
|
+
}
|
87
|
+
})
|
88
|
+
} else {
|
89
|
+
self.createAccountButton.hideLoading()
|
90
|
+
}
|
91
|
+
})
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
@IBAction func termsTapped(_ sender: UIButton) {
|
97
|
+
if !termsAndConditionsAccepted {
|
98
|
+
let tintedImage = Asset.tick.image.withRenderingMode(.alwaysTemplate)
|
99
|
+
sender.setImage(tintedImage, for: .normal)
|
100
|
+
sender.tintColor = UIColor.init(red: 51/255, green: 125/255, blue: 173/255, alpha: 1.0)
|
101
|
+
if let image = sender.imageView {
|
102
|
+
sender.bringSubview(toFront: image)
|
103
|
+
}
|
104
|
+
termsAndConditionsAccepted = true
|
105
|
+
} else {
|
106
|
+
sender.setImage(nil, for: .normal)
|
107
|
+
termsAndConditionsAccepted = false
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
@IBAction func termsAndConditionsTapped(_ sender: UIButton) {
|
112
|
+
// TODO: View url
|
113
|
+
if let url = URL(string: "http://dils.com/terms-and-conditions") {
|
114
|
+
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
@IBAction func alreadyHaveAccountTapped(_ sender: UIButton) {
|
119
|
+
self.dismiss(animated: true, completion: nil)
|
120
|
+
}
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
// MARK: - Private
|
125
|
+
|
126
|
+
private func configureText(){
|
127
|
+
|
128
|
+
self.emailTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Email", comment: ""),
|
129
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
130
|
+
|
131
|
+
self.nameTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Name", comment: ""),
|
132
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
133
|
+
|
134
|
+
self.lastnameTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Lastname", comment: ""),
|
135
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
136
|
+
|
137
|
+
self.cellphoneTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Cellphone (optional)", comment: ""),
|
138
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
139
|
+
|
140
|
+
self.passwordTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Password", comment: ""),
|
141
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
142
|
+
|
143
|
+
self.confirmPasswordTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Confirm password", comment: ""),
|
144
|
+
attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: Fonts.roboto(type: 1, fontSize: 17)])
|
145
|
+
|
146
|
+
|
147
|
+
}
|
148
|
+
|
149
|
+
private func configureViews(){
|
150
|
+
self.pastelView = PastelView(frame: self.tableView.bounds)
|
151
|
+
|
152
|
+
// Custom Direction
|
153
|
+
pastelView.startPastelPoint = .bottomLeft
|
154
|
+
pastelView.endPastelPoint = .topRight
|
155
|
+
|
156
|
+
// Custom Duration
|
157
|
+
pastelView.animationDuration = 3.0
|
158
|
+
|
159
|
+
// Custom Color
|
160
|
+
pastelView.setColors([Colors.firstGradientColor, Colors.textColor, Colors.secondGradientColor, Colors.thirdGradientColor])
|
161
|
+
|
162
|
+
self.tableView.backgroundView = pastelView
|
163
|
+
|
164
|
+
}
|
165
|
+
|
166
|
+
private func validateForm() -> Bool {
|
167
|
+
|
168
|
+
if let password = self.passwordTextField.text, let confirmPassword = self.confirmPasswordTextField.text {
|
169
|
+
if password != confirmPassword {
|
170
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Passwords don't match", comment: ""), and: "")
|
171
|
+
return false
|
172
|
+
}
|
173
|
+
}
|
174
|
+
if let name = self.nameTextField.text, name.isEmpty {
|
175
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Check the entered data", comment: ""), and: "")
|
176
|
+
return false
|
177
|
+
}
|
178
|
+
if let lastname = self.lastnameTextField.text, lastname.isEmpty {
|
179
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Check the entered data", comment: ""), and: "")
|
180
|
+
return false
|
181
|
+
}
|
182
|
+
if let email = self.emailTextField.text{
|
183
|
+
if !isValidEmail(email.trimmingCharacters(in: .whitespacesAndNewlines)) {
|
184
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Invalid email", comment: ""), and: "")
|
185
|
+
return false
|
186
|
+
}
|
187
|
+
}
|
188
|
+
if !termsAndConditionsAccepted{
|
189
|
+
APIHelper.sharedInstance.showErrorMessage(with: NSLocalizedString("Check the entered data", comment: ""), and: NSLocalizedString("You have to accept terms & conditions", comment: ""))
|
190
|
+
return false
|
191
|
+
}
|
192
|
+
return true
|
193
|
+
}
|
194
|
+
|
195
|
+
private func isValidEmail(_ testStr:String) -> Bool {
|
196
|
+
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
|
197
|
+
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
|
198
|
+
return emailTest.evaluate(with: testStr)
|
199
|
+
}
|
200
|
+
|
201
|
+
private func isValidName(_ testStr:String) -> Bool {
|
202
|
+
let nameRegEx = "[A-Za-z\\s]+"
|
203
|
+
let nameTest = NSPredicate(format:"SELF MATCHES %@", nameRegEx)
|
204
|
+
return nameTest.evaluate(with: testStr)
|
205
|
+
}
|
206
|
+
|
207
|
+
private func isValidPhone(_ testStr:String) -> Bool {
|
208
|
+
let phoneRegEx = "[0-9]*"
|
209
|
+
let phoneTest = NSPredicate(format:"SELF MATCHES %@", phoneRegEx)
|
210
|
+
return phoneTest.evaluate(with: testStr)
|
211
|
+
}
|
212
|
+
|
213
|
+
}
|