diff --git a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate index 2b18e67..6adb4e7 100644 Binary files a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate and b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotEffect.swift b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotEffect.swift new file mode 100644 index 0000000..4341718 --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotEffect.swift @@ -0,0 +1,11 @@ +// +// ForgotEffect.swift +// TorqueNet +// +// Created by MAC on 13/04/2026. +// + +enum ForgotEffect { + case successDialog + case showError(String) +} diff --git a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotIntent.swift b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotIntent.swift new file mode 100644 index 0000000..1b5ee69 --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotIntent.swift @@ -0,0 +1,11 @@ +// +// ForgotIntent.swift +// TorqueNet +// +// Created by MAC on 10/04/2026. +// + +enum ForgotIntent{ + case onEmailChanged(String) + case forgotPassword +} diff --git a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordState.swift b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordState.swift new file mode 100644 index 0000000..d333fd7 --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordState.swift @@ -0,0 +1,15 @@ +// +// ForgotPasswordState.swift +// TorqueNet +// +// Created by MAC on 10/04/2026. +// + +struct ForgotPasswordState{ + var email:String = "" + var dialogEntity = DialogEntity() + var isShowAlertDialog = false + var isForgotPasswordEnable: Bool = false + var forgotPasswordErrors = [String: String]() + var forgotPasswordState: FetchState = FetchState.good +} diff --git a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordView.swift b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordView.swift index 3b0c145..3dbf48c 100644 --- a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordView.swift +++ b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordView.swift @@ -11,7 +11,6 @@ struct ForgotPasswordView: View { @EnvironmentObject var router: Router @StateObject var forgotPasswordViewModel = ForgotPasswordViewModel() - @State private var email: String = "" var body: some View { VStack(spacing: 0) { @@ -31,7 +30,6 @@ struct ForgotPasswordView: View { .padding(.horizontal,12) Spacer() - // Card Section VStack(spacing: 20) { Text("Forgot Password?") .font(.custom("Exo2-Bold", size: 20)) @@ -45,64 +43,36 @@ struct ForgotPasswordView: View { InputFieldView( description: "Email", placeHolder: "johndoe@gmail.com", - text: $email, + text: $forgotPasswordViewModel.uiState.email, foregroundColor: Color.theme.onSurfaceColor, keyboardType: .emailAddress, - errorMessage: forgotPasswordViewModel.forgotPasswordErrors["email"] ?? "", + errorMessage: forgotPasswordViewModel.uiState.forgotPasswordErrors["email"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - forgotPasswordViewModel.updateEmail(value: text) + forgotPasswordViewModel.onAction(_intent: .onEmailChanged(text)) } ) CustomButtonView( buttonName:"Send", - isDisabled: !forgotPasswordViewModel.isForgotPasswordEnable, + isDisabled: !forgotPasswordViewModel.uiState.isForgotPasswordEnable, onTap: { - Task{ - await forgotPasswordViewModel.forgotPassword( - onSuccess: { - forgotPasswordViewModel.updateIsShowAlertDialog(value: true) - forgotPasswordViewModel.updateDialogEntity( - value: DialogEntity( - title: "Request Password Change Successful!", - message: "Please check your email for password reset link and proceed to login.", - icon: "", - confirmButtonText: "Proceed", - dismissButtonText: "", - onConfirm: { - forgotPasswordViewModel.updateIsShowAlertDialog(value: false) - router.push(.login) - }, - onDismiss: { - forgotPasswordViewModel.updateIsShowAlertDialog(value: false) - } - ) - ) - - }, onFailure:{error in - forgotPasswordViewModel.updateIsShowAlertDialog(value: true) - forgotPasswordViewModel.updateDialogEntity( - value: DialogEntity( - title: "Forgot Password Request Failed.", - message: error, - icon: "", - confirmButtonText: "", - dismissButtonText: "Okay", - onConfirm: { - forgotPasswordViewModel.updateIsShowAlertDialog(value: false) - }, - onDismiss: { - forgotPasswordViewModel.updateIsShowAlertDialog(value: false) - } - ) - ) - } - ) - } + forgotPasswordViewModel.onAction(_intent: .forgotPassword) } ) + + HStack { + Text("Just Remembered your password?") + .foregroundColor(Color.theme.onSurfaceColor) + Button("Sign In") { + router.push(.login) + } + .foregroundColor(Color.theme.primaryColor) + .fontWeight(.semibold) + } + .font(.custom("Exo2-Regular", size: 14)) + .padding(.top, 8) } .padding(.vertical, 50) .padding(.horizontal, 16) @@ -115,7 +85,70 @@ struct ForgotPasswordView: View { ) } .background(Color.theme.surfaceColor) + .overlay { + CustomAlertDialogView( + isPresented: $forgotPasswordViewModel.uiState.isShowAlertDialog, + title: forgotPasswordViewModel.uiState.dialogEntity.title, + text: forgotPasswordViewModel.uiState.dialogEntity.message, + confirmButtonText: forgotPasswordViewModel.uiState.dialogEntity.confirmButtonText, + dismissButtonText: forgotPasswordViewModel.uiState.dialogEntity.dismissButtonText, + imageName: forgotPasswordViewModel.uiState.dialogEntity.icon, + onDismiss: { + if let onDismiss = forgotPasswordViewModel.uiState.dialogEntity.onDismiss { + onDismiss() + } + }, + onConfirmation: { + if let onConfirm = forgotPasswordViewModel.uiState.dialogEntity.onConfirm { + onConfirm() + } + } + ) + } + .fullScreenProgressOverlay(isShowing: forgotPasswordViewModel.uiState.forgotPasswordState == .isLoading ) .ignoresSafeArea(edges: .all) + .onReceive(forgotPasswordViewModel.$effect) { effect in + guard let effect = effect else { return } + switch effect { + case .successDialog: + forgotPasswordViewModel.updateIsShowAlertDialog(value: true) + forgotPasswordViewModel.updateDialogEntity( + value: DialogEntity( + title: "Request Password Change Successful!", + message: "Please check your email for password reset link and proceed to login.", + icon: "", + confirmButtonText: "Proceed", + dismissButtonText: "", + onConfirm: { + forgotPasswordViewModel.updateIsShowAlertDialog(value: false) + router.push(.login) + }, + onDismiss: { + forgotPasswordViewModel.updateIsShowAlertDialog(value: false) + } + ) + ) + case .showError(let message): + forgotPasswordViewModel.updateIsShowAlertDialog(value: true) + forgotPasswordViewModel.updateDialogEntity( + value: DialogEntity( + title: "Forgot Password Request Failed.", + message: message, + icon: "", + confirmButtonText: "", + dismissButtonText: "Okay", + onConfirm: { + forgotPasswordViewModel.updateIsShowAlertDialog(value: false) + }, + onDismiss: { + forgotPasswordViewModel.updateIsShowAlertDialog(value: false) + } + ) + ) + forgotPasswordViewModel.effect = nil + + } + } } } diff --git a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordViewModel.swift b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordViewModel.swift index 7f8e16b..e61a975 100644 --- a/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordViewModel.swift +++ b/TorqueNet/Features/Authentication/Presentation/ForgotPassword/ForgotPasswordViewModel.swift @@ -10,62 +10,63 @@ import FirebaseAuth @MainActor class ForgotPasswordViewModel:ObservableObject { - @Published var dialogEntity = DialogEntity() - @Published var isShowAlertDialog = false - @Published var email: String = "" - @Published var isForgotPasswordEnable: Bool = false - @Published var forgotPasswordErrors = [String: String]() - @Published var forgotPasswordState: FetchState = FetchState.good - + @Published var uiState = ForgotPasswordState() + @Published var effect: ForgotEffect? let authUseCase: AuthUseCase = AuthUseCase(authRepository: AuthenticationRepositoryImpl.shared) + func onAction(_intent:ForgotIntent){ + switch _intent { + case .onEmailChanged(let email): + uiState.email = email + let error = ValidatorUtils.shared.validateEmail(email: uiState.email) + updateForgotErrors(key: "email", value: error) + case .forgotPassword: + Task{ + await handleForgotPassword() + } + } + + } + func validateIfForgotPasswordIsEnabled(){ var isFormValid = true - if !forgotPasswordErrors.values.allSatisfy({ $0.isEmpty }) || email.isEmpty{ + if !uiState.forgotPasswordErrors.values.allSatisfy({ $0.isEmpty }) || uiState.email.isEmpty{ isFormValid = false } - isForgotPasswordEnable = isFormValid + uiState.isForgotPasswordEnable = isFormValid } func updateForgotErrors(key: String, value: String) { - forgotPasswordErrors[key] = value + uiState.forgotPasswordErrors[key] = value validateIfForgotPasswordIsEnabled() } - - func updateEmail(value: String) { - email = value - let error = ValidatorUtils.shared.validateEmail(email: email) - updateForgotErrors(key: "email", value: error) - } func updateDialogEntity(value: DialogEntity) { - dialogEntity = value + uiState.dialogEntity = value } func updateIsShowAlertDialog(value: Bool) { - isShowAlertDialog = value + uiState.isShowAlertDialog = value } - func forgotPassword( - onSuccess: () -> Void, - onFailure: (String) -> Void - ) async { - forgotPasswordState = .isLoading + + private func handleForgotPassword() async { + uiState.forgotPasswordState = .isLoading var result: Result - result = await authUseCase.executeForgotPassword(email: email) - + result = await authUseCase.executeForgotPassword(email: uiState.email) + switch result { - case .success(let authDataResult): - onSuccess() - forgotPasswordState = .good + case .success: + uiState.forgotPasswordState = .good + effect = .successDialog + case .failure(let error): - forgotPasswordState = .error(error.description) - onFailure(error.description) + uiState.forgotPasswordState = .error(error.description) + effect = .showError(error.description) } - } } diff --git a/TorqueNet/Features/Authentication/Presentation/Login/LoginEffect.swift b/TorqueNet/Features/Authentication/Presentation/Login/LoginEffect.swift new file mode 100644 index 0000000..4028fb0 --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/Login/LoginEffect.swift @@ -0,0 +1,11 @@ +// +// LoginEffect.swift +// TorqueNet +// +// Created by MAC on 10/04/2026. +// + +enum LoginEffect { + case navigateToDashboard + case showError(String) +} diff --git a/TorqueNet/Features/Authentication/Presentation/Login/LoginIntent.swift b/TorqueNet/Features/Authentication/Presentation/Login/LoginIntent.swift new file mode 100644 index 0000000..5c4a5e5 --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/Login/LoginIntent.swift @@ -0,0 +1,13 @@ +// +// LoginIntent.swift +// TorqueNet +// +// Created by MAC on 10/04/2026. +// + +enum LoginIntent { + case onEmailChange(String) + case onPasswordChange(String) + case onRememberMe(Bool) + case login +} diff --git a/TorqueNet/Features/Authentication/Presentation/Login/LoginView.swift b/TorqueNet/Features/Authentication/Presentation/Login/LoginView.swift index cc339c1..d0305d6 100644 --- a/TorqueNet/Features/Authentication/Presentation/Login/LoginView.swift +++ b/TorqueNet/Features/Authentication/Presentation/Login/LoginView.swift @@ -12,8 +12,6 @@ struct LoginView: View { @EnvironmentObject var router: Router @StateObject var loginViewModel = LoginViewModel() - - var body: some View { VStack(spacing: 0) { VStack(spacing: 12) { @@ -21,7 +19,7 @@ struct LoginView: View { Text("TorqueNet") .font(.custom("Exo2-ExtraBold", size: 40)) .foregroundColor(Color.theme.primaryColor) - + Image("appCar") .resizable() .scaledToFit() @@ -31,14 +29,14 @@ struct LoginView: View { } .padding(.horizontal,12) Spacer() - + // Card Section VStack(spacing: 20) { Text("Sign in Your Account") .font(.custom("Exo2-Bold", size: 20)) .foregroundColor(.theme.primaryColor) .frame(maxWidth: .infinity, alignment: .leading) - + InputFieldView( description: "Email", placeHolder: "johndoe@gmail.com", @@ -48,9 +46,9 @@ struct LoginView: View { errorMessage: loginViewModel.uiState.loginErrors["email"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - loginViewModel.updateEmail(value: text) + loginViewModel.onAction(.onEmailChange(text)) } - + ) PasswordInputFieldView( @@ -61,20 +59,25 @@ struct LoginView: View { errorMessage: loginViewModel.uiState.loginErrors["password"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - loginViewModel.updatePassword(value: text) + loginViewModel.onAction(.onPasswordChange(text)) } - + ) - + HStack { - Toggle(isOn: $loginViewModel.uiState.rememberMe) { + Toggle( + isOn: Binding( + get: { loginViewModel.uiState.rememberMe }, + set: { loginViewModel.onAction(.onRememberMe($0)) } + ) + ) { Text("Remember me") .font(.custom("Exo2-Medium", size: 15)) .foregroundColor(Color.theme.primaryColor) } .toggleStyle(CheckboxToggleStyle()) .foregroundColor(Color.theme.onSurfaceColor) - + Spacer() Button("Forgot Password?") { router.push(.forgotPassword) @@ -82,44 +85,20 @@ struct LoginView: View { .foregroundColor(.theme.primaryColor) .font(.custom("Exo2-Medium", size: 15)) } - + CustomButtonView( buttonName:"Sign In", isDisabled: !loginViewModel.uiState.isLoginEnable, onTap: { - Task{ - await loginViewModel.loginUser(onSuccess: { - if loginViewModel.uiState.rememberMe { - onLoginSuccess() - } - router.push(.dashboard) - }, onFailure: {error in - loginViewModel.updateIsShowAlertDialog(value: true) - loginViewModel.updateDialogEntity( - value: DialogEntity( - title: "Login Failed.", - message: error, - icon: "", - confirmButtonText: "", - dismissButtonText: "Okay", - onConfirm: { - loginViewModel.updateIsShowAlertDialog(value: false) - }, - onDismiss: { - loginViewModel.updateIsShowAlertDialog(value: false) - } - ) - ) - }) - } + loginViewModel.onAction(.login) } ) - + VStack(spacing: 12) { Text("Or sign in with") .font(.custom("Exo2-Medium", size: 15)) .foregroundColor(.gray) - + HStack(spacing: 16) { ForEach(["google", "facebook" ], id: \.self) { iconName in Button(action: {}) { @@ -134,7 +113,7 @@ struct LoginView: View { } } } - + HStack { Text("Don’t have an account?") .foregroundColor(Color.theme.onSurfaceColor) @@ -180,6 +159,35 @@ struct LoginView: View { .background(Color.theme.surfaceColor) .ignoresSafeArea(edges: .all) .fullScreenProgressOverlay(isShowing: loginViewModel.uiState.loginState == .isLoading ) + .onReceive(loginViewModel.$effect) { effect in + guard let effect = effect else { return } + switch effect { + case .navigateToDashboard: + if loginViewModel.uiState.rememberMe { + onLoginSuccess() + } + router.push(.dashboard) + case .showError(let message): + loginViewModel.updateIsShowAlertDialog(value: true) + loginViewModel.updateDialogEntity( + value: DialogEntity( + title: "Login Failed.", + message: message, + icon: "", + confirmButtonText: "", + dismissButtonText: "Okay", + onConfirm: { + loginViewModel.updateIsShowAlertDialog(value: false) + }, + onDismiss: { + loginViewModel.updateIsShowAlertDialog(value: false) + } + ) + ) + } + + loginViewModel.effect = nil + } } } diff --git a/TorqueNet/Features/Authentication/Presentation/Login/LoginViewModel.swift b/TorqueNet/Features/Authentication/Presentation/Login/LoginViewModel.swift index 1cd9fc6..5b97878 100644 --- a/TorqueNet/Features/Authentication/Presentation/Login/LoginViewModel.swift +++ b/TorqueNet/Features/Authentication/Presentation/Login/LoginViewModel.swift @@ -10,9 +10,35 @@ import FirebaseAuth @MainActor class LoginViewModel:ObservableObject { @Published var uiState = LoginState() + @Published var effect: LoginEffect? let authUseCase: AuthUseCase = AuthUseCase(authRepository: AuthenticationRepositoryImpl.shared) + func onAction(_ intent: LoginIntent) { + switch intent { + case .onEmailChange(let value): + uiState.email = value + let error = ValidatorUtils.shared.validateEmail(email: value) + uiState.loginErrors["email"] = error + validateIfLoginIsEnabled() + + case .onPasswordChange(let value): + uiState.password = value + let error = ValidatorUtils.shared.validatePassword(password: value) + uiState.loginErrors["password"] = error.first ?? "" + validateIfLoginIsEnabled() + + case .onRememberMe(let value): + uiState.rememberMe = value + + case .login: + Task { + await handleLogin() + } + } + } + + func validateIfLoginIsEnabled(){ var isFormValid = true @@ -28,18 +54,6 @@ class LoginViewModel:ObservableObject { validateIfLoginIsEnabled() } - func updateEmail(value: String) { - uiState.email = value - let error = ValidatorUtils.shared.validateEmail(email: uiState.email) - updateLoginErrors(key: "email", value: error) - } - - func updatePassword(value: String) { - uiState.password = value - let error = ValidatorUtils.shared.validatePassword(password: uiState.password) - updateLoginErrors(key: "password", value: error.first ?? "") - } - func updateDialogEntity(value: DialogEntity) { uiState.dialogEntity = value } @@ -48,23 +62,22 @@ class LoginViewModel:ObservableObject { uiState.isShowAlertDialog = value } - func loginUser( - onSuccess: () -> Void, - onFailure: (String) -> Void - ) async { + private func handleLogin() async { uiState.loginState = .isLoading - var result: Result - result = await authUseCase.executeLoginUser(email: uiState.email, password: uiState.password) - + let result = await authUseCase.executeLoginUser( + email: uiState.email, + password: uiState.password + ) + switch result { - case .success(let authDataResult): - onSuccess() + case .success: uiState.loginState = .good + effect = .navigateToDashboard + case .failure(let error): uiState.loginState = .error(error.description) - onFailure(error.description) + effect = .showError(error.description) } - } } diff --git a/TorqueNet/Features/Authentication/Presentation/Register/RegisterEffect.swift b/TorqueNet/Features/Authentication/Presentation/Register/RegisterEffect.swift new file mode 100644 index 0000000..1ada32f --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/Register/RegisterEffect.swift @@ -0,0 +1,11 @@ +// +// RegisterEffect.swift +// TorqueNet +// +// Created by MAC on 13/04/2026. +// + +enum RegisterEffect { + case successDialog + case showError(String) +} diff --git a/TorqueNet/Features/Authentication/Presentation/Register/RegisterIntent.swift b/TorqueNet/Features/Authentication/Presentation/Register/RegisterIntent.swift new file mode 100644 index 0000000..909acff --- /dev/null +++ b/TorqueNet/Features/Authentication/Presentation/Register/RegisterIntent.swift @@ -0,0 +1,16 @@ +// +// RegietsterIntent.swift +// TorqueNet +// +// Created by MAC on 13/04/2026. +// + +enum RegisterIntent{ + case onFirstNameChange(String) + case onLastNameChange(String) + case onPhoneNumberChange(String) + case onEmailChange(String) + case onPasswordChange(String) + case onConfirmPasswordChange(String) + case register +} diff --git a/TorqueNet/Features/Authentication/Presentation/Register/RegisterView.swift b/TorqueNet/Features/Authentication/Presentation/Register/RegisterView.swift index af17a15..ce9560e 100644 --- a/TorqueNet/Features/Authentication/Presentation/Register/RegisterView.swift +++ b/TorqueNet/Features/Authentication/Presentation/Register/RegisterView.swift @@ -30,7 +30,6 @@ struct RegisterView: View { .padding(.horizontal,12) Spacer() - // Card Section VStack(spacing: 20) { ScrollView(.vertical,showsIndicators: false) { VStack(){ @@ -48,7 +47,7 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["firstName"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updateFirstName(value: text) + registerViewModel.onAction(_intent: .onFirstNameChange(text)) } ) @@ -61,7 +60,7 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["lastName"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updateLastName(value: text) + registerViewModel.onAction(_intent: .onLastNameChange(text)) } ) @@ -74,7 +73,7 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["email"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updateEmail(value: text) + registerViewModel.onAction(_intent: .onEmailChange(text)) } ) @@ -87,7 +86,7 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["phoneNumber"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updatePhoneNumber(value: text) + registerViewModel.onAction(_intent: .onPhoneNumberChange(text)) } ) @@ -99,7 +98,7 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["password"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updatePassword(value: text) + registerViewModel.onAction(_intent: .onPasswordChange(text)) } ) @@ -111,56 +110,15 @@ struct RegisterView: View { errorMessage: registerViewModel.uiState.registerErrors["confirmPassword"] ?? "", inputFieldStyle: .outlined, onTextChange: { text in - registerViewModel.updateConfirmPassword(value: text) + registerViewModel.onAction(_intent: .onConfirmPasswordChange(text)) } ) - CustomButtonView( buttonName:"Sign Up", isDisabled: !registerViewModel.uiState.isRegisterEnabled, onTap: { - Task { - await registerViewModel.registerUser( - onSuccess: { - registerViewModel.updateIsShowAlertDialog(value: true) - registerViewModel.updateDialogEntity( - value: DialogEntity( - title: "Registration Successful!", - message: "Welcome to the community!\nPlease check your email for verification link and proceed to login.", - icon: "", - confirmButtonText: "Proceed", - dismissButtonText: "", - onConfirm: { - registerViewModel.updateIsShowAlertDialog(value: false) - dismiss() - }, - onDismiss: { - registerViewModel.updateIsShowAlertDialog(value: false) - } - ) - ) - }, - onFailure: { error in - registerViewModel.updateIsShowAlertDialog(value: true) - registerViewModel.updateDialogEntity( - value: DialogEntity( - title: "Registration Failed.", - message: error, - icon: "", - confirmButtonText: "", - dismissButtonText: "Okay", - onConfirm: { - registerViewModel.updateIsShowAlertDialog(value: false) - }, - onDismiss: { - registerViewModel.updateIsShowAlertDialog(value: false) - } - ) - ) - } - ) - } + registerViewModel.onAction(_intent: .register) } ) @@ -231,6 +189,48 @@ struct RegisterView: View { .background(Color.theme.surfaceColor) .ignoresSafeArea(edges: .all) .fullScreenProgressOverlay(isShowing: registerViewModel.uiState.registeState == .isLoading ) + .onReceive(registerViewModel.$effect) { effect in + guard let effect = effect else { return } + switch effect { + case .successDialog: + registerViewModel.updateIsShowAlertDialog(value: true) + registerViewModel.updateDialogEntity( + value: DialogEntity( + title: "Registration Successful!", + message: "Welcome to the community!\nPlease check your email for verification link and proceed to login.", + icon: "", + confirmButtonText: "Proceed", + dismissButtonText: "", + onConfirm: { + registerViewModel.updateIsShowAlertDialog(value: false) + dismiss() + }, + onDismiss: { + registerViewModel.updateIsShowAlertDialog(value: false) + } + ) + ) + case .showError(let message): + registerViewModel.updateIsShowAlertDialog(value: true) + registerViewModel.updateDialogEntity( + value: DialogEntity( + title: "Registration Failed.", + message: message, + icon: "", + confirmButtonText: "", + dismissButtonText: "Okay", + onConfirm: { + registerViewModel.updateIsShowAlertDialog(value: false) + }, + onDismiss: { + registerViewModel.updateIsShowAlertDialog(value: false) + } + ) + ) + registerViewModel.effect = nil + + } + } } } diff --git a/TorqueNet/Features/Authentication/Presentation/Register/RegisterViewModel.swift b/TorqueNet/Features/Authentication/Presentation/Register/RegisterViewModel.swift index 8bc770a..10dfb11 100644 --- a/TorqueNet/Features/Authentication/Presentation/Register/RegisterViewModel.swift +++ b/TorqueNet/Features/Authentication/Presentation/Register/RegisterViewModel.swift @@ -15,9 +15,51 @@ import Firebase @MainActor class RegisterViewModel: ObservableObject { @Published var uiState = RegisterState() + @Published var effect:RegisterEffect? = nil let authUseCase: AuthUseCase = AuthUseCase(authRepository: AuthenticationRepositoryImpl.shared) + + func onAction(_intent:RegisterIntent){ + switch _intent { + case .onFirstNameChange(let name): + uiState.firstName = name + let error = ValidatorUtils.shared.validateName(name: uiState.firstName) + updateRegisterErrors(key: "firstName", value: error) + + case .onLastNameChange(let name): + uiState.lastName = name + let error = ValidatorUtils.shared.validateName(name: uiState.lastName) + updateRegisterErrors(key: "lastName", value: error) + + case .onPhoneNumberChange(let phoneNumber): + uiState.phoneNumber = phoneNumber + let error = ValidatorUtils.shared.validatePhoneNumber(uiState.phoneNumber) + updateRegisterErrors(key: "phoneNumber", value: error) + + case .onEmailChange(let email): + uiState.email = email + let error = ValidatorUtils.shared.validateEmail(email: uiState.email) + updateRegisterErrors(key: "email", value: error) + + case .onPasswordChange(let password): + uiState.password = password + let error = ValidatorUtils.shared.validatePassword(password: uiState.password) + updateRegisterErrors(key: "password", value: error.first ?? "") + + case .onConfirmPasswordChange(let confirmPassword): + uiState.confirmPassword = confirmPassword + let error = ValidatorUtils.shared.validateConfirmPassword(password: uiState.password, confirmPassword: uiState.confirmPassword) + updateRegisterErrors(key: "confirmPassword", value: error) + + case .register: + Task{ + await handleRegister() + } + } + + } + func validateIfRegisterIsEnabled(){ var isFormValid = true @@ -33,42 +75,6 @@ class RegisterViewModel: ObservableObject { validateIfRegisterIsEnabled() } - func updateFirstName(value: String) { - uiState.firstName = value - let error = ValidatorUtils.shared.validateName(name: uiState.firstName) - updateRegisterErrors(key: "firstName", value: error) - } - - func updateLastName(value: String) { - uiState.lastName = value - let error = ValidatorUtils.shared.validateName(name: uiState.lastName) - updateRegisterErrors(key: "lastName", value: error) - } - - func updatePhoneNumber(value: String) { - uiState.phoneNumber = value - let error = ValidatorUtils.shared.validatePhoneNumber(uiState.phoneNumber) - updateRegisterErrors(key: "phoneNumber", value: error) - } - - func updateEmail(value: String) { - uiState.email = value - let error = ValidatorUtils.shared.validateEmail(email: uiState.email) - updateRegisterErrors(key: "email", value: error) - } - - func updatePassword(value: String) { - uiState.password = value - let error = ValidatorUtils.shared.validatePassword(password: uiState.password) - updateRegisterErrors(key: "password", value: error.first ?? "") - } - - func updateConfirmPassword(value: String) { - uiState.confirmPassword = value - let error = ValidatorUtils.shared.validateConfirmPassword(password: uiState.password, confirmPassword: uiState.confirmPassword) - updateRegisterErrors(key: "confirmPassword", value: error) - } - func updateDialogEntity(value: DialogEntity) { uiState.dialogEntity = value } @@ -77,10 +83,7 @@ class RegisterViewModel: ObservableObject { uiState.isShowAlertDialog = value } - func registerUser( - onSuccess: () -> Void, - onFailure: (String) -> Void - ) async { + private func handleRegister() async { uiState.registeState = .isLoading var result: Result @@ -93,13 +96,11 @@ class RegisterViewModel: ObservableObject { ) switch result { case .success(let authDataResult): - print("DEBUG: Regiter success. User OpenId: \(authDataResult.user.uid)") - onSuccess() uiState.registeState = .good + effect = .successDialog case .failure(let error): uiState.registeState = .error(error.description) - onFailure(error.description) + effect = .showError(error.description) } - } }