Feat(ToolsScreen and ToolStarScreen): Show skeleton when loading tool

This commit is contained in:
2024-08-27 17:24:27 +08:00
parent c9363ee34b
commit 93b22ea14b
5 changed files with 184 additions and 2 deletions

View File

@@ -185,4 +185,5 @@ dependencies {
implementation(libs.room.runtime) implementation(libs.room.runtime)
implementation(libs.room.ktx) implementation(libs.room.ktx)
implementation(libs.timber) implementation(libs.timber)
implementation(libs.compose.shimmer)
} }

View File

@@ -1,5 +1,8 @@
package top.fatweb.oxygen.toolbox.ui.component package top.fatweb.oxygen.toolbox.ui.component
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -15,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
@@ -30,6 +34,11 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.valentinilk.shimmer.LocalShimmerTheme
import com.valentinilk.shimmer.ShimmerBounds
import com.valentinilk.shimmer.rememberShimmer
import com.valentinilk.shimmer.shimmer
import com.valentinilk.shimmer.shimmerSpec
import top.fatweb.oxygen.toolbox.R import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
@@ -244,3 +253,131 @@ private fun AuthorInfo(
) )
} }
} }
@Composable
fun ToolCardSkeleton(
modifier: Modifier = Modifier
) {
val shimmer = rememberShimmer(
shimmerBounds = ShimmerBounds.Window,
theme = LocalShimmerTheme.current.copy(
animationSpec = infiniteRepeatable(
animation = shimmerSpec(
durationMillis = 1_500,
easing = LinearEasing,
delayMillis = 200
),
repeatMode = RepeatMode.Restart
)
)
)
Card(
modifier = modifier
.clip(RoundedCornerShape(8.dp)),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier
.height(28.dp)
) {
Surface(
modifier = modifier
.fillMaxHeight()
.width(64.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
}
Spacer(Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier
.size(80.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
}
Spacer(Modifier.height(16.dp))
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Surface(
modifier = Modifier
.size(width = 40.dp, height = 22.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
Surface(
modifier = Modifier
.size(width = 60.dp, height = 20.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
Column(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Surface(
modifier = Modifier
.size(width = 120.dp, height = 12.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
Surface(
modifier = Modifier
.size(width = 120.dp, height = 12.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
Surface(
modifier = Modifier
.size(width = 80.dp, height = 12.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
}
}
Spacer(Modifier.height(16.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
) {
Surface(
modifier = Modifier
.size(24.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(12.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
Surface(
modifier = Modifier
.size(width = 120.dp, height = 12.dp)
.shimmer(shimmer),
shape = RoundedCornerShape(4.dp),
color = MaterialTheme.colorScheme.surfaceContainer
) {}
}
}
}
}
const val DEFAULT_TOOL_CARD_SKELETON_COUNT = 8

View File

@@ -43,11 +43,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import top.fatweb.oxygen.toolbox.R import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
import top.fatweb.oxygen.toolbox.ui.component.DEFAULT_TOOL_CARD_SKELETON_COUNT
import top.fatweb.oxygen.toolbox.ui.component.DialogClickerRow import top.fatweb.oxygen.toolbox.ui.component.DialogClickerRow
import top.fatweb.oxygen.toolbox.ui.component.DialogSectionGroup import top.fatweb.oxygen.toolbox.ui.component.DialogSectionGroup
import top.fatweb.oxygen.toolbox.ui.component.DialogTitle import top.fatweb.oxygen.toolbox.ui.component.DialogTitle
import top.fatweb.oxygen.toolbox.ui.component.Indicator import top.fatweb.oxygen.toolbox.ui.component.Indicator
import top.fatweb.oxygen.toolbox.ui.component.ToolCard import top.fatweb.oxygen.toolbox.ui.component.ToolCard
import top.fatweb.oxygen.toolbox.ui.component.ToolCardSkeleton
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
@@ -96,6 +98,24 @@ internal fun StarScreen(
when (starScreenUiState) { when (starScreenUiState) {
StarScreenUiState.Loading -> { StarScreenUiState.Loading -> {
Indicator() Indicator()
LazyVerticalStaggeredGrid(
modifier = Modifier
.fillMaxSize(),
columns = StaggeredGridCells.Adaptive(160.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalItemSpacing = 24.dp,
state = state
) {
items(count = DEFAULT_TOOL_CARD_SKELETON_COUNT) {
ToolCardSkeleton()
}
item(span = StaggeredGridItemSpan.FullLine) {
Spacer(Modifier.height(8.dp))
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
}
}
} }
StarScreenUiState.Nothing -> { StarScreenUiState.Nothing -> {
@@ -210,6 +230,7 @@ private fun ToolMenu(
@Composable @Composable
private fun howManyTools(starScreenUiState: StarScreenUiState) = private fun howManyTools(starScreenUiState: StarScreenUiState) =
when (starScreenUiState) { when (starScreenUiState) {
StarScreenUiState.Loading, StarScreenUiState.Nothing -> 0 StarScreenUiState.Loading -> DEFAULT_TOOL_CARD_SKELETON_COUNT
StarScreenUiState.Nothing -> 0
is StarScreenUiState.Success -> starScreenUiState.tools.size is StarScreenUiState.Success -> starScreenUiState.tools.size
} }

View File

@@ -47,11 +47,13 @@ import kotlinx.coroutines.launch
import top.fatweb.oxygen.toolbox.R import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
import top.fatweb.oxygen.toolbox.ui.component.DEFAULT_TOOL_CARD_SKELETON_COUNT
import top.fatweb.oxygen.toolbox.ui.component.DialogClickerRow import top.fatweb.oxygen.toolbox.ui.component.DialogClickerRow
import top.fatweb.oxygen.toolbox.ui.component.DialogSectionGroup import top.fatweb.oxygen.toolbox.ui.component.DialogSectionGroup
import top.fatweb.oxygen.toolbox.ui.component.DialogTitle import top.fatweb.oxygen.toolbox.ui.component.DialogTitle
import top.fatweb.oxygen.toolbox.ui.component.Indicator import top.fatweb.oxygen.toolbox.ui.component.Indicator
import top.fatweb.oxygen.toolbox.ui.component.ToolCard import top.fatweb.oxygen.toolbox.ui.component.ToolCard
import top.fatweb.oxygen.toolbox.ui.component.ToolCardSkeleton
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
@@ -116,6 +118,24 @@ internal fun ToolsScreen(
when (toolsScreenUiState) { when (toolsScreenUiState) {
ToolsScreenUiState.Loading -> { ToolsScreenUiState.Loading -> {
Indicator() Indicator()
LazyVerticalStaggeredGrid(
modifier = Modifier
.fillMaxSize(),
columns = StaggeredGridCells.Adaptive(160.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalItemSpacing = 24.dp,
state = state
) {
items(count = DEFAULT_TOOL_CARD_SKELETON_COUNT) {
ToolCardSkeleton()
}
item(span = StaggeredGridItemSpan.FullLine) {
Spacer(Modifier.height(8.dp))
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
}
}
} }
ToolsScreenUiState.Nothing -> { ToolsScreenUiState.Nothing -> {
@@ -254,6 +274,7 @@ private fun ToolMenu(
@Composable @Composable
private fun howManyTools(toolsScreenUiState: ToolsScreenUiState) = private fun howManyTools(toolsScreenUiState: ToolsScreenUiState) =
when (toolsScreenUiState) { when (toolsScreenUiState) {
ToolsScreenUiState.Loading, ToolsScreenUiState.Nothing -> 0 ToolsScreenUiState.Loading -> DEFAULT_TOOL_CARD_SKELETON_COUNT
ToolsScreenUiState.Nothing -> 0
is ToolsScreenUiState.Success -> toolsScreenUiState.tools.size is ToolsScreenUiState.Success -> toolsScreenUiState.tools.size
} }

View File

@@ -33,6 +33,7 @@ androidsvg = "1.4"
webviewCompose = "0.33.6" webviewCompose = "0.33.6"
room = "2.6.1" room = "2.6.1"
timber = "5.0.1" timber = "5.0.1"
composeShimmer = "1.3.1"
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }
@@ -102,3 +103,4 @@ room-compiler = { group = "androidx.room", name = "room-compiler", version.ref =
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
compose-shimmer = { group = "com.valentinilk.shimmer", name = "compose-shimmer", version.ref = "composeShimmer" }