diff --git a/.DS_Store b/.DS_Store
index 747efb4..bf7fb6f 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/CleanCodeApp/AppDelegate/SceneDelegate.swift b/CleanCodeApp/AppDelegate/SceneDelegate.swift
index 12622aa..670464a 100644
--- a/CleanCodeApp/AppDelegate/SceneDelegate.swift
+++ b/CleanCodeApp/AppDelegate/SceneDelegate.swift
@@ -17,7 +17,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: UIScreen.main.bounds)
- let rootViewController = HomeFactory3.make()//getRootViewController(forUser: .jorgeRoberto)
+
+ let rootViewController = getRootViewController(forUser: .gabrielEirado)
+
self.window?.rootViewController = UINavigationController(rootViewController: rootViewController)
self.window?.windowScene = windowScene
self.window?.makeKeyAndVisible()
diff --git a/CleanCodeApp/Modules/Features/Lua/Base.lproj/LuaUser.storyboard b/CleanCodeApp/Modules/Features/Lua/Base.lproj/LuaUser.storyboard
index 009e24f..8c06795 100644
--- a/CleanCodeApp/Modules/Features/Lua/Base.lproj/LuaUser.storyboard
+++ b/CleanCodeApp/Modules/Features/Lua/Base.lproj/LuaUser.storyboard
@@ -360,7 +360,7 @@ E-mail enviado para:
-
+
diff --git a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaAppURLTarget.swift b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaAppURLTarget.swift
new file mode 100644
index 0000000..313adc6
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaAppURLTarget.swift
@@ -0,0 +1,37 @@
+//
+// LuaAppURLTarget.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+import Foundation
+
+public enum LuaAppURLTarget {
+
+ case phone(String)
+ case mail(String)
+ case whatsapp(String)
+
+ var url: URL? {
+ switch self {
+ case .phone(let phoneNumer):
+ return URL(string: "tel://\(phoneNumer)")
+
+ case .mail(let mail):
+ return URL(string: "mailto:\(mail)")
+
+ case .whatsapp(let whatsappNumber):
+ return URL(string:"whatsapp://send?phone=\(whatsappNumber)&text=Oi)")
+ }
+ }
+
+ var fallBackURL: URL? {
+ switch self {
+ case .whatsapp:
+ return URL(string:"https://apps.apple.com/app/whatsapp-messenger/id310633997")
+ default:
+ return nil
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsStrings.swift b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsStrings.swift
new file mode 100644
index 0000000..5b19c1d
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsStrings.swift
@@ -0,0 +1,18 @@
+//
+// LuaContactUsStrings.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 20/02/25.
+//
+
+
+enum LuaContactUsStrings {
+ static var defaultErrorTitle = "Ocorreu algum erro"
+ static var defaultErrorDescription = "Algo de errado aconteceu. Tente novamente mais tarde."
+ static var textFieldDefaultMessage = "Escreva sua mensagem aqui"
+ static var emptyMessageTitle = "Mensagem vazia"
+ static var emptyMessageDescription = "Por favor, escreva uma mensagem antes de enviar."
+ static var sendMessageErrorDescription = "Erro ao enviar mensagem"
+ static var defaultSuccessTitle = "Sucesso.."
+ static var defaultSuccessDescription = "Sua mensagem foi enviada com sucesso."
+}
\ No newline at end of file
diff --git a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsView.swift b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsView.swift
new file mode 100644
index 0000000..8bbeefd
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsView.swift
@@ -0,0 +1,194 @@
+import UIKit
+
+final class LuaContactUsView: UIView {
+
+ // MARK: - Lazy UI Components
+ private lazy var scrollView: UIScrollView = {
+ let scrollView = UIScrollView()
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
+ return scrollView
+ }()
+
+ private lazy var contentView: UIView = {
+ let view = UIView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
+ private lazy var titleLabel: UILabel = {
+ let label = UILabel()
+ label.textColor = .black
+ label.font = UIFont.systemFont(ofSize: 24, weight: .semibold)
+ label.text = "Escolha o canal para contato"
+ label.translatesAutoresizingMaskIntoConstraints = false
+ return label
+ }()
+
+ public lazy var phoneButton: UIButton = {
+ let button = UIButton()
+ button.backgroundColor = .systemGray4
+ button.layer.cornerRadius = 10
+ let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 36)
+ button.setImage(UIImage(systemName: "phone")?.withConfiguration(symbolConfiguration), for: .normal)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var emailButton: UIButton = {
+ let button = UIButton()
+ button.backgroundColor = .systemGray4
+ button.layer.cornerRadius = 10
+ let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 36)
+ button.setImage(UIImage(systemName: "envelope")?.withConfiguration(symbolConfiguration), for: .normal)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var chatButton: UIButton = {
+ let button = UIButton()
+ button.backgroundColor = .systemGray4
+ button.layer.cornerRadius = 10
+ let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 36)
+ button.setImage(UIImage(systemName: "message")?.withConfiguration(symbolConfiguration), for: .normal)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var sendMessageButton: UIButton = {
+ let button = UIButton()
+ button.backgroundColor = .blue
+ button.setTitle(" Enviar ", for: .normal)
+ button.setTitleColor(.white, for: .normal)
+ button.layer.cornerRadius = 10
+ button.setContentHuggingPriority(.required, for: .horizontal)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var closeButton: UIButton = {
+ let button = UIButton()
+ button.setTitle("Voltar", for: .normal)
+ button.setTitleColor(.blue, for: .normal)
+ button.backgroundColor = .clear
+ button.layer.borderWidth = 1
+ button.layer.borderColor = UIColor.blue.cgColor
+ button.layer.cornerRadius = 10
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var messageLabel: UILabel = {
+ let label = UILabel()
+ label.textColor = .black
+ label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
+ label.text = "Ou envie uma mensagem"
+ label.numberOfLines = 2
+ label.setContentHuggingPriority(.defaultLow, for: .horizontal)
+ label.translatesAutoresizingMaskIntoConstraints = false
+ return label
+ }()
+
+ public lazy var textView: UITextView = {
+ let textView = UITextView()
+ textView.text = "Escreva sua mensagem aqui"
+ textView.font = .systemFont(ofSize: 13)
+ textView.backgroundColor = .systemGray5
+ textView.layer.cornerRadius = 10
+ textView.translatesAutoresizingMaskIntoConstraints = false
+ return textView
+ }()
+
+ public var textInputted: String {
+ get {
+ guard let textInputted = textView.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
+ return ""
+ }
+ return textInputted
+ }
+ }
+
+ // MARK: - Lifecycle
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupView()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ // MARK: - Setup UI
+ private func setupView() {
+ backgroundColor = .systemGray6
+ addSubview(scrollView)
+ scrollView.addSubview(contentView)
+ addSubviewsToContentView()
+ addConstraintToUIElements()
+ }
+
+ private func addSubviewsToContentView() {
+ [titleLabel, phoneButton, emailButton, chatButton, messageLabel, textView, sendMessageButton, closeButton ].forEach { contentView.addSubview($0) }
+ }
+
+ private func addConstraintToUIElements() {
+ NSLayoutConstraint.activate([
+
+ scrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
+ scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
+ scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
+ scrollView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
+
+ // ContentView
+ contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
+ contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
+ contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
+ contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
+ contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
+
+ // Title Label
+ titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
+ titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+
+ // Phone Button
+ phoneButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 30),
+ phoneButton.widthAnchor.constraint(equalToConstant: 80),
+ phoneButton.heightAnchor.constraint(equalToConstant: 80),
+ phoneButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+
+ // Email Button
+ emailButton.centerYAnchor.constraint(equalTo: phoneButton.centerYAnchor),
+ emailButton.widthAnchor.constraint(equalToConstant: 80),
+ emailButton.heightAnchor.constraint(equalToConstant: 80),
+ emailButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
+
+ // Chat Button
+ chatButton.centerYAnchor.constraint(equalTo: phoneButton.centerYAnchor),
+ chatButton.widthAnchor.constraint(equalToConstant: 80),
+ chatButton.heightAnchor.constraint(equalToConstant: 80),
+ chatButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+
+ // Message Label
+ messageLabel.topAnchor.constraint(equalTo: phoneButton.bottomAnchor, constant: 30),
+ messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ messageLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+
+ // TextView
+ textView.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 20),
+ textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ textView.heightAnchor.constraint(equalToConstant: 350),
+
+ sendMessageButton.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20),
+ sendMessageButton.heightAnchor.constraint(equalToConstant: 40),
+ sendMessageButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ sendMessageButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+
+ closeButton.topAnchor.constraint(equalTo: sendMessageButton.bottomAnchor, constant: 20),
+ closeButton.heightAnchor.constraint(equalToConstant: 40),
+ closeButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ closeButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ closeButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20)
+ ])
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewController.swift b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewController.swift
index dc5e58f..f4b032d 100644
--- a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewController.swift
+++ b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewController.swift
@@ -7,211 +7,170 @@
import UIKit
-class LuaContactUsViewController: LoadingInheritageController {
- var model: ContactUsModel?
- let textView = UITextView()
+final class LuaContactUsViewController: UIViewController, LuaViewControllerProtocol, LuaAlertHandlerProtocol {
+ typealias ViewCode = LuaContactUsView
+ internal let viewCode = LuaContactUsView()
+ private let viewModel: LuaContactUsViewModelProtocol
+
+ init(viewModel: LuaContactUsViewModelProtocol) {
+ self.viewModel = viewModel
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func loadView() {
+ view = viewCode
+ }
override func viewDidLoad() {
super.viewDidLoad()
-
- view.backgroundColor = .systemGray6
- let titleLabel = UILabel()
- titleLabel.textColor = .black
- titleLabel.font = UIFont.systemFont(ofSize: 24, weight: .semibold)
- titleLabel.text = "Escolha o canal para contato"
-
- // Criar botões
- let phoneButton = UIButton()
- phoneButton.backgroundColor = .systemGray4
- phoneButton.layer.cornerRadius = 10
- phoneButton.addTarget(self, action: #selector(phoneClick), for: .touchUpInside)
- let emailButton = UIButton()
- emailButton.backgroundColor = .systemGray4
- emailButton.layer.cornerRadius = 10
- emailButton.addTarget(self, action: #selector(emailClick), for: .touchUpInside)
-
- let chatButton = UIButton()
- chatButton.backgroundColor = .systemGray4
- chatButton.layer.cornerRadius = 10
- chatButton.addTarget(self, action: #selector(chatClicked), for: .touchUpInside)
-
- let messageLabel = UILabel()
- messageLabel.textColor = .black
- messageLabel.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
- messageLabel.text = "Ou envie uma mensagem"
- messageLabel.numberOfLines = 2
- messageLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
-
- let sendMessageButton = UIButton()
- sendMessageButton.backgroundColor = .blue
- sendMessageButton.setTitle(" Enviar ", for: .normal)
- sendMessageButton.setTitleColor(.white, for: .normal)
- sendMessageButton.layer.cornerRadius = 10
- sendMessageButton.setContentHuggingPriority(.required, for: .horizontal)
- sendMessageButton.addTarget(self, action: #selector(messageSend), for: .touchUpInside)
-
- textView.text = "Escreva sua mensagem aqui"
- let closeButton = UIButton()
- closeButton.setTitle("Voltar", for: .normal)
- closeButton.setTitleColor(.blue, for: .normal)
- closeButton.backgroundColor = .clear
- closeButton.layer.borderWidth = 1
- closeButton.layer.borderColor = UIColor.blue.cgColor
- closeButton.layer.cornerRadius = 10
- closeButton.addTarget(self, action: #selector(close), for: .touchUpInside)
-
-
- let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 36)
- phoneButton.setImage(UIImage.init(systemName: "phone")?.withConfiguration(symbolConfiguration), for: .normal)
- emailButton.setImage(UIImage.init(systemName: "envelope")?.withConfiguration(symbolConfiguration), for: .normal)
- chatButton.setImage(UIImage.init(systemName: "message")?.withConfiguration(symbolConfiguration), for: .normal)
-
- titleLabel.translatesAutoresizingMaskIntoConstraints = false
- phoneButton.translatesAutoresizingMaskIntoConstraints = false
- emailButton.translatesAutoresizingMaskIntoConstraints = false
- chatButton.translatesAutoresizingMaskIntoConstraints = false
- messageLabel.translatesAutoresizingMaskIntoConstraints = false
- textView.translatesAutoresizingMaskIntoConstraints = false
- sendMessageButton.translatesAutoresizingMaskIntoConstraints = false
- closeButton.translatesAutoresizingMaskIntoConstraints = false
-
- view.addSubview(titleLabel)
- view.addSubview(phoneButton)
- view.addSubview(emailButton)
- view.addSubview(chatButton)
- view.addSubview(messageLabel)
- view.addSubview(textView)
- view.addSubview(sendMessageButton)
- view.addSubview(closeButton)
-
- NSLayoutConstraint.activate([
-
- titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
- titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
- titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
-
- phoneButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 30),
- emailButton.centerYAnchor.constraint(equalTo: phoneButton.centerYAnchor),
- chatButton.centerYAnchor.constraint(equalTo: phoneButton.centerYAnchor),
-
- phoneButton.widthAnchor.constraint(equalToConstant: 80),
- phoneButton.heightAnchor.constraint(equalToConstant: 80),
- phoneButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
-
- emailButton.widthAnchor.constraint(equalToConstant: 80),
- emailButton.heightAnchor.constraint(equalToConstant: 80),
- emailButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
-
- chatButton.widthAnchor.constraint(equalToConstant: 80),
- chatButton.heightAnchor.constraint(equalToConstant: 80),
- chatButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
-
- messageLabel.topAnchor.constraint(equalTo: phoneButton.bottomAnchor, constant: 30),
- messageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
- messageLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
-// stackView.heightAnchor.constraint(equalToConstant: 30),
-
- textView.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 20),
- textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
- textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
- textView.bottomAnchor.constraint(equalTo: sendMessageButton.topAnchor, constant: -30),
-
-
- sendMessageButton.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -20),
- sendMessageButton.heightAnchor.constraint(equalToConstant: 40),
- sendMessageButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
- sendMessageButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
-
- closeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
- closeButton.heightAnchor.constraint(equalToConstant: 40),
- closeButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
- closeButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
- ])
-
- pegarDados()
- }
-
- @objc
- func phoneClick() {
- if let tel = model?.phone,
- let url = URL(string: "tel://\(tel)") {
- UIApplication.shared.open(url, options: [:], completionHandler: nil)
- }
- }
-
- @objc
- func emailClick() {
- if let mail = model?.mail,
- let url = URL(string: "mailto:\(mail)") {
- UIApplication.shared.open(url, options: [:], completionHandler: nil)
- }
- }
-
- @objc
- func chatClicked() {
- if let phoneNumber = model?.phone, let whatsappURL = URL(string: "whatsapp://send?phone=\(phoneNumber)&text=Oi)") {
- if UIApplication.shared.canOpenURL(whatsappURL) {
- UIApplication.shared.open(whatsappURL, options: [:], completionHandler: nil)
- } else {
- if let appStoreURL = URL(string: "https://apps.apple.com/app/whatsapp-messenger/id310633997") {
- UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil)
- }
+ fetchContactUsData()
+ configureButtons()
+ hideKeyboardWhenTappedAround()
+ }
+
+ private func fetchContactUsData() {
+ Task {
+ do {
+ await luaShowLoading()
+ try await viewModel.fetchContactUsData()
+ await luaStopLoading()
+ } catch {
+ await luaStopLoading()
+ showAlertError(error: error, alertTitle: LuaContactUsStrings.defaultErrorTitle)
+ self.dismiss(animated: true)
+ }
+ }
+ }
+
+ private func startSendMessageProcess() {
+ view.endEditing(true)
+ let hasMessage = viewCode.textInputted.isNotEmpty && viewCode.textInputted != LuaContactUsStrings.textFieldDefaultMessage
+ if hasMessage {
+ let message = viewCode.textInputted
+ guard let mail = viewModel.contactUsModel?.mail else {
+ return
+ }
+ sendMessage(message: message, mail: mail)
+ return
+ }
+ showAlert(alertTitle: LuaContactUsStrings.emptyMessageTitle , message: LuaContactUsStrings.emptyMessageDescription)
+ }
+
+ private func sendMessage(message: String, mail: String) {
+ Task {
+ do {
+ await luaShowLoading()
+ try await viewModel.sendMessage(message: message, mail: mail)
+ showAlert(alertTitle: LuaContactUsStrings.defaultSuccessTitle, message: LuaContactUsStrings.defaultSuccessDescription)
+ } catch {
+ await luaStopLoading()
+ showAlertError(error: error, alertTitle: LuaContactUsStrings.sendMessageErrorDescription)
}
+ await luaStopLoading()
}
}
+}
+
+// MARK: - Comportamentos de layout
+private extension LuaContactUsViewController {
+
+ func configureButtons() {
+ viewCode.phoneButton.addTarget(self, action: #selector(phoneButtonTapped), for: .touchUpInside)
+ viewCode.emailButton.addTarget(self, action: #selector(emailButtonTapped), for: .touchUpInside)
+ viewCode.chatButton.addTarget(self, action: #selector(chatButtonTapped), for: .touchUpInside)
+ viewCode.sendMessageButton.addTarget(self, action: #selector(sendMessageButtonTapped), for: .touchUpInside)
+ viewCode.closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
+ }
+
+ @objc func sendMessageButtonTapped() {
+ startSendMessageProcess()
+ }
- @objc
- func close() {
+ @objc func phoneButtonTapped() {
+ do {
+ try openPhone()
+ } catch let error as LuaPersonalInfoError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ } catch {
+ showAlertError(error: error, alertTitle: LuaContactUsStrings.defaultErrorDescription)
+ }
+ }
+
+ @objc func emailButtonTapped() {
+ do {
+ try openMail()
+ } catch let error as LuaPersonalInfoError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ } catch {
+ showAlertError(error: error, alertTitle: LuaContactUsStrings.defaultErrorDescription)
+ }
+ }
+
+ @objc func chatButtonTapped() {
+ do {
+ try openWhatsapp()
+ } catch let error as LuaPersonalInfoError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ } catch {
+ showAlertError(error: error, alertTitle: LuaContactUsStrings.defaultErrorDescription)
+ }
+ }
+
+ @objc func closeButtonTapped() {
dismiss(animated: true)
}
+ func openPhone() throws {
+ guard let phoneNumer = viewModel.contactUsModel?.phone else {
+ throw LuaPersonalInfoError.invalidPhoneNumber
+ }
+ do {
+ try openExternalApp(appURLTarget: .phone(phoneNumer))
+ }
+ catch let error as LuaUIApplicationURLError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ }
+ }
- func pegarDados() {
- showLoadingView()
- let url = Endpoints.contactUs
- AF.shared.request(url, method: .get, parameters: nil, headers: nil) { result in
- self.removeLoadingView()
- switch result {
- case .success(let data):
- let decoder = JSONDecoder()
- if let returned = try? decoder.decode(ContactUsModel.self, from: data) {
- self.model = returned
- } else {
- Globals.alertMessage(title: "Ops..", message: "Ocorreu algum erro", targetVC: self) {
- self.dismiss(animated: true)
- }
- }
- case .failure(let error):
- print("error api: \(error.localizedDescription)")
- Globals.alertMessage(title: "Ops..", message: "Ocorreu algum erro", targetVC: self) {
- self.dismiss(animated: true)
- }
- }
+ func openWhatsapp() throws {
+ guard let phoneNumer = viewModel.contactUsModel?.phone else {
+ throw LuaPersonalInfoError.invalidPhoneNumber
+ }
+ do {
+ try openExternalApp(appURLTarget: .whatsapp(phoneNumer))
+ }
+ catch let error as LuaUIApplicationURLError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
}
}
- @objc
- func messageSend() {
- view.endEditing(true)
- let email = model?.mail ?? ""
- if let message = textView.text, textView.text.count > 0 {
- let parameters: [String: String] = [
- "email": email,
- "mensagem": message
- ]
- showLoadingView()
- let url = Endpoints.sendMessage
- AF.shared.request(url, method: .post, parameters: parameters, headers: nil) { result in
- self.removeLoadingView()
- switch result {
- case .success:
- Globals.alertMessage(title: "Sucesso..", message: "Sua mensagem foi enviada", targetVC: self) {
- self.dismiss(animated: true)
- }
- case .failure:
- Globals.alertMessage(title: "Ops..", message: "Ocorreu algum erro", targetVC: self)
- }
- }
+ func openMail() throws {
+ guard let mail = viewModel.contactUsModel?.mail else {
+ throw LuaPersonalInfoError.invalidMail
+ }
+ do {
+ try openExternalApp(appURLTarget: .mail(mail))
+ }
+ catch let error as LuaUIApplicationURLError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ }
+ }
+
+ func openExternalApp(appURLTarget: LuaAppURLTarget) throws {
+ guard let url = appURLTarget.url else {
+ throw LuaUIApplicationURLError.invalidAppURL
+ }
+ if canOpenURL(url) {
+ openExternalURL(url)
+ return
+ }
+ guard let fallBackURL = appURLTarget.fallBackURL else {
+ throw LuaUIApplicationURLError.unableToOpenAppURL
}
+ openExternalURL(fallBackURL)
}
}
diff --git a/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewModel.swift b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewModel.swift
new file mode 100644
index 0000000..5ff129b
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ContactsUs/LuaContactUsViewModel.swift
@@ -0,0 +1,53 @@
+//
+// LuaContactUsViewModel.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+protocol LuaContactUsViewModelProtocol {
+ var contactUsModel: ContactUsModel? { get }
+ func sendMessage(message: String, mail: String) async throws
+ func fetchContactUsData() async throws
+}
+
+final class LuaContactUsViewModel: LuaContactUsViewModelProtocol {
+
+ private let networkManager: LuaNetworkManager
+ public var contactUsModel: ContactUsModel?
+
+ init(networkManager: LuaNetworkManager) {
+ self.networkManager = networkManager
+ }
+
+ public func sendMessage(message: String, mail: String) async throws {
+ do {
+ let params = makeSendMessageParams(message: message, mail: mail)
+ let response: [String: String] = try await networkManager.request(.sendContactUsMessage(params))
+ print(response)
+ } catch {
+ print(error.localizedDescription)
+ throw LuaNetworkError.anyUnintendedResponse
+ }
+ }
+
+ public func fetchContactUsData() async throws {
+ Task {
+ do {
+ contactUsModel = try await networkManager.request(.getContactUsData)
+ print(contactUsModel)
+ } catch {
+ print(error.localizedDescription)
+ throw LuaNetworkError.anyUnintendedResponse
+ }
+ }
+ }
+
+ private func makeSendMessageParams(message: String, mail: String) -> [String:String] {
+ let params: [String: String] = [
+ "email": mail,
+ "mensagem": message
+ ]
+ return params
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/Coordinator/LuaBasicCoordinator.swift b/CleanCodeApp/Modules/Features/Lua/Coordinator/LuaBasicCoordinator.swift
new file mode 100644
index 0000000..93e1189
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/Coordinator/LuaBasicCoordinator.swift
@@ -0,0 +1,30 @@
+//
+// BasicCoodinator.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 11/02/25.
+//
+
+import UIKit
+
+public protocol LuaCoordinatorProtocol {
+ var viewController: UIViewController? {get set}
+ func openLuaContactUsScreen()
+ func openLuaCreateAccountScreen()
+}
+
+final class LuaBasicCoordinator: LuaCoordinatorProtocol {
+
+ weak var viewController: UIViewController?
+
+ func openLuaContactUsScreen() {
+ let luaContactUsViewController = LuaContactUsViewControllerFactory.makeLuaContactUsViewController()
+ viewController?.present(luaContactUsViewController, animated: true, completion: nil)
+ }
+
+ func openLuaCreateAccountScreen() {
+ let luaCreateAccountViewController = LuaCreateAccountViewControllerFactory.makeLuaCreateAccountViewController()
+ viewController?.present(luaCreateAccountViewController, animated: true)
+ }
+}
+
diff --git a/CleanCodeApp/Modules/Features/Lua/Login/LuaLoginViewController.swift b/CleanCodeApp/Modules/Features/Lua/Login/LuaLoginViewController.swift
index 42b3ee2..a300db1 100644
--- a/CleanCodeApp/Modules/Features/Lua/Login/LuaLoginViewController.swift
+++ b/CleanCodeApp/Modules/Features/Lua/Login/LuaLoginViewController.swift
@@ -94,10 +94,8 @@ class LuaLoginViewController: UIViewController {
}
@IBAction func resetPasswordButton(_ sender: Any) {
- let storyboard = UIStoryboard(name: "LuaUser", bundle: nil)
- let vc = storyboard.instantiateViewController(withIdentifier: "LuaResetPasswordViewController") as! LuaResetPasswordViewController
- vc.modalPresentationStyle = .fullScreen
- present(vc, animated: true)
+ let luaResetPasswordViewController = LuaResetPasswordViewControllerFactory.makeLuaResetPasswordViewController()
+ present(luaResetPasswordViewController, animated: true)
}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaNetworkError.swift b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaNetworkError.swift
new file mode 100644
index 0000000..314ec1d
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaNetworkError.swift
@@ -0,0 +1,40 @@
+//
+// LuaNetworkError.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 10/02/25.
+//
+
+import Foundation
+
+enum LuaNetworkError: Error {
+ case badRequest // 400
+ case unauthorized // 401
+ case forbidden // 403
+ case notFound // 404
+ case serverError // 500
+ case unknown
+ case noInternetConnection
+ case anyUnintendedResponse
+}
+
+extension LuaNetworkError: LocalizedError {
+
+ var errorTitle: String? {
+ switch self {
+ case .noInternetConnection:
+ return "Sem Conexão com a Internet."
+ default:
+ return ""
+ }
+ }
+
+ var errorDescription: String? {
+ switch self {
+ case .noInternetConnection:
+ return "Você não está conectado à internet. Verifique sua conexão e tente novamente."
+ default:
+ return ""
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaPersonalInfoError.swift b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaPersonalInfoError.swift
new file mode 100644
index 0000000..f58cb45
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaPersonalInfoError.swift
@@ -0,0 +1,22 @@
+//
+// LuaPersonalInfoError.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+public enum LuaPersonalInfoError: Error {
+ case invalidPhoneNumber
+ case invalidMail
+}
+
+extension LuaPersonalInfoError {
+ var errorTitle: String? {
+ switch self {
+ case .invalidMail:
+ return "Email inválido"
+ case .invalidPhoneNumber:
+ return "Número de telefone inválido"
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUIApplicationURLError.swift b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUIApplicationURLError.swift
new file mode 100644
index 0000000..a5a325a
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUIApplicationURLError.swift
@@ -0,0 +1,20 @@
+//
+// LuaUIApplicationURLError.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+public enum LuaUIApplicationURLError: Error {
+ case unableToOpenAppURL
+ case invalidAppURL
+}
+
+extension LuaUIApplicationURLError {
+ var errorTitle: String? {
+ switch self {
+ case .invalidAppURL, .unableToOpenAppURL:
+ return "Error ao abrir link externo"
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUserAccountError.swift b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUserAccountError.swift
new file mode 100644
index 0000000..1ee1484
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaErrors/LuaUserAccountError.swift
@@ -0,0 +1,22 @@
+//
+// LuaPasswordRecoveryError.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 10/02/25.
+//
+
+import Foundation
+
+enum LuaUserAccountError: Error {
+ case invalidEmail
+}
+
+extension LuaUserAccountError: LocalizedError {
+
+ var errorDescription: String? {
+ switch self {
+ case .invalidEmail:
+ return "O e-mail informado é inválido. Verifique e tente novamente."
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaStringExtension.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaStringExtension.swift
new file mode 100644
index 0000000..0cb5227
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaStringExtension.swift
@@ -0,0 +1,14 @@
+//
+// LuaTextFieldExtension.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 13/02/25.
+//
+
+import UIKit
+
+extension String {
+ var isNotEmpty: Bool {
+ return !self.isEmpty
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIColorExtensions.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIColorExtensions.swift
new file mode 100644
index 0000000..9235019
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIColorExtensions.swift
@@ -0,0 +1,14 @@
+//
+// LuaUIColorExtension.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 14/02/25.
+//
+
+import UIKit
+
+extension UIColor {
+ static var defaultViolet: UIColor {
+ return UIColor(red: 0.53, green: 0.45, blue: 1.0, alpha: 1.0)
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIViewControllerExtension.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIViewControllerExtension.swift
new file mode 100644
index 0000000..bd00cf9
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Extensions/LuaUIViewControllerExtension.swift
@@ -0,0 +1,38 @@
+//
+// LuaUIViewControllerExtension.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 17/02/25.
+//
+
+import UIKit
+
+extension UIViewController {
+
+ func luaShowLoading() async {
+ let loadingController = LoadingController() // this is too slow, find other solution later
+ loadingController.modalPresentationStyle = .fullScreen
+ loadingController.modalTransitionStyle = .crossDissolve
+
+ await withCheckedContinuation { continuation in
+ present(loadingController, animated: true) {
+ print("Loading presentation completed")
+ continuation.resume()
+ }
+ }
+ }
+
+ func luaStopLoading() async {
+ guard let presented = presentedViewController as? LoadingController else {
+ print("No loading controller presented")
+ return
+ }
+
+ await withCheckedContinuation { continuation in
+ presented.dismiss(animated: true) {
+ print("Loading dismissal completed")
+ continuation.resume()
+ }
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/ExternarURLProtocol.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/ExternarURLProtocol.swift
new file mode 100644
index 0000000..77457c2
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/ExternarURLProtocol.swift
@@ -0,0 +1,35 @@
+//
+// ExternarURLProtocol.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+
+import UIKit
+
+public protocol ExternalURLProtocol {
+ func openExternalURL(
+ _ url: URL,
+ options: [UIApplication.OpenExternalURLOptionsKey: Any],
+ completionHandler: (@MainActor @Sendable (Bool) -> Void)?
+ )
+
+ func canOpenURL(_ url: URL) -> Bool
+}
+
+extension ExternalURLProtocol {
+ public func openExternalURL(
+ _ url: URL,
+ options: [UIApplication.OpenExternalURLOptionsKey: Any] = [:],
+ completionHandler: (@MainActor @Sendable (Bool) -> Void)? = nil
+ ) {
+ UIApplication.shared.open(url, options: options, completionHandler: completionHandler)
+ }
+
+ public func canOpenURL(_ url: URL) -> Bool {
+ return UIApplication.shared.canOpenURL(url)
+ }
+}
+
+extension UIViewController: ExternalURLProtocol { }
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/LuaAlertHandlerProtocol.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/LuaAlertHandlerProtocol.swift
new file mode 100644
index 0000000..4da1b71
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/LuaAlertHandlerProtocol.swift
@@ -0,0 +1,38 @@
+//
+// LuaAlert.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 11/02/25.
+//
+
+import UIKit
+
+public protocol LuaAlertHandlerProtocol {
+ func showAlert(alertTitle: String?, message: String, style: UIAlertController.Style)
+ func showAlertError(error: Error, alertTitle: String?, style: UIAlertController.Style)
+}
+
+extension LuaAlertHandlerProtocol where Self: UIViewController {
+
+ public func showAlertError(error: Error, alertTitle: String? = "Error", style: UIAlertController.Style = .alert) {
+ let alertController = UIAlertController(
+ title: alertTitle,
+ message: error.localizedDescription,
+ preferredStyle: style
+ )
+ let action = UIAlertAction(title: "OK", style: .default)
+ alertController.addAction(action)
+ self.present(alertController, animated: true)
+ }
+
+ public func showAlert(alertTitle: String?, message: String, style: UIAlertController.Style = .alert) {
+ let alertController = UIAlertController(
+ title: alertTitle,
+ message: message,
+ preferredStyle: style
+ )
+ let action = UIAlertAction(title: "OK", style: .default)
+ alertController.addAction(action)
+ self.present(alertController, animated: true)
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/UIviewControllerProtocol.swift b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/UIviewControllerProtocol.swift
new file mode 100644
index 0000000..b79fa44
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaHelpers/Protocols/UIviewControllerProtocol.swift
@@ -0,0 +1,15 @@
+//
+// UIviewControllerProtocol.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+import UIKit
+
+public protocol LuaViewControllerProtocol {
+ associatedtype ViewCode: UIView
+ var viewCode: ViewCode { get }
+ func hideKeyboardWhenTappedAround()
+}
+
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaAPITarget.swift b/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaAPITarget.swift
new file mode 100644
index 0000000..4167b26
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaAPITarget.swift
@@ -0,0 +1,118 @@
+//
+// APITarget.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+import Foundation
+
+public enum LuaAPIAuthTarget {
+ case login
+ case createUser
+ case resetPassword([String:String])
+}
+
+public enum LuaAPITarget {
+ case authTarget(LuaAPIAuthTarget)
+ case getContactUsData
+ case sendContactUsMessage([String: Any])
+
+ private var baseURL: URL! {
+ switch self {
+ case .sendContactUsMessage, .getContactUsData:
+ return URL(string: "www.apiQualquer.com")
+ case .authTarget(let authTarget):
+ switch authTarget{
+ case .createUser, .login:
+ return URL(string: "www.aplicandoCleanCode.muito.foda")
+ case .resetPassword:
+ return URL(string: "www.aplicandocleancodedemaneirafodastico.com")
+ }
+ }
+ }
+
+ private var path: String {
+ switch self {
+ case .getContactUsData:
+ return "/contactUs"
+ case .sendContactUsMessage:
+ return "/sendMessage"
+ case .authTarget(let authTarget):
+ switch authTarget{
+ case .createUser:
+ return "/create"
+ case .login:
+ return "/login"
+ case .resetPassword:
+ return "/resetPassword"
+ }
+ }
+ }
+
+ var method: String {
+ switch self {
+ case .getContactUsData:
+ return "GET"
+ case .sendContactUsMessage:
+ return "POST"
+ default:
+ return "GET"
+ }
+ }
+
+ var parameters: [String: Any] {
+ switch self {
+
+ case .sendContactUsMessage(let params):
+ return params
+ default:
+ return [:]
+ }
+ }
+
+ var headers: [String: String] {
+ return ["Content-Type": "application/json"]
+ }
+
+ public var url: URL {
+ return baseURL.appendingPathComponent(path)
+ }
+
+ var dummyData: Data {
+ switch self {
+ case .authTarget(.login):
+ let session = Session(id: UUID().uuidString, token: UUID().uuidString)
+ return try! JSONEncoder().encode(session)
+
+ case .authTarget(.createUser):
+ let session = Session(id: UUID().uuidString, token: UUID().uuidString)
+ return try! JSONEncoder().encode(session)
+
+ case .authTarget(.resetPassword):
+ return Data()
+
+ case .getContactUsData:
+ let contact = ContactUsModel(
+ whatsapp: "37998988822",
+ phone: "08001234567",
+ mail: "cleanCode@devPass.com"
+ )
+ return try! JSONEncoder().encode(contact)
+
+ case .sendContactUsMessage(let params):
+
+ let email = params["email"] as? String ?? "email_nao_informado"
+ let mensagem = params["mensagem"] as? String ?? "mensagem_nao_informada"
+
+ let response = [
+ "message": "sucesso",
+ "email": email,
+ "mensagem": mensagem
+ ]
+
+ return try! JSONEncoder().encode(response)
+ }
+ }
+}
+
diff --git a/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaNetworkManager.swift b/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaNetworkManager.swift
new file mode 100644
index 0000000..51fa6e4
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/LuaNetwork/LuaNetworkManager.swift
@@ -0,0 +1,77 @@
+//
+// LuaNetworkManager.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 15/02/25.
+//
+
+import Foundation
+
+
+
+public protocol LuaNetworkSessionProtocol {
+ func customDataTaskPublisher(for request: URLRequest) async throws -> (Data, URLResponse)
+}
+
+extension URLSession: LuaNetworkSessionProtocol {
+ public func customDataTaskPublisher(for request: URLRequest) async throws -> (Data, URLResponse) {
+ let (data, response) = try await self.data(for: request)
+ return (data, response)
+ }
+}
+
+private protocol LuaNetworkManagerProtocol {
+ func request(_ target: LuaAPITarget) async throws -> T
+}
+
+final class LuaNetworkManager: LuaNetworkManagerProtocol {
+
+ private let session: LuaNetworkSessionProtocol
+ private let useMockData: Bool
+
+ init(session: LuaNetworkSessionProtocol = URLSession.shared, useMockData: Bool = true) {
+ self.session = session
+ self.useMockData = useMockData
+ }
+
+ public func request(_ target: LuaAPITarget) async throws -> T {
+
+ if useMockData {
+ let data = target.dummyData
+ let decodedData = try JSONDecoder().decode(T.self, from: data)
+ return decodedData
+ }
+
+ var request = URLRequest(url: target.url)
+ request.httpMethod = target.method
+ request.allHTTPHeaderFields = target.headers
+
+ if target.method == "POST" {
+ request.httpBody = try? JSONSerialization.data(withJSONObject: target.parameters, options: .prettyPrinted)
+ }
+
+ let (data, response) = try await session.customDataTaskPublisher(for: request)
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw LuaNetworkError.unknown
+ }
+
+ switch httpResponse.statusCode {
+ case 200..<300:
+ let decodedData = try JSONDecoder().decode(T.self, from: data)
+ return decodedData
+ case 400:
+ throw LuaNetworkError.badRequest
+ case 401:
+ throw LuaNetworkError.unauthorized
+ case 403:
+ throw LuaNetworkError.forbidden
+ case 404:
+ throw LuaNetworkError.notFound
+ case 500:
+ throw LuaNetworkError.serverError
+ default:
+ throw LuaNetworkError.unknown
+ }
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/Main/LuaContactUsViewControllerFactory.swift b/CleanCodeApp/Modules/Features/Lua/Main/LuaContactUsViewControllerFactory.swift
new file mode 100644
index 0000000..147ce9b
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/Main/LuaContactUsViewControllerFactory.swift
@@ -0,0 +1,18 @@
+//
+// LuaContactUsViewControllerFactory.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 11/02/25.
+//
+
+import UIKit
+
+public struct LuaContactUsViewControllerFactory {
+ static func makeLuaContactUsViewController() -> UIViewController {
+ let viewModel = LuaContactUsViewModel(networkManager: LuaNetworkManager())
+ let luaContactUsViewController = LuaContactUsViewController(viewModel: viewModel)
+ luaContactUsViewController.modalPresentationStyle = .fullScreen
+ luaContactUsViewController.modalTransitionStyle = .coverVertical
+ return luaContactUsViewController
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/Main/LuaCreateAccountViewControllerFactory.swift b/CleanCodeApp/Modules/Features/Lua/Main/LuaCreateAccountViewControllerFactory.swift
new file mode 100644
index 0000000..b3f1731
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/Main/LuaCreateAccountViewControllerFactory.swift
@@ -0,0 +1,16 @@
+//
+// LuaCreateAccountViewControllerFabric.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 11/02/25.
+//
+
+import UIKit
+
+public struct LuaCreateAccountViewControllerFactory {
+ static func makeLuaCreateAccountViewController() -> UIViewController {
+ let luaCreateAccountViewController = LuaCreateAccountViewController()
+ luaCreateAccountViewController.modalPresentationStyle = .fullScreen
+ return luaCreateAccountViewController
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewControllerFactory.swift b/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewControllerFactory.swift
new file mode 100644
index 0000000..697cd27
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewControllerFactory.swift
@@ -0,0 +1,19 @@
+//
+// LuaResetPasswordViewControllerFactory.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 13/02/25.
+//
+
+import UIKit
+
+struct LuaResetPasswordViewControllerFactory {
+ static func makeLuaResetPasswordViewController() -> UIViewController {
+ let viewModel = LuaResetPasswordViewModelFactory.makeLuaResetPasswordViewModel()
+ let coordinator = LuaBasicCoordinator()
+ let viewController = LuaResetPasswordViewController(viewModel: viewModel, coordinator: coordinator)
+ viewController.modalPresentationStyle = .fullScreen
+ coordinator.viewController = viewController
+ return viewController
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewModelFactory.swift b/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewModelFactory.swift
new file mode 100644
index 0000000..b66e53b
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/Main/LuaResetPasswordViewModelFactory.swift
@@ -0,0 +1,13 @@
+//
+// LuaResetPasswordViewModelFabric.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 11/02/25.
+//
+
+public struct LuaResetPasswordViewModelFactory {
+ static func makeLuaResetPasswordViewModel() -> LuaResetPasswordViewModelProtocol {
+ let viewModel = LuaResetPasswordViewModel(networkManager: LuaNetworkManager())
+ return viewModel
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordView.swift b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordView.swift
new file mode 100644
index 0000000..03f54fa
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordView.swift
@@ -0,0 +1,263 @@
+import UIKit
+
+class LuaResetPasswordView: UIView {
+
+ // MARK: - UI Components
+ private lazy var scrollView: UIScrollView = {
+ let scrollView = UIScrollView()
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
+ return scrollView
+ }()
+
+ private lazy var contentView: UIView = {
+ let view = UIView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
+ private lazy var smokeImageView: UIImageView = {
+ let imageView = UIImageView()
+ imageView.image = UIImage(systemName: "smoke.fill")
+ imageView.contentMode = .scaleAspectFit
+ imageView.translatesAutoresizingMaskIntoConstraints = false
+ return imageView
+ }()
+
+ public lazy var passwordRecoverySuccessView: UIView = {
+ let view = UIView()
+ view.backgroundColor = .systemGray6
+ view.isHidden = true
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
+ public lazy var successLabel: UILabel = {
+ let label = UILabel()
+ label.text = "Se o e-mail informado estiver cadastrado, você receberá em instantes um link de recuperação de senha. E-mail enviado para:"
+ label.font = .systemFont(ofSize: 14)
+ label.textColor = .lightGray
+ label.textAlignment = .center
+ label.numberOfLines = 5
+ label.translatesAutoresizingMaskIntoConstraints = false
+ return label
+ }()
+
+ public lazy var emailSentLabel: UILabel = {
+ let label = UILabel()
+ label.text = "email@email.com"
+ label.font = .systemFont(ofSize: 14)
+ label.textColor = .white
+ label.textAlignment = .center
+ label.translatesAutoresizingMaskIntoConstraints = false
+ return label
+ }()
+
+ public lazy var emailLabel: UILabel = {
+ let label = UILabel()
+ label.text = "Informe o e-mail associado à sua conta"
+ label.font = .systemFont(ofSize: 12)
+ label.textColor = .lightGray
+ label.textAlignment = .center
+ label.translatesAutoresizingMaskIntoConstraints = false
+ return label
+ }()
+
+ public lazy var emailTextField: UITextField = {
+ let textField = UITextField()
+ textField.placeholder = "E-mail"
+ textField.borderStyle = .roundedRect
+ textField.font = .systemFont(ofSize: 12)
+ textField.textColor = .black
+ textField.setDefaultColor()
+ textField.keyboardType = .emailAddress
+ textField.autocorrectionType = .no
+ textField.translatesAutoresizingMaskIntoConstraints = false
+ return textField
+ }()
+
+ public lazy var passwordRecoveryButton: UIButton = {
+ let button = UIButton(type: .system)
+ button.setTitle("RECUPERAR SENHA", for: .normal)
+ button.titleLabel?.font = .systemFont(ofSize: 15)
+ button.backgroundColor = UIColor.defaultViolet
+ button.setTitleColor(.white, for: .normal)
+ button.layer.cornerRadius = 20
+ button.layer.borderWidth = 1
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var loginButton: UIButton = {
+ let button = UIButton(type: .system)
+ button.setTitle("LOGIN", for: .normal)
+ button.titleLabel?.font = .systemFont(ofSize: 15)
+ button.backgroundColor = .clear
+ button.setTitleColor(UIColor.defaultViolet, for: .normal)
+ button.backgroundColor = .white
+ button.layer.cornerRadius = 20
+ button.layer.borderWidth = 1
+ button.layer.borderColor = UIColor.blue.cgColor
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var helpButton: UIButton = {
+ let button = UIButton(type: .system)
+ button.setTitle("PRECISO DE AJUDA", for: .normal)
+ button.titleLabel?.font = .systemFont(ofSize: 15)
+ button.backgroundColor = .clear
+ button.setTitleColor(UIColor.defaultViolet, for: .normal)
+ button.backgroundColor = .white
+ button.layer.cornerRadius = 20
+ button.layer.borderWidth = 1
+ button.layer.borderColor = UIColor.blue.cgColor
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var createAccountButton: UIButton = {
+ let button = UIButton(type: .system)
+ button.setTitle("CRIAR UMA CONTA", for: .normal)
+ button.titleLabel?.font = .systemFont(ofSize: 15)
+ button.backgroundColor = .clear
+ button.setTitleColor( UIColor.defaultViolet, for: .normal)
+ button.backgroundColor = .white
+ button.layer.cornerRadius = 20
+ button.layer.borderWidth = 1
+ button.layer.borderColor = UIColor.blue.cgColor
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public lazy var closeButton: UIButton = {
+ let button = UIButton(type: .system)
+ button.setImage(UIImage(systemName: "xmark.app.fill"), for: .normal)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ public var emailInputted: String {
+ get {
+ guard let emailInput = emailTextField.text?.trimmingCharacters(in: .whitespaces) else {
+ return ""
+ }
+ return emailInput
+ }
+ }
+
+ // MARK: - Lifecycle
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupView()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ // MARK: - Setup UI
+ func setupView() {
+ backgroundColor = .systemGray6
+ addSubview(scrollView)
+ scrollView.addSubview(contentView)
+ addSubviewsToContentView()
+ addConstraintsToUIComponents()
+ }
+
+ private func addSubviewsToContentView() {
+ [smokeImageView, passwordRecoverySuccessView, emailLabel, emailTextField, passwordRecoveryButton, closeButton].forEach { contentView.addSubview($0) }
+ }
+
+ private func createSuccessContentStack() -> UIStackView {
+ let successContentStack = UIStackView(arrangedSubviews: [successLabel, emailSentLabel])
+ successContentStack.axis = .vertical
+ successContentStack.spacing = 0
+ successContentStack.isLayoutMarginsRelativeArrangement = true
+ successContentStack.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
+ return successContentStack
+ }
+
+ private func createButtonsStack() -> UIStackView {
+ let buttonsStack = UIStackView(arrangedSubviews: [loginButton, helpButton, createAccountButton])
+ buttonsStack.axis = .vertical
+ buttonsStack.spacing = 20
+
+ return buttonsStack
+ }
+
+ func addConstraintsToUIComponents() {
+
+ let successContentStack = createSuccessContentStack()
+
+ let buttonsStack = createButtonsStack()
+
+ [successContentStack, buttonsStack].forEach {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ }
+ passwordRecoverySuccessView.addSubview(successContentStack)
+ contentView.addSubview(buttonsStack)
+
+ NSLayoutConstraint.activate([
+
+ scrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
+ scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
+ scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
+ scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
+
+ contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
+ contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
+ contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
+ contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
+ contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
+
+ smokeImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 65),
+ smokeImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ smokeImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ smokeImageView.heightAnchor.constraint(equalToConstant: 75),
+
+ passwordRecoverySuccessView.topAnchor.constraint(equalTo: smokeImageView.bottomAnchor, constant: 8),
+ passwordRecoverySuccessView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ passwordRecoverySuccessView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ passwordRecoverySuccessView.heightAnchor.constraint(equalToConstant: 138),
+
+ // Success Content Stack (replaces individual label constraints)
+ successContentStack.topAnchor.constraint(equalTo: passwordRecoverySuccessView.topAnchor),
+ successContentStack.leadingAnchor.constraint(equalTo: passwordRecoverySuccessView.leadingAnchor),
+ successContentStack.trailingAnchor.constraint(equalTo: passwordRecoverySuccessView.trailingAnchor),
+ successContentStack.bottomAnchor.constraint(equalTo: passwordRecoverySuccessView.bottomAnchor),
+
+ // Fixed heights
+ successLabel.heightAnchor.constraint(equalToConstant: 98),
+
+ // Rest of the layout (unchanged)
+ emailLabel.topAnchor.constraint(equalTo: smokeImageView.bottomAnchor, constant: 70),
+ emailLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ emailLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ emailLabel.heightAnchor.constraint(equalToConstant: 20),
+
+ emailTextField.topAnchor.constraint(equalTo: emailLabel.bottomAnchor, constant: 8),
+ emailTextField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ emailTextField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ emailTextField.heightAnchor.constraint(equalToConstant: 48),
+
+ passwordRecoveryButton.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 20),
+ passwordRecoveryButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ passwordRecoveryButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ passwordRecoveryButton.heightAnchor.constraint(equalToConstant: 48),
+
+ buttonsStack.topAnchor.constraint(equalTo: passwordRecoveryButton.bottomAnchor, constant: 84),
+ buttonsStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+ buttonsStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+ buttonsStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40),
+
+ loginButton.heightAnchor.constraint(equalToConstant: 48),
+ helpButton.heightAnchor.constraint(equalToConstant: 48),
+ createAccountButton.heightAnchor.constraint(equalToConstant: 48),
+
+ closeButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
+ closeButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
+ closeButton.widthAnchor.constraint(equalToConstant: 45),
+ closeButton.heightAnchor.constraint(equalToConstant: 45)
+ ])
+ }
+}
diff --git a/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewController.swift b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewController.swift
index 9ec9ab6..fc72516 100644
--- a/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewController.swift
+++ b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewController.swift
@@ -1,160 +1,80 @@
import UIKit
-enum LuaPasswordRecoveryError: Error {
- case invalidEmail
- case noInternetConnection
-}
-
-class LuaResetPasswordViewController: UIViewController {
-
- @IBOutlet weak var emailTextField: UITextField!
- @IBOutlet weak var recoverPasswordButton: UIButton!
- @IBOutlet weak var loginButton: UIButton!
- @IBOutlet weak var helpButton: UIButton!
- @IBOutlet weak var createAccountButton: UIButton!
- @IBOutlet weak var emailErrorLabel: UILabel!
- @IBOutlet weak var passwordRecoverySuccessView: UIView!
- @IBOutlet weak var emailLabel: UILabel!
-
- var emaiInputed: String {
- get {
- guard let emailInput = emailTextField.text?.trimmingCharacters(in: .whitespaces) else {
- return ""
- }
- return emailInput
- }
- }
- var hasRequestedRecovery = false
+final class LuaResetPasswordViewController: UIViewController, LuaViewControllerProtocol, LuaAlertHandlerProtocol {
- override func viewDidLoad() {
- super.viewDidLoad()
- setupView()
+ typealias ViewCode = LuaResetPasswordView
+ internal let viewCode = LuaResetPasswordView()
+ private let viewModel: LuaResetPasswordViewModelProtocol
+ private let coordinator: LuaCoordinatorProtocol
+ private var hasRequestedRecovery = false
+
+ init(viewModel: LuaResetPasswordViewModelProtocol, coordinator: LuaBasicCoordinator) {
+ self.viewModel = viewModel
+ self.coordinator = coordinator
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
}
- open override var preferredStatusBarStyle: UIStatusBarStyle {
- return .lightContent
+ override func loadView() {
+ view = viewCode
}
- @IBAction func closeButtonAction(_ sender: Any) {
- dismiss(animated: true)
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ configureButtons()
+ configureTextField()
+ updatePasswordRecoveryButtonState()
+ validateExistingEmailInput()
}
- @IBAction func onPasswordRecoveryButtonTapped(_ sender: Any) {
- startPasswordRecoveringProcess()
+ public override var preferredStatusBarStyle: UIStatusBarStyle {
+ return .lightContent
}
- func startPasswordRecoveringProcess() {
+ private func startPasswordRecoveringProcess() async {
if hasRequestedRecovery {
dismiss(animated: true)
return
}
do {
- try validatePasswordRecovery()
- let parametersForRecoverPassword = makePasswordResetParams()
- sendPasswordResetRequest(parameters: parametersForRecoverPassword)
+ try validateEmailFormat()
+ await resquestPasswordReset()
self.view.endEditing(true)
- } catch let error {
- showPasswordRecoveryError(error: error as! LuaPasswordRecoveryError)
- return
+ } catch {
+ displayFormError(textField: viewCode.emailTextField, label: viewCode.emailLabel, errorText: LuaUserAccountError.invalidEmail.localizedDescription)
}
}
- func showPasswordRecoveryError(error: LuaPasswordRecoveryError) {
- switch error {
- case LuaPasswordRecoveryError.invalidEmail:
- showFormError(textField: emailTextField, label: emailErrorLabel, errorText: "Verifique o e-mail informado")
- case LuaPasswordRecoveryError.noInternetConnection:
- Globals.showNoInternetCOnnection(controller: self)
+ private func resquestPasswordReset() async {
+ do {
+ try await viewModel.startPasswordReseting(targetViewController: self, emailInputted: viewCode.emailInputted)
+ displayPasswordResetSuccessUI()
+ } catch let error as LuaNetworkError {
+ showAlertError(error: error, alertTitle: error.errorTitle)
+ } catch {
+ showAlertError(error: error, alertTitle: "Algo de errado aconteceu. Tente novamente mais tarde.")
}
}
- func validatePasswordRecovery() throws {
- guard validateEmailForm() else {
- throw LuaPasswordRecoveryError.invalidEmail
- }
- guard ConnectivityManager.shared.isConnected else {
- throw LuaPasswordRecoveryError.noInternetConnection
+ private func validateEmailFormat() throws {
+ guard viewModel.validateEmailFormat(inputedEmail: viewCode.emailInputted) else {
+ throw LuaUserAccountError.invalidEmail
}
}
- func makePasswordResetParams() -> [String : String] {
- let passwordResetParameters = [
- "email": emaiInputed
- ]
- return passwordResetParameters
- }
-
- func sendPasswordResetRequest(parameters: [String : String]) {
- BadNetworkLayer.shared.resetPassword(self, parameters: parameters) { (success) in
- if success {
- self.showPasswordResetSuccessUI()
- return
- }
- self.showPasswordResetErrorAlert()
- }
- }
-
- func showPasswordResetSuccessUI() {
+ private func displayPasswordResetSuccessUI() {
self.hasRequestedRecovery = true
- self.emailTextField.isHidden = true
- self.emailErrorLabel.isHidden = true
- self.passwordRecoverySuccessView.isHidden = false
- self.emailLabel.text = emaiInputed
- self.recoverPasswordButton.setTitle("Voltar", for: .normal)
- }
-
- func showPasswordResetErrorAlert() {
- let alertController = UIAlertController(
- title: "Ops..",
- message: "Algo de errado aconteceu. Tente novamente mais tarde.",
- preferredStyle: .alert)
- let action = UIAlertAction(title: "OK", style: .default)
- alertController.addAction(action)
- self.present(alertController, animated: true)
- }
-
- @IBAction func onLoginButtonTapped(_ sender: Any) {
- dismiss(animated: true)
+ viewCode.emailTextField.isHidden = true
+ viewCode.emailLabel.isHidden = true
+ viewCode.passwordRecoverySuccessView.isHidden = false
+ viewCode.emailSentLabel.text = viewCode.emailInputted
+ viewCode.passwordRecoveryButton.setTitle("Voltar", for: .normal)
}
- @IBAction func onHelpButtonTapped(_ sender: Any) {
- presentLuaContactUsViewController()
- }
-
- func presentLuaContactUsViewController() {
- let LuaContactUsViewController = LuaContactUsViewController()
- LuaContactUsViewController.modalPresentationStyle = .fullScreen
- LuaContactUsViewController.modalTransitionStyle = .coverVertical
- self.present(LuaContactUsViewController, animated: true, completion: nil)
- }
-
- @IBAction func onCreateAccountButtonTapped(_ sender: Any) {
- presentLuaCreateAccountViewController()
- }
-
- func presentLuaCreateAccountViewController() {
- let LuaCreateAccountViewController = LuaCreateAccountViewController()
- LuaCreateAccountViewController.modalPresentationStyle = .fullScreen
- present(LuaCreateAccountViewController, animated: true)
- }
-
-
- func validateEmailForm() -> Bool {
- let isEmailFormatValid = validateEmailFormat()
- if isEmailFormatValid {
- return true
- }
- return false
- }
-
- func validateEmailFormat() -> Bool {
- let isEmailFormatValid = emaiInputed.contains(".") &&
- emaiInputed.contains("@") &&
- emaiInputed.count > 5
- return isEmailFormatValid
- }
-
- func showFormError(textField: UITextField, label: UILabel, errorText: String) {
+ private func displayFormError(textField: UITextField, label: UILabel, errorText: String) {
textField.setErrorColor()
label.textColor = .red
label.text = errorText
@@ -162,79 +82,75 @@ class LuaResetPasswordViewController: UIViewController {
}
// MARK: - Comportamentos de layout
-extension LuaResetPasswordViewController {
-
- func setupView() {
- setupRecoverPasswordButton()
- setupLoginButton()
- setupHelpButton()
- setupAccountButton()
- emailTextField.setDefaultColor()
- validateExistingEmailInput()
- updatePasswordRecoveryButtonState()
+private extension LuaResetPasswordViewController {
+
+ func configureButtons() {
+ viewCode.loginButton.addTarget(self, action: #selector(onLoginButtonTapped), for: .touchUpInside)
+ viewCode.helpButton.addTarget(self, action: #selector(onHelpButtonTapped), for: .touchUpInside)
+ viewCode.createAccountButton.addTarget(self, action: #selector(onCreateAccountButtonTapped), for: .touchUpInside)
+ viewCode.closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
+ viewCode.passwordRecoveryButton.addTarget(target, action: #selector(onPasswordRecoveryButtonTapped), for: .touchUpInside)
}
- func setupRecoverPasswordButton() {
- recoverPasswordButton.layer.cornerRadius = recoverPasswordButton.bounds.height / 2
- recoverPasswordButton.backgroundColor = .blue
- recoverPasswordButton.setTitleColor(.white, for: .normal)
+ func configureTextField() {
+ viewCode.emailTextField.addTarget(self, action: #selector(emailTextFieldDidBeginEditing), for: .editingDidBegin)
+ viewCode.emailTextField.addTarget(self, action: #selector(onEmailTextFieldEdit), for: .editingChanged)
+ viewCode.emailTextField.addTarget(self, action: #selector(emailTextFieldDidEndEditing), for: .editingDidEnd)
}
- func setupLoginButton() {
- loginButton.layer.cornerRadius = createAccountButton.frame.height / 2
- loginButton.layer.borderWidth = 1
- loginButton.layer.borderColor = UIColor.blue.cgColor
- loginButton.setTitleColor(.blue, for: .normal)
- loginButton.backgroundColor = .white
+ @IBAction func closeButtonTapped(_ sender: Any) {
+ dismiss(animated: true)
}
- func setupHelpButton() {
- helpButton.layer.cornerRadius = createAccountButton.frame.height / 2
- helpButton.layer.borderWidth = 1
- helpButton.layer.borderColor = UIColor.blue.cgColor
- helpButton.setTitleColor(.blue, for: .normal)
- helpButton.backgroundColor = .white
+ @IBAction func onPasswordRecoveryButtonTapped(_ sender: Any) {
+ Task {
+ await startPasswordRecoveringProcess()
+ }
}
- func setupAccountButton() {
- createAccountButton.layer.cornerRadius = createAccountButton.frame.height / 2
- createAccountButton.layer.borderWidth = 1
- createAccountButton.layer.borderColor = UIColor.blue.cgColor
- createAccountButton.setTitleColor(.blue, for: .normal)
- createAccountButton.backgroundColor = .white
+ @IBAction func onLoginButtonTapped(_ sender: Any) {
+ dismiss(animated: true)
+ }
+
+ @IBAction func onHelpButtonTapped(_ sender: Any) {
+ coordinator.openLuaContactUsScreen()
+ }
+
+ @IBAction func onCreateAccountButtonTapped(_ sender: Any) {
+ coordinator.openLuaCreateAccountScreen()
}
@IBAction func emailTextFieldDidBeginEditing(_ sender: Any) {
- emailTextField.setEditingColor()
+ viewCode.emailTextField.setEditingColor()
}
@IBAction func onEmailTextFieldEdit(_ sender: Any) {
- emailTextField.setEditingColor()
+ viewCode.emailTextField.setEditingColor()
updatePasswordRecoveryButtonState()
}
@IBAction func emailTextFieldDidEndEditing(_ sender: Any) {
- emailTextField.setDefaultColor()
+ viewCode.emailTextField.setDefaultColor()
}
}
-extension LuaResetPasswordViewController {
+private extension LuaResetPasswordViewController {
func validateExistingEmailInput(){
- if !emaiInputed.isEmpty {
- emailTextField.text = emaiInputed
- emailTextField.isEnabled = false
+ if viewCode.emailInputted.isNotEmpty {
+ viewCode.emailTextField.text = viewCode.emailInputted
+ viewCode.emailTextField.isEnabled = false
}
}
func updatePasswordRecoveryButtonState() {
- let isEnabled = !emaiInputed.isEmpty
- recoverPasswordButton.setTitleColor(.white, for: .normal)
+ let isEnabled = viewCode.emailInputted.isNotEmpty
updatePasswordRecoverButtonStatus(newStatus: isEnabled)
}
func updatePasswordRecoverButtonStatus(newStatus: Bool) {
- recoverPasswordButton.backgroundColor = newStatus ? .blue : .gray
- recoverPasswordButton.isEnabled = newStatus
+ viewCode.passwordRecoveryButton.backgroundColor = newStatus ? .defaultViolet : .darkGray
+ viewCode.passwordRecoveryButton.setTitleColor(newStatus ? .white: .defaultViolet, for: .normal)
+ viewCode.passwordRecoveryButton.isEnabled = newStatus
}
}
diff --git a/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewModel.swift b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewModel.swift
new file mode 100644
index 0000000..0b9ba94
--- /dev/null
+++ b/CleanCodeApp/Modules/Features/Lua/ResetPassword/LuaResetPasswordViewModel.swift
@@ -0,0 +1,55 @@
+//
+// Untitled.swift
+// CleanCodeApp
+//
+// Created by Gabriel Amaral on 10/02/25.
+//
+
+import UIKit
+
+protocol LuaResetPasswordViewModelProtocol {
+ func validateEmailFormat(inputedEmail: String) -> Bool
+ func startPasswordReseting(targetViewController: UIViewController, emailInputted: String) async throws
+}
+
+final class LuaResetPasswordViewModel: LuaResetPasswordViewModelProtocol {
+
+ private let networkManager: LuaNetworkManager
+
+ init(networkManager: LuaNetworkManager) {
+ self.networkManager = networkManager
+ }
+
+ func startPasswordReseting(targetViewController: UIViewController, emailInputted: String) async throws {
+ do {
+ try validateConnectivity(emailInputted: emailInputted)
+ let passwordParameters = makePasswordResetParams(inputedEmail: emailInputted)
+ let _: Data = try await networkManager.request(.authTarget(.resetPassword(passwordParameters)))
+ } catch _ as LuaNetworkError {
+ throw LuaNetworkError.noInternetConnection
+ } catch {
+ throw error
+ }
+ }
+
+ private func makePasswordResetParams(inputedEmail: String) -> [String : String] {
+ let passwordResetParameters = [
+ "email": inputedEmail
+ ]
+ return passwordResetParameters
+ }
+
+ private func validateConnectivity(emailInputted: String) throws {
+ guard ConnectivityManager.shared.isConnected else {
+ throw LuaNetworkError.noInternetConnection
+ }
+ }
+
+ func validateEmailFormat(inputedEmail: String) -> Bool {
+ let isEmailFormatValid = inputedEmail.contains(".") &&
+ inputedEmail.contains("@") &&
+ inputedEmail.count > 5
+ return isEmailFormatValid
+ }
+}
+
diff --git a/CleanCodeApp/Modules/Utils/Extensions/UIViewController+extensions.swift b/CleanCodeApp/Modules/Utils/Extensions/UIViewController+extensions.swift
index 98060f0..0ff9c39 100644
--- a/CleanCodeApp/Modules/Utils/Extensions/UIViewController+extensions.swift
+++ b/CleanCodeApp/Modules/Utils/Extensions/UIViewController+extensions.swift
@@ -5,12 +5,12 @@ extension UIViewController {
let loadingController = LoadingController()
loadingController.modalPresentationStyle = .fullScreen
loadingController.modalTransitionStyle = .crossDissolve
- self.present(loadingController, animated: true)
+ self.present(loadingController, animated: false)
}
func stopLoading() {
if let presentedController = presentedViewController as? LoadingController {
- presentedController.dismiss(animated: true)
+ presentedController.dismiss(animated: false)
}
}
diff --git a/CleanCodeApp/Modules/Utils/Globals/LoadingInheritageController.swift b/CleanCodeApp/Modules/Utils/Globals/LoadingInheritageController.swift
index b9b9dd4..048133a 100644
--- a/CleanCodeApp/Modules/Utils/Globals/LoadingInheritageController.swift
+++ b/CleanCodeApp/Modules/Utils/Globals/LoadingInheritageController.swift
@@ -38,7 +38,6 @@ class LoadingInheritageController: UIViewController {
self.loadingView.removeFromSuperview()
}
}
-
}