diff --git a/Podfile b/Podfile index 5be9b38..f45c0b5 100644 --- a/Podfile +++ b/Podfile @@ -8,14 +8,13 @@ target 'WOKA' do pod 'IQKeyboardManagerSwift', '~> 7.0.2' # GIF Animations - pod 'lottie-ios' + pod 'lottie-ios', '~> 4.4.1' #Network call - pod 'Alamofire' + pod 'Alamofire' , '~> 5.9.1' - pod 'CollectionViewCenteredFlowLayout' - - pod 'SDWebImage' + # Image Loading & Caching + pod 'SDWebImage', '~> 5.19.1' # Bottom line is for removing IPHONEOS_DEPLOYMENT_TARGET post_install do |installer| diff --git a/WOKA.xcodeproj/project.pbxproj b/WOKA.xcodeproj/project.pbxproj index 6ce1302..232d457 100644 --- a/WOKA.xcodeproj/project.pbxproj +++ b/WOKA.xcodeproj/project.pbxproj @@ -90,6 +90,9 @@ 52D774F12BDFC53B001D87DE /* StringSubScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D774F02BDFC53B001D87DE /* StringSubScript.swift */; }; 52FB2D8F2BDF898F0009B0C7 /* TextFieldPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FB2D8E2BDF898F0009B0C7 /* TextFieldPadding.swift */; }; 619A5A1BD8BD968ADC83C106 /* Pods_WOKA.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBA543A4216400A2864E3D3E /* Pods_WOKA.framework */; }; + 9C0A853F2BEE35340093783D /* ForgotPassDM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C0A853E2BEE35340093783D /* ForgotPassDM.swift */; }; + 9C0A85412BEE35670093783D /* ResetPassUserNameVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C0A85402BEE35670093783D /* ResetPassUserNameVM.swift */; }; + 9C0A85432BEE3EC90093783D /* NewPasswordVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C0A85422BEE3EC90093783D /* NewPasswordVM.swift */; }; 9C27E1602BDB6ECA00EC1DA9 /* UserDefaultsStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C27E15F2BDB6ECA00EC1DA9 /* UserDefaultsStruct.swift */; }; 9C27E1632BDB6F1900EC1DA9 /* AuthFunc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C27E1622BDB6F1900EC1DA9 /* AuthFunc.swift */; }; 9C27E1652BDB6FBC00EC1DA9 /* StoryBoardID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C27E1642BDB6FBC00EC1DA9 /* StoryBoardID.swift */; }; @@ -104,6 +107,7 @@ 9C56E8462BDBEE6400E4CA14 /* EmailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C56E8452BDBEE6400E4CA14 /* EmailVC.swift */; }; 9C56E8482BDBEFAB00E4CA14 /* AssetColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C56E8472BDBEFAB00E4CA14 /* AssetColor.swift */; }; 9C56E84B2BDBF03F00E4CA14 /* AuthenticationSB.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C56E84A2BDBF03F00E4CA14 /* AuthenticationSB.storyboard */; }; + 9C9BEEC72BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */; }; 9CBCB29B2BE4D614007D7934 /* LoginVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB29A2BE4D614007D7934 /* LoginVC.swift */; }; 9CBCB29D2BE4D6BB007D7934 /* LoginVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB29C2BE4D6BB007D7934 /* LoginVM.swift */; }; 9CBCB29F2BE4E13A007D7934 /* ValidatorClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB29E2BE4E13A007D7934 /* ValidatorClass.swift */; }; @@ -218,6 +222,9 @@ 52E7E0F62BDF7DD500C86E10 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AuthenticationSB.strings; sourceTree = ""; }; 52E7E0F82BDF7DD900C86E10 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/AuthenticationSB.strings; sourceTree = ""; }; 52FB2D8E2BDF898F0009B0C7 /* TextFieldPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldPadding.swift; sourceTree = ""; }; + 9C0A853E2BEE35340093783D /* ForgotPassDM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgotPassDM.swift; sourceTree = ""; }; + 9C0A85402BEE35670093783D /* ResetPassUserNameVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetPassUserNameVM.swift; sourceTree = ""; }; + 9C0A85422BEE3EC90093783D /* NewPasswordVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPasswordVM.swift; sourceTree = ""; }; 9C27E15F2BDB6ECA00EC1DA9 /* UserDefaultsStruct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsStruct.swift; sourceTree = ""; }; 9C27E1622BDB6F1900EC1DA9 /* AuthFunc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFunc.swift; sourceTree = ""; }; 9C27E1642BDB6FBC00EC1DA9 /* StoryBoardID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryBoardID.swift; sourceTree = ""; }; @@ -238,6 +245,7 @@ 9C56E8452BDBEE6400E4CA14 /* EmailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailVC.swift; sourceTree = ""; }; 9C56E8472BDBEFAB00E4CA14 /* AssetColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetColor.swift; sourceTree = ""; }; 9C56E8492BDBF03F00E4CA14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AuthenticationSB.storyboard; sourceTree = ""; }; + 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCenteredFlowLayout.swift; sourceTree = ""; }; 9CBCB29A2BE4D614007D7934 /* LoginVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginVC.swift; sourceTree = ""; }; 9CBCB29C2BE4D6BB007D7934 /* LoginVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginVM.swift; sourceTree = ""; }; 9CBCB29E2BE4E13A007D7934 /* ValidatorClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorClass.swift; sourceTree = ""; }; @@ -344,6 +352,7 @@ 52C8B0552BDA57CE003B51D0 /* Constants K */, 52C8B0512BDA4B51003B51D0 /* Helpers */, 523ED2932BDA3D0100CFED02 /* Assets */, + 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */, 523ED2682BDA2BC900CFED02 /* LaunchScreen.storyboard */, 523ED26B2BDA2BC900CFED02 /* Info.plist */, 5259541E2BE8E93500191286 /* Config.xcconfig */, @@ -599,6 +608,7 @@ 525954332BEA620800191286 /* IntrestTopicDM.swift */, 5259545B2BEBB80400191286 /* AvatarDM.swift */, 52A3F6A42BECBA8D0000BB0B /* LinkedChildDM.swift */, + 9C0A853E2BEE35340093783D /* ForgotPassDM.swift */, ); path = Model; sourceTree = ""; @@ -626,6 +636,8 @@ 52C6E0282BE3B52500E22D59 /* SelectAvatarVM.swift */, 9CBCB29C2BE4D6BB007D7934 /* LoginVM.swift */, 52A3F6AE2BECC0690000BB0B /* LinkedChildVM.swift */, + 9C0A85402BEE35670093783D /* ResetPassUserNameVM.swift */, + 9C0A85422BEE3EC90093783D /* NewPasswordVM.swift */, ); path = ViewModel; sourceTree = ""; @@ -889,6 +901,7 @@ 52D774EB2BDFC0BF001D87DE /* OTPVC.swift in Sources */, 9C27E16F2BDB866500EC1DA9 /* CellIdentifier.swift in Sources */, 9C27E1632BDB6F1900EC1DA9 /* AuthFunc.swift in Sources */, + 9C0A85412BEE35670093783D /* ResetPassUserNameVM.swift in Sources */, 52C6E0292BE3B52500E22D59 /* SelectAvatarVM.swift in Sources */, 52C8B0592BDA57FA003B51D0 /* StaticFilesString.swift in Sources */, 52C8B05D2BDA5AA7003B51D0 /* ApplyGradrient.swift in Sources */, @@ -909,6 +922,7 @@ 525954212BE8EB7900191286 /* APIEndPoints.swift in Sources */, 5259545A2BEB67D200191286 /* DateFormatterLib.swift in Sources */, 523ED2602BDA2BC700CFED02 /* SceneDelegate.swift in Sources */, + 9C0A853F2BEE35340093783D /* ForgotPassDM.swift in Sources */, 9CBCB2AA2BE51A52007D7934 /* HomeVC.swift in Sources */, 52D774E92BDFBDA4001D87DE /* AuthenticationStringConstant.swift in Sources */, 5259541B2BE8D6F900191286 /* NetworkReachibility.swift in Sources */, @@ -925,6 +939,8 @@ 525953CF2BE8B28F00191286 /* Utilities.swift in Sources */, 9CBCB2A12BE4E50A007D7934 /* TextFieldPassword.swift in Sources */, 9C56E8482BDBEFAB00E4CA14 /* AssetColor.swift in Sources */, + 9C0A85432BEE3EC90093783D /* NewPasswordVM.swift in Sources */, + 9C9BEEC72BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift in Sources */, 9CBCB29D2BE4D6BB007D7934 /* LoginVM.swift in Sources */, 5272FCE52BDFDC8C000ECB1D /* UserDetailsRegisterVM.swift in Sources */, 525954272BE9178F00191286 /* UserDataDM.swift in Sources */, @@ -1156,12 +1172,14 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WOKA/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WOKA; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1192,12 +1210,14 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WOKA/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WOKA; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/WOKA/Alerts/CustomAlerts.storyboard b/WOKA/Alerts/CustomAlerts.storyboard index ac703c8..282ded8 100644 --- a/WOKA/Alerts/CustomAlerts.storyboard +++ b/WOKA/Alerts/CustomAlerts.storyboard @@ -1,12 +1,11 @@ - + - @@ -15,25 +14,10 @@ - - - - - - - - - - - - - - - - + @@ -141,8 +125,5 @@ - - - diff --git a/WOKA/Authentication/Base.lproj/AuthenticationSB.storyboard b/WOKA/Authentication/Base.lproj/AuthenticationSB.storyboard index 507735f..d74eb55 100644 --- a/WOKA/Authentication/Base.lproj/AuthenticationSB.storyboard +++ b/WOKA/Authentication/Base.lproj/AuthenticationSB.storyboard @@ -660,7 +660,7 @@ Sent to Your Parent’s Email - + @@ -1420,40 +1420,40 @@ Sent to Your Parent’s Email - + - + - + - + - + - + - + - + - + diff --git a/WOKA/Authentication/Controller/LoginVC.swift b/WOKA/Authentication/Controller/LoginVC.swift index 878dcc5..a5a962c 100644 --- a/WOKA/Authentication/Controller/LoginVC.swift +++ b/WOKA/Authentication/Controller/LoginVC.swift @@ -18,6 +18,8 @@ class LoginVC: UIViewController { var vm = LoginVM() + // MARK: - View LifeCycle + override func viewDidLoad() { super.viewDidLoad() vm.vc = self diff --git a/WOKA/Authentication/Controller/NewPasswordVC.swift b/WOKA/Authentication/Controller/NewPasswordVC.swift index b58a24a..de76115 100644 --- a/WOKA/Authentication/Controller/NewPasswordVC.swift +++ b/WOKA/Authentication/Controller/NewPasswordVC.swift @@ -13,25 +13,62 @@ class NewPasswordVC: UIViewController { @IBOutlet weak var confirmPassTF: TextFieldShadow! @IBOutlet weak var nextBtn: LocalisedElementsButton! + var vm = NewPasswordVM() + + // MARK: - View LifeCycle + override func viewDidLoad() { super.viewDidLoad() - initView() - enterNewPasswordTF.enablePasswordToggle() - confirmPassTF.enablePasswordToggle() - enterNewPasswordTF.delegate = self - confirmPassTF.delegate = self + vm.vc = self + vm.initView() } - func initView(){ - enterNewPasswordTF.roundCorner() - confirmPassTF.roundCorner() - let color1 = #colorLiteral(red: 0.144693464, green: 0.1426281333, blue: 0.6686832905, alpha: 1) - let color2 = #colorLiteral(red: 0.6901960784, green: 0.2745098039, blue: 0.7568627451, alpha: 1) - nextBtn.applyGradient(colors: [color1, color2], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0.8, y: 0)) + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(false, animated: animated) } - @IBAction func nextBtnTapped(_ sender: LocalisedElementsButton) { + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(true, animated: animated) + } + @IBAction func nextBtnTapped(_ sender: LocalisedElementsButton) { + guard let pass = enterNewPasswordTF.text , let confirmPass = confirmPassTF.text else{return} + + /* + Check for password + */ + if pass.count < 6 || confirmPass.count < 6{ + let sb = UIStoryboard(name: K.StoryBoard.customAlerts, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.CustomAlerts.alertCustomVC) as! AlertCustomVC + + vcPush.contentLabel = "Password is too short." + vcPush.mainTitleText = "Error" +// vcPush.onDoneBlock = { isDone in } + vcPush.modalPresentationStyle = .overCurrentContext + vcPush.modalTransitionStyle = .crossDissolve + self.present(vcPush, animated: true) + return + } + + /* + Check if password are matching + */ + if pass != confirmPass{ + let sb = UIStoryboard(name: K.StoryBoard.customAlerts, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.CustomAlerts.alertCustomVC) as! AlertCustomVC + + vcPush.contentLabel = "Password doesn't match" + vcPush.mainTitleText = "Error" +// vcPush.onDoneBlock = { isDone in } + vcPush.modalPresentationStyle = .overCurrentContext + vcPush.modalTransitionStyle = .crossDissolve + self.present(vcPush, animated: true) + return + } + + vm.updatePassword() } } diff --git a/WOKA/Authentication/Controller/ResetPassUserNameVC.swift b/WOKA/Authentication/Controller/ResetPassUserNameVC.swift index 1bffde7..f37ec4a 100644 --- a/WOKA/Authentication/Controller/ResetPassUserNameVC.swift +++ b/WOKA/Authentication/Controller/ResetPassUserNameVC.swift @@ -11,17 +11,14 @@ class ResetPassUserNameVC: UIViewController { @IBOutlet weak var usernameTF: TextFieldShadow! @IBOutlet weak var nextBtn: LocalisedElementsButton! + var vm = ResetPassUserNameVM() + // MARK: - View LifeCycle + override func viewDidLoad() { super.viewDidLoad() - initView() - } - - func initView(){ - usernameTF.roundCorner() - let color1 = #colorLiteral(red: 0.144693464, green: 0.1426281333, blue: 0.6686832905, alpha: 1) - let color2 = #colorLiteral(red: 0.6901960784, green: 0.2745098039, blue: 0.7568627451, alpha: 1) - nextBtn.applyGradient(colors: [color1, color2], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0.8, y: 0)) + vm.vc = self + vm.initView() } override func viewWillAppear(_ animated: Bool) { @@ -36,9 +33,43 @@ class ResetPassUserNameVC: UIViewController { @IBAction func nextBtnTapped(_ sender: LocalisedElementsButton) { - let sb = UIStoryboard(name: K.StoryBoard.authenticationSB, bundle: nil) - let vc = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Authentication.oTPVC) as! OTPVC - vc.navigateCheck = .newPass - self.navigationController?.pushViewController(vc, animated: true) + /* + Check for username + */ + if usernameTF.text!.count < 3{ + usernameTF.rightView?.isHidden = false + usernameTF.setError(K.ConstantString.shortUsername, show: true) + return + } + + vm.sendOtpToUserName() } } + +// MARK: - Textfield Delegate + +extension ResetPassUserNameVC : UITextFieldDelegate{ + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{ + switch textField{ + case usernameTF: + textField.hideError() + if !string.numberAndCharacterAndSpecialChar(){return false} + return ValidatorClass.sharedInstanec.limitCharacter(length: 16,textField, shouldChangeCharactersIn: range, replacementString: string) + + default: + return true + } + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + switch textField{ + case usernameTF: + textField.resignFirstResponder() + default: + break + } + return true + } + +} diff --git a/WOKA/Authentication/Model/ForgotPassDM.swift b/WOKA/Authentication/Model/ForgotPassDM.swift new file mode 100644 index 0000000..c0edc5a --- /dev/null +++ b/WOKA/Authentication/Model/ForgotPassDM.swift @@ -0,0 +1,14 @@ +// +// ForgotPassDM.swift +// WOKA +// +// Created by Bilal on 10/05/2024. +// + +import Foundation + +// MARK: - ForgotPassDM + +struct ForgotPassDM: Codable { + let email: String? +} diff --git a/WOKA/Authentication/ViewModel/NewPasswordVM.swift b/WOKA/Authentication/ViewModel/NewPasswordVM.swift new file mode 100644 index 0000000..7155074 --- /dev/null +++ b/WOKA/Authentication/ViewModel/NewPasswordVM.swift @@ -0,0 +1,61 @@ +// +// NewPasswordVM.swift +// WOKA +// +// Created by Bilal on 10/05/2024. +// + +import UIKit +import Alamofire + +class NewPasswordVM{ + + weak var vc : NewPasswordVC! + var userName : String? + + func initView(){ + vc.enterNewPasswordTF.enablePasswordToggle() + vc.confirmPassTF.enablePasswordToggle() + vc.enterNewPasswordTF.delegate = self.vc + vc.confirmPassTF.delegate = self.vc + + vc.enterNewPasswordTF.roundCorner() + vc.confirmPassTF.roundCorner() + let color1 = #colorLiteral(red: 0.144693464, green: 0.1426281333, blue: 0.6686832905, alpha: 1) + let color2 = #colorLiteral(red: 0.6901960784, green: 0.2745098039, blue: 0.7568627451, alpha: 1) + vc.nextBtn.applyGradient(colors: [color1, color2], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0.8, y: 0)) + vc.nextBtn.roundCorner() + } + + func updatePassword(){ + guard let userName else{return} + let params: Parameters = [ + "username": userName, + "password": vc.enterNewPasswordTF.text!, + "password_confirmation": vc.confirmPassTF.text! + ] + + Utilities.startProgressHUD() + NetworkManager.shareInstance.apiRequest(url: APIEndPoints.Auth.update_password, method: .post ,parameters: params) {(result : Result, NetworkManager.APIError>) in + switch result{ + case .success(let data): + switch data.success{ + case 0: // some error + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) + case 1: // Success + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 1) { + + } + default: + Utilities.dismissProgressHUD() + self.vc.toast(msg: K.ConstantString.unRecognised , time: 1) + } + case .failure(let error): + Utilities.dismissProgressHUD() + self.vc.toast(msg: error.localizedDescription , time: 2) + } + } + } +} diff --git a/WOKA/Authentication/ViewModel/OTPVM.swift b/WOKA/Authentication/ViewModel/OTPVM.swift index 0aa84ef..ed4a348 100644 --- a/WOKA/Authentication/ViewModel/OTPVM.swift +++ b/WOKA/Authentication/ViewModel/OTPVM.swift @@ -13,6 +13,9 @@ class OTPVM{ weak var vc : OTPVC! var validateString = String() + //if user name is not null then the user has forgot the pass. + var userName : String? + var timer: Timer? var remainingTime: TimeInterval = 10 * 60 // 10 minutes in seconds @@ -83,8 +86,17 @@ class OTPVM{ return } + if let userName{ + validateForgotPassOTP(otp: otp, userName: userName) + }else{ + validateNormalOTP(otp: otp) + } + + } + + func validateNormalOTP(otp : String){ let params: Parameters = [ - "unique_string": validateString, + "unique_string": validateString, "otp": otp ] @@ -112,6 +124,39 @@ class OTPVM{ } } + func validateForgotPassOTP(otp : String, userName : String){ + let params: Parameters = [ + "username": userName, + "otp": otp + ] + + Utilities.startProgressHUD() + NetworkManager.shareInstance.apiRequest(url: APIEndPoints.Auth.forgot_password_verify_otp, method: .post ,parameters: params) {(result : Result, NetworkManager.APIError>) in + switch result{ + case .success(let data): + switch data.success{ + case 0: // some error + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) + case 1: // Success + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 1) { + let sb = UIStoryboard(name: K.StoryBoard.authenticationSB, bundle: nil) + let vc = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Authentication.newPasswordVC) as! NewPasswordVC + vc.vm.userName = userName + self.vc.navigationController?.pushViewController(vc, animated: true) + } + default: + Utilities.dismissProgressHUD() + self.vc.toast(msg: K.ConstantString.unRecognised , time: 1) + } + case .failure(let error): + Utilities.dismissProgressHUD() + self.vc.toast(msg: error.localizedDescription , time: 2) + } + } + } + func resendOTP(){ let params: Parameters = [ "email": AuthFunc.shareInstance.regData.email!, diff --git a/WOKA/Authentication/ViewModel/ResetPassUserNameVM.swift b/WOKA/Authentication/ViewModel/ResetPassUserNameVM.swift new file mode 100644 index 0000000..d075bbe --- /dev/null +++ b/WOKA/Authentication/ViewModel/ResetPassUserNameVM.swift @@ -0,0 +1,65 @@ +// +// ResetPassUserNameVM.swift +// WOKA +// +// Created by Bilal on 10/05/2024. +// + +import UIKit +import Alamofire + +class ResetPassUserNameVM{ + + weak var vc : ResetPassUserNameVC! + + func initView(){ + vc.usernameTF.roundCorner() + let color1 = #colorLiteral(red: 0.144693464, green: 0.1426281333, blue: 0.6686832905, alpha: 1) + let color2 = #colorLiteral(red: 0.6901960784, green: 0.2745098039, blue: 0.7568627451, alpha: 1) + vc.nextBtn.applyGradient(colors: [color1, color2], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0.8, y: 0)) + vc.nextBtn.roundCorner() + + vc.usernameTF.delegate = self.vc + + //Adding error view to the name tf + vc.usernameTF.addRightButton(title: "", tintColor: UIColor.red, btnImage: UIImage(systemName: "exclamationmark.circle.fill"), target: self, action: #selector(errorUserName)) + vc.usernameTF.rightView?.isHidden = true + } + + @objc func errorUserName(){ + let errorView = errorViews.object(forKey: vc.usernameTF) + if let errorView = errorView { + errorView.isHidden.toggle() + } + } + + // MARK: - Send OTP via username + + func sendOtpToUserName(){ + let params : Parameters = ["username" : vc.usernameTF.text!] + Utilities.startProgressHUD() + NetworkManager.shareInstance.apiRequest(url: APIEndPoints.Auth.forgot_password_send_otp, method: .post, parameters: params) {(result : Result, NetworkManager.APIError>) in + switch result{ + case .success(let data): + switch data.success{ + case 0: + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) + case 1: + Utilities.dismissProgressHUD() + self.vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) { + let sb = UIStoryboard(name: K.StoryBoard.authenticationSB, bundle: nil) + let vc = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Authentication.oTPVC) as! OTPVC + vc.vm.userName = self.vc.usernameTF.text! + self.vc.navigationController?.pushViewController(vc, animated: true) + } + default: + break + } + case .failure(let error): + Utilities.dismissProgressHUD() + self.vc.toast(msg: error.localizedDescription, time: 2) + } + } + } +} diff --git a/WOKA/CollectionViewCenteredFlowLayout.swift b/WOKA/CollectionViewCenteredFlowLayout.swift new file mode 100644 index 0000000..87e5c4c --- /dev/null +++ b/WOKA/CollectionViewCenteredFlowLayout.swift @@ -0,0 +1,112 @@ +// Copyright (c) 2017 Cœur +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import UIKit + +/** + * Simple UICollectionViewFlowLayout that centers the cells rather than justify them + * + * Based on https://github.com/Coeur/UICollectionViewLeftAlignedLayout + */ +open class CollectionViewCenteredFlowLayout: UICollectionViewFlowLayout { + open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + guard let layoutAttributesForElements = super.layoutAttributesForElements(in: rect) else { + return nil + } + guard let collectionView = collectionView else { + return layoutAttributesForElements + } + // we group copies of the elements from the same row/column + var representedElements: [UICollectionViewLayoutAttributes] = [] + var cells: [[UICollectionViewLayoutAttributes]] = [[]] + var previousFrame: CGRect? + if scrollDirection == .vertical { + for layoutAttributes in layoutAttributesForElements { + guard layoutAttributes.representedElementKind == nil else { + representedElements.append(layoutAttributes) + continue + } + // copying is required to avoid "UICollectionViewFlowLayout cache mismatched frame" + let currentItemAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes + // if the current frame, once stretched to the full row doesn't intersect the previous frame then they are on different rows + if previousFrame != nil && !currentItemAttributes.frame.intersects(CGRect(x: -.greatestFiniteMagnitude, y: previousFrame!.origin.y, width: .infinity, height: previousFrame!.size.height)) { + cells.append([]) + } + cells[cells.endIndex - 1].append(currentItemAttributes) + previousFrame = currentItemAttributes.frame + } + // we reposition all elements + return representedElements + cells.flatMap { group -> [UICollectionViewLayoutAttributes] in + guard let section = group.first?.indexPath.section else { + return group + } + let evaluatedSectionInset = evaluatedSectionInsetForSection(at: section) + let evaluatedMinimumInteritemSpacing = evaluatedMinimumInteritemSpacingForSection(at: section) + let evaluatedCollectionView = (collectionView.bounds.width + evaluatedSectionInset.left - evaluatedSectionInset.right - group.reduce(0, { $0 + $1.frame.size.width }) - CGFloat(group.count - 1) * evaluatedMinimumInteritemSpacing) + var origin = evaluatedCollectionView / 2 + // we reposition each element of a group + return group.map { + $0.frame.origin.x = origin + origin += $0.frame.size.width + evaluatedMinimumInteritemSpacing + return $0 + } + } + } else { + for layoutAttributes in layoutAttributesForElements { + guard layoutAttributes.representedElementKind == nil else { + representedElements.append(layoutAttributes) + continue + } + // copying is required to avoid "UICollectionViewFlowLayout cache mismatched frame" + let currentItemAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes + // if the current frame, once stretched to the full column doesn't intersect the previous frame then they are on different columns + if previousFrame != nil && !currentItemAttributes.frame.intersects(CGRect(x: previousFrame!.origin.x, y: -.greatestFiniteMagnitude, width: previousFrame!.size.width, height: .infinity)) { + cells.append([]) + } + cells[cells.endIndex - 1].append(currentItemAttributes) + previousFrame = currentItemAttributes.frame + } + // we reposition all elements + return representedElements + cells.flatMap { group -> [UICollectionViewLayoutAttributes] in + guard let section = group.first?.indexPath.section else { + return group + } + let evaluatedSectionInset = evaluatedSectionInsetForSection(at: section) + let evaluatedMinimumInteritemSpacing = evaluatedMinimumInteritemSpacingForSection(at: section) + let evaluatedCollectionView = (collectionView.bounds.height + evaluatedSectionInset.top - evaluatedSectionInset.bottom - group.reduce(0, { $0 + $1.frame.size.height }) - CGFloat(group.count - 1) * evaluatedMinimumInteritemSpacing) + var origin = evaluatedCollectionView / 2 + // we reposition each element of a group + return group.map { + $0.frame.origin.y = origin + origin += $0.frame.size.height + evaluatedMinimumInteritemSpacing + return $0 + } + } + } + } +} + +extension UICollectionViewFlowLayout { + internal func evaluatedSectionInsetForSection(at section: Int) -> UIEdgeInsets { + (collectionView?.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView!, layout: self, insetForSectionAt: section) ?? sectionInset + } + internal func evaluatedMinimumInteritemSpacingForSection(at section: Int) -> CGFloat { + (collectionView?.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView!, layout: self, minimumInteritemSpacingForSectionAt: section) ?? minimumInteritemSpacing + } +} diff --git a/WOKA/Info.plist b/WOKA/Info.plist index b1fdb3e..8b364c3 100644 --- a/WOKA/Info.plist +++ b/WOKA/Info.plist @@ -2,14 +2,21 @@ - STAGING_URL - $(STAGING_URL) API_KEY_ID $(API_KEY_ID) API_KEY_PASS $(API_KEY_PASS) - UIUserInterfaceStyle - Light + STAGING_URL + $(STAGING_URL) + UIAppFonts + + Exo2-Bold.ttf + Exo2-ExtraBold.ttf + Exo2-Medium.ttf + Exo2-Regular.ttf + Exo2-SemiBold.ttf + Exo2-Thin.ttf + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -29,14 +36,5 @@ - UIAppFonts - - Exo2-Bold.ttf - Exo2-ExtraBold.ttf - Exo2-Medium.ttf - Exo2-Regular.ttf - Exo2-SemiBold.ttf - Exo2-Thin.ttf - diff --git a/WOKA/Network Adapter/APIEndPoints.swift b/WOKA/Network Adapter/APIEndPoints.swift index 9421524..0b45934 100644 --- a/WOKA/Network Adapter/APIEndPoints.swift +++ b/WOKA/Network Adapter/APIEndPoints.swift @@ -15,6 +15,9 @@ enum EnvironmentCheck{ struct APIEndPoints { + // Private init to prevent external initialization + private init() {} + struct BaseURL { static let staging = "https://wokaland.com/admin/api/" static let production = "https://simplitend.com" @@ -31,6 +34,13 @@ struct APIEndPoints { static let avatar_listing = makeURL(path: "avatar_listing") static let child_registration = makeURL(path: "child_registration") static let get_linked_child = makeURL(path: "get_linked_child") + + /* + Password forgot api's + */ + static let forgot_password_send_otp = makeURL(path: "forgot_password_send_otp") + static let forgot_password_verify_otp = makeURL(path: "forgot_password_verify_otp") + static let update_password = makeURL(path: "update_password") } // Other endpoint categories...