feat: error handeling

This commit is contained in:
2026-06-08 20:12:02 +03:30
parent 34ae6a1b54
commit e73269889f
7 changed files with 57 additions and 21 deletions
@@ -67,15 +67,11 @@ class DrugRepositoryImpl(
val searchResult = parser.parseSearchResultsWithPagination(htmlResponse) val searchResult = parser.parseSearchResultsWithPagination(htmlResponse)
if (searchResult.drugs.isNotEmpty()) {
Result.success(searchResult) Result.success(searchResult)
} else {
Result.failure(Exception("No drugs found for query: '${params.query}'"))
}
} catch (e: IOException) { } catch (e: IOException) {
Result.failure(Exception("Network error: ${e.message}", e)) Result.failure(e)
} catch (e: Exception) { } catch (e: Exception) {
Result.failure(Exception("An error occurred: ${e.message}", e)) Result.failure(e)
} }
} }
} }
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.approagency.pharmacy.domain.model.DrugDetail import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.pharmacy.domain.usecase.DrugDetailYabUseCase import com.approagency.pharmacy.domain.usecase.DrugDetailYabUseCase
import com.approagency.pharmacy.utils.toUserMessage
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -22,7 +23,7 @@ class DrugDetailViewModel(
val result = drugDetailUseCase(detailUrl) val result = drugDetailUseCase(detailUrl)
_detailState.value = when { _detailState.value = when {
result.isSuccess -> DrugDetailYabState.Success(result.getOrNull()!!) result.isSuccess -> DrugDetailYabState.Success(result.getOrNull()!!)
else -> DrugDetailYabState.Error(result.exceptionOrNull()?.message ?: "خطا در دریافت اطلاعات") else -> DrugDetailYabState.Error(result.exceptionOrNull().toUserMessage())
} }
} }
} }
@@ -9,6 +9,8 @@ import com.approagency.pharmacy.domain.model.DrugSearchParams
import com.approagency.pharmacy.domain.usecase.GetDarmanUseCase import com.approagency.pharmacy.domain.usecase.GetDarmanUseCase
import com.approagency.pharmacy.domain.usecase.GetDrugDetailUseCase import com.approagency.pharmacy.domain.usecase.GetDrugDetailUseCase
import com.approagency.pharmacy.domain.usecase.GetDrugSearchUseCase import com.approagency.pharmacy.domain.usecase.GetDrugSearchUseCase
import com.approagency.pharmacy.utils.ErrorMessages
import com.approagency.pharmacy.utils.toUserMessage
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -49,9 +51,9 @@ class HomeViewModel (
) )
} }
}catch (e: Exception) { }catch (e: Exception) {
handleError(e.message) handleError(e.toUserMessage())
}catch (e: Exception) { }catch (e: Exception) {
handleError(e.localizedMessage ?: "Unknown error") handleError(e.toUserMessage())
} }
} }
} }
@@ -73,10 +75,10 @@ class HomeViewModel (
) } ) }
} catch (e: Exception) { } catch (e: Exception) {
println(e.message) println(e.message)
handleError(e.message) handleError(e.toUserMessage())
}catch (e: Exception) { }catch (e: Exception) {
println(e.message) println(e.message)
handleError(e.localizedMessage ?: "Unknown error") handleError(e.toUserMessage())
} }
} }
} }
@@ -103,9 +105,9 @@ class HomeViewModel (
} }
println(result) println(result)
} catch (e: Exception) { } catch (e: Exception) {
handleError(e.message) handleError(e.toUserMessage())
}catch (e: Exception) { }catch (e: Exception) {
handleError(e.localizedMessage ?: "Unknown error") handleError(e.toUserMessage())
} }
} }
} }
@@ -126,7 +128,7 @@ class HomeViewModel (
) , drugDetailState = it.drugDetailState.copy( ) , drugDetailState = it.drugDetailState.copy(
isLoading = false isLoading = false
)) } )) }
_event.emit(HomeEvent.ShowError(message ?: "Error occurred")) _event.emit(HomeEvent.ShowError(message ?: ErrorMessages.GENERIC))
} }
// Clear error when consumed // Clear error when consumed
@@ -8,6 +8,7 @@ import com.approagency.pharmacy.domain.model.TestItem
import com.approagency.pharmacy.domain.usecase.GetTestGroupUseCase import com.approagency.pharmacy.domain.usecase.GetTestGroupUseCase
import com.approagency.pharmacy.domain.usecase.GetTestItemByGroupId import com.approagency.pharmacy.domain.usecase.GetTestItemByGroupId
import com.approagency.pharmacy.domain.usecase.SearchTestsUseCase import com.approagency.pharmacy.domain.usecase.SearchTestsUseCase
import com.approagency.pharmacy.utils.toUserMessage
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -87,7 +88,7 @@ class LabViewModel (
it.copy( it.copy(
isLoading = false, isLoading = false,
searchResults = null, searchResults = null,
error = e.message ?: "خطا در جستجو" error = e.toUserMessage()
) )
} }
} }
@@ -109,7 +110,7 @@ class LabViewModel (
) )
} }
} catch (e:HttpException){ } catch (e:HttpException){
handleError(e.message) handleError(e.toUserMessage())
} }
} }
@@ -132,7 +133,7 @@ class LabViewModel (
) )
} }
} catch (e:HttpException){ } catch (e:HttpException){
handleError(e.message) handleError(e.toUserMessage())
} }
} }
} }
@@ -7,6 +7,7 @@ import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.pharmacy.domain.model.PharmacyItem import com.approagency.pharmacy.domain.model.PharmacyItem
import com.approagency.pharmacy.domain.usecase.GetPharmaciesUseCase import com.approagency.pharmacy.domain.usecase.GetPharmaciesUseCase
import com.approagency.pharmacy.domain.usecase.GetPharmacyDetailUseCase import com.approagency.pharmacy.domain.usecase.GetPharmacyDetailUseCase
import com.approagency.pharmacy.utils.toUserMessage
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -61,7 +62,7 @@ class PharmacyViewModel(
} }
} catch (e: Exception) { } catch (e: Exception) {
_pharmacyDetailStates.value = _pharmacyDetailStates.value.toMutableMap().apply { _pharmacyDetailStates.value = _pharmacyDetailStates.value.toMutableMap().apply {
put(pharmacyUrl, PharmacyDetailState.Error(e.message ?: "خطا در دریافت اطلاعات")) put(pharmacyUrl, PharmacyDetailState.Error(e.toUserMessage()))
} }
} }
} }
@@ -90,7 +91,7 @@ class PharmacyViewModel(
val result = getPharmaciesUseCase(genericDrugId, brandIrc, provinceId) val result = getPharmaciesUseCase(genericDrugId, brandIrc, provinceId)
_state.value = PharmacyState.Success(result) _state.value = PharmacyState.Success(result)
} catch (e: Exception) { } catch (e: Exception) {
_state.value = PharmacyState.Error(e.message ?: "خطا در دریافت اطلاعات") _state.value = PharmacyState.Error(e.toUserMessage())
} }
} }
} }
@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.approagency.pharmacy.domain.model.DrugSearchResult import com.approagency.pharmacy.domain.model.DrugSearchResult
import com.approagency.pharmacy.domain.usecase.SearchDrugsYabUseCase import com.approagency.pharmacy.domain.usecase.SearchDrugsYabUseCase
import com.approagency.pharmacy.utils.toUserMessage
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -77,7 +78,7 @@ class SearchViewModel(
} }
else -> { else -> {
isLoadingMore = false isLoadingMore = false
SearchState.Error(result.exceptionOrNull()?.message ?: "خطا در جستجو") SearchState.Error(result.exceptionOrNull().toUserMessage())
} }
} }
} }
@@ -0,0 +1,34 @@
package com.approagency.pharmacy.utils
import retrofit2.HttpException
import java.io.IOException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
/** پیام‌های خطای فارسیِ امن — بدون افشای منبع داده یا متن خام استثناء. */
object ErrorMessages {
const val NETWORK = "اتصال به اینترنت برقرار نیست. لطفاً اتصال خود را بررسی کرده و دوباره تلاش کنید."
const val TIMEOUT = "زمان پاسخ‌گویی به پایان رسید. لطفاً دوباره تلاش کنید."
const val SERVER = "در حال حاضر امکان دریافت اطلاعات وجود ندارد. لطفاً کمی بعد دوباره تلاش کنید."
const val NOT_FOUND = "موردی یافت نشد."
const val GENERIC = "خطایی رخ داد. لطفاً دوباره تلاش کنید."
}
/**
* تبدیل استثناء به پیام فارسیِ قابل‌نمایش به کاربر.
* هرگز متن خام استثناء، آدرس سرور یا نام منبع داده را برنمی‌گرداند.
*/
fun Throwable?.toUserMessage(): String = when (this) {
null -> ErrorMessages.GENERIC
is UnknownHostException -> ErrorMessages.NETWORK
is SocketTimeoutException -> ErrorMessages.TIMEOUT
is HttpException -> ErrorMessages.SERVER
is IOException -> ErrorMessages.NETWORK
else -> when (cause) {
is UnknownHostException -> ErrorMessages.NETWORK
is SocketTimeoutException -> ErrorMessages.TIMEOUT
is HttpException -> ErrorMessages.SERVER
is IOException -> ErrorMessages.NETWORK
else -> ErrorMessages.GENERIC
}
}