feat: Shoow validation errors properly
This commit is contained in:
parent
724d3486a9
commit
ddf4468a7f
|
@ -3,12 +3,21 @@ package com.p_vacho.neat_calendar
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.p_vacho.neat_calendar.auth.RegisterResult
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RegisterActivity : AppCompatActivity() {
|
||||
private val authRepository by lazy { (application as MyApplication).authRepository }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
@ -33,7 +42,51 @@ class RegisterActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun handleRegisterSubmit() {
|
||||
TODO("Not yet implemented")
|
||||
val usernameInput = findViewById<EditText>(R.id.usernameInput)
|
||||
val passwordInput = findViewById<EditText>(R.id.passwordInput)
|
||||
val emailInput = findViewById<EditText>(R.id.emailInput)
|
||||
|
||||
val username = usernameInput.text.toString().trim()
|
||||
val password = passwordInput.text.toString().trim()
|
||||
val email = emailInput.text.toString().trim()
|
||||
|
||||
|
||||
if (username.isEmpty() || password.isEmpty() || email.isEmpty()) {
|
||||
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
performRegister(username, password, email)
|
||||
}
|
||||
}
|
||||
|
||||
private fun performRegister(username: String, password: String, email: String) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val result = authRepository.register(username, password, email)
|
||||
when (result) {
|
||||
is RegisterResult.Success -> {
|
||||
Toast.makeText(this@RegisterActivity, "User registered, you may now log in", Toast.LENGTH_SHORT).show()
|
||||
navigateToLoginActivity()
|
||||
}
|
||||
is RegisterResult.ValidationError -> {
|
||||
val errorMessages = result.errorData.detail.joinToString("\n") { detail ->
|
||||
val field = detail.loc.lastOrNull() ?: "Unknown field"
|
||||
"$field: ${detail.msg}"
|
||||
}
|
||||
|
||||
// Show the errors in a dialog
|
||||
AlertDialog.Builder(this@RegisterActivity)
|
||||
.setTitle("Validation Errors")
|
||||
.setMessage(errorMessages)
|
||||
.setPositiveButton("OK", null)
|
||||
.show()
|
||||
}
|
||||
is RegisterResult.UserAlreadyExists -> {
|
||||
Toast.makeText(this@RegisterActivity, "User already exists. Please log in.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
is RegisterResult.UnknownError -> {
|
||||
Toast.makeText(this@RegisterActivity, "Registration failed. Try again later.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToLoginActivity() {
|
||||
|
|
|
@ -32,6 +32,8 @@ object RetrofitClient {
|
|||
fun initialize(context: Context) {
|
||||
val authInterceptor = AuthInterceptor(context)
|
||||
|
||||
// TODO: Add another interceptor that makes sure the api remains reachable
|
||||
// (or modify AuthInterceptor & probably also rename it)
|
||||
val okHttpClient = OkHttpClient.Builder()
|
||||
.addInterceptor(authInterceptor) // Adds the AuthInterceptor
|
||||
.build()
|
||||
|
|
|
@ -7,6 +7,15 @@ import com.p_vacho.neat_calendar.api.models.RegisterResponse
|
|||
import com.p_vacho.neat_calendar.api.models.SessionResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import com.google.gson.Gson
|
||||
|
||||
sealed class RegisterResult {
|
||||
data class Success(val response: RegisterResponse) : RegisterResult()
|
||||
data class ValidationError(val errorData: com.p_vacho.neat_calendar.auth.ValidationError) : RegisterResult()
|
||||
object UserAlreadyExists : RegisterResult()
|
||||
data class UnknownError(val exception: Throwable) : RegisterResult()
|
||||
}
|
||||
|
||||
class AuthRepository(private val tokenManager: TokenManager) {
|
||||
|
||||
|
@ -27,19 +36,37 @@ class AuthRepository(private val tokenManager: TokenManager) {
|
|||
}
|
||||
|
||||
// Register user (doesn't perform a login)
|
||||
suspend fun register(username: String, password: String, email: String): RegisterResponse? {
|
||||
suspend fun register(username: String, password: String, email: String): RegisterResult {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val response = RetrofitClient.authService.register(RegisterRequest(username, password, email))
|
||||
Log.i("API", "Register request succesful")
|
||||
response
|
||||
Log.i("API", "Register request successful")
|
||||
RegisterResult.Success(response)
|
||||
} catch (e: HttpException) {
|
||||
when (e.code()) {
|
||||
422 -> {
|
||||
val errorBody = e.response()?.errorBody()?.string()
|
||||
val validationError = Gson().fromJson(errorBody, ValidationError::class.java)
|
||||
Log.w("API", "Validation error: $validationError")
|
||||
RegisterResult.ValidationError(validationError)
|
||||
}
|
||||
409 -> {
|
||||
Log.w("API", "User already exists")
|
||||
RegisterResult.UserAlreadyExists
|
||||
}
|
||||
else -> {
|
||||
Log.w("API", "Register request failed (${e.message()})", e)
|
||||
RegisterResult.UnknownError(e)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w("API", "Register request failed", e)
|
||||
null
|
||||
Log.e("API", "Unexpected error during registration", e)
|
||||
RegisterResult.UnknownError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate the access token by fetching session info
|
||||
suspend fun validateAccessToken(): SessionResponse? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.p_vacho.neat_calendar.auth
|
||||
|
||||
/**
|
||||
* Error data from HTTP 422 (Unprocessable entity) responses.
|
||||
*/
|
||||
data class ValidationError(
|
||||
val detail: List<ValidationErrorDetail>
|
||||
)
|
||||
|
||||
data class ValidationErrorDetail(
|
||||
val type: String,
|
||||
val loc: List<String>,
|
||||
val msg: String,
|
||||
val input: String?,
|
||||
val ctx: ValidationErrorContext?
|
||||
)
|
||||
|
||||
data class ValidationErrorContext(
|
||||
val reason: String
|
||||
)
|
Loading…
Reference in a new issue