Improve session handling and UI state updates
Refactored JmapApi to ensure session is initialized before API calls, reducing potential errors. Updated LoginViewModel to remove all whitespace from tokens before login. MaskedEmailDetailViewModel now consistently resets isUpdating flag on data load and error, improving UI state accuracy. Added ProGuard rules to suppress warnings for Google Error Prone annotations.
This commit is contained in:
parent
5eee3a4fed
commit
d6a86a1a69
4 changed files with 26 additions and 5 deletions
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
|
|
@ -37,3 +37,9 @@
|
||||||
# OkHttp
|
# OkHttp
|
||||||
-dontwarn okhttp3.**
|
-dontwarn okhttp3.**
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
|
|
||||||
|
# Google Tink / Error Prone annotations
|
||||||
|
-dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue
|
||||||
|
-dontwarn com.google.errorprone.annotations.CheckReturnValue
|
||||||
|
-dontwarn com.google.errorprone.annotations.Immutable
|
||||||
|
-dontwarn com.google.errorprone.annotations.RestrictedApi
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,14 @@ class JmapApi @Inject constructor(
|
||||||
|
|
||||||
fun getApiUrl(): String = cachedSession?.apiUrl ?: JmapService.FASTMAIL_API_URL
|
fun getApiUrl(): String = cachedSession?.apiUrl ?: JmapService.FASTMAIL_API_URL
|
||||||
|
|
||||||
|
private suspend fun ensureSession(token: String): String {
|
||||||
|
cachedAccountId?.let { return it }
|
||||||
|
getSession(token).getOrThrow()
|
||||||
|
return cachedAccountId ?: throw IllegalStateException("Failed to get account ID from session")
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getMaskedEmails(token: String): Result<List<MaskedEmailDto>> = runCatching {
|
suspend fun getMaskedEmails(token: String): Result<List<MaskedEmailDto>> = runCatching {
|
||||||
val accountId = cachedAccountId ?: throw IllegalStateException("Session not initialized")
|
val accountId = ensureSession(token)
|
||||||
val authHeader = "Bearer $token"
|
val authHeader = "Bearer $token"
|
||||||
|
|
||||||
val methodCall = buildJsonArray {
|
val methodCall = buildJsonArray {
|
||||||
|
|
@ -63,7 +69,7 @@ class JmapApi @Inject constructor(
|
||||||
token: String,
|
token: String,
|
||||||
create: MaskedEmailCreate
|
create: MaskedEmailCreate
|
||||||
): Result<MaskedEmailDto> = runCatching {
|
): Result<MaskedEmailDto> = runCatching {
|
||||||
val accountId = cachedAccountId ?: throw IllegalStateException("Session not initialized")
|
val accountId = ensureSession(token)
|
||||||
val authHeader = "Bearer $token"
|
val authHeader = "Bearer $token"
|
||||||
|
|
||||||
val createObject = buildJsonObject {
|
val createObject = buildJsonObject {
|
||||||
|
|
@ -104,7 +110,7 @@ class JmapApi @Inject constructor(
|
||||||
id: String,
|
id: String,
|
||||||
update: MaskedEmailUpdate
|
update: MaskedEmailUpdate
|
||||||
): Result<Unit> = runCatching {
|
): Result<Unit> = runCatching {
|
||||||
val accountId = cachedAccountId ?: throw IllegalStateException("Session not initialized")
|
val accountId = ensureSession(token)
|
||||||
val authHeader = "Bearer $token"
|
val authHeader = "Bearer $token"
|
||||||
|
|
||||||
val updateObject = buildJsonObject {
|
val updateObject = buildJsonObject {
|
||||||
|
|
@ -140,7 +146,7 @@ class JmapApi @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteMaskedEmail(token: String, id: String): Result<Unit> = runCatching {
|
suspend fun deleteMaskedEmail(token: String, id: String): Result<Unit> = runCatching {
|
||||||
val accountId = cachedAccountId ?: throw IllegalStateException("Session not initialized")
|
val accountId = ensureSession(token)
|
||||||
val authHeader = "Bearer $token"
|
val authHeader = "Bearer $token"
|
||||||
|
|
||||||
val methodCall = buildJsonArray {
|
val methodCall = buildJsonArray {
|
||||||
|
|
@ -227,6 +233,11 @@ class JmapApi @Inject constructor(
|
||||||
setResponse.notUpdated?.get(id)?.let { error ->
|
setResponse.notUpdated?.get(id)?.let { error ->
|
||||||
throw JmapException("Failed to update: ${error.type} - ${error.description}")
|
throw JmapException("Failed to update: ${error.type} - ${error.description}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the update actually succeeded
|
||||||
|
if (setResponse.updated?.containsKey(id) != true) {
|
||||||
|
throw JmapException("Update not confirmed by server - ID not found in updated response")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseSetResponseDestroyed(response: JmapResponse, id: String) {
|
private fun parseSetResponseDestroyed(response: JmapResponse, id: String) {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ class LoginViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun login() {
|
fun login() {
|
||||||
val token = _uiState.value.token.trim()
|
// Remove all whitespace characters (spaces, newlines, tabs) from the token
|
||||||
|
val token = _uiState.value.token.filterNot { it.isWhitespace() }
|
||||||
if (token.isBlank()) {
|
if (token.isBlank()) {
|
||||||
_uiState.update { it.copy(error = "Please enter your API token") }
|
_uiState.update { it.copy(error = "Please enter your API token") }
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class MaskedEmailDetailViewModel @Inject constructor(
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
isUpdating = false,
|
||||||
email = email,
|
email = email,
|
||||||
editedDescription = email.description ?: "",
|
editedDescription = email.description ?: "",
|
||||||
editedForDomain = email.forDomain ?: "",
|
editedForDomain = email.forDomain ?: "",
|
||||||
|
|
@ -62,6 +63,7 @@ class MaskedEmailDetailViewModel @Inject constructor(
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
isUpdating = false,
|
||||||
error = "Email not found"
|
error = "Email not found"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +73,7 @@ class MaskedEmailDetailViewModel @Inject constructor(
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
isUpdating = false,
|
||||||
error = error.message ?: "Failed to load email"
|
error = error.message ?: "Failed to load email"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue