diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt
index 32aef6b..ef56e1a 100644
--- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt
+++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt
@@ -48,7 +48,7 @@ internal class ToolStorePagingSource(
}
} ?: toolEntity
},
- prevKey = if (currentPage == 0) null else currentPage - 1,
+ prevKey = if (currentPage == 1) null else currentPage - 1,
nextKey = if (currentPage < pages) currentPage + 1 else null
)
} catch (e: Throwable) {
diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ClickableText.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ClickableText.kt
new file mode 100644
index 0000000..ea0ba91
--- /dev/null
+++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ClickableText.kt
@@ -0,0 +1,42 @@
+package top.fatweb.oxygen.toolbox.ui.component
+
+import androidx.annotation.StringRes
+import androidx.compose.foundation.text.ClickableText
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withStyle
+import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
+
+@Composable
+fun ClickableText(
+ @StringRes text: Int,
+ @StringRes replaceText: Int,
+ onClick: (Int) -> Unit
+ ) {
+ val context = LocalContext.current
+ val primaryColor = MaterialTheme.colorScheme.primary
+
+ val annotatedString = buildAnnotatedString {
+ val clickablePart = ResourcesUtils.getString(
+ context = context,
+ resId = replaceText
+ )
+ val mainText = ResourcesUtils.getString(
+ context = context,
+ resId = text,
+ clickablePart
+ )
+ append(mainText.substringBefore(clickablePart))
+ pushStringAnnotation(tag = "Click", annotation = clickablePart)
+ withStyle(style = SpanStyle(color = primaryColor)) {
+ append(clickablePart)
+ }
+ pop()
+ append(mainText.substringAfter(clickablePart))
+ }
+
+ ClickableText(text = annotatedString, onClick = onClick)
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/store/ToolStoreScreen.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/store/ToolStoreScreen.kt
index 553455f..2e5bf28 100644
--- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/store/ToolStoreScreen.kt
+++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/store/ToolStoreScreen.kt
@@ -1,5 +1,6 @@
package top.fatweb.oxygen.toolbox.ui.store
+import android.widget.Toast
import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
@@ -23,7 +24,6 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
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.items
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -46,6 +46,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@@ -55,10 +56,14 @@ import androidx.paging.compose.collectAsLazyPagingItems
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
+import top.fatweb.oxygen.toolbox.ui.component.ClickableText
+import top.fatweb.oxygen.toolbox.ui.component.DEFAULT_TOOL_CARD_SKELETON_COUNT
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.rememberDraggableScroller
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
+import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
@Composable
internal fun ToolStoreRoute(
@@ -101,9 +106,11 @@ internal fun ToolStoreScreen(
onChangeInstallType: (type: ToolStoreUiState.InstallInfo.Type) -> Unit,
onInstallTool: (installTool: ToolEntity) -> Unit
) {
+ val context = LocalContext.current
+
val isToolLoading =
- toolStorePagingItems.loadState.refresh is LoadState.Loading
- || toolStorePagingItems.loadState.append is LoadState.Loading
+ toolStorePagingItems.loadState.refresh == LoadState.Loading
+ || toolStorePagingItems.loadState.append == LoadState.Loading
ReportDrawnWhen { !isToolLoading }
@@ -114,11 +121,19 @@ internal fun ToolStoreScreen(
}
}
LaunchedEffect(toolStorePagingItems.loadState.refresh) {
- if (toolStorePagingItems.loadState.refresh is LoadState.Loading) {
- pullToRefreshState.startRefresh()
- } else {
+ if (toolStorePagingItems.loadState.refresh != LoadState.Loading) {
pullToRefreshState.endRefresh()
}
+ if (toolStorePagingItems.loadState.refresh is LoadState.Error) {
+ Toast.makeText(
+ context,
+ ResourcesUtils.getString(
+ context = context,
+ resId = R.string.feature_store_reload_error
+ ),
+ Toast.LENGTH_LONG
+ ).show()
+ }
}
val itemsAvailable = toolStorePagingItems.itemCount
@@ -142,39 +157,55 @@ internal fun ToolStoreScreen(
.clipToBounds()
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
- LazyVerticalStaggeredGrid(
- modifier = Modifier
- .fillMaxSize(),
- columns = StaggeredGridCells.Adaptive(160.dp),
- contentPadding = PaddingValues(16.dp),
- horizontalArrangement = Arrangement.spacedBy(16.dp),
- verticalItemSpacing = 24.dp,
- state = state
- ) {
- toolsPanel(
- toolStorePagingItems = toolStorePagingItems,
- onAction = { tool, installType ->
- installTool = tool
- onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.Pending)
- onChangeInstallType(installType)
- },
- onClick = {
- onNavigateToToolView(it.authorUsername, it.toolId, it.upgrade != null)
+ if (itemsAvailable > 0 || (toolStorePagingItems.loadState.refresh == LoadState.Loading && itemsAvailable == 0)) {
+ LazyVerticalStaggeredGrid(
+ modifier = Modifier
+ .fillMaxSize(),
+ columns = StaggeredGridCells.Adaptive(160.dp),
+ contentPadding = PaddingValues(16.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalItemSpacing = 24.dp,
+ state = state
+ ) {
+ if (itemsAvailable > 0) {
+ toolsPanel(
+ toolStorePagingItems = toolStorePagingItems,
+ onAction = { tool, installType ->
+ installTool = tool
+ onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.Pending)
+ onChangeInstallType(installType)
+ },
+ onClick = {
+ onNavigateToToolView(it.authorUsername, it.toolId, it.upgrade != null)
+ }
+ )
+ }
+ if (itemsAvailable == 0 || toolStorePagingItems.loadState.append == LoadState.Loading) {
+ items(count = DEFAULT_TOOL_CARD_SKELETON_COUNT) {
+ ToolCardSkeleton()
+ }
+ }
+ if (toolStorePagingItems.loadState.append is LoadState.Error) {
+ item(span = StaggeredGridItemSpan.FullLine) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ ClickableText(
+ text = R.string.feature_store_load_more_error,
+ replaceText = R.string.feature_store_retry
+ ) {
+ toolStorePagingItems.retry()
+ }
+ }
+ }
+ }
+ item(span = StaggeredGridItemSpan.FullLine) {
+ Spacer(Modifier.height(8.dp))
+ Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
}
- )
-
- item(span = StaggeredGridItemSpan.FullLine) {
- Spacer(Modifier.height(8.dp))
- Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
}
}
- PullToRefreshContainer(
- modifier = Modifier
- .align(Alignment.TopCenter),
- state = pullToRefreshState,
- )
-
if (itemsAvailable == 0 && !isToolLoading) {
Column(
modifier = Modifier
@@ -183,15 +214,30 @@ internal fun ToolStoreScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
- Text(
- text = stringResource(
- if (searchValue.isEmpty()) R.string.core_nothing
- else R.string.core_nothing_found
+ if (toolStorePagingItems.loadState.refresh is LoadState.Error) {
+ ClickableText(
+ text = R.string.feature_store_load_error,
+ replaceText = R.string.feature_store_retry
+ ) {
+ toolStorePagingItems.refresh()
+ }
+ } else {
+ Text(
+ text = stringResource(
+ if (searchValue.isEmpty()) R.string.core_nothing
+ else R.string.core_nothing_found
+ )
)
- )
+ }
}
}
+ PullToRefreshContainer(
+ modifier = Modifier
+ .align(Alignment.TopCenter),
+ state = pullToRefreshState,
+ )
+
state.DraggableScrollbar(
modifier = Modifier
.fillMaxHeight()
@@ -218,21 +264,21 @@ private fun LazyStaggeredGridScope.toolsPanel(
onClick: (ToolEntity) -> Unit
) {
items(
- items = toolStorePagingItems.itemSnapshotList,
- key = { it!!.id },
+ count = toolStorePagingItems.itemCount
) {
+ val item = toolStorePagingItems[it]!!
ToolCard(
- tool = it!!,
- specifyVer = it.upgrade,
- actionIcon = if (it.upgrade != null) OxygenIcons.Upgrade else if (!it.isInstalled) OxygenIcons.Download else null,
+ tool = item,
+ specifyVer = item.upgrade,
+ actionIcon = if (item.upgrade != null) OxygenIcons.Upgrade else if (!item.isInstalled) OxygenIcons.Download else null,
actionIconContentDescription = stringResource(R.string.core_install),
onAction = {
onAction(
- it,
- if (it.upgrade != null) ToolStoreUiState.InstallInfo.Type.Upgrade else ToolStoreUiState.InstallInfo.Type.Install
+ item,
+ if (item.upgrade != null) ToolStoreUiState.InstallInfo.Type.Upgrade else ToolStoreUiState.InstallInfo.Type.Install
)
},
- onClick = { onClick(it) }
+ onClick = { onClick(item) }
)
}
}
diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/util/ResourcesUtils.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/util/ResourcesUtils.kt
index fe3ae36..550bee8 100644
--- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/util/ResourcesUtils.kt
+++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/util/ResourcesUtils.kt
@@ -42,6 +42,6 @@ object ResourcesUtils {
-1
}
- fun getString(context: Context, @StringRes resId: Int): String =
- context.resources.getString(resId)
+ fun getString(context: Context, @StringRes resId: Int, vararg formatArgs: Any): String =
+ context.resources.getString(resId, *formatArgs)
}
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 9505b89..27179d5 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -26,6 +26,10 @@
未找到相关内容
商店
+ 重试
+ ⚠️ 无法加载商店内容,请稍后%1$s……
+ ⚠️ 重载失败,请稍后重试……
+ ⚠️ 无法载入更多内容,请稍后%1$s……
安装工具
确定安装由用户 %1$s 提供的工具 %2$s 吗?
安装成功
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 00e941b..f80eca7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,6 +25,10 @@
Nothing found
Store
+ try again
+ ⚠️ Unable to load store content, please %1$s later…
+ ⚠️ Reload failed, please try again later…
+ ⚠️ Unable to load more content, please %1$s later…
Install Tool
Are you sure you want to install tool %1$s provided by user %2$s?
Install Success
@@ -40,7 +44,7 @@
Tools
Desc
- ⚠️ Can not open the tool
+ ⚠️ Unable to open the tool
No tools installed yet
Go to store…