manager: Bump dependencies, Adjust pull refresh UI, Add transitions back (#2139)

Bump dependencies


| Add transitions back for predictive back | Fix SnackBar overlap on FAB
& Make SnackBar can dismiss
| :-- | :-- |
|
![Screenshot_20241016-235706](https://github.com/user-attachments/assets/f2718523-9800-42ff-ad2f-ad8583c56be7)
|
![Screenshot_20241018-220552](https://github.com/user-attachments/assets/ed53338b-1ac4-4d0a-a5fb-7056f81cac18)
|

Use `androidx.compose.material3.pulltorefresh.PullToRefreshBox` instead
of `androidx.compose.material.pullrefresh.*`
| Before | After |
| --: | --: |
|
![Screenshot_20241016-234930](https://github.com/user-attachments/assets/6b9dbb87-627b-4a02-8f77-9f9f81ae1b4a)
|
![Screenshot_20241016-235336](https://github.com/user-attachments/assets/9134dde4-93c8-4f85-8540-56a7c5a1b0af)
|

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Light_summer
2024-10-19 08:40:02 +08:00
committed by GitHub
parent 05a90542c6
commit 7b3e732404
7 changed files with 80 additions and 81 deletions

View File

@@ -5,6 +5,12 @@ import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.displayCutout import androidx.compose.foundation.layout.displayCutout
@@ -16,7 +22,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -26,9 +31,11 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
import com.ramcosta.composedestinations.generated.NavGraphs import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
@@ -54,19 +61,24 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
KernelSUTheme { KernelSUTheme {
val navController = rememberNavController() val navController = rememberNavController()
val snackbarHostState = remember { SnackbarHostState() } val snackBarHostState = remember { SnackbarHostState() }
Scaffold( Scaffold(
bottomBar = { BottomBar(navController) }, bottomBar = { BottomBar(navController) },
snackbarHost = { SnackbarHost(snackbarHostState) },
contentWindowInsets = WindowInsets(0, 0, 0, 0) contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { innerPadding -> ) { innerPadding ->
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackbarHost provides snackbarHostState, LocalSnackbarHost provides snackBarHostState,
) { ) {
DestinationsNavHost( DestinationsNavHost(
modifier = Modifier.padding(innerPadding), modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root, navGraph = NavGraphs.root,
navController = navController navController = navController,
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
get() = { fadeIn(animationSpec = tween(340)) }
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
get() = { fadeOut(animationSpec = tween(340)) }
}
) )
} }
} }

View File

@@ -32,6 +32,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
@@ -89,7 +90,7 @@ fun AppProfileScreen(
appInfo: SuperUserViewModel.AppInfo, appInfo: SuperUserViewModel.AppInfo,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val snackbarHost = LocalSnackbarHost.current val snackBarHost = LocalSnackbarHost.current
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
@@ -111,6 +112,7 @@ fun AppProfileScreen(
scrollBehavior = scrollBehavior scrollBehavior = scrollBehavior
) )
}, },
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { paddingValues -> ) { paddingValues ->
AppProfileInner( AppProfileInner(
@@ -143,12 +145,12 @@ fun AppProfileScreen(
scope.launch { scope.launch {
if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) { if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) {
if (!setSepolicy(profile.name, it.rules)) { if (!setSepolicy(profile.name, it.rules)) {
snackbarHost.showSnackbar(failToUpdateSepolicy) snackBarHost.showSnackbar(failToUpdateSepolicy)
return@launch return@launch
} }
} }
if (!Natives.setAppProfile(it)) { if (!Natives.setAppProfile(it)) {
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid)) snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
} else { } else {
profile = it profile = it
} }
@@ -188,7 +190,9 @@ private fun AppProfileInner(
) )
Crossfade(targetState = isRootGranted, label = "") { current -> Crossfade(targetState = isRootGranted, label = "") { current ->
Column { Column(
modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */)
) {
if (current) { if (current) {
val initialMode = if (profile.rootUseDefault) { val initialMode = if (profile.rootUseDefault) {
Mode.Default Mode.Default

View File

@@ -22,6 +22,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
@@ -152,6 +153,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
) )
} }
}, },
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
KeyEventBlocker { KeyEventBlocker {

View File

@@ -1,6 +1,7 @@
package me.weishu.kernelsu.ui.screen package me.weishu.kernelsu.ui.screen
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
@@ -31,9 +32,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@@ -42,6 +40,9 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -49,6 +50,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -102,6 +104,7 @@ import okhttp3.OkHttpClient
fun ModuleScreen(navigator: DestinationsNavigator) { fun ModuleScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<ModuleViewModel>() val viewModel = viewModel<ModuleViewModel>()
val context = LocalContext.current val context = LocalContext.current
val snackBarHost = LocalSnackbarHost.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) { if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
@@ -154,7 +157,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
) { innerPadding -> ) { innerPadding ->
when { when {
hasMagisk -> { hasMagisk -> {
@@ -187,21 +191,25 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
.putExtra("name", name) .putExtra("name", name)
) )
} }
} },
context = context,
snackBarHost = snackBarHost
) )
} }
} }
} }
} }
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
private fun ModuleList( private fun ModuleList(
viewModel: ModuleViewModel, viewModel: ModuleViewModel,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
boxModifier: Modifier = Modifier, boxModifier: Modifier = Modifier,
onInstallModule: (Uri) -> Unit, onInstallModule: (Uri) -> Unit,
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit,
context: Context,
snackBarHost: SnackbarHostState
) { ) {
val failedEnable = stringResource(R.string.module_failed_to_enable) val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable) val failedDisable = stringResource(R.string.module_failed_to_disable)
@@ -219,9 +227,6 @@ private fun ModuleList(
val startDownloadingText = stringResource(R.string.module_start_downloading) val startDownloadingText = stringResource(R.string.module_start_downloading)
val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed) val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed)
val snackBarHost = LocalSnackbarHost.current
val context = LocalContext.current
val loadingDialog = rememberLoadingDialog() val loadingDialog = rememberLoadingDialog()
val confirmDialog = rememberConfirmDialog() val confirmDialog = rememberConfirmDialog()
@@ -320,18 +325,21 @@ private fun ModuleList(
} else { } else {
null null
} }
val result = snackBarHost.showSnackbar(message, actionLabel = actionLabel) val result = snackBarHost.showSnackbar(
message = message,
actionLabel = actionLabel,
duration = SnackbarDuration.Long
)
if (result == SnackbarResult.ActionPerformed) { if (result == SnackbarResult.ActionPerformed) {
reboot() reboot()
} }
} }
PullToRefreshBox(
val refreshState = rememberPullRefreshState( modifier = boxModifier,
refreshing = viewModel.isRefreshing, onRefresh = {
onRefresh = { viewModel.fetchModuleList() } viewModel.fetchModuleList()
) },
Box( isRefreshing = viewModel.isRefreshing
boxModifier.pullRefresh(refreshState)
) { ) {
LazyColumn( LazyColumn(
modifier = modifier, modifier = modifier,
@@ -341,7 +349,7 @@ private fun ModuleList(
start = 16.dp, start = 16.dp,
top = 16.dp, top = 16.dp,
end = 16.dp, end = 16.dp,
bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */ bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */
) )
}, },
) { ) {
@@ -398,7 +406,9 @@ private fun ModuleList(
viewModel.fetchModuleList() viewModel.fetchModuleList()
val result = snackBarHost.showSnackbar( val result = snackBarHost.showSnackbar(
rebootToApply, actionLabel = reboot message = rebootToApply,
actionLabel = reboot,
duration = SnackbarDuration.Long
) )
if (result == SnackbarResult.ActionPerformed) { if (result == SnackbarResult.ActionPerformed) {
reboot() reboot()
@@ -430,11 +440,6 @@ private fun ModuleList(
DownloadListener(context, onInstallModule) DownloadListener(context, onInstallModule)
PullRefreshIndicator(
refreshing = viewModel.isRefreshing, state = refreshState, modifier = Modifier.align(
Alignment.TopCenter
)
)
} }
} }

View File

@@ -9,12 +9,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -99,14 +96,12 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
val refreshState = rememberPullRefreshState( PullToRefreshBox(
refreshing = viewModel.isRefreshing, modifier = Modifier.padding(innerPadding),
onRefresh = { scope.launch { viewModel.fetchAppList() } }, onRefresh = {
) scope.launch { viewModel.fetchAppList() }
Box( },
modifier = Modifier isRefreshing = viewModel.isRefreshing
.padding(innerPadding)
.pullRefresh(refreshState)
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
@@ -117,15 +112,8 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
AppItem(app) { AppItem(app) {
navigator.navigate(AppProfileScreenDestination(app)) navigator.navigate(AppProfileScreenDestination(app))
} }
} }
} }
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
} }
} }

View File

@@ -2,7 +2,6 @@ package me.weishu.kernelsu.ui.screen
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
@@ -21,9 +20,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ImportExport import androidx.compose.material.icons.filled.ImportExport
import androidx.compose.material.icons.filled.Sync import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@@ -37,6 +33,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -45,7 +42,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
@@ -155,33 +151,25 @@ fun AppProfileTemplateScreen(
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
val refreshState = rememberPullRefreshState( PullToRefreshBox(
refreshing = viewModel.isRefreshing, modifier = Modifier.padding(innerPadding),
onRefresh = { scope.launch { viewModel.fetchTemplates() } }, isRefreshing = viewModel.isRefreshing,
) onRefresh = {
Box( scope.launch { viewModel.fetchTemplates() }
modifier = Modifier }
.padding(innerPadding)
.pullRefresh(refreshState)
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection), .nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = remember { contentPadding = remember {
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) PaddingValues(bottom = 16.dp + 56.dp + 16.dp /* Scaffold Fab Spacing + Fab container height */)
} }
) { ) {
items(viewModel.templateList, key = { it.id }) { app -> items(viewModel.templateList, key = { it.id }) { app ->
TemplateItem(navigator, app) TemplateItem(navigator, app)
} }
} }
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
} }
} }

View File

@@ -1,17 +1,17 @@
[versions] [versions]
agp = "8.6.1" agp = "8.7.1"
kotlin = "2.0.20" kotlin = "2.0.21"
ksp = "2.0.20-1.0.25" ksp = "2.0.21-1.0.25"
compose-bom = "2024.09.02" compose-bom = "2024.10.00"
lifecycle = "2.8.6" lifecycle = "2.8.6"
navigation = "2.8.1" navigation = "2.8.3"
activity-compose = "1.9.2" activity-compose = "1.9.3"
kotlinx-coroutines = "1.9.0" kotlinx-coroutines = "1.9.0"
coil-compose = "2.7.0" coil-compose = "2.7.0"
compose-destination = "2.1.0-beta12" compose-destination = "2.1.0-beta13"
sheets-compose-dialogs = "1.3.0" sheets-compose-dialogs = "1.3.0"
markdown = "4.6.2" markdown = "4.6.2"
webkit = "1.12.0" webkit = "1.12.1"
appiconloader-coil = "1.5.0" appiconloader-coil = "1.5.0"
parcelablelist = "2.0.1" parcelablelist = "2.0.1"
libsu = "6.0.0" libsu = "6.0.0"