Feat(ToolScreen): Finish tool store list

This commit is contained in:
2024-05-11 03:26:02 +08:00
parent c596767c37
commit 3d8bc944e3
15 changed files with 437 additions and 57 deletions

View File

@@ -168,4 +168,5 @@ dependencies {
implementation(libs.okhttp.logging)
implementation(libs.paging.runtime)
implementation(libs.paging.compose)
implementation(libs.androidsvg.aar)
}

View File

@@ -0,0 +1,80 @@
package top.fatweb.oxygen.toolbox.icon
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Preview
@Composable
private fun VectorPreview() {
Image(OxygenIcons.Loading, null)
}
private var loading: ImageVector? = null
val OxygenIcons.Loading: ImageVector
get() {
if (loading != null) {
return loading!!
}
loading = ImageVector.Builder(
name = "Loading",
defaultWidth = 1024.dp,
defaultHeight = 1024.dp,
viewportWidth = 1024f,
viewportHeight = 1024f
).apply {
path(
fill = SolidColor(Color.Black),
fillAlpha = 1.0f,
stroke = null,
strokeAlpha = 1.0f,
strokeLineWidth = 1.0f,
strokeLineCap = StrokeCap.Butt,
strokeLineJoin = StrokeJoin.Miter,
strokeLineMiter = 1.0f,
pathFillType = PathFillType.NonZero
) {
moveTo(988f, 548f)
curveToRelative(-19.9f, 0f, -36f, -16.1f, -36f, -36f)
curveToRelative(0f, -59.4f, -11.6f, -117f, -34.6f, -171.3f)
arcToRelative(
440.45f,
440.45f,
0f,
isMoreThanHalf = false,
isPositiveArc = false,
-94.3f,
-139.9f
)
arcToRelative(
437.71f,
437.71f,
0f,
isMoreThanHalf = false,
isPositiveArc = false,
-139.9f,
-94.3f
)
curveTo(629f, 83.6f, 571.4f, 72f, 512f, 72f)
curveToRelative(-19.9f, 0f, -36f, -16.1f, -36f, -36f)
reflectiveCurveToRelative(16.1f, -36f, 36f, -36f)
curveToRelative(69.1f, 0f, 136.2f, 13.5f, 199.3f, 40.3f)
curveTo(772.3f, 66f, 827f, 103f, 874f, 150f)
curveToRelative(47f, 47f, 83.9f, 101.8f, 109.7f, 162.7f)
curveToRelative(26.7f, 63.1f, 40.2f, 130.2f, 40.2f, 199.3f)
curveToRelative(0.1f, 19.9f, -16f, 36f, -35.9f, 36f)
close()
}
}.build()
return loading!!
}

View File

@@ -1,5 +1,7 @@
package top.fatweb.oxygen.toolbox.icon
import android.graphics.BitmapFactory
import android.graphics.drawable.PictureDrawable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccessTime
import androidx.compose.material.icons.filled.Build
@@ -16,6 +18,12 @@ import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material.icons.rounded.Star
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.graphics.drawable.toBitmap
import com.caverock.androidsvg.SVG
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
object OxygenIcons {
val ArrowDown = Icons.Rounded.KeyboardArrowDown
@@ -33,4 +41,23 @@ object OxygenIcons {
val StarBorder = Icons.Outlined.StarBorder
val Time = Icons.Default.AccessTime
val Tool = Icons.Default.Build
}
fun fromSvgBase64(base64String: String): ImageBitmap {
val svg = SVG.getFromString(base64DecodeToString(base64String))
val drawable = PictureDrawable(svg.renderToPicture())
return drawable.toBitmap().asImageBitmap()
}
fun fromPngBase64(base64String: String): ImageBitmap {
val byteArray = base64DecodeToByteArray(base64String)
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size).asImageBitmap()
}
@OptIn(ExperimentalEncodingApi::class)
private fun base64DecodeToString(base64String: String): String =
Base64.decode(base64String).decodeToString()
@OptIn(ExperimentalEncodingApi::class)
private fun base64DecodeToByteArray(base64String: String): ByteArray =
Base64.decode(base64String)
}

View File

@@ -10,7 +10,8 @@ fun OxygenNavHost(
modifier: Modifier = Modifier,
appState: OxygenAppState,
onShowSnackbar: suspend (String, String?) -> Boolean,
startDestination: String
startDestination: String,
handleOnCanScrollChange: (Boolean) -> Unit
) {
val navController = appState.navController
NavHost(
@@ -29,7 +30,8 @@ fun OxygenNavHost(
onBackClick = navController::popBackStack
)
toolsScreen(
onShowSnackbar = onShowSnackbar,
handleOnCanScrollChange = handleOnCanScrollChange
)
starScreen(

View File

@@ -10,10 +10,16 @@ const val TOOLS_ROUTE = "tools_route"
fun NavController.navigateToTools(navOptions: NavOptions) = navigate(TOOLS_ROUTE, navOptions)
fun NavGraphBuilder.toolsScreen() {
fun NavGraphBuilder.toolsScreen(
onShowSnackbar: suspend (String, String?) -> Boolean,
handleOnCanScrollChange: (Boolean) -> Unit
) {
composable(
route = TOOLS_ROUTE
) {
ToolsRoute()
ToolsRoute(
onShowSnackbar = onShowSnackbar,
handleOnCanScrollChange = handleOnCanScrollChange
)
}
}

View File

@@ -78,7 +78,12 @@ fun OxygenApp(appState: OxygenAppState) {
val noConnectMessage = stringResource(R.string.core_no_connect)
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
var canScroll by remember { mutableStateOf(false) }
val handleOnCanScrollChange = { value: Boolean ->
canScroll = value
}
val topAppBarScrollBehavior =
TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { canScroll })
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
LaunchedEffect(isOffline) {
@@ -175,7 +180,8 @@ fun OxygenApp(appState: OxygenAppState) {
startDestination = when (appState.launchPageConfig) {
LaunchPageConfig.TOOLS -> TOOLS_ROUTE
LaunchPageConfig.STAR -> STAR_ROUTE
}
},
handleOnCanScrollChange = handleOnCanScrollChange
)
}
}

View File

@@ -3,6 +3,11 @@ package top.fatweb.oxygen.toolbox.ui.about
import android.content.Intent
import android.net.Uri
import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.animation.core.Ease
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.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -21,6 +26,7 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsBottomHeight
@@ -33,6 +39,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -46,6 +53,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
@@ -54,6 +62,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.Loading
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.ui.component.OxygenTopAppBar
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
@@ -105,6 +114,8 @@ internal fun LibrariesScreen(
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { state.canScrollForward })
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
var activeSearch by remember { mutableStateOf(false) }
var searchValue by remember { mutableStateOf("") }
@@ -156,8 +167,26 @@ internal fun LibrariesScreen(
)
) {
when (librariesScreenUiState) {
LibrariesScreenUiState.Loading -> {
Text(text = stringResource(R.string.feature_settings_loading))
LibrariesScreenUiState.Loading -> {Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val angle by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = Ease),
), label = "angle"
)
Icon(
modifier = Modifier
.size(32.dp)
.graphicsLayer { rotationZ = angle },
imageVector = OxygenIcons.Loading,
contentDescription = ""
)
}
}
LibrariesScreenUiState.Nothing -> {

View File

@@ -0,0 +1,161 @@
package top.fatweb.oxygen.toolbox.ui.component
import androidx.compose.foundation.Image
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.Row
import androidx.compose.foundation.layout.Spacer
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.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.Tool
@Composable
fun ToolCard(
modifier: Modifier = Modifier,
tool: Tool,
onClickToolCard: () -> Unit
) {
Card(
modifier = modifier,
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
onClick = onClickToolCard
) {
Column(
modifier = Modifier.padding(16.dp)
) {
ToolVer(ver = tool.ver)
Spacer(modifier = Modifier.height(16.dp))
ToolIcon(icon = tool.icon)
Spacer(modifier = Modifier.height(16.dp))
ToolInfo(
toolName = tool.name,
toolId = tool.toolId,
toolDesc = tool.description
)
Spacer(modifier = Modifier.height(16.dp))
AuthorInfo(
avatar = tool.author.avatar,
nickname = tool.author.nickname
)
}
}
}
@Composable
fun ToolVer(
modifier: Modifier = Modifier,
ver: String
) {
Card(
modifier = modifier,
colors = CardDefaults.cardColors(contentColor = MaterialTheme.colorScheme.onSecondaryContainer)
) {
Column(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.surfaceContainer)
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Text(
style = MaterialTheme.typography.bodyMedium,
text = ver
)
}
}
}
@Composable
fun ToolIcon(
modifier: Modifier = Modifier,
icon: String
) {
Box(
modifier = modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.size(80.dp),
bitmap = OxygenIcons.fromSvgBase64(icon),
contentDescription = ""
)
}
}
@Composable
fun ToolInfo(
modifier: Modifier = Modifier,
toolName: String,
toolId: String,
toolDesc: String
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.ExtraBold,
text = toolName
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = "ID: $toolId"
)
Text(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline,
text = "${stringResource(R.string.feature_tools_description)}: $toolDesc"
)
}
}
@Composable
fun AuthorInfo(
modifier: Modifier = Modifier,
avatar: String,
nickname: String
) {
Row(
modifier = modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
) {
Surface(
modifier = Modifier
.size(24.dp),
shape = RoundedCornerShape(12.dp)
) {
Image(
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer),
bitmap = OxygenIcons.fromPngBase64(avatar), contentDescription = "Avatar"
)
}
Text(
style = MaterialTheme.typography.bodyMedium,
text = nickname
)
}
}

View File

@@ -1,14 +1,22 @@
package top.fatweb.oxygen.toolbox.ui.settings
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Ease
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.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
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.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
@@ -26,6 +34,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
@@ -34,6 +43,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.Loading
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.userdata.DarkThemeConfig
import top.fatweb.oxygen.toolbox.model.userdata.LanguageConfig
@@ -82,6 +92,7 @@ fun SettingsDialog(
onNavigateToAbout: () -> Unit
) {
val configuration = LocalConfiguration.current
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
AlertDialog(
modifier = modifier
@@ -101,10 +112,26 @@ fun SettingsDialog(
) {
when (settingsUiState) {
SettingsUiState.Loading -> {
Text(
modifier = Modifier.padding(vertical = 16.dp),
text = stringResource(R.string.feature_settings_loading)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val angle by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = Ease),
), label = "angle"
)
Icon(
modifier = Modifier
.size(32.dp)
.graphicsLayer { rotationZ = angle },
imageVector = OxygenIcons.Loading,
contentDescription = ""
)
}
}
is SettingsUiState.Success -> {

View File

@@ -2,17 +2,21 @@ package top.fatweb.oxygen.toolbox.ui.tool
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.material3.Text
import androidx.paging.compose.LazyPagingItems
import top.fatweb.oxygen.toolbox.model.tool.Tool
import top.fatweb.oxygen.toolbox.ui.component.ToolCard
fun LazyStaggeredGridScope.toolsPanel(
toolStorePagingItems: LazyPagingItems<Tool>
toolStorePagingItems: LazyPagingItems<Tool>,
onClickToolCard: (username: String, toolId: String) -> Unit
) {
items(
items = toolStorePagingItems.itemSnapshotList,
key = { it!!.id },
) {
Text(text = it!!.name)
ToolCard(
tool = it!!,
onClickToolCard = {onClickToolCard(it.author.username, it.toolId)}
)
}
}

View File

@@ -1,18 +1,26 @@
package top.fatweb.oxygen.toolbox.ui.tool
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.animation.core.Ease
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.gestures.Orientation
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.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
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.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.windowInsetsPadding
@@ -20,14 +28,21 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import top.fatweb.oxygen.toolbox.icon.Loading
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.Tool
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller
@@ -36,12 +51,16 @@ import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
@Composable
internal fun ToolsRoute(
modifier: Modifier = Modifier,
viewModel: ToolsScreenViewModel = hiltViewModel()
viewModel: ToolsScreenViewModel = hiltViewModel(),
onShowSnackbar: suspend (String, String?) -> Boolean,
handleOnCanScrollChange: (Boolean) -> Unit
) {
val toolStorePagingItems = viewModel.getStoreData().collectAsLazyPagingItems()
val toolStorePagingItems = viewModel.storeData.collectAsLazyPagingItems()
ToolsScreen(
modifier = modifier,
onShowSnackbar = onShowSnackbar,
handleOnCanScrollChange = handleOnCanScrollChange,
toolStorePagingItems = toolStorePagingItems
)
}
@@ -49,12 +68,15 @@ internal fun ToolsRoute(
@Composable
internal fun ToolsScreen(
modifier: Modifier = Modifier,
onShowSnackbar: suspend (String, String?) -> Boolean,
handleOnCanScrollChange: (Boolean) -> Unit,
toolStorePagingItems: LazyPagingItems<Tool>
) {
val isToolLoading = toolStorePagingItems.loadState.refresh is LoadState.Loading
val context = LocalContext.current
val isToolLoading =
toolStorePagingItems.loadState.refresh is LoadState.Loading
|| toolStorePagingItems.loadState.append is LoadState.Loading
Log.d("TAG", "ToolsScreen: ${toolStorePagingItems.loadState}")
ReportDrawnWhen { !isToolLoading }
val itemsAvailable = toolStorePagingItems.itemCount
@@ -62,18 +84,30 @@ internal fun ToolsScreen(
val state = rememberLazyStaggeredGridState()
val scrollbarState = state.scrollbarState(itemsAvailable = itemsAvailable)
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
val handleOnClickToolCard = { username: String, toolId: String ->
Toast.makeText(context, "$username:$toolId", Toast.LENGTH_LONG).show()
}
LaunchedEffect(state.canScrollForward) {
handleOnCanScrollChange(state.canScrollForward)
}
Box(
modifier.fillMaxSize()
) {
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Adaptive(300.dp),
columns = StaggeredGridCells.Adaptive(160.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalItemSpacing = 24.dp,
state = state
) {
toolsPanel(toolStorePagingItems = toolStorePagingItems)
toolsPanel(
toolStorePagingItems = toolStorePagingItems,
onClickToolCard = handleOnClickToolCard
)
item(span = StaggeredGridItemSpan.FullLine) {
Spacer(modifier = Modifier.height(8.dp))
@@ -81,6 +115,29 @@ internal fun ToolsScreen(
}
}
if (toolStorePagingItems.loadState.refresh is LoadState.Loading || toolStorePagingItems.loadState.append is LoadState.Loading) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val angle by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = Ease),
), label = "angle"
)
Icon(
modifier = Modifier
.size(32.dp)
.graphicsLayer { rotationZ = angle },
imageVector = OxygenIcons.Loading,
contentDescription = ""
)
}
}
state.DraggableScrollbar(
modifier = Modifier
.fillMaxHeight()
@@ -91,26 +148,4 @@ internal fun ToolsScreen(
onThumbMoved = state.rememberDraggableScroller(itemsAvailable = itemsAvailable)
)
}
}
/*
@OxygenPreviews
@Composable
fun ToolsScreenLoadingPreview() {
OxygenTheme {
ToolsScreen(toolsScreenUiState = ToolsScreenUiState.Loading)
}
}
@OxygenPreviews
@Composable
fun ToolsScreenPreview() {
OxygenTheme {
ToolsScreen(
toolsScreenUiState = ToolsScreenUiState.Success(
runBlocking {
ToolDataSource().tool.first()
})
)
}
}*/
}

View File

@@ -24,14 +24,12 @@ class ToolsScreenViewModel @Inject constructor(
private val currentPage = savedStateHandle.getStateFlow(CURRENT_PAGE, 1)
@OptIn(ExperimentalCoroutinesApi::class)
fun getStoreData(): Flow<PagingData<Tool>> {
return combine(
searchValue,
currentPage,
::Pair
).flatMapLatest { (searchValue, currentPage) ->
toolRepository.getStore(searchValue, currentPage).cachedIn(viewModelScope)
}
val storeData: Flow<PagingData<Tool>> = combine(
searchValue,
currentPage,
::Pair
).flatMapLatest { (searchValue, currentPage) ->
toolRepository.getStore(searchValue, currentPage).cachedIn(viewModelScope)
}
}

View File

@@ -9,11 +9,11 @@
<string name="core_unknown">未知</string>
<string name="core_website">网站</string>
<string name="core_search">搜索</string>
<string name="core_loading">加载中…</string>
<string name="core_no_connect">⚠️ 无法连接至互联网</string>
<string name="feature_tools_title">工具</string>
<string name="feature_star_title">收藏</string>
<string name="feature_settings_title">设置</string>
<string name="feature_settings_loading">加载中…</string>
<string name="feature_settings_language">语言</string>
<string name="feature_settings_language_system_default">系统默认</string>
<string name="feature_settings_launch_page">启动页</string>
@@ -34,4 +34,5 @@
<string name="feature_settings_more_about">关于</string>
<string name="feature_settings_top_app_bar_action_icon_description">更多</string>
<string name="feature_settings_top_app_bar_navigation_icon_description">搜索</string>
<string name="feature_tools_description">简介</string>
</resources>

View File

@@ -8,11 +8,11 @@
<string name="core_unknown">Unknown</string>
<string name="core_website">Website</string>
<string name="core_search">Search</string>
<string name="core_loading">Loading…</string>
<string name="core_no_connect">⚠️ Unable to connect to the internet</string>
<string name="feature_tools_title">Tools</string>
<string name="feature_star_title">Star</string>
<string name="feature_settings_title">Settings</string>
<string name="feature_settings_loading">Loading…</string>
<string name="feature_settings_language">Language</string>
<string name="feature_settings_language_system_default">System Default</string>
<string name="feature_settings_language_chinese" translatable="false">中文</string>
@@ -35,4 +35,5 @@
<string name="feature_settings_more_about">About</string>
<string name="feature_settings_top_app_bar_action_icon_description">More</string>
<string name="feature_settings_top_app_bar_navigation_icon_description">Search</string>
<string name="feature_tools_description">Desc</string>
</resources>