perf: optimize screen load times with progressive un-gating and SWR caching (#1192)
This commit is contained in:
55 files changed
+2494
-401
No files matched your search
@@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import com.m57.hermescontrol.data.model.WebhookSubscription
|
||||
import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
@@ -71,6 +72,7 @@ fun WebhooksScreen(
|
||||
viewModel: WebhooksViewModel = viewModel { WebhooksViewModel() },
|
||||
) {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val dataScope by AuthManager.dataScopeFlow.collectAsStateWithLifecycle()
|
||||
|
||||
var query by remember { mutableStateOf("") }
|
||||
var showDetail by remember { mutableStateOf<WebhookSubscription?>(null) }
|
||||
@@ -84,7 +86,8 @@ fun WebhooksScreen(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
LaunchedEffect(dataScope) {
|
||||
viewModel.clearScopeOwnedState()
|
||||
viewModel.loadWebhooks()
|
||||
}
|
||||
|
||||
@@ -232,7 +235,7 @@ fun WebhooksScreen(
|
||||
title = { Text(stringResource(R.string.screen_webhooks)) },
|
||||
navigationIcon = onOpenDrawer?.let { NavIcon.Menu(it) },
|
||||
isRefreshing = state.isLoading,
|
||||
onRefresh = { viewModel.loadWebhooks() },
|
||||
onRefresh = { viewModel.loadWebhooks(forceRefresh = true) },
|
||||
actions = {
|
||||
IconButton(onClick = { viewModel.showCreateDialog() }) {
|
||||
Icon(
|
||||
|
||||
@@ -2,15 +2,18 @@ package com.m57.hermescontrol.ui.webhooks
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.m57.hermescontrol.data.local.SwrCache
|
||||
import com.m57.hermescontrol.data.model.CreateWebhookRequest
|
||||
import com.m57.hermescontrol.data.model.WebhookSubscription
|
||||
import com.m57.hermescontrol.data.model.WebhookToggleSubscriptionRequest
|
||||
import com.m57.hermescontrol.data.model.WebhooksResponse
|
||||
import com.m57.hermescontrol.data.model.WebhooksToggleRequest
|
||||
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 com.m57.hermescontrol.ui.common.safeLaunchSwrLoad
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -48,8 +51,37 @@ class WebhooksViewModel :
|
||||
private val _uiState = MutableStateFlow(WebhooksUiState())
|
||||
val uiState: StateFlow<WebhooksUiState> = _uiState.asStateFlow()
|
||||
|
||||
fun loadWebhooks() {
|
||||
safeLaunchLoad(
|
||||
private val webhooksCache = SwrCache<String, WebhooksResponse>()
|
||||
|
||||
fun clearScopeOwnedState() {
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
enabled = false,
|
||||
baseUrl = null,
|
||||
subscriptions = emptyList(),
|
||||
errorMessage = null,
|
||||
deleteTarget = null,
|
||||
togglingName = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadWebhooks(forceRefresh: Boolean = false) {
|
||||
safeLaunchSwrLoad(
|
||||
cache = webhooksCache,
|
||||
forceRefresh = forceRefresh,
|
||||
onCacheHit = { data ->
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
enabled = data.enabled,
|
||||
baseUrl = data.base_url,
|
||||
subscriptions = data.subscriptions.orEmpty(),
|
||||
errorMessage = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.getWebhooks() } },
|
||||
onStart = { _uiState.update { it.copy(isLoading = true, errorMessage = null) } },
|
||||
onSuccess = { data ->
|
||||
@@ -90,7 +122,7 @@ class WebhooksViewModel :
|
||||
toastMessage = "Webhooks global status ${if (targetEnabled) "enabled" else "disabled"}",
|
||||
)
|
||||
}
|
||||
loadWebhooks()
|
||||
loadWebhooks(forceRefresh = true)
|
||||
}
|
||||
|
||||
is NetworkResult.Failure -> {
|
||||
@@ -185,7 +217,7 @@ class WebhooksViewModel :
|
||||
toastMessage = "Subscription \"$name\" created",
|
||||
)
|
||||
}
|
||||
loadWebhooks()
|
||||
loadWebhooks(forceRefresh = true)
|
||||
}
|
||||
|
||||
is NetworkResult.Failure -> {
|
||||
@@ -226,7 +258,7 @@ class WebhooksViewModel :
|
||||
toastMessage = "Subscription \"$name\" ${if (enabled) "enabled" else "disabled"}",
|
||||
)
|
||||
}
|
||||
loadWebhooks()
|
||||
loadWebhooks(forceRefresh = true)
|
||||
}
|
||||
|
||||
is NetworkResult.Failure -> {
|
||||
@@ -263,7 +295,7 @@ class WebhooksViewModel :
|
||||
_uiState.update {
|
||||
it.copy(toastMessage = "Subscription \"${target.name}\" deleted")
|
||||
}
|
||||
loadWebhooks()
|
||||
loadWebhooks(forceRefresh = true)
|
||||
}
|
||||
|
||||
is NetworkResult.Failure -> {
|
||||
|
||||
Reference in new issue
Block a user