Feat(ToolStarScreen): Support star tool
This commit is contained in:
@@ -31,6 +31,15 @@ interface ToolDao {
|
|||||||
"ORDER BY updateTime DESC")
|
"ORDER BY updateTime DESC")
|
||||||
fun selectAllTools(searchValue: String): Flow<List<ToolEntity>>
|
fun selectAllTools(searchValue: String): Flow<List<ToolEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM tools " +
|
||||||
|
"WHERE isStar = 1 " +
|
||||||
|
"AND (:searchValue = '' " +
|
||||||
|
"OR name LIKE '%' || :searchValue || '%' COLLATE NOCASE " +
|
||||||
|
"OR keywords LIKE '%\"%' || :searchValue || '%\"%' COLLATE NOCASE" +
|
||||||
|
") " +
|
||||||
|
"ORDER BY updateTime DESC")
|
||||||
|
fun selectStarTools(searchValue: String): Flow<List<ToolEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM tools " +
|
@Query("SELECT * FROM tools " +
|
||||||
"WHERE authorUsername = :username " +
|
"WHERE authorUsername = :username " +
|
||||||
"and toolId = :toolId LIMIT 1")
|
"and toolId = :toolId LIMIT 1")
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ fun OxygenNavHost(
|
|||||||
onBackClick = navController::popBackStack
|
onBackClick = navController::popBackStack
|
||||||
)
|
)
|
||||||
starScreen(
|
starScreen(
|
||||||
isVertical = isVertical
|
isVertical = isVertical,
|
||||||
|
searchValue = searchValue,
|
||||||
|
onNavigateToToolView = navController::navigateToToolView
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,9 @@ const val STAR_ROUTE = "star_route"
|
|||||||
fun NavController.navigateToStar(navOptions: NavOptions) = navigate(STAR_ROUTE, navOptions)
|
fun NavController.navigateToStar(navOptions: NavOptions) = navigate(STAR_ROUTE, navOptions)
|
||||||
|
|
||||||
fun NavGraphBuilder.starScreen(
|
fun NavGraphBuilder.starScreen(
|
||||||
isVertical: Boolean
|
isVertical: Boolean,
|
||||||
|
searchValue: String,
|
||||||
|
onNavigateToToolView: (username: String, toolId: String, preview: Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = STAR_ROUTE,
|
route = STAR_ROUTE,
|
||||||
@@ -38,6 +40,9 @@ fun NavGraphBuilder.starScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
StarRoute()
|
StarRoute(
|
||||||
|
searchValue = searchValue,
|
||||||
|
onNavigateToToolView = onNavigateToToolView
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ interface ToolRepository {
|
|||||||
|
|
||||||
fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>>
|
fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>>
|
||||||
|
|
||||||
|
fun getStarToolsStream(searchValue: String): Flow<List<ToolEntity>>
|
||||||
|
|
||||||
fun getToolById(id: Long): Flow<ToolEntity?>
|
fun getToolById(id: Long): Flow<ToolEntity?>
|
||||||
|
|
||||||
fun getToolByUsernameAndToolId(username: String, toolId: String): Flow<ToolEntity?>
|
fun getToolByUsernameAndToolId(username: String, toolId: String): Flow<ToolEntity?>
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ class OfflineToolRepository @Inject constructor(
|
|||||||
override fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>> =
|
override fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>> =
|
||||||
toolDao.selectAllTools(searchValue)
|
toolDao.selectAllTools(searchValue)
|
||||||
|
|
||||||
|
override fun getStarToolsStream(searchValue: String): Flow<List<ToolEntity>> =
|
||||||
|
toolDao.selectStarTools(searchValue)
|
||||||
|
|
||||||
override fun getToolById(id: Long): Flow<ToolEntity?> =
|
override fun getToolById(id: Long): Flow<ToolEntity?> =
|
||||||
toolDao.selectToolById(id)
|
toolDao.selectToolById(id)
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package top.fatweb.oxygen.toolbox.ui.star
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun StarRoute(
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
StarScreen(
|
|
||||||
modifier = modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun StarScreen(
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
package top.fatweb.oxygen.toolbox.ui.star
|
||||||
|
|
||||||
|
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
|
||||||
|
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.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
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.tool.ToolEntity
|
||||||
|
import top.fatweb.oxygen.toolbox.ui.component.DialogClickerRow
|
||||||
|
import top.fatweb.oxygen.toolbox.ui.component.DialogSectionGroup
|
||||||
|
import top.fatweb.oxygen.toolbox.ui.component.DialogTitle
|
||||||
|
import top.fatweb.oxygen.toolbox.ui.component.ToolCard
|
||||||
|
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
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun StarRoute(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: StarScreenViewModel = hiltViewModel(),
|
||||||
|
searchValue: String,
|
||||||
|
onNavigateToToolView: (username: String, toolId: String, preview: Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
val starScreenUiState by viewModel.starScreenUiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(searchValue) {
|
||||||
|
viewModel.onSearchValueChange(searchValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
StarScreen(
|
||||||
|
modifier = modifier,
|
||||||
|
starScreenUiState = starScreenUiState,
|
||||||
|
onNavigateToToolView = onNavigateToToolView,
|
||||||
|
onUnstar = viewModel::unstar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun StarScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
starScreenUiState: StarScreenUiState,
|
||||||
|
onNavigateToToolView: (username: String, toolId: String, preview: Boolean) -> Unit,
|
||||||
|
onUnstar: (ToolEntity) -> Unit
|
||||||
|
) {
|
||||||
|
ReportDrawnWhen { starScreenUiState !is StarScreenUiState.Loading }
|
||||||
|
|
||||||
|
val itemsAvailable = howManyTools(starScreenUiState)
|
||||||
|
|
||||||
|
val state = rememberLazyStaggeredGridState()
|
||||||
|
val scrollbarState = state.scrollbarState(itemsAvailable = itemsAvailable)
|
||||||
|
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||||
|
|
||||||
|
var selectedTool by remember { mutableStateOf<ToolEntity?>(null) }
|
||||||
|
var isShowMenu by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
when (starScreenUiState) {
|
||||||
|
StarScreenUiState.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 = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StarScreenUiState.Nothing -> {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.feature_star_no_tools_starred))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is StarScreenUiState.Success -> {
|
||||||
|
LazyVerticalStaggeredGrid(
|
||||||
|
columns = StaggeredGridCells.Adaptive(160.dp),
|
||||||
|
contentPadding = PaddingValues(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalItemSpacing = 24.dp,
|
||||||
|
state = state
|
||||||
|
) {
|
||||||
|
toolsPanel(
|
||||||
|
toolItems = starScreenUiState.tools,
|
||||||
|
onClick = onNavigateToToolView,
|
||||||
|
onLongClick = {
|
||||||
|
selectedTool = it
|
||||||
|
isShowMenu = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
item(span = StaggeredGridItemSpan.FullLine) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.DraggableScrollbar(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.windowInsetsPadding(WindowInsets.systemBars)
|
||||||
|
.padding(horizontal = 2.dp)
|
||||||
|
.align(Alignment.CenterEnd),
|
||||||
|
state = scrollbarState,
|
||||||
|
orientation = Orientation.Vertical,
|
||||||
|
onThumbMoved = state.rememberDraggableScroller(itemsAvailable = itemsAvailable)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isShowMenu && selectedTool != null) {
|
||||||
|
ToolMenu(
|
||||||
|
onDismiss = { isShowMenu = false },
|
||||||
|
selectedTool = selectedTool!!,
|
||||||
|
onUnstar = {
|
||||||
|
isShowMenu = false
|
||||||
|
onUnstar(selectedTool!!)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyStaggeredGridScope.toolsPanel(
|
||||||
|
toolItems: List<ToolEntity>,
|
||||||
|
onClick: (username: String, toolId: String, preview: Boolean) -> Unit,
|
||||||
|
onLongClick: (ToolEntity) -> Unit
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = toolItems,
|
||||||
|
key = { it.id }
|
||||||
|
) {
|
||||||
|
ToolCard(
|
||||||
|
tool = it,
|
||||||
|
actionIcon = if (it.isStar) OxygenIcons.Star else null,
|
||||||
|
actionIconContentDescription = stringResource(R.string.core_star),
|
||||||
|
onClick = { onClick(it.authorUsername, it.toolId, false) },
|
||||||
|
onLongClick = { onLongClick(it) },
|
||||||
|
onAction = { onClick(it.authorUsername, it.toolId, false) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun ToolMenu(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
selectedTool: ToolEntity,
|
||||||
|
onUnstar: () -> Unit
|
||||||
|
) {
|
||||||
|
ModalBottomSheet(onDismissRequest = onDismiss, dragHandle = {}) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier.padding(16.dp)
|
||||||
|
){
|
||||||
|
DialogTitle(text = selectedTool.name)
|
||||||
|
HorizontalDivider()
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
DialogSectionGroup {
|
||||||
|
DialogClickerRow(
|
||||||
|
icon = OxygenIcons.StarBorder,
|
||||||
|
text = stringResource(R.string.core_unstar),
|
||||||
|
onClick = onUnstar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun howManyTools(starScreenUiState: StarScreenUiState) =
|
||||||
|
when (starScreenUiState) {
|
||||||
|
StarScreenUiState.Loading, StarScreenUiState.Nothing -> 0
|
||||||
|
is StarScreenUiState.Success -> starScreenUiState.tools.size
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package top.fatweb.oxygen.toolbox.ui.star
|
||||||
|
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
|
||||||
|
import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class StarScreenViewModel @Inject constructor(
|
||||||
|
private val toolRepository: ToolRepository,
|
||||||
|
private val savedStateHandle: SavedStateHandle
|
||||||
|
) : ViewModel() {
|
||||||
|
private val searchValue = savedStateHandle.getStateFlow(SEARCH_VALUE, "")
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
val starScreenUiState: StateFlow<StarScreenUiState> =
|
||||||
|
searchValue.flatMapLatest { searchValue ->
|
||||||
|
toolRepository.getStarToolsStream(searchValue).map {
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
StarScreenUiState.Nothing
|
||||||
|
} else {
|
||||||
|
StarScreenUiState.Success(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
initialValue = StarScreenUiState.Loading,
|
||||||
|
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onSearchValueChange(value: String) {
|
||||||
|
savedStateHandle[SEARCH_VALUE] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unstar(tool: ToolEntity) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toolRepository.updateTool(tool.copy(isStar = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface StarScreenUiState {
|
||||||
|
data object Loading : StarScreenUiState
|
||||||
|
data object Nothing : StarScreenUiState
|
||||||
|
data class Success(val tools: List<ToolEntity>) : StarScreenUiState
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val SEARCH_VALUE = "searchValue"
|
||||||
@@ -86,19 +86,21 @@ internal fun ToolsRoute(
|
|||||||
onNavigateToToolStore = onNavigateToToolStore,
|
onNavigateToToolStore = onNavigateToToolStore,
|
||||||
toolsScreenUiState = toolsScreenUiStateState,
|
toolsScreenUiState = toolsScreenUiStateState,
|
||||||
onUninstall = viewModel::uninstall,
|
onUninstall = viewModel::uninstall,
|
||||||
onUndo = viewModel::undo
|
onUndo = viewModel::undo,
|
||||||
|
onChangeStar = viewModel::changeStar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ToolsScreen(
|
internal fun ToolsScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
toolsScreenUiState: ToolsScreenUiState,
|
||||||
onShowSnackbar: suspend (message: String, action: String?) -> Boolean,
|
onShowSnackbar: suspend (message: String, action: String?) -> Boolean,
|
||||||
onNavigateToToolView: (username: String, toolId: String, preview: Boolean) -> Unit,
|
onNavigateToToolView: (username: String, toolId: String, preview: Boolean) -> Unit,
|
||||||
onNavigateToToolStore: () -> Unit,
|
onNavigateToToolStore: () -> Unit,
|
||||||
toolsScreenUiState: ToolsScreenUiState,
|
|
||||||
onUninstall: (ToolEntity) -> Unit,
|
onUninstall: (ToolEntity) -> Unit,
|
||||||
onUndo: (ToolEntity) -> Unit
|
onUndo: (ToolEntity) -> Unit,
|
||||||
|
onChangeStar: (ToolEntity, Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
val localContext = LocalContext.current
|
val localContext = LocalContext.current
|
||||||
|
|
||||||
@@ -164,16 +166,18 @@ internal fun ToolsScreen(
|
|||||||
state = state
|
state = state
|
||||||
) {
|
) {
|
||||||
|
|
||||||
toolsPanel(toolItems = toolsScreenUiState.tools,
|
toolsPanel(
|
||||||
|
toolItems = toolsScreenUiState.tools,
|
||||||
onClick = onNavigateToToolView,
|
onClick = onNavigateToToolView,
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
selectedTool = it
|
selectedTool = it
|
||||||
isShowMenu = true
|
isShowMenu = true
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
item(span = StaggeredGridItemSpan.FullLine) {
|
item(span = StaggeredGridItemSpan.FullLine) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
|
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +211,10 @@ internal fun ToolsScreen(
|
|||||||
onUndo(selectedTool!!)
|
onUndo(selectedTool!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onChangeStar = {
|
||||||
|
isShowMenu = false
|
||||||
|
onChangeStar(selectedTool!!, it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -219,11 +227,16 @@ private fun LazyStaggeredGridScope.toolsPanel(
|
|||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = toolItems,
|
items = toolItems,
|
||||||
key = { it.id },
|
key = { it.id }
|
||||||
) {
|
) {
|
||||||
ToolCard(tool = it,
|
ToolCard(
|
||||||
|
tool = it,
|
||||||
|
actionIcon = if (it.isStar) OxygenIcons.Star else null,
|
||||||
|
actionIconContentDescription = stringResource(R.string.core_star),
|
||||||
onClick = { onClick(it.authorUsername, it.toolId, false) },
|
onClick = { onClick(it.authorUsername, it.toolId, false) },
|
||||||
onLongClick = { onLongClick(it) })
|
onLongClick = { onLongClick(it) },
|
||||||
|
onAction = { onClick(it.authorUsername, it.toolId, false) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +246,8 @@ private fun ToolMenu(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
selectedTool: ToolEntity,
|
selectedTool: ToolEntity,
|
||||||
onUninstall: () -> Unit
|
onUninstall: () -> Unit,
|
||||||
|
onChangeStar: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
ModalBottomSheet(onDismissRequest = onDismiss, dragHandle = {}) {
|
ModalBottomSheet(onDismissRequest = onDismiss, dragHandle = {}) {
|
||||||
Column(
|
Column(
|
||||||
@@ -248,6 +262,11 @@ private fun ToolMenu(
|
|||||||
text = stringResource(R.string.core_uninstall),
|
text = stringResource(R.string.core_uninstall),
|
||||||
onClick = onUninstall
|
onClick = onUninstall
|
||||||
)
|
)
|
||||||
|
DialogClickerRow(
|
||||||
|
icon = if (selectedTool.isStar) OxygenIcons.StarBorder else OxygenIcons.Star,
|
||||||
|
text = stringResource(if (selectedTool.isStar) R.string.core_unstar else R.string.core_star),
|
||||||
|
onClick = { onChangeStar(!selectedTool.isStar) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
|
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
|
||||||
import top.fatweb.oxygen.toolbox.repository.tool.StoreRepository
|
|
||||||
import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository
|
import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ToolsScreenViewModel @Inject constructor(
|
class ToolsScreenViewModel @Inject constructor(
|
||||||
private val storeRepository: StoreRepository,
|
|
||||||
private val toolRepository: ToolRepository,
|
private val toolRepository: ToolRepository,
|
||||||
private val savedStateHandle: SavedStateHandle
|
private val savedStateHandle: SavedStateHandle
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@@ -57,6 +55,12 @@ class ToolsScreenViewModel @Inject constructor(
|
|||||||
toolRepository.saveTool(tool)
|
toolRepository.saveTool(tool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeStar(tool: ToolEntity, star: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toolRepository.updateTool(tool.copy(isStar = star))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface ToolsScreenUiState {
|
sealed interface ToolsScreenUiState {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
<string name="core_upgrading">更新中……</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_star">收藏</string>
|
||||||
|
<string name="core_unstar">取消收藏</string>
|
||||||
<string name="core_cancel">取消</string>
|
<string name="core_cancel">取消</string>
|
||||||
<string name="core_undo">撤消</string>
|
<string name="core_undo">撤消</string>
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@
|
|||||||
<string name="feature_tool_view_preview_suffix">%1$s (预览)</string>
|
<string name="feature_tool_view_preview_suffix">%1$s (预览)</string>
|
||||||
|
|
||||||
<string name="feature_star_title">收藏</string>
|
<string name="feature_star_title">收藏</string>
|
||||||
|
<string name="feature_star_no_tools_starred">暂无工具已收藏</string>
|
||||||
|
|
||||||
<string name="feature_settings_title">设置</string>
|
<string name="feature_settings_title">设置</string>
|
||||||
<string name="feature_settings_language">语言</string>
|
<string name="feature_settings_language">语言</string>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<string name="core_upgrading">Upgrading…</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_star">Star</string>
|
||||||
|
<string name="core_unstar">Unstar</string>
|
||||||
<string name="core_cancel">Cancel</string>
|
<string name="core_cancel">Cancel</string>
|
||||||
<string name="core_undo">Undo</string>
|
<string name="core_undo">Undo</string>
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
<string name="feature_tool_view_preview_suffix">%1$s (Preview)</string>
|
<string name="feature_tool_view_preview_suffix">%1$s (Preview)</string>
|
||||||
|
|
||||||
<string name="feature_star_title">Star</string>
|
<string name="feature_star_title">Star</string>
|
||||||
|
<string name="feature_star_no_tools_starred">No tools starred yet</string>
|
||||||
|
|
||||||
<string name="feature_settings_title">Settings</string>
|
<string name="feature_settings_title">Settings</string>
|
||||||
<string name="feature_settings_language">Language</string>
|
<string name="feature_settings_language">Language</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user