Adds SkeletonListState to StateViews.kt — an animated shimmer placeholder list whose geometry matches a typical list row (avatar circle + title line + subtitle line). The shimmer colors derive from MaterialTheme.colorScheme.surfaceVariant (theme-aware, works on light/dark/AMOLED/Material You). Sweeps horizontally via an infiniteTransition tween — no new dependencies, pure Compose foundation animations. Wired SkeletonListState into 22 list-loading call sites across 22 screens: skills, cron, plugins, mcp, toolsets, webhooks, system, sessions (x2), config (x2 — list + YAMLEditor), model, profiles, keys, channels, achievements, gateways, kanban, logs, analytics, providers, processes, hub skills tab, installed skills tab (list branch). Preserved LoadingState() (centered spinner) where the issue explicitly calls for it: - PairingScreen (one-shot auth flow) - SkillEditorDialog / ModelPickerDialog / CronJobEditorDialog interiors - SkillPreviewDialog and SkillEditorDialog inline-loading branches in SkillsScreen.kt (lines 790, 914) - InstalledSkillsTab dialog-interior branch (line 393) SkeletonListState testTag: "skeleton_list_state". Verified: ktlintCheck, assembleDebug, testDebugUnitTest all green locally. Co-authored-by: Hy4ri <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
0a0b612d3a
commit
0018e5d172
23 files changed
+186
-53
No files matched your search
@@ -57,10 +57,10 @@ import com.m57.hermescontrol.data.model.RecentUnlock
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SectionHeader
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
@@ -169,7 +169,7 @@ fun AchievementsScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.achievements.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.achievements.isEmpty() -> {
|
||||
|
||||
@@ -46,8 +46,8 @@ import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
|
||||
@@ -74,7 +74,7 @@ fun AnalyticsScreen(
|
||||
) {
|
||||
when {
|
||||
state.isLoading && state.usage == null -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.usage == null -> {
|
||||
|
||||
@@ -50,9 +50,9 @@ import com.m57.hermescontrol.ui.channels.components.TelegramOnboardingDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -132,10 +132,7 @@ fun ChannelsScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.platforms.isEmpty() -> {
|
||||
LoadingState(
|
||||
subtitle = stringResource(R.string.loading_state_subtitle_channels),
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.platforms.isEmpty() -> {
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
package com.m57.hermescontrol.ui.common
|
||||
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Inbox
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
@@ -19,12 +31,20 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.theme.LocalSpacing
|
||||
@@ -168,6 +188,125 @@ fun EmptyState(
|
||||
}
|
||||
}
|
||||
|
||||
// ── Skeleton (shimmer) — see issue #617 ────────────────────────────────
|
||||
//
|
||||
// Skeleton loaders for list screens. Replaces bare `CircularProgressIndicator`
|
||||
// in list screens to (1) reduce perceived latency (~2× per NN/g research)
|
||||
// and (2) eliminate layout shift when real content lands. Pair with
|
||||
// `LoadingState()` for centered/one-shot contexts (Pairing flow, dialog
|
||||
// interiors) where a spinner is still appropriate.
|
||||
|
||||
/**
|
||||
* An animated shimmer [Brush] derived from the current theme's
|
||||
* `MaterialTheme.colorScheme.surfaceVariant`. Sweeps horizontally across the
|
||||
* canvas; offset driven by an infinite `1.2s` tween so the shimmer never
|
||||
* freezes. Theme-derived — works on light, dark, AMOLED, Material You.
|
||||
*/
|
||||
@Composable
|
||||
private fun shimmerBrush(): Brush {
|
||||
val base = MaterialTheme.colorScheme.surfaceVariant
|
||||
val highlight = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
|
||||
val transition = rememberInfiniteTransition(label = "shimmer")
|
||||
val widthPx = with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
|
||||
val offset by transition.animateFloat(
|
||||
initialValue = -widthPx,
|
||||
targetValue = widthPx,
|
||||
animationSpec =
|
||||
infiniteRepeatable(
|
||||
animation = tween(durationMillis = 1200, easing = FastOutSlowInEasing),
|
||||
repeatMode = RepeatMode.Restart,
|
||||
),
|
||||
label = "shimmer_offset",
|
||||
)
|
||||
return Brush.linearGradient(
|
||||
colors = listOf(base, highlight, base),
|
||||
start = Offset(offset, 0f),
|
||||
end = Offset(offset + widthPx, 0f),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerBox(
|
||||
widthFraction: Float,
|
||||
height: Dp,
|
||||
shape: Shape,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.height(height)
|
||||
.fillMaxWidth(widthFraction)
|
||||
.clip(shape)
|
||||
.background(shimmerBrush()),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SkeletonRow(height: Dp) {
|
||||
val spacing = LocalSpacing.current
|
||||
val shape = RoundedCornerShape(12.dp)
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.padding(vertical = spacing.sm),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
// Avatar circle — 40dp to match default LoadingState spinner size.
|
||||
ShimmerBox(
|
||||
widthFraction = 0f,
|
||||
height = 40.dp,
|
||||
shape = CircleShape,
|
||||
modifier = Modifier.size(40.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(spacing.md))
|
||||
// Title + subtitle lines
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(spacing.sm),
|
||||
) {
|
||||
ShimmerBox(widthFraction = 0.6f, height = 14.dp, shape = shape)
|
||||
ShimmerBox(widthFraction = 0.4f, height = 12.dp, shape = shape)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skeleton placeholder list for loading states on list/management screens.
|
||||
*
|
||||
* Renders [itemCount] shimmering rows whose geometry matches the typical list
|
||||
* row (avatar circle + title line + subtitle line). The shimmer colors derive
|
||||
* from theme tokens — works on light, dark, AMOLED, and Material You. No
|
||||
* layout shift on transition to real content (acceptance criterion, issue #617).
|
||||
*
|
||||
* @param itemCount number of placeholder rows to render (default 6 — long
|
||||
* enough to fill a phone viewport under typical content density).
|
||||
* @param rowHeight height of each placeholder row (default 72dp — same as a
|
||||
* one-line `NavigationDrawerItem`). Override per-screen to match real row
|
||||
* geometry when known.
|
||||
* @param modifier outer modifier; pass `Modifier.padding(paddingValues)` from
|
||||
* the scaffold so the skeleton offsets for the top bar like real content does.
|
||||
*/
|
||||
@Composable
|
||||
fun SkeletonListState(
|
||||
itemCount: Int = 6,
|
||||
rowHeight: Dp = 72.dp,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 12.dp)
|
||||
.testTag("skeleton_list_state"),
|
||||
verticalArrangement = listItemSpacing,
|
||||
) {
|
||||
repeat(itemCount) { SkeletonRow(height = rowHeight) }
|
||||
}
|
||||
}
|
||||
|
||||
// ── Padding conventions ──────────────────────────────────────────────
|
||||
|
||||
val listContentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
|
||||
|
||||
@@ -58,9 +58,9 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.model.SchemaField
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -101,7 +101,7 @@ fun ConfigScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.config == null -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.config == null -> {
|
||||
@@ -281,7 +281,7 @@ private fun YAMLEditor(
|
||||
onSave: () -> Unit,
|
||||
) {
|
||||
if (isLoading) {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
@@ -96,7 +97,7 @@ fun CronJobsScreen(
|
||||
) {
|
||||
when {
|
||||
state.isLoading && state.jobs.isEmpty() -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -38,8 +38,8 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -66,7 +66,7 @@ fun GatewayScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.status == null -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -46,9 +46,9 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.model.KanbanTask
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
|
||||
private const val DEFAULT_COLUMN = "todo"
|
||||
@@ -94,7 +94,7 @@ fun KanbanScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.boards.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -51,10 +51,10 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SectionHeader
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -106,7 +106,7 @@ fun KeysScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && !hasAnyVars -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && !hasAnyVars -> {
|
||||
|
||||
@@ -54,9 +54,9 @@ import com.m57.hermescontrol.theme.LocalSpacing
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -191,7 +191,7 @@ fun LogsScreen(
|
||||
) {
|
||||
when {
|
||||
state.isLoading && state.logs.isEmpty() -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -65,10 +65,10 @@ import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SectionHeader
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
@@ -120,7 +120,7 @@ fun McpServersScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.servers.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -58,9 +58,9 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.model.components.MoaConfigDialog
|
||||
import com.m57.hermescontrol.ui.model.components.ModelPickerDialog
|
||||
@@ -101,10 +101,7 @@ fun ModelScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.providers.isEmpty() -> {
|
||||
LoadingState(
|
||||
subtitle = stringResource(R.string.loading_state_subtitle_models),
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -57,9 +57,9 @@ import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -131,7 +131,7 @@ fun PluginsScreen(
|
||||
) {
|
||||
when {
|
||||
state.isLoading && state.plugins.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -46,8 +46,8 @@ import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -85,7 +85,7 @@ fun ProcessesScreen(
|
||||
}
|
||||
|
||||
state.isLoading && state.processes.isEmpty() -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -54,8 +54,8 @@ import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.profiles.components.ProfileBuilderView
|
||||
import com.m57.hermescontrol.ui.profiles.components.validateProfileName
|
||||
@@ -134,7 +134,7 @@ fun ProfilesScreen(
|
||||
} else {
|
||||
when {
|
||||
state.isLoading && state.profiles.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -52,8 +52,8 @@ import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -102,10 +102,7 @@ fun ProvidersScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.providers.isEmpty() -> {
|
||||
LoadingState(
|
||||
subtitle = stringResource(R.string.loading_state_subtitle_providers),
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.providers.isEmpty() -> {
|
||||
|
||||
@@ -90,9 +90,9 @@ import com.m57.hermescontrol.theme.LocalSpacing
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatCard
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
@@ -491,7 +491,7 @@ fun SessionsScreen(
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
when {
|
||||
state.isSearching && state.searchResults.isEmpty() -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.searchError != null -> {
|
||||
@@ -579,7 +579,7 @@ fun SessionsScreen(
|
||||
}
|
||||
|
||||
state.isLoading && state.sessions.isEmpty() -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -74,6 +74,7 @@ import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -317,7 +318,7 @@ private fun InstalledSkillsView(
|
||||
|
||||
when {
|
||||
state.isLoading -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
@@ -587,7 +588,7 @@ private fun HubBrowseView(
|
||||
|
||||
when {
|
||||
state.isHubSearching -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.hubSearchError != null -> {
|
||||
|
||||
@@ -48,7 +48,7 @@ import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.DetailRow
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
import com.m57.hermescontrol.ui.skills.SkillsUiState
|
||||
@@ -117,7 +117,7 @@ internal fun HubBrowseView(
|
||||
|
||||
when {
|
||||
state.isHubSearching -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.hubSearchError != null -> {
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
import com.m57.hermescontrol.ui.common.toDetailRows
|
||||
@@ -177,7 +178,7 @@ internal fun InstalledSkillsView(
|
||||
|
||||
when {
|
||||
state.isLoading -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -78,9 +78,9 @@ import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.InfoRow
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SectionHeader
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatCard
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
@@ -252,7 +252,7 @@ fun SystemScreen(
|
||||
when {
|
||||
state.isLoading && state.stats == null && state.doctorReport == null &&
|
||||
state.status == null -> {
|
||||
LoadingState()
|
||||
SkeletonListState()
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -44,9 +44,9 @@ import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
import com.m57.hermescontrol.ui.common.listContentPadding
|
||||
import com.m57.hermescontrol.ui.common.listItemSpacing
|
||||
@@ -87,7 +87,7 @@ fun ToolsetsScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.toolsets.isEmpty() -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null -> {
|
||||
|
||||
@@ -53,9 +53,9 @@ import com.m57.hermescontrol.ui.common.DetailDialog
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
import com.m57.hermescontrol.ui.common.ErrorState
|
||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import com.m57.hermescontrol.ui.common.LoadingState
|
||||
import com.m57.hermescontrol.ui.common.NavIcon
|
||||
import com.m57.hermescontrol.ui.common.SearchBar
|
||||
import com.m57.hermescontrol.ui.common.SkeletonListState
|
||||
import com.m57.hermescontrol.ui.common.StatusBadge
|
||||
import com.m57.hermescontrol.ui.common.StatusBadgeType
|
||||
import com.m57.hermescontrol.ui.common.ToastEffect
|
||||
@@ -244,7 +244,7 @@ fun WebhooksScreen(
|
||||
) { paddingValues ->
|
||||
when {
|
||||
state.isLoading && state.baseUrl == null -> {
|
||||
LoadingState(modifier = Modifier.padding(paddingValues))
|
||||
SkeletonListState(modifier = Modifier.padding(paddingValues))
|
||||
}
|
||||
|
||||
state.errorMessage != null && state.baseUrl == null -> {
|
||||
|
||||
Reference in new issue
Block a user