diff --git a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/DishListScreen.kt b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/DishListScreen.kt index 80830381..f10e5693 100644 --- a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/DishListScreen.kt +++ b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/DishListScreen.kt @@ -30,9 +30,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState -import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.FloatingActionButton @@ -42,6 +40,8 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -52,11 +52,11 @@ import cz.lastaapps.menza.features.settings.domain.model.DishListMode import cz.lastaapps.menza.features.settings.domain.model.DishListMode.COMPACT import cz.lastaapps.menza.features.settings.domain.model.DishListMode.GRID import cz.lastaapps.menza.features.settings.domain.model.DishListMode.HORIZONTAL -import cz.lastaapps.menza.features.settings.ui.widget.ImageSizeSetting import cz.lastaapps.menza.features.today.ui.vm.DishListState import cz.lastaapps.menza.features.today.ui.vm.DishListViewModel import cz.lastaapps.menza.features.today.ui.widget.DishListViewModeSwitch import cz.lastaapps.menza.features.today.ui.widget.Experimental +import cz.lastaapps.menza.features.today.ui.widget.ImageSizeSetting import cz.lastaapps.menza.features.today.ui.widget.TodayDishGrid import cz.lastaapps.menza.features.today.ui.widget.TodayDishHorizontal import cz.lastaapps.menza.features.today.ui.widget.TodayDishList @@ -70,8 +70,7 @@ internal fun DishListScreen( viewModel: DishListViewModel, hostState: SnackbarHostState, modifier: Modifier = Modifier, - scrollState: LazyListState = rememberLazyListState(), - scrollGridState: LazyStaggeredGridState = rememberLazyStaggeredGridState(), + scrollStates: ScrollStates, ) { DishListEffects(viewModel, hostState) @@ -86,8 +85,7 @@ internal fun DishListScreen( onImageScale = viewModel::setImageScale, onOliverRow = viewModel::setOliverRow, onDishSelected = onDishSelected, - scrollListState = scrollState, - scrollGridState = scrollGridState, + scrollStates = scrollStates, ) } @@ -111,8 +109,7 @@ private fun DishListContent( onImageScale: (Float) -> Unit, onDishSelected: (Dish) -> Unit, onOliverRow: (Boolean) -> Unit, - scrollListState: LazyListState, - scrollGridState: LazyStaggeredGridState, + scrollStates: ScrollStates, modifier: Modifier = Modifier, ) = Column { val userSettings = state.userSettings @@ -181,7 +178,7 @@ private fun DishListContent( } }, modifier = modifier.fillMaxSize(), - scroll = scrollListState, + scroll = scrollStates.list, ) GRID -> @@ -201,7 +198,7 @@ private fun DishListContent( } }, modifier = modifier.fillMaxSize(), - scrollGrid = scrollGridState, + scrollGrid = scrollStates.grid, ) HORIZONTAL -> @@ -222,7 +219,7 @@ private fun DishListContent( } }, modifier = modifier.fillMaxSize(), - scroll = scrollListState, + scroll = scrollStates.horizontal, ) null -> {} @@ -249,4 +246,35 @@ private fun LiveVideoFeedFab( modifier = Modifier.size(size / 2), ) } +} + +/** + * Holds all the scroll states for different scroll modes. + * The states must not be shared as some animations will get broken + */ +internal data class ScrollStates( + val list: LazyListState = LazyListState(), + val grid: LazyStaggeredGridState = LazyStaggeredGridState(), + val horizontal: LazyListState = LazyListState(), +) { + companion object { + val Saver: Saver = listSaver( + save = { + listOf( + with(LazyListState.Saver) { save(it.list) }, + with(LazyStaggeredGridState.Saver) { save(it.grid) }, + with(LazyListState.Saver) { save(it.horizontal) }, + ) + }, + restore = { list -> + @Suppress("UNCHECKED_CAST") + val llsSaver = LazyListState.Saver as Saver + ScrollStates( + list[0]?.let { llsSaver.restore(it) }!!, + list[1]?.let { LazyStaggeredGridState.Saver.restore(it) }!!, + list[2]?.let { llsSaver.restore(it) }!!, + ) + }, + ) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/TodayScreen.kt b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/TodayScreen.kt index fe94096a..11334f38 100644 --- a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/TodayScreen.kt +++ b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/screen/TodayScreen.kt @@ -28,8 +28,6 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ErrorOutline import androidx.compose.material.icons.filled.Videocam @@ -113,14 +111,10 @@ private fun TodayContent( modifier: Modifier = Modifier, // resets scroll position when new menza is selected - scrollState: LazyListState = rememberSaveable( + scrollStates: ScrollStates = rememberSaveable( state.selectedMenza, - saver = LazyListState.Saver, - ) { LazyListState() }, - scrollGridState: LazyStaggeredGridState = rememberSaveable( - state.selectedMenza, - saver = LazyStaggeredGridState.Saver, - ) { LazyStaggeredGridState() }, + saver = ScrollStates.Saver, + ) { ScrollStates() }, ) { var videoFeedUrl by remember(state.selectedMenza) { mutableStateOf(null) @@ -133,8 +127,7 @@ private fun TodayContent( viewModel = dishListViewModel, modifier = Modifier.fillMaxSize(), hostState = hostState, - scrollState = scrollState, - scrollGridState = scrollGridState, + scrollStates = scrollStates, ) } diff --git a/app/src/main/kotlin/cz/lastaapps/menza/features/settings/ui/widget/ImageSize.kt b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/ImageSize.kt similarity index 98% rename from app/src/main/kotlin/cz/lastaapps/menza/features/settings/ui/widget/ImageSize.kt rename to app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/ImageSize.kt index 387ef132..87ace4b7 100644 --- a/app/src/main/kotlin/cz/lastaapps/menza/features/settings/ui/widget/ImageSize.kt +++ b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/ImageSize.kt @@ -17,7 +17,7 @@ * along with Menza. If not, see . */ -package cz.lastaapps.menza.features.settings.ui.widget +package cz.lastaapps.menza.features.today.ui.widget import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.interaction.MutableInteractionSource @@ -49,7 +49,7 @@ import cz.lastaapps.menza.ui.theme.Padding import cz.lastaapps.menza.ui.util.PreviewWrapper private const val imageSizeMin = .5f -private const val imageSizeMax = 3f +private const val imageSizeMax = 2f @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishHorizontal.kt b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishHorizontal.kt index b54600ff..5570509a 100644 --- a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishHorizontal.kt +++ b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishHorizontal.kt @@ -41,6 +41,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -127,13 +128,13 @@ private fun DishContent( } data.forEach { category -> - item { + item(key = category.name + "_cat_header") { DishHeader( courseType = category, modifier = Modifier.padding(bottom = Padding.Smaller), ) } - item { + item(key = category.name + "_content") { val isOnlyItem = category.dishList.size == 1 if (isOnlyItem) { @@ -142,7 +143,9 @@ private fun DishContent( onDishSelected = onDishSelected, userSettings = userSettings, isOnMetered = isOnMetered, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .animateItem(), ) } else { @@ -168,6 +171,7 @@ private fun DishContent( modifier = Modifier .fillMaxWidth() .horizontalScroll(rememberScrollState()) + .animateItem() .animateContentSize(), ) { category.dishList.forEach { dish -> @@ -180,6 +184,7 @@ private fun DishContent( horizontalArrangement = horizontalArrangement, modifier = Modifier .fillMaxWidth() + .animateItem() .animateContentSize(), ) { items( @@ -249,7 +254,7 @@ private fun DishItem( ) { DishNameRow( dish = dish, - modifier = modifier.weight(1f), + modifier = Modifier.weight(1f), ) if (dish.photoLink == null) { DishBadge( @@ -324,4 +329,5 @@ private fun OliverRowSwitch( } } -private fun amIOliver() = Build.MODEL == "CPH2305" +@Composable +private fun amIOliver() = remember { Build.MODEL == "CPH2305" } diff --git a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishList.kt b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishList.kt index 831eb84d..966bf530 100644 --- a/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishList.kt +++ b/app/src/main/kotlin/cz/lastaapps/menza/features/today/ui/widget/TodayDishList.kt @@ -165,7 +165,8 @@ private fun DishContent( val placedItems = 2 + // header, footer data.size + // sticky headers data.sumOf { it.dishList.size } // items - delay(420.milliseconds) + // as the other nodes are resizing, we need to wait for them to (almost) stop + delay(666.milliseconds) scroll.animateScrollToItem(placedItems) } }