perf: optimize data loading across Config, System, Profiles, and Billing tabs (#678)

* perf(model): optimize model tab load speed with two-phase fetch and loadQuick

* perf: optimize data loading across Config, System, Profiles, and Billing tabs

* chore: remove leftover bottom nav references and unused updatePrimaryScreens

* style: fix ktlint violations in Config/Profiles/Billing ViewModels

- property/argument-list wrapping for async(...) calls
- multiline-expression wrapping for async blocks
- remove unused import in ConfigViewModel
- break long error-message string literals to stay under 120 cols

---------

Co-authored-by: M57 <Hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-07-22 15:03:30 +03:00
1 parent f7afbfae85
commit 1cca7764cc
7 files changed
+222 -172

No files matched your search

@@ -9,8 +9,9 @@ import com.m57.hermescontrol.data.remote.ApiClient
import com.m57.hermescontrol.data.remote.NetworkResult
import com.m57.hermescontrol.data.remote.safeApiCall
import com.m57.hermescontrol.ui.common.ToastHost
import com.m57.hermescontrol.ui.common.safeLaunchLoad
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -51,53 +52,55 @@ class ConfigViewModel :
}
fun loadAll() {
safeLaunchLoad(
apiCall = {
safeApiCall { ApiClient.hermesApi.getConfig() }
},
onStart = { _uiState.update { it.copy(isLoading = true, errorMessage = null) } },
onSuccess = { configData ->
viewModelScope.launch {
val schemaResult =
withContext(Dispatchers.IO) {
safeApiCall { ApiClient.hermesApi.getConfigSchema() }
}
val defaultsResult =
withContext(Dispatchers.IO) {
safeApiCall { ApiClient.hermesApi.getConfigDefaults() }
}
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
try {
coroutineScope {
val configDeferred = async(Dispatchers.IO) { safeApiCall { ApiClient.hermesApi.getConfig() } }
val schemaDeferred = async(Dispatchers.IO) { safeApiCall { ApiClient.hermesApi.getConfigSchema() } }
val defaultsDeferred =
async(Dispatchers.IO) { safeApiCall { ApiClient.hermesApi.getConfigDefaults() } }
val rawDeferred = async(Dispatchers.IO) { safeApiCall { ApiClient.hermesApi.getRawConfig() } }
val schema = (schemaResult as? NetworkResult.Success)?.data
val defaults = (defaultsResult as? NetworkResult.Success)?.data
val rawResult =
withContext(Dispatchers.IO) {
safeApiCall { ApiClient.hermesApi.getRawConfig() }
}
val path = (rawResult as? NetworkResult.Success)?.data?.path
val configResult = configDeferred.await()
val schemaResult = schemaDeferred.await()
val defaultsResult = defaultsDeferred.await()
val rawResult = rawDeferred.await()
_uiState.update {
it.copy(
isLoading = false,
config = configData,
schema = schema,
defaults = defaults,
path = path,
activeCategory =
if (schema?.category_order?.isNotEmpty() == true) {
schema.category_order.first()
} else {
""
},
)
if (configResult is NetworkResult.Success) {
val configData = configResult.data
val schema = (schemaResult as? NetworkResult.Success)?.data
val defaults = (defaultsResult as? NetworkResult.Success)?.data
val path = (rawResult as? NetworkResult.Success)?.data?.path
_uiState.update {
it.copy(
isLoading = false,
config = configData,
schema = schema,
defaults = defaults,
path = path,
activeCategory =
if (schema?.category_order?.isNotEmpty() == true) {
schema.category_order.first()
} else {
""
},
)
}
} else {
val errorMsg = (configResult as? NetworkResult.Failure)?.error?.message ?: "Unknown error"
_uiState.update {
it.copy(isLoading = false, errorMessage = "Failed to load config: $errorMsg")
}
}
}
},
onError = { errorMsg ->
} catch (e: Exception) {
_uiState.update {
it.copy(isLoading = false, errorMessage = "Failed to load config: $errorMsg")
it.copy(isLoading = false, errorMessage = "Failed to load config: ${e.message}")
}
},
)
}
}
}
fun updateField(