Created
March 10, 2025 01:04
-
-
Save sajjadyousefnia/d7bb08741211f559b80f219aa369fab8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.divadventure.divadventure.ui | |
import androidx.compose.foundation.BorderStroke | |
import androidx.compose.foundation.Image | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.clickable | |
import androidx.compose.foundation.layout.Box | |
import androidx.compose.foundation.layout.Column | |
import androidx.compose.foundation.layout.IntrinsicSize | |
import androidx.compose.foundation.layout.PaddingValues | |
import androidx.compose.foundation.layout.Row | |
import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.fillMaxWidth | |
import androidx.compose.foundation.layout.height | |
import androidx.compose.foundation.layout.padding | |
import androidx.compose.foundation.shape.RoundedCornerShape | |
import androidx.compose.foundation.text.KeyboardOptions | |
import androidx.compose.material3.Card | |
import androidx.compose.material3.CardDefaults | |
import androidx.compose.material3.Text | |
import androidx.compose.material3.TextField | |
import androidx.compose.material3.TextFieldDefaults | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.LaunchedEffect | |
import androidx.compose.runtime.collectAsState | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.setValue | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.graphics.vector.ImageVector | |
import androidx.compose.ui.res.vectorResource | |
import androidx.compose.ui.text.TextStyle | |
import androidx.compose.ui.text.font.FontWeight | |
import androidx.compose.ui.text.input.KeyboardType | |
import androidx.compose.ui.text.input.PasswordVisualTransformation | |
import androidx.compose.ui.text.style.TextAlign | |
import androidx.compose.ui.unit.dp | |
import androidx.compose.ui.unit.sp | |
import com.divadventure.divadventure.Intent.AuthIntent | |
import com.divadventure.divadventure.R | |
import com.divadventure.divadventure.Screen | |
import com.divadventure.divadventure.ViewModel.AuthViewModel | |
import com.divadventure.divadventure.ViewModel.NavigationViewModel | |
import timber.log.Timber | |
@Composable | |
fun SignUpScreen( | |
padding: PaddingValues, | |
navigationViewModel: NavigationViewModel, | |
viewModel: AuthViewModel | |
) { | |
var showError by remember { mutableStateOf(false) } | |
val state by viewModel.state.collectAsState() | |
Timber.d("state: $state") | |
val email = remember { | |
mutableStateOf("") | |
} | |
val password = remember { | |
mutableStateOf("") | |
} | |
val passwordConfirmation = remember { | |
mutableStateOf("") | |
} | |
var enterButtonColor by remember { | |
mutableStateOf( | |
Color(0xffBFBFBF) | |
) | |
} | |
LaunchedEffect(state.error) { | |
if (state.error != null) { | |
Timber.e("Error: ${state.error}") | |
showError = true | |
} else { | |
Timber.e("Error: ${state.error}") | |
showError = false | |
} | |
} | |
if (state.isLogged) { | |
Timber.d("Navigating to HomeScreen") | |
navigationViewModel.navigateTo( | |
Screen.HomeScreen | |
) | |
} | |
when (state.rawDataAccepted) { | |
(true) -> { | |
if (enterButtonColor != Color(0xff30D158)) { | |
enterButtonColor = Color(0xff30D158) | |
} | |
} | |
(false) -> { | |
if (enterButtonColor != Color(0xffBFBFBF)) { | |
enterButtonColor = Color(0xffBFBFBF) | |
} | |
} | |
} | |
Box( | |
modifier = Modifier.background(Color.White) | |
) { | |
Column( | |
modifier = Modifier | |
.fillMaxSize() | |
.padding(50.dp, 0.dp), | |
horizontalAlignment = Alignment.CenterHorizontally | |
) { | |
Text( | |
modifier = Modifier | |
.padding(0.dp, 70.dp) | |
.weight(0.4f, false) | |
.fillMaxWidth(), | |
text = "DivAdventure", | |
style = TextStyle( | |
textAlign = TextAlign.Center, | |
fontWeight = FontWeight.Bold, | |
fontSize = 32.sp, | |
color = Color(0xff30D158), | |
) | |
) | |
TextField( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(0.dp, 20.dp), | |
value = email.value, | |
onValueChange = { | |
email.value = it | |
viewModel.sendIntent( | |
AuthIntent.OnEmailChanged( | |
email = email.value | |
) | |
) | |
}, | |
textStyle = TextStyle( | |
color = Color.Black, fontSize = 17.sp | |
), | |
placeholder = { | |
Text( | |
text = "Your Email", style = TextStyle( | |
fontSize = 17.sp, color = Color(0xff3C3C4399) | |
) | |
) | |
}, | |
colors = TextFieldDefaults.colors( | |
focusedIndicatorColor = Color.Black, | |
unfocusedIndicatorColor = Color(0xff3C3C4399), | |
focusedContainerColor = Color.Transparent, | |
disabledContainerColor = Color.Transparent, | |
unfocusedContainerColor = Color.Transparent | |
), | |
label = { | |
Row { | |
Text("Email", color = Color.Black) | |
Text("*", style = TextStyle(color = Color.Red)) | |
} | |
}, | |
keyboardOptions = KeyboardOptions( | |
keyboardType = KeyboardType.Email | |
), | |
) | |
TextField( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(0.dp, 0.dp), | |
value = password.value, | |
onValueChange = { | |
password.value = it | |
viewModel.sendIntent( | |
AuthIntent.OnPasswordChanged( | |
password = password.value | |
) | |
) | |
}, | |
textStyle = TextStyle( | |
color = Color.Black, fontSize = 17.sp | |
), | |
placeholder = { | |
Text( | |
text = "Your Password", style = TextStyle( | |
fontSize = 17.sp, color = Color(0xff3C3C4399) | |
) | |
) | |
}, | |
colors = TextFieldDefaults.colors( | |
focusedIndicatorColor = Color.Black, | |
unfocusedIndicatorColor = Color(0xff3C3C4399), | |
focusedContainerColor = Color.Transparent, | |
disabledContainerColor = Color.Transparent, | |
unfocusedContainerColor = Color.Transparent | |
), | |
label = { | |
Row { | |
Text("Password", color = Color.Black) | |
Text("*", style = TextStyle(color = Color.Red)) | |
} | |
}, | |
keyboardOptions = KeyboardOptions( | |
keyboardType = KeyboardType.Password | |
), | |
visualTransformation = PasswordVisualTransformation(), | |
) | |
TextField( | |
modifier = Modifier | |
.height(50.dp) | |
.height(IntrinsicSize.Min) | |
.padding(0.dp, 0.dp), | |
value = passwordConfirmation.value, | |
onValueChange = { | |
passwordConfirmation.value = it | |
viewModel.sendIntent( | |
AuthIntent.OnPasswordConfirmationChanged( | |
passwordConfirmation = passwordConfirmation.value | |
) | |
) | |
}, | |
textStyle = TextStyle( | |
color = Color.Black, fontSize = 17.sp | |
), | |
placeholder = { | |
Text( | |
modifier = Modifier.height(IntrinsicSize.Min), | |
text = "Confirm Password", | |
style = TextStyle( | |
fontSize = 17.sp, color = Color(0xff3C3C4399) | |
) | |
) | |
}, | |
visualTransformation = PasswordVisualTransformation(), | |
colors = TextFieldDefaults.colors( | |
focusedIndicatorColor = Color.Black, | |
unfocusedIndicatorColor = Color(0xff3C3C4399), | |
focusedContainerColor = Color.Transparent, | |
disabledContainerColor = Color.Transparent, | |
unfocusedContainerColor = Color.Transparent | |
), | |
keyboardOptions = KeyboardOptions( | |
keyboardType = KeyboardType.Password | |
), | |
) | |
Card( | |
modifier = Modifier | |
.clickable { | |
viewModel.sendIntent( | |
AuthIntent.SignUp( | |
email.value, | |
password.value, | |
passwordConfirmation.value | |
) | |
) | |
} | |
.fillMaxWidth() | |
.padding(0.dp, 20.dp) | |
.height(50.dp), | |
colors = CardDefaults.cardColors( | |
containerColor = enterButtonColor, | |
disabledContainerColor = enterButtonColor, | |
contentColor = Color.White, | |
disabledContentColor = Color.White | |
), | |
shape = RoundedCornerShape(4.dp), | |
) { | |
Box(modifier = Modifier.fillMaxSize()) { | |
Text( | |
modifier = Modifier.align(Alignment.Center), style = TextStyle( | |
fontSize = 17.sp, | |
fontWeight = FontWeight.Bold, | |
textAlign = TextAlign.Center, | |
), text = "Create Account" | |
) | |
} | |
} | |
Card( | |
modifier = Modifier | |
.clickable { | |
viewModel.sendIntent(AuthIntent.SignUpWithGoogle) | |
} | |
.fillMaxWidth() | |
.padding(0.dp, 0.dp) | |
.height(50.dp), | |
border = BorderStroke(1.dp, Color(0xffBFBFBF)), | |
colors = CardDefaults.cardColors( | |
containerColor = Color.White, | |
disabledContainerColor = Color.White, | |
contentColor = Color.White, | |
disabledContentColor = Color.White | |
), | |
shape = RoundedCornerShape(4.dp), | |
) { | |
Box(modifier = Modifier.fillMaxSize()) { | |
Row( | |
modifier = Modifier.align(Alignment.Center), | |
verticalAlignment = Alignment.CenterVertically | |
) { | |
Image( | |
modifier = Modifier.padding(0.dp, 0.dp, 10.dp, 0.dp), | |
imageVector = ImageVector.vectorResource(R.drawable.ic_google), | |
contentDescription = "google", | |
) | |
Text( | |
modifier = Modifier.padding( | |
10.dp, 0.dp, 0.dp, 0.dp | |
), style = TextStyle( | |
fontSize = 17.sp, | |
fontWeight = FontWeight.Bold, | |
textAlign = TextAlign.Center, | |
color = Color(0x36000000) | |
), text = "Sign Up with Google" | |
) | |
} | |
} | |
} | |
Card( | |
modifier = Modifier | |
.clickable { | |
} | |
.fillMaxWidth() | |
.padding(0.dp, 20.dp) | |
.height(50.dp), | |
colors = CardDefaults.cardColors( | |
containerColor = Color(0xff2553B4), | |
disabledContainerColor = Color(0xff2553B4), | |
contentColor = Color.White, | |
disabledContentColor = Color.White | |
), | |
shape = RoundedCornerShape(4.dp), | |
) { | |
Box(modifier = Modifier.fillMaxSize()) { | |
Row( | |
modifier = Modifier.align(Alignment.Center), | |
verticalAlignment = Alignment.CenterVertically | |
) { | |
Image( | |
modifier = Modifier.padding(0.dp, 0.dp, 10.dp, 0.dp), | |
imageVector = ImageVector.vectorResource(R.drawable.ic_facebook), | |
contentDescription = "facebook", | |
) | |
Text( | |
modifier = Modifier.padding( | |
10.dp, 0.dp, 0.dp, 0.dp | |
), style = TextStyle( | |
fontSize = 17.sp, | |
fontWeight = FontWeight.Bold, | |
textAlign = TextAlign.Center, | |
color = Color.White | |
), text = "Sign Up with Facebook" | |
) | |
} | |
} | |
} | |
} | |
} | |
} | |
// another file | |
package com.divadventure.divadventure.domain | |
import com.divadventure.divadventure.ResultWrapper | |
import com.divadventure.divadventure.data.AuthRepository | |
import com.divadventure.divadventure.data.Model.SignupRequest | |
import kotlinx.coroutines.CoroutineScope | |
import kotlinx.coroutines.Dispatchers | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.flow | |
import kotlinx.coroutines.launch | |
import timber.log.Timber | |
import javax.inject.Inject | |
class AuthUseCases @Inject constructor( | |
// val signInUseCase: SignInUseCase, | |
val signUpUseCase: SignUpUseCase, | |
// val forgotPasswordUseCase: ForgotPasswordUseCase, | |
// val isLoggedInUseCase: IsLoggedInUseCase, | |
// val verifyEmailUseCase: VerifyEmailUseCase, | |
// val getCurrentUserUseCase: GetCurrentUserUseCase, | |
// val logoutUseCase: LogoutUseCase | |
) | |
/* | |
class SignInUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(email: String, password: String): ResultWrapper<User> { | |
// Validate email and password if needed | |
if (!isValidEmail(email)) { | |
return Result.Error(Exception("Invalid email format")) | |
} | |
if (password.length < 6) { | |
return Result.Error(Exception("Password must be at least 6 characters long")) | |
} | |
return try { | |
authRepository.signIn(email, password) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
*/ | |
class SignUpUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(signupRequest: SignupRequest): Flow<ResultWrapper<SignupRequest>> = | |
flow { | |
// Validate email and password if needed | |
emit(ResultWrapper.Loading) | |
if (!isValidEmail(signupRequest.email)) { | |
Timber.d("Invalid email format") | |
ResultWrapper.Error("Invalid email format") | |
return@flow | |
} | |
if (signupRequest.password.length < 8) { | |
Timber.d("Password must be at least 6 characters long") | |
emit(ResultWrapper.Error("Password must be at least 6 characters long")) | |
return@flow | |
} | |
if (signupRequest.password != signupRequest.password_confirmation) { | |
Timber.d("Passwords do not match") | |
emit(ResultWrapper.Error(("Passwords do not match"))) | |
return@flow | |
} | |
try { | |
CoroutineScope( | |
Dispatchers.IO | |
).launch { | |
authRepository.signup(signupRequest) | |
} | |
// Emit Success state | |
// emit(ResultWrapper.Success(signupRequest)) | |
} catch (e: Exception) { | |
ResultWrapper.Error("failed to sign up") | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
/* | |
class ForgotPasswordUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(email: String): Result<Unit> { | |
// Validate email if needed | |
if (!isValidEmail(email)) { | |
return Result.Error(Exception("Invalid email format")) | |
} | |
return try { | |
authRepository.forgotPassword(email) | |
Result.Success(Unit) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
*/ | |
/* | |
class IsLoggedInUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Boolean { | |
// Check if the user is logged in | |
return authRepository.isLoggedIn() | |
} | |
}*/ | |
/*class VerifyEmailUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Result<Unit> { | |
// Verify the user email | |
return try { | |
authRepository.verifyEmail() | |
Result.Success(Unit) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
}*/ | |
/* | |
class GetCurrentUserUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Result<User?> { | |
// Get the user | |
return try { | |
val user = authRepository.getCurrentUser() | |
Result.Success(user) | |
} catch (e:*/ | |
// another file | |
package com.divadventure.divadventure.domain | |
import com.divadventure.divadventure.ResultWrapper | |
import com.divadventure.divadventure.data.AuthRepository | |
import com.divadventure.divadventure.data.Model.SignupRequest | |
import kotlinx.coroutines.CoroutineScope | |
import kotlinx.coroutines.Dispatchers | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.flow | |
import kotlinx.coroutines.launch | |
import timber.log.Timber | |
import javax.inject.Inject | |
class AuthUseCases @Inject constructor( | |
// val signInUseCase: SignInUseCase, | |
val signUpUseCase: SignUpUseCase, | |
// val forgotPasswordUseCase: ForgotPasswordUseCase, | |
// val isLoggedInUseCase: IsLoggedInUseCase, | |
// val verifyEmailUseCase: VerifyEmailUseCase, | |
// val getCurrentUserUseCase: GetCurrentUserUseCase, | |
// val logoutUseCase: LogoutUseCase | |
) | |
/* | |
class SignInUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(email: String, password: String): ResultWrapper<User> { | |
// Validate email and password if needed | |
if (!isValidEmail(email)) { | |
return Result.Error(Exception("Invalid email format")) | |
} | |
if (password.length < 6) { | |
return Result.Error(Exception("Password must be at least 6 characters long")) | |
} | |
return try { | |
authRepository.signIn(email, password) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
*/ | |
class SignUpUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(signupRequest: SignupRequest): Flow<ResultWrapper<SignupRequest>> = | |
flow { | |
// Validate email and password if needed | |
emit(ResultWrapper.Loading) | |
if (!isValidEmail(signupRequest.email)) { | |
Timber.d("Invalid email format") | |
ResultWrapper.Error("Invalid email format") | |
return@flow | |
} | |
if (signupRequest.password.length < 8) { | |
Timber.d("Password must be at least 6 characters long") | |
emit(ResultWrapper.Error("Password must be at least 6 characters long")) | |
return@flow | |
} | |
if (signupRequest.password != signupRequest.password_confirmation) { | |
Timber.d("Passwords do not match") | |
emit(ResultWrapper.Error(("Passwords do not match"))) | |
return@flow | |
} | |
try { | |
CoroutineScope( | |
Dispatchers.IO | |
).launch { | |
authRepository.signup(signupRequest) | |
} | |
// Emit Success state | |
// emit(ResultWrapper.Success(signupRequest)) | |
} catch (e: Exception) { | |
ResultWrapper.Error("failed to sign up") | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
/* | |
class ForgotPasswordUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(email: String): Result<Unit> { | |
// Validate email if needed | |
if (!isValidEmail(email)) { | |
return Result.Error(Exception("Invalid email format")) | |
} | |
return try { | |
authRepository.forgotPassword(email) | |
Result.Success(Unit) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
private fun isValidEmail(email: String): Boolean { | |
// You can add a better email validation logic if needed | |
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() | |
} | |
} | |
*/ | |
/* | |
class IsLoggedInUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Boolean { | |
// Check if the user is logged in | |
return authRepository.isLoggedIn() | |
} | |
}*/ | |
/*class VerifyEmailUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Result<Unit> { | |
// Verify the user email | |
return try { | |
authRepository.verifyEmail() | |
Result.Success(Unit) | |
} catch (e: Exception) { | |
Result.Error(e) | |
} | |
} | |
}*/ | |
/* | |
class GetCurrentUserUseCase @Inject constructor(private val authRepository: AuthRepository) { | |
suspend operator fun invoke(): Result<User?> { | |
// Get the user | |
return try { | |
val user = authRepository.getCurrentUser() | |
Result.Success(user) | |
} catch (e:*/ | |
// another file | |
package com.divadventure.divadventure.data.Model | |
import com.google.gson.annotations.SerializedName | |
data class SignupRequest( | |
val email: String, | |
val password: String, | |
val password_confirmation: String, | |
val platform: String = "Android" // Default value for the platform | |
) | |
data class SignUpResponse( | |
@SerializedName("data") val data: UserData | |
) | |
data class UserData( | |
@SerializedName("id") val id: String, | |
@SerializedName("avatar") val avatar: String?, | |
@SerializedName("first_name") val firstName: String?, | |
@SerializedName("last_name") val lastName: String?, | |
@SerializedName("username") val username: String?, | |
@SerializedName("email") val email: String, | |
@SerializedName("birthdate") val birthdate: String?, | |
@SerializedName("bio") val bio: String?, | |
@SerializedName("location") val location: String?, | |
@SerializedName("privacy_settings") val privacySettings: PrivacySettings, | |
@SerializedName("current_access_token") val currentAccessToken: CurrentAccessToken | |
) | |
data class PrivacySettings( | |
@SerializedName("bio") val bio: String, | |
@SerializedName("birthdate") val birthdate: String, | |
@SerializedName("adventures") val adventures: String, | |
@SerializedName("friends") val friends: String, | |
@SerializedName("location") val location: String | |
) | |
data class CurrentAccessToken( | |
@SerializedName("platform") val platform: String, | |
@SerializedName("token") val token: String, | |
@SerializedName("refresh_token") val refreshToken: String, | |
@SerializedName("expires_at") val expiresAt: String, | |
@SerializedName("refresh_token_expires_at") val refreshTokenExpiresAt: String | |
) | |
// another file | |
package com.divadventure.divadventure.data | |
import com.divadventure.divadventure.ResultWrapper | |
import com.divadventure.divadventure.data.Model.SignUpResponse | |
import com.divadventure.divadventure.data.Model.SignupRequest | |
import com.google.gson.Gson | |
import com.google.gson.JsonParseException | |
import okio.IOException | |
import retrofit2.HttpException | |
import timber.log.Timber | |
import javax.inject.Inject | |
class AuthRepository @Inject constructor( | |
private val authService: AuthService | |
) { | |
suspend fun signup(request: SignupRequest): ResultWrapper<SignUpResponse> { | |
Timber.d("Starting signup request: $request") | |
return try { | |
val response = authService.signup(request).execute() | |
Timber.d("Signup response: $response") | |
if (response.isSuccessful) { | |
/* | |
val responseBody = response.raw().body?.toString() | |
*/ | |
/* | |
val authResponse = Gson().fromJson( | |
responseBody, SignUpResponse::class.java | |
) | |
*/ | |
val authResponse = response.body() as SignUpResponse? | |
// Extract the authResponse from response.body() | |
Timber.d("Signup successful: $authResponse") | |
if (authResponse != null) { | |
ResultWrapper.Success(authResponse) | |
} else { | |
Timber.w("Response body is null") | |
ResultWrapper.Error("Response body is null") | |
} | |
} else { | |
val errorBody = response.errorBody()?.string() | |
val message = if (errorBody != null) { | |
try { | |
val errorJson = Gson().fromJson(errorBody, Map::class.java) | |
Timber.w("Signup failed with error body: $errorJson") | |
errorJson["message"].toString() // Extract the message field | |
} catch (e: JsonParseException) { | |
Timber.e(e, "Invalid error response") | |
"Invalid error response: ${e.message}" | |
} | |
} else { | |
Timber.w("Signup failed with code: ${response.code()}") | |
"Signup failed with code: ${response.code()}" | |
} | |
ResultWrapper.Error(message) | |
} | |
} catch (httpException: HttpException) { | |
Timber.e(httpException, "HTTP exception occurred") | |
// Handle HTTP exceptions (4xx or 5xx status codes) | |
val message = httpException.message() ?: "HTTP Error" | |
ResultWrapper.Error("HTTP error: $message") | |
} catch (ioException: IOException) { | |
Timber.e(ioException, "Network exception occurred") | |
// Handle network issues (e.g., no internet connection) | |
ResultWrapper.Error("Network error: ${ioException.message}") | |
} catch (jsonParseException: JsonParseException) { | |
Timber.e(jsonParseException, "JSON parsing exception occurred") | |
// Handle JSON parsing issues | |
ResultWrapper.Error("JSON parsing error: ${jsonParseException.message}") | |
} catch (e: Exception) { | |
Timber.e(e, "An unexpected exception occurred") | |
// Handle other unexpected exceptions | |
ResultWrapper.Error("An unexpected error occurred: ${e.message}") | |
} | |
} | |
// Add other repository methods here... | |
} | |
// another file | |
package com.divadventure.divadventure.data | |
import com.divadventure.divadventure.data.Model.SignUpResponse | |
import com.divadventure.divadventure.data.Model.SignupRequest | |
import retrofit2.Call | |
import retrofit2.http.Body | |
import retrofit2.http.POST | |
interface AuthService { | |
@POST("auth/sign-up") | |
fun signup(@Body request: SignupRequest): Call<SignUpResponse> | |
} | |
// another file | |
package com.divadventure.divadventure.ViewModel | |
import androidx.lifecycle.viewModelScope | |
import com.divadventure.divadventure.BaseViewModel | |
import com.divadventure.divadventure.Intent.AuthIntent | |
import com.divadventure.divadventure.ResultWrapper | |
import com.divadventure.divadventure.State.AuthState | |
import com.divadventure.divadventure.data.AuthRepository | |
import com.divadventure.divadventure.data.Model.SignupRequest | |
import com.divadventure.divadventure.domain.AuthUseCases | |
import dagger.hilt.android.lifecycle.HiltViewModel | |
import kotlinx.coroutines.flow.collectLatest | |
import kotlinx.coroutines.launch | |
import timber.log.Timber | |
import javax.inject.Inject | |
@HiltViewModel | |
class AuthViewModel @Inject constructor( | |
private val authUseCases: AuthUseCases, private val authRepository: AuthRepository | |
) : BaseViewModel<AuthIntent, AuthState>(AuthState()) { | |
override suspend fun handleIntent(intent: AuthIntent) { | |
when (intent) { | |
is AuthIntent.SignUp -> { | |
updateState(state.value.copy(isLoading = true)) | |
authUseCases.signUpUseCase( | |
SignupRequest( | |
email = intent.email, | |
password = intent.password, | |
password_confirmation = intent.password | |
) | |
).collectLatest { result -> | |
when (result) { | |
is ResultWrapper.Success -> { | |
viewModelScope.launch { | |
updateState( | |
state.value.copy( | |
isLoading = false, | |
error = null, | |
isLogged = true | |
) | |
) | |
Timber.d("Signup successful: ${result.data}") | |
} | |
} | |
is ResultWrapper.Error -> { | |
viewModelScope.launch { | |
Timber.d("Error: ${result.message}") | |
} | |
} | |
ResultWrapper.Loading -> { | |
viewModelScope.launch { | |
Timber.d("Loading...") | |
} | |
} | |
} | |
} | |
} | |
AuthIntent.CheckIfLoggedIn -> {} | |
AuthIntent.ClearError -> {} | |
AuthIntent.ClearNavigation -> {} | |
is AuthIntent.ForgotPassword -> {} | |
AuthIntent.GoToForgotPassword -> {} | |
AuthIntent.GoToLanding -> {} | |
AuthIntent.GoToSignIn -> {} | |
AuthIntent.GoToSignUp -> {} | |
AuthIntent.NavigateToEmailVerification -> {} | |
AuthIntent.NavigateToForgotPassword -> {} | |
AuthIntent.NavigateToLandingPage -> {} | |
AuthIntent.NavigateToMain -> {} | |
AuthIntent.NavigateToSignIn -> {} | |
AuthIntent.NavigateToSignUp -> {} | |
is AuthIntent.SignIn -> {} | |
AuthIntent.SignUpWithGoogle -> { | |
updateState( | |
state.value.copy( | |
isLogged = true | |
) | |
) | |
Timber.d("Signing up with Google...") | |
} | |
is AuthIntent.OnEmailChanged -> { | |
updateState( | |
state.value.copy( | |
email = intent.email | |
) | |
) | |
checkSignupCardColor() | |
} | |
is AuthIntent.OnPasswordChanged -> { | |
updateState( | |
state.value.copy( | |
password = intent.password | |
) | |
) | |
checkSignupCardColor() | |
} | |
is AuthIntent.OnPasswordConfirmationChanged -> { | |
updateState( | |
state.value.copy( | |
passwordConfirmation = intent.passwordConfirmation | |
) | |
) | |
checkSignupCardColor() | |
} | |
} | |
} | |
private fun checkSignupCardColor() { | |
if (state.value.email.isNotEmpty() && state.value.password.isNotEmpty() && | |
isEmailValid(state.value.email) && isAtleast8Characters(state.value.password) && | |
passwordsMatch( | |
password = state.value.password, | |
passwordConfirmation = state.value.passwordConfirmation | |
) | |
) { | |
updateState( | |
state.value.copy( | |
rawDataAccepted = true | |
) | |
) | |
} else { | |
updateState( | |
state.value.copy( | |
rawDataAccepted = false | |
) | |
) | |
} | |
} | |
private fun passwordsMatch(password: String, passwordConfirmation: String): Boolean { | |
return password == passwordConfirmation | |
} | |
private fun isAtleast8Characters(password: String): Boolean { | |
return password.length >= 8 | |
} | |
private fun isEmailValid(email: String): Boolean { | |
val emailRegex = """^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$""".toRegex() | |
return emailRegex.matches(email) | |
} | |
} | |
package com.divadventure.divadventure | |
sealed class ResultWrapper<out T> { | |
data class Success<out T>(val data: T) : ResultWrapper<T>() | |
data class Error(val message: String) : ResultWrapper<Nothing>() | |
object Loading : ResultWrapper<Nothing>() | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment