Feat(ToolView): Add ToolView to support execute tool
This commit is contained in:
@@ -7,8 +7,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -43,7 +42,9 @@ import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
|
||||
|
||||
@Composable
|
||||
internal fun AboutRoute(
|
||||
modifier: Modifier = Modifier, onBackClick: () -> Unit, onNavigateToLibraries: () -> Unit
|
||||
modifier: Modifier = Modifier,
|
||||
onNavigateToLibraries: () -> Unit,
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
AboutScreen(
|
||||
modifier = modifier.safeDrawingPadding(),
|
||||
@@ -55,7 +56,9 @@ internal fun AboutRoute(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun AboutScreen(
|
||||
modifier: Modifier = Modifier, onBackClick: () -> Unit, onNavigateToLibraries: () -> Unit
|
||||
modifier: Modifier = Modifier,
|
||||
onNavigateToLibraries: () -> Unit,
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val topAppBarScrollBehavior =
|
||||
@@ -66,7 +69,19 @@ internal fun AboutScreen(
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
containerColor = Color.Transparent,
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||
topBar = {
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.windowInsetsPadding(
|
||||
WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Horizontal
|
||||
)
|
||||
)
|
||||
.verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
OxygenTopAppBar(
|
||||
scrollBehavior = topAppBarScrollBehavior,
|
||||
titleRes = R.string.feature_settings_more_about,
|
||||
@@ -78,21 +93,6 @@ internal fun AboutScreen(
|
||||
),
|
||||
onNavigationClick = onBackClick
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.windowInsetsPadding(
|
||||
WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Horizontal
|
||||
)
|
||||
)
|
||||
.verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(64.dp)
|
||||
)
|
||||
|
||||
@@ -14,13 +14,11 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -29,11 +27,9 @@ 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
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
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.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -112,7 +108,8 @@ internal fun LibrariesScreen(
|
||||
var dialogContent by remember { mutableStateOf("") }
|
||||
var dialogUrl by remember { mutableStateOf("") }
|
||||
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { state.canScrollForward })
|
||||
val topAppBarScrollBehavior =
|
||||
TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { state.canScrollForward })
|
||||
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||
|
||||
@@ -124,7 +121,18 @@ internal fun LibrariesScreen(
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
containerColor = Color.Transparent,
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||
topBar = {
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.windowInsetsPadding(
|
||||
WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Horizontal
|
||||
)
|
||||
)
|
||||
) {
|
||||
OxygenTopAppBar(
|
||||
scrollBehavior = topAppBarScrollBehavior,
|
||||
titleRes = R.string.feature_settings_open_source_license,
|
||||
@@ -153,88 +161,73 @@ internal fun LibrariesScreen(
|
||||
onSearch("")
|
||||
}
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.windowInsetsPadding(
|
||||
WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Horizontal
|
||||
)
|
||||
)
|
||||
) {
|
||||
when (librariesScreenUiState) {
|
||||
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 -> {
|
||||
Text(text = "Nothing")
|
||||
}
|
||||
|
||||
LibrariesScreenUiState.NotFound -> {
|
||||
Text(text = "Not Found")
|
||||
}
|
||||
|
||||
is LibrariesScreenUiState.Success -> {
|
||||
val handleOnClickLicense = { key: String ->
|
||||
val license = librariesScreenUiState.dependencies.licenses[key]
|
||||
if (license != null) {
|
||||
showDialog = true
|
||||
dialogTitle = license.name
|
||||
dialogContent = license.content ?: ""
|
||||
dialogUrl = license.url ?: ""
|
||||
Box(modifier = Modifier) {
|
||||
when (librariesScreenUiState) {
|
||||
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 = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Adaptive(300.dp),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalItemSpacing = 24.dp,
|
||||
state = state
|
||||
) {
|
||||
librariesPanel(
|
||||
librariesScreenUiState = librariesScreenUiState,
|
||||
onClickLicense = handleOnClickLicense
|
||||
LibrariesScreenUiState.Nothing -> {
|
||||
Text(text = "Nothing")
|
||||
}
|
||||
|
||||
LibrariesScreenUiState.NotFound -> {
|
||||
Text(text = "Not Found")
|
||||
}
|
||||
|
||||
is LibrariesScreenUiState.Success -> {
|
||||
val handleOnClickLicense = { key: String ->
|
||||
val license = librariesScreenUiState.dependencies.licenses[key]
|
||||
if (license != null) {
|
||||
showDialog = true
|
||||
dialogTitle = license.name
|
||||
dialogContent = license.content ?: ""
|
||||
dialogUrl = license.url ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Adaptive(300.dp),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalItemSpacing = 24.dp,
|
||||
state = state
|
||||
) {
|
||||
librariesPanel(
|
||||
librariesScreenUiState = librariesScreenUiState,
|
||||
onClickLicense = handleOnClickLicense
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
item(span = StaggeredGridItemSpan.FullLine) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import android.R as androidR
|
||||
fun OxygenTopAppBar(
|
||||
modifier: Modifier = Modifier,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
@StringRes titleRes: Int,
|
||||
@StringRes titleRes: Int? = null,
|
||||
navigationIcon: ImageVector? = null,
|
||||
navigationIconContentDescription: String? = null,
|
||||
actionIcon: ImageVector? = null,
|
||||
@@ -120,7 +120,7 @@ fun OxygenTopAppBar(
|
||||
if ("\n" !in it) onQueryChange(it)
|
||||
}
|
||||
)
|
||||
else Text(stringResource(titleRes))
|
||||
else if (titleRes != null) Text(stringResource(titleRes))
|
||||
},
|
||||
navigationIcon = {
|
||||
navigationIcon?.let {
|
||||
|
||||
@@ -108,7 +108,7 @@ fun ToolInfo(
|
||||
toolDesc: String
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package top.fatweb.oxygen.toolbox.ui.tool
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
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.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.kevinnzou.web.WebView
|
||||
import com.kevinnzou.web.rememberWebViewStateWithHTMLData
|
||||
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
|
||||
|
||||
@Composable
|
||||
internal fun ToolViewRoute(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: ToolViewScreenViewModel = hiltViewModel(),
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
val toolViewUiState by viewModel.toolViewUiState.collectAsStateWithLifecycle()
|
||||
|
||||
ToolViewScreen(
|
||||
modifier = modifier.safeDrawingPadding(),
|
||||
onBackClick = onBackClick,
|
||||
toolViewUiState = toolViewUiState
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun ToolViewScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
onBackClick: () -> Unit,
|
||||
toolViewUiState: ToolViewUiState
|
||||
) {
|
||||
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||
|
||||
Box(
|
||||
modifier = modifier.windowInsetsPadding(WindowInsets(0, 0, 0, 0)),
|
||||
) {
|
||||
OxygenTopAppBar(
|
||||
modifier = Modifier.zIndex(100f),
|
||||
navigationIcon = OxygenIcons.Back,
|
||||
navigationIconContentDescription = stringResource(R.string.core_back),
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Transparent
|
||||
),
|
||||
onNavigationClick = onBackClick
|
||||
)
|
||||
when (toolViewUiState) {
|
||||
ToolViewUiState.Loading -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
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 = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ToolViewUiState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(text = stringResource(R.string.feature_tools_can_not_open))
|
||||
}
|
||||
}
|
||||
|
||||
is ToolViewUiState.Success -> {
|
||||
val webViewState = rememberWebViewStateWithHTMLData(
|
||||
data = toolViewUiState.htmlData,
|
||||
)
|
||||
WebView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = webViewState,
|
||||
onCreated = {
|
||||
it.settings.javaScriptEnabled = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package top.fatweb.oxygen.toolbox.ui.tool
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import top.fatweb.oxygen.toolbox.model.Result
|
||||
import top.fatweb.oxygen.toolbox.navigation.ToolViewArgs
|
||||
import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository
|
||||
import top.fatweb.oxygen.toolbox.util.decodeToStringWithZip
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@HiltViewModel
|
||||
class ToolViewScreenViewModel @Inject constructor(
|
||||
toolRepository: ToolRepository,
|
||||
savedStateHandle: SavedStateHandle
|
||||
) : ViewModel() {
|
||||
private val toolViewArgs = ToolViewArgs(savedStateHandle)
|
||||
val username = toolViewArgs.username
|
||||
val toolId = toolViewArgs.toolId
|
||||
|
||||
val toolViewUiState: StateFlow<ToolViewUiState> = toolViewUiState(
|
||||
username = username,
|
||||
toolId = toolId,
|
||||
toolRepository = toolRepository
|
||||
)
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
initialValue = ToolViewUiState.Loading,
|
||||
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds)
|
||||
)
|
||||
}
|
||||
|
||||
private fun toolViewUiState(
|
||||
username: String,
|
||||
toolId: String,
|
||||
toolRepository: ToolRepository
|
||||
): Flow<ToolViewUiState> {
|
||||
val result = toolRepository.detail(
|
||||
username = username,
|
||||
toolId = toolId
|
||||
)
|
||||
val toolViewTemplate = toolRepository.toolViewTemplate
|
||||
|
||||
return combine(result, toolViewTemplate, ::Pair).map { (result, toolViewTemplate) ->
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val dist = result.data.dist!!
|
||||
val base = result.data.base!!
|
||||
ToolViewUiState.Success(
|
||||
processHtml(
|
||||
toolViewTemplate = toolViewTemplate,
|
||||
distBase64 = dist,
|
||||
baseBase64 = base
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is Result.Loading -> ToolViewUiState.Loading
|
||||
is Result.Error -> {
|
||||
Log.e("TAG", "toolViewUiState: can not load tool", result.exception)
|
||||
|
||||
ToolViewUiState.Error
|
||||
}
|
||||
|
||||
is Result.Fail -> ToolViewUiState.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ToolViewUiState {
|
||||
data class Success(val htmlData: String) : ToolViewUiState
|
||||
|
||||
data object Error : ToolViewUiState
|
||||
|
||||
data object Loading : ToolViewUiState
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun processHtml(toolViewTemplate: String, distBase64: String, baseBase64: String): String {
|
||||
val dist = Base64.decodeToStringWithZip(distBase64)
|
||||
val base = Base64.decodeToStringWithZip(baseBase64)
|
||||
|
||||
return toolViewTemplate.replace("{{replace_code}}", "$dist\n$base")
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package top.fatweb.oxygen.toolbox.ui.tool
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.ReportDrawnWhen
|
||||
import androidx.compose.animation.core.Ease
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
@@ -35,7 +34,6 @@ 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
|
||||
@@ -52,6 +50,7 @@ import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
|
||||
internal fun ToolsRoute(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: ToolsScreenViewModel = hiltViewModel(),
|
||||
onNavigateToToolView: (username: String, toolId: String) -> Unit,
|
||||
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||
handleOnCanScrollChange: (Boolean) -> Unit
|
||||
) {
|
||||
@@ -59,6 +58,7 @@ internal fun ToolsRoute(
|
||||
|
||||
ToolsScreen(
|
||||
modifier = modifier,
|
||||
onNavigateToToolView = onNavigateToToolView,
|
||||
onShowSnackbar = onShowSnackbar,
|
||||
handleOnCanScrollChange = handleOnCanScrollChange,
|
||||
toolStorePagingItems = toolStorePagingItems
|
||||
@@ -68,11 +68,11 @@ internal fun ToolsRoute(
|
||||
@Composable
|
||||
internal fun ToolsScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
onNavigateToToolView: (username: String, toolId: String) -> Unit,
|
||||
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||
handleOnCanScrollChange: (Boolean) -> Unit,
|
||||
toolStorePagingItems: LazyPagingItems<Tool>
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val isToolLoading =
|
||||
toolStorePagingItems.loadState.refresh is LoadState.Loading
|
||||
|| toolStorePagingItems.loadState.append is LoadState.Loading
|
||||
@@ -86,10 +86,6 @@ internal fun ToolsScreen(
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -106,7 +102,7 @@ internal fun ToolsScreen(
|
||||
|
||||
toolsPanel(
|
||||
toolStorePagingItems = toolStorePagingItems,
|
||||
onClickToolCard = handleOnClickToolCard
|
||||
onClickToolCard = onNavigateToToolView
|
||||
)
|
||||
|
||||
item(span = StaggeredGridItemSpan.FullLine) {
|
||||
|
||||
Reference in New Issue
Block a user