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() } } - }