Refactor: Extract boilerplate: safeLaunchLoad, clearToast, ToastEffect (CODE-21/22/23) (#312)

- Extracted `safeLaunchLoad` inline extension function to `ViewModelExt.kt` to encapsulate API call success/failure handling logic and replaced usages across 18 viewmodels.
- Extracted `ToastHost` interface to standardize `clearToast()` behavior across all ViewModels displaying toasts.
- Extracted `ToastEffect` composable to `SharedComponents.kt` and applied it to 16 screens, reducing repetitive side effect code blocks.

Fixes #286
This commit is contained in:
M57 authored and GitHub committed 2026-06-22 23:54:42 +03:00
1 parent 6abfabdac0
commit 98c02879b4
36 files changed
+427 -585

No files matched your search

@@ -1,18 +1,14 @@
package com.m57.hermescontrol.ui.sessions
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.m57.hermescontrol.data.model.SessionInfo
import com.m57.hermescontrol.data.remote.ApiClient
import com.m57.hermescontrol.data.remote.NetworkResult
import com.m57.hermescontrol.data.remote.safeApiCall
import kotlinx.coroutines.Dispatchers
import com.m57.hermescontrol.ui.common.safeLaunchLoad
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
data class SessionsUiState(
val isLoading: Boolean = false,
@@ -25,31 +21,25 @@ class SessionsViewModel : ViewModel() {
val uiState: StateFlow<SessionsUiState> = _uiState.asStateFlow()
fun loadSessions() {
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
viewModelScope.launch {
val result =
withContext(Dispatchers.IO) {
safeApiCall { ApiClient.hermesApi.getSessions() }
safeLaunchLoad(
apiCall = { safeApiCall { ApiClient.hermesApi.getSessions() } },
onStart = { _uiState.update { it.copy(isLoading = true, errorMessage = null) } },
onSuccess = { data ->
_uiState.update {
it.copy(
isLoading = false,
sessions = data?.sessions.orEmpty(),
)
}
when (result) {
is NetworkResult.Success -> {
_uiState.update {
it.copy(
isLoading = false,
sessions = result.data?.sessions.orEmpty(),
)
}
},
onError = { errorMsg ->
_uiState.update {
it.copy(
isLoading = false,
errorMessage = "Failed to load sessions: $errorMsg",
)
}
is NetworkResult.Failure -> {
_uiState.update {
it.copy(
isLoading = false,
errorMessage = "Failed to load sessions: ${result.error.message}",
)
}
}
}
}
},
)
}
}