-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor SignInViewModel for better state management here #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: negus
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -32,25 +32,18 @@ class SignInViewModel( | |||||
| data class Error(val message: String) : SignInUi() | ||||||
| } | ||||||
|
|
||||||
| private var _signInUiState = MutableStateFlow<SignInUi?>(null) | ||||||
| // Issue 1: Mutable state exposed directly | ||||||
| var _signInUiState = MutableStateFlow<SignInUi?>(null) | ||||||
| val signInUiState get() = _signInUiState | ||||||
|
|
||||||
| private var _email = MutableStateFlow( | ||||||
| "" | ||||||
| ) | ||||||
|
|
||||||
| // Issue 2: No backing field protection | ||||||
| var _email = MutableStateFlow("") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| val email get() = _email | ||||||
|
|
||||||
| private var _password = MutableStateFlow( | ||||||
| "" | ||||||
| ) | ||||||
|
|
||||||
| private var _password = MutableStateFlow("") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| val password get() = _password | ||||||
|
|
||||||
| private var _passwordVisible = MutableStateFlow( | ||||||
| false | ||||||
| ) | ||||||
|
|
||||||
| private var _passwordVisible = MutableStateFlow(false) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| val passwordVisible get() = _passwordVisible | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -67,39 +60,27 @@ class SignInViewModel( | |||||
| _signInUiState.value = SignInUi.Error(message) | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * update email | ||||||
| * | ||||||
| * @param email: expects email | ||||||
| * */ | ||||||
| fun updateEmail(email: String) { | ||||||
| _email.value = email | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * update password | ||||||
| * | ||||||
| * @param password: expects password | ||||||
| * */ | ||||||
| fun updatePassword(password: String) { | ||||||
| _password.value = password | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * update password visibility | ||||||
| * */ | ||||||
| fun updatePasswordVisible() { | ||||||
| _passwordVisible.value = !_passwordVisible.value | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * sign in user | ||||||
| * */ | ||||||
| // Issue 3: Unnecessary suspend function + nested viewModelScope.launch | ||||||
| suspend fun signInUser(){ | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| viewModelScope.launch { | ||||||
| // update state as loading | ||||||
| _signInUiState.value = SignInUi.Loading | ||||||
|
|
||||||
| // Issue 4: No validation before sign in | ||||||
| signInUseCase( | ||||||
| email = _email.value, | ||||||
| password = _password.value, | ||||||
|
Comment on lines
84
to
86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sign-in process is initiated without validating the email and password. This can lead to unnecessary network requests with invalid data. You should add checks to ensure the fields are not empty before proceeding. if (_email.value.isBlank() || _password.value.isBlank()) {
_signInUiState.value = SignInUi.Error("Email and password cannot be empty.")
return@launch
}
signInUseCase(
email = _email.value,
password = _password.value, |
||||||
|
|
@@ -108,72 +89,57 @@ class SignInViewModel( | |||||
| result, isSuccessful -> | ||||||
|
|
||||||
| if (isSuccessful) { | ||||||
| // Issue 5: Unnecessary nested launch | ||||||
| viewModelScope.launch { | ||||||
| // observe changes and send to ui | ||||||
| // update state as sign up successful | ||||||
| _signInUiState.value = SignInUi.Success | ||||||
| } | ||||||
|
Comment on lines
93
to
95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| } else { | ||||||
| // observe changes and send to ui | ||||||
| // update state as Error occurred | ||||||
| _signInUiState.value = SignInUi.Error(result) | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * sign in user with google | ||||||
| * */ | ||||||
| // Issue 6: No error handling for exceptions | ||||||
| suspend fun signInUserWithGoogle(credential: Credential){ | ||||||
| viewModelScope.launch { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code inside this |
||||||
| // update state as loading | ||||||
| _signInUiState.value = SignInUi.Loading | ||||||
|
|
||||||
| signInUseCase( | ||||||
| credential = credential, | ||||||
| signInMethod = UserConstants.SignUpMethods.GOOGLE, | ||||||
| onResultCallback = { | ||||||
| result, _ -> | ||||||
|
|
||||||
| // observe changes and send to ui | ||||||
| // update state as Error occurred | ||||||
| _signInUiState.value = SignInUi.Error(result) | ||||||
|
|
||||||
| }, | ||||||
| onGoogleSignInCallBack = { | ||||||
| user, _ -> | ||||||
| viewModelScope.launch { | ||||||
|
|
||||||
| // save user info to db | ||||||
| // Issue 7: Force unwrapping with !! could cause crash | ||||||
| saveUser( | ||||||
| userId = user?.uid.toString(), | ||||||
| userId = user!!.uid.toString(), | ||||||
| user = User( | ||||||
| id = "", | ||||||
| firstName = user?.displayName.toString(), | ||||||
| email = user?.email.toString() | ||||||
| firstName = user.displayName!!, | ||||||
| email = user.email!! | ||||||
| ) | ||||||
| ) | ||||||
|
|
||||||
| // observe changes and send to ui | ||||||
| // update state as sign up successful | ||||||
| _signInUiState.value = SignInUi.Success | ||||||
|
Comment on lines
123
to
132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the not-null assertion operator ( user?.let { firebaseUser ->
saveUser(
userId = firebaseUser.uid,
user = User(
id = "",
firstName = firebaseUser.displayName ?: "",
email = firebaseUser.email ?: ""
)
)
_signInUiState.value = SignInUi.Success
} ?: run {
_signInUiState.value = SignInUi.Error("Failed to retrieve user information.")
} |
||||||
| } | ||||||
| } | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * save user into db | ||||||
| * | ||||||
| * @param userId expects user id generated by firebase on sign in successful | ||||||
| * @param user expects user new record template | ||||||
| * */ | ||||||
| // Issue 8: No error handling for database operations | ||||||
| private suspend fun saveUser(userId: String, user: User){ | ||||||
| withContext(Dispatchers.IO){ | ||||||
| createUserUseCase( | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This call to |
||||||
|
|
@@ -182,6 +148,5 @@ class SignInViewModel( | |||||
| signUpMethod = UserConstants.SignUpMethods.GOOGLE | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposing
MutableStateFlowas a publicvarbreaks encapsulation. It allows any part of your app to not only change the state directly but also reassign theStateFlowobject itself, leading to unpredictable behavior and bugs that are hard to trace. The backing property should beprivate val. Additionally, the public propertysignInUiStateshould expose an immutableStateFlowto prevent external modifications.