Feat(ToolStore): Support upgrade tool

This commit is contained in:
2024-08-14 15:26:10 +08:00
parent f5cfbd7296
commit 6732eb6a22
7 changed files with 189 additions and 74 deletions

View File

@@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Inbox import androidx.compose.material.icons.filled.Inbox
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Reorder import androidx.compose.material.icons.filled.Reorder
import androidx.compose.material.icons.filled.Upgrade
import androidx.compose.material.icons.outlined.Home import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.StarBorder import androidx.compose.material.icons.outlined.StarBorder
@@ -55,6 +56,7 @@ object OxygenIcons {
val Success = Icons.Rounded.CheckCircle val Success = Icons.Rounded.CheckCircle
val Time = Icons.Default.AccessTime val Time = Icons.Default.AccessTime
val Tool = Icons.Default.Build val Tool = Icons.Default.Build
val Upgrade = Icons.Default.Upgrade
@OptIn(ExperimentalEncodingApi::class) @OptIn(ExperimentalEncodingApi::class)
fun fromSvgBase64(base64String: String): ImageBitmap { fun fromSvgBase64(base64String: String): ImageBitmap {

View File

@@ -54,6 +54,25 @@ data class ToolEntity(
@ColumnInfo(defaultValue = "NULL") @ColumnInfo(defaultValue = "NULL")
val upgrade: String? = null val upgrade: String? = null
) { ) {
constructor(toolId: String, authorUsername: String, ver: String, upgrade: String? = null) :
this(
id = -1,
name = "Unknown",
toolId = toolId,
icon = "",
platform = Platform.Android,
authorUsername = authorUsername,
authorNickname = "Unknown",
authorAvatar = "",
ver = ver,
keywords = emptyList(),
categories = emptyList(),
entryPoint = "",
createTime = LocalDateTime(1970, 1, 1, 0, 0),
updateTime = LocalDateTime(1970, 1, 1, 0, 0),
upgrade = upgrade
)
enum class Platform { enum class Platform {
Web, Web,

View File

@@ -39,6 +39,7 @@ import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
fun ToolCard( fun ToolCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
tool: ToolEntity, tool: ToolEntity,
specifyVer: String? = null,
actionIcon: ImageVector? = null, actionIcon: ImageVector? = null,
actionIconContentDescription: String = "", actionIconContentDescription: String = "",
onAction: () -> Unit = {}, onAction: () -> Unit = {},
@@ -59,7 +60,7 @@ fun ToolCard(
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
) { ) {
ToolHeader( ToolHeader(
ver = tool.ver, ver = specifyVer ?: tool.ver,
actionIcon = actionIcon, actionIcon = actionIcon,
actionIconContentDescription = actionIconContentDescription, actionIconContentDescription = actionIconContentDescription,
onAction = onAction onAction = onAction

View File

@@ -73,7 +73,10 @@ internal fun ToolStoreRoute(
modifier = modifier, modifier = modifier,
onNavigateToToolView = onNavigateToToolView, onNavigateToToolView = onNavigateToToolView,
toolStorePagingItems = toolStorePagingItems, toolStorePagingItems = toolStorePagingItems,
onChangeInstallStatus = viewModel::changeInstallStatus, onChangeInstallStatus = viewModel::changeInstallInfo,
onChangeInstallType = {
viewModel.changeInstallInfo(type = it)
},
onInstallTool = viewModel::installTool, onInstallTool = viewModel::installTool,
installInfo = installInfo installInfo = installInfo
) )
@@ -84,8 +87,9 @@ internal fun ToolStoreScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onNavigateToToolView: (username: String, toolId: String) -> Unit, onNavigateToToolView: (username: String, toolId: String) -> Unit,
toolStorePagingItems: LazyPagingItems<ToolEntity>, toolStorePagingItems: LazyPagingItems<ToolEntity>,
onChangeInstallStatus: (installStatus: ToolStoreUiState.Status) -> Unit, onChangeInstallStatus: (status: ToolStoreUiState.InstallInfo.Status) -> Unit,
onInstallTool: (username: String, toolId: String) -> Unit, onChangeInstallType: (type: ToolStoreUiState.InstallInfo.Type) -> Unit,
onInstallTool: (installTool: ToolEntity) -> Unit,
installInfo: ToolStoreUiState.InstallInfo installInfo: ToolStoreUiState.InstallInfo
) { ) {
val isToolLoading = val isToolLoading =
@@ -101,8 +105,7 @@ internal fun ToolStoreScreen(
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition") val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
var installToolUsername by remember { mutableStateOf("Unknown") } var installTool by remember { mutableStateOf(ToolEntity("Unknown", "Unknown", "Unknown")) }
var installToolId by remember { mutableStateOf("Unknown") }
Box( Box(
modifier.fillMaxSize() modifier.fillMaxSize()
@@ -116,12 +119,14 @@ internal fun ToolStoreScreen(
) { ) {
toolsPanel( toolsPanel(
toolStorePagingItems = toolStorePagingItems, toolStorePagingItems = toolStorePagingItems,
onAction = { username, toolId -> onAction = { tool, installType ->
installToolUsername = username installTool = tool
installToolId = toolId onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.Pending)
onChangeInstallStatus(ToolStoreUiState.Status.Pending) onChangeInstallType(installType)
}, },
onClick = onNavigateToToolView onClick = {
onNavigateToToolView(it.authorUsername, it.toolId)
}
) )
item(span = StaggeredGridItemSpan.FullLine) { item(span = StaggeredGridItemSpan.FullLine) {
@@ -165,18 +170,17 @@ internal fun ToolStoreScreen(
} }
InstallAlertDialog( InstallAlertDialog(
status = installInfo.status, installTool = installTool,
installInfo = installInfo,
onChangeInstallStatus = onChangeInstallStatus, onChangeInstallStatus = onChangeInstallStatus,
onInstallTool = { onInstallTool(installToolUsername, installToolId) }, onInstallTool = { onInstallTool(installTool) }
username = installToolUsername,
toolId = installToolId
) )
} }
private fun LazyStaggeredGridScope.toolsPanel( private fun LazyStaggeredGridScope.toolsPanel(
toolStorePagingItems: LazyPagingItems<ToolEntity>, toolStorePagingItems: LazyPagingItems<ToolEntity>,
onAction: (username: String, toolId: String) -> Unit, onAction: (installTool: ToolEntity, installType: ToolStoreUiState.InstallInfo.Type) -> Unit,
onClick: (username: String, toolId: String) -> Unit onClick: (ToolEntity) -> Unit
) { ) {
items( items(
items = toolStorePagingItems.itemSnapshotList, items = toolStorePagingItems.itemSnapshotList,
@@ -184,28 +188,34 @@ private fun LazyStaggeredGridScope.toolsPanel(
) { ) {
ToolCard( ToolCard(
tool = it!!, tool = it!!,
actionIcon = if (!it.isInstalled) OxygenIcons.Download else null, specifyVer = it.upgrade,
actionIcon = if (it.upgrade != null) OxygenIcons.Upgrade else if (!it.isInstalled) OxygenIcons.Download else null,
actionIconContentDescription = stringResource(R.string.core_install), actionIconContentDescription = stringResource(R.string.core_install),
onAction = { onAction(it.authorUsername, it.toolId) }, onAction = {
onClick = { onClick(it.authorUsername, it.toolId) }, onAction(
onLongClick = { onClick(it.authorUsername, it.toolId) }, it,
if (it.upgrade != null) ToolStoreUiState.InstallInfo.Type.Upgrade else ToolStoreUiState.InstallInfo.Type.Install
)
},
onClick = { onClick(it) }
) )
} }
} }
@Composable @Composable
private fun InstallAlertDialog( private fun InstallAlertDialog(
status: ToolStoreUiState.Status, installTool: ToolEntity,
onChangeInstallStatus: (ToolStoreUiState.Status) -> Unit, installInfo: ToolStoreUiState.InstallInfo,
onInstallTool: () -> Unit, onChangeInstallStatus: (ToolStoreUiState.InstallInfo.Status) -> Unit,
username: String, onInstallTool: () -> Unit
toolId: String
) { ) {
if (status != ToolStoreUiState.Status.None) { val (status, type) = installInfo
if (status != ToolStoreUiState.InstallInfo.Status.None) {
AlertDialog( AlertDialog(
onDismissRequest = { onDismissRequest = {
if (status == ToolStoreUiState.Status.Pending) { if (status == ToolStoreUiState.InstallInfo.Status.Pending) {
onChangeInstallStatus(ToolStoreUiState.Status.None) onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None)
} }
}, },
title = { title = {
@@ -214,20 +224,34 @@ private fun InstallAlertDialog(
) { ) {
Icon( Icon(
imageVector = when (status) { imageVector = when (status) {
ToolStoreUiState.Status.Success -> OxygenIcons.Success ToolStoreUiState.InstallInfo.Status.Success -> OxygenIcons.Success
ToolStoreUiState.Status.Fail -> OxygenIcons.Error ToolStoreUiState.InstallInfo.Status.Fail -> OxygenIcons.Error
else -> OxygenIcons.Info else -> OxygenIcons.Info
}, },
contentDescription = stringResource(R.string.core_install) contentDescription = stringResource(
when (type) {
ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_install
ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrade
}
)
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = stringResource( text = stringResource(
when (status) { when (type) {
ToolStoreUiState.Status.Success -> R.string.feature_store_install_success ToolStoreUiState.InstallInfo.Type.Install -> when (status) {
ToolStoreUiState.Status.Fail -> R.string.feature_store_install_fail ToolStoreUiState.InstallInfo.Status.Success -> R.string.feature_store_install_success
else -> R.string.feature_store_install_tool ToolStoreUiState.InstallInfo.Status.Fail -> R.string.feature_store_install_fail
else -> R.string.feature_store_install_tool
}
ToolStoreUiState.InstallInfo.Type.Upgrade -> when (status) {
ToolStoreUiState.InstallInfo.Status.Success -> R.string.feature_store_upgrade_success
ToolStoreUiState.InstallInfo.Status.Fail -> R.string.feature_store_upgrade_fail
else -> R.string.feature_store_upgrade_tool
}
} }
) )
) )
} }
@@ -239,39 +263,71 @@ private fun InstallAlertDialog(
.padding(vertical = 16.dp) .padding(vertical = 16.dp)
) { ) {
when (status) { when (status) {
ToolStoreUiState.Status.Pending -> ToolStoreUiState.InstallInfo.Status.Pending ->
Text( Text(
text = stringResource( text = when (type) {
R.string.feature_store_ask_install, ToolStoreUiState.InstallInfo.Type.Install -> stringResource(
username, R.string.feature_store_ask_install,
toolId installTool.authorUsername,
) installTool.toolId
)
ToolStoreUiState.InstallInfo.Type.Upgrade -> stringResource(
R.string.feature_store_ask_upgrade,
installTool.authorUsername,
installTool.toolId,
installTool.ver,
installTool.upgrade ?: installTool.ver
)
}
) )
ToolStoreUiState.Status.Installing -> ToolStoreUiState.InstallInfo.Status.Processing ->
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text(text = stringResource(R.string.core_installing)) Text(
text = stringResource(
when (type) {
ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_installing
ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrading
}
)
)
} }
ToolStoreUiState.Status.Success -> ToolStoreUiState.InstallInfo.Status.Success ->
Text(text = stringResource(R.string.feature_store_install_success_info)) Text(
text = stringResource(
when (type) {
ToolStoreUiState.InstallInfo.Type.Install -> R.string.feature_store_install_success_info
ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.feature_store_upgrade_success_info
}
)
)
ToolStoreUiState.Status.Fail -> ToolStoreUiState.InstallInfo.Status.Fail ->
Text(text = stringResource(R.string.feature_store_install_fail_info)) Text(
text = stringResource(
when (type) {
ToolStoreUiState.InstallInfo.Type.Install -> R.string.feature_store_install_fail_info
ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.feature_store_upgrade_fail_info
}
)
)
else -> Unit else -> Unit
} }
} }
}, },
dismissButton = { dismissButton = {
if (status == ToolStoreUiState.Status.Pending) { if (status == ToolStoreUiState.InstallInfo.Status.Pending) {
TextButton(onClick = { TextButton(onClick = {
onChangeInstallStatus(ToolStoreUiState.Status.None) onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None)
}) { }) {
Text(text = stringResource(R.string.core_cancel)) Text(text = stringResource(R.string.core_cancel))
} }
@@ -279,19 +335,23 @@ private fun InstallAlertDialog(
}, },
confirmButton = { confirmButton = {
when (status) { when (status) {
ToolStoreUiState.Status.Pending -> ToolStoreUiState.InstallInfo.Status.Pending ->
TextButton(onClick = onInstallTool) { TextButton(onClick = onInstallTool) {
Text(text = stringResource(R.string.core_install)) Text(text = stringResource(
when (type) {
ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_install
ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrade
}))
} }
ToolStoreUiState.Status.Success, ToolStoreUiState.InstallInfo.Status.Success,
ToolStoreUiState.Status.Fail -> ToolStoreUiState.InstallInfo.Status.Fail ->
TextButton(onClick = { TextButton(onClick = {
onChangeInstallStatus(ToolStoreUiState.Status.None) onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None)
}) { }) {
Text( Text(
text = stringResource( text = stringResource(
if (status == ToolStoreUiState.Status.Success) R.string.core_ok if (status == ToolStoreUiState.InstallInfo.Status.Success) R.string.core_ok
else R.string.core_close else R.string.core_close
) )
) )

View File

@@ -38,24 +38,36 @@ class ToolStoreViewModel @Inject constructor(
) )
} }
fun changeInstallStatus(installStatus: ToolStoreUiState.Status) { fun changeInstallInfo(
savedStateHandle[INSTALL_INFO] = ToolStoreUiState.InstallInfo(installStatus) status: ToolStoreUiState.InstallInfo.Status = installInfo.value.status,
type: ToolStoreUiState.InstallInfo.Type = installInfo.value.type
) {
savedStateHandle[INSTALL_INFO] = ToolStoreUiState.InstallInfo(status, type)
} }
fun installTool(username: String, toolId: String) { fun installTool(
toolEntity: ToolEntity
) {
viewModelScope.launch { viewModelScope.launch {
storeRepository.detail(username, toolId).collect { storeRepository.detail(toolEntity.authorUsername, toolEntity.toolId).collect { result ->
when (it) { when (result) {
Result.Loading -> savedStateHandle[INSTALL_INFO] = Result.Loading -> changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Processing)
ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Installing)
is Result.Error, is Result.Fail -> savedStateHandle[INSTALL_INFO] = is Result.Error, is Result.Fail -> changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Fail)
ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Fail)
is Result.Success -> { is Result.Success -> {
toolRepository.saveTool(it.data) when (installInfo.value.type) {
savedStateHandle[INSTALL_INFO] = ToolStoreUiState.InstallInfo.Type.Install -> toolRepository.saveTool(
ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Success) result.data
)
ToolStoreUiState.InstallInfo.Type.Upgrade -> {
toolRepository.removeTool(toolEntity)
toolRepository.saveTool(result.data)
}
}
changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Success)
} }
} }
} }
@@ -69,11 +81,16 @@ data class ToolStoreUiState(
) : Parcelable { ) : Parcelable {
@Parcelize @Parcelize
data class InstallInfo( data class InstallInfo(
var status: Status = Status.None var status: Status = Status.None,
) : Parcelable var type: Type = Type.Install
) : Parcelable {
enum class Status {
None, Pending, Processing, Success, Fail
}
enum class Status { enum class Type {
None, Pending, Installing, Success, Fail Install, Upgrade
}
} }
} }

View File

@@ -14,6 +14,8 @@
<string name="core_no_connect">⚠️ 无法连接至互联网</string> <string name="core_no_connect">⚠️ 无法连接至互联网</string>
<string name="core_install">安装</string> <string name="core_install">安装</string>
<string name="core_installing">安装中……</string> <string name="core_installing">安装中……</string>
<string name="core_upgrade">更新</string>
<string name="core_upgrading">更新中……</string>
<string name="core_uninstall">卸载</string> <string name="core_uninstall">卸载</string>
<string name="core_uninstall_success">卸载成功</string> <string name="core_uninstall_success">卸载成功</string>
<string name="core_cancel">取消</string> <string name="core_cancel">取消</string>
@@ -21,11 +23,17 @@
<string name="feature_store_title">商店</string> <string name="feature_store_title">商店</string>
<string name="feature_store_install_tool">安装工具</string> <string name="feature_store_install_tool">安装工具</string>
<string name="feature_store_ask_install">确定安装由用户 %1$s 提供的工具 %2$s 吗?</string> <string name="feature_store_ask_install">确定安装由用户 %1$s 提供的工具 %2$s 吗</string>
<string name="feature_store_install_success">安装成功</string> <string name="feature_store_install_success">安装成功</string>
<string name="feature_store_install_success_info">恭喜!工具安装成功。</string> <string name="feature_store_install_success_info">恭喜!工具安装成功。</string>
<string name="feature_store_install_fail">安装失败</string> <string name="feature_store_install_fail">安装失败</string>
<string name="feature_store_install_fail_info">安装失败!请稍后重试……</string> <string name="feature_store_install_fail_info">安装失败!请稍后重试……</string>
<string name="feature_store_upgrade_tool">更新工具</string>
<string name="feature_store_ask_upgrade">确定要将用户 %2$s 提供的工具 %1$s 从版本 %3$s 更新到版本 %4$s 吗?</string>
<string name="feature_store_upgrade_success">更新成功</string>
<string name="feature_store_upgrade_success_info">恭喜!工具更新成功。</string>
<string name="feature_store_upgrade_fail">更新失败</string>
<string name="feature_store_upgrade_fail_info">更新失败!请稍后重试……</string>
<string name="feature_tools_title">工具</string> <string name="feature_tools_title">工具</string>
<string name="feature_tools_description">简介</string> <string name="feature_tools_description">简介</string>

View File

@@ -13,6 +13,8 @@
<string name="core_no_connect">⚠️ Unable to connect to the internet</string> <string name="core_no_connect">⚠️ Unable to connect to the internet</string>
<string name="core_install">Install</string> <string name="core_install">Install</string>
<string name="core_installing">Installing…</string> <string name="core_installing">Installing…</string>
<string name="core_upgrade">Upgrade</string>
<string name="core_upgrading">Upgrading…</string>
<string name="core_uninstall">Uninstall</string> <string name="core_uninstall">Uninstall</string>
<string name="core_uninstall_success">Uninstalled successfully</string> <string name="core_uninstall_success">Uninstalled successfully</string>
<string name="core_cancel">Cancel</string> <string name="core_cancel">Cancel</string>
@@ -20,11 +22,17 @@
<string name="feature_store_title">Store</string> <string name="feature_store_title">Store</string>
<string name="feature_store_install_tool">Install Tool</string> <string name="feature_store_install_tool">Install Tool</string>
<string name="feature_store_ask_install">Are you sure to install tool %1$s provided by user %2$s?</string> <string name="feature_store_ask_install">Are you sure you want to install tool %1$s provided by user %2$s?</string>
<string name="feature_store_install_success">Install Success</string> <string name="feature_store_install_success">Install Success</string>
<string name="feature_store_install_success_info">Congratulations, the tool installation is successful.</string> <string name="feature_store_install_success_info">Congratulations, the tool installation is successful.</string>
<string name="feature_store_install_fail">Install Failed</string> <string name="feature_store_install_fail">Install Failed</string>
<string name="feature_store_install_fail_info">Installation failed, please try again later…</string> <string name="feature_store_install_fail_info">Installation failed, please try again later…</string>
<string name="feature_store_upgrade_tool">Upgrade Tool</string>
<string name="feature_store_ask_upgrade">Are you sure you want to upgrade tool %1$s provided by user %2$s from version %3$s to version %4$s?</string>
<string name="feature_store_upgrade_success">Upgrade Success</string>
<string name="feature_store_upgrade_success_info">Congratulations, the tool upgrade was successful.</string>
<string name="feature_store_upgrade_fail">Upgrade Failed</string>
<string name="feature_store_upgrade_fail_info">Upgrade failed, please try again later…</string>
<string name="feature_tools_title">Tools</string> <string name="feature_tools_title">Tools</string>
<string name="feature_tools_description">Desc</string> <string name="feature_tools_description">Desc</string>