Improve error message handling with user-friendly messages

- Add ErrorMessageParser in Kotlin and Swift to detect network errors
  and technical messages, replacing them with human-readable text
- Update all ViewModels to use ErrorMessageParser.parse() for error display
- Remove redundant error popup from LoginView (error shows inline only)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-11 20:46:43 -06:00
parent 1839bd0e11
commit 258ccf7354
18 changed files with 201 additions and 66 deletions

View File

@@ -9,23 +9,58 @@ import kotlinx.serialization.json.jsonPrimitive
*/
object ErrorMessageParser {
// Network/connection error patterns to detect
private val networkErrorPatterns = listOf(
"Could not connect to the server" to "Unable to connect to the server. Please check your internet connection.",
"NSURLErrorDomain" to "Unable to connect to the server. Please check your internet connection.",
"UnresolvedAddressException" to "Unable to connect to the server. Please check your internet connection.",
"ConnectException" to "Unable to connect to the server. Please check your internet connection.",
"SocketTimeoutException" to "Request timed out. Please try again.",
"TimeoutException" to "Request timed out. Please try again.",
"No address associated" to "Unable to connect to the server. Please check your internet connection.",
"Network is unreachable" to "No internet connection. Please check your network settings.",
"Connection refused" to "Unable to connect to the server. The server may be down.",
"Connection reset" to "Connection was interrupted. Please try again.",
"SSLHandshakeException" to "Secure connection failed. Please try again.",
"CertificateException" to "Secure connection failed. Please try again.",
"UnknownHostException" to "Unable to connect to the server. Please check your internet connection.",
"java.net.SocketException" to "Connection error. Please try again.",
"CFNetwork" to "Unable to connect to the server. Please check your internet connection.",
"kCFStreamError" to "Unable to connect to the server. Please check your internet connection.",
"Code=-1004" to "Unable to connect to the server. Please check your internet connection.",
"Code=-1009" to "No internet connection. Please check your network settings.",
"Code=-1001" to "Request timed out. Please try again."
)
/**
* Parses error messages to extract user-friendly text
* If the error message is JSON, extract relevant error details
* @param rawMessage The raw error message from the API
* Handles network errors, JSON error responses, and raw error messages
* @param rawMessage The raw error message from the API or exception
* @return A user-friendly error message
*/
fun parse(rawMessage: String): String {
val trimmed = rawMessage.trim()
// Check if the message looks like JSON (starts with { or [)
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
// Not JSON, return as-is
return rawMessage
// Check for network/connection errors first (these are technical messages from exceptions)
for ((pattern, friendlyMessage) in networkErrorPatterns) {
if (trimmed.contains(pattern, ignoreCase = true)) {
return friendlyMessage
}
}
// If it's JSON, it's not meant for user display
// Try to parse and extract meaningful error info
// Check if it looks like a technical exception message
if (isTechnicalError(trimmed)) {
return "Something went wrong. Please try again."
}
// Check if the message looks like JSON (starts with { or [)
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
// Not JSON - if it's a short, readable message, return it
// Otherwise return a generic message
return if (isUserFriendly(trimmed)) trimmed else "Something went wrong. Please try again."
}
// If it's JSON, try to parse and extract meaningful error info
return try {
val jsonElement = Json.parseToJsonElement(trimmed)
@@ -51,4 +86,37 @@ object ErrorMessageParser {
"An error occurred. Please try again."
}
}
/**
* Checks if the message looks like a technical/developer error (stack trace, exception, etc)
*/
private fun isTechnicalError(message: String): Boolean {
val technicalIndicators = listOf(
"Exception",
"Error Domain=",
"UserInfo=",
"at com.",
"at org.",
"at java.",
"at kotlin.",
"at io.",
"Caused by:",
"Stack trace:",
".kt:",
".java:",
"0x",
"Code=",
"interface:",
"LocalDataTask"
)
return technicalIndicators.any { message.contains(it, ignoreCase = true) }
}
/**
* Checks if a message is user-friendly (short, no technical jargon)
*/
private fun isUserFriendly(message: String): Boolean {
// If it's short and doesn't contain technical indicators, it's probably user-friendly
return message.length < 200 && !isTechnicalError(message)
}
}