refactor(ui): CompositionLocal for scaffold padding + scaffold hygiene

Follow-up polish to the disappearing-scaffold migration addressing
the review items:

1. Replace the `scaffoldPadding: PaddingValues` sprawl across ~15 feed
   entry points with a single `LocalDisappearingScaffoldPadding`
   CompositionLocal published by the scaffold, plus a
   `rememberFeedContentPadding(inner)` helper that merges it with the
   list's own baseline padding. The contract becomes implicit at the
   call site ("inner LazyColumn uses rememberFeedContentPadding") and
   there's no more optional parameter for a future dev to forget.
   `rememberMergedPadding` stays available for manual use.

2. Fix the stale-closure bug in `DisappearingScaffold`: the NSC is
   `remember`'d and keeps its captured `canScroll` lambda across
   recompositions, but the lambda was reading `allowBarHide`,
   `isActive`, and `accountViewModel` by closure — so later parameter
   updates wouldn't be visible. Wrapped them in `rememberUpdatedState`
   so the NSC's `canScroll` always sees current values.

3. Gate `ResetBarsOnResume` and the `Modifier.nestedScroll(connection)`
   install on `allowBarHide`. Chat detail screens (pinned chrome) no
   longer wire up a lifecycle observer or dispatch scroll events
   through the NSC for bars that never move.

4. Unify header-slot naming: `SearchScreen.DisplaySearchResults` now
   uses `headerContent` to match `CardFeedView.RenderCardFeed`.

5. Add `DisappearingBarNestedScrollTest` covering:
   - onPostScroll never consumes
   - hide/reveal deltas applied to both bars
   - per-bar clamping at their independent limits
   - consumed + available sum (so overscroll works)
   - canScroll=false freezes the bars
   - reverseLayout inverts the delta sign
   - setting a smaller heightLimit clamps the current offset
   - onPostFling returns Velocity.Zero to swallow phantom velocity

All 41 consumer / shared-helper files touched to remove the now-unused
`scaffoldPadding` parameter, replaced with `rememberFeedContentPadding(
FeedPadding)` at the innermost LazyColumn / LazyVerticalGrid. Behaviour
is unchanged; API surface is smaller.

https://claude.ai/code/session_01M3Bj24jLc9aVhMuvn55jXa
This commit is contained in:
Claude
2026-04-19 16:32:55 +00:00
parent 3730d2fb73
commit 154adcf111
42 changed files with 277 additions and 182 deletions
@@ -21,14 +21,12 @@
package com.vitorpamplona.amethyst.ui.feeds package com.vitorpamplona.amethyst.ui.feeds
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
@@ -41,13 +39,12 @@ fun RefresheableFeedContentStateView(
routeForLastRead: String?, routeForLastRead: String?,
enablePullRefresh: Boolean = true, enablePullRefresh: Boolean = true,
scrollStateKey: String? = null, scrollStateKey: String? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
RefresheableBox(feedContentState, enablePullRefresh) { RefresheableBox(feedContentState, enablePullRefresh) {
SaveableFeedContentState(feedContentState, scrollStateKey) { listState -> SaveableFeedContentState(feedContentState, scrollStateKey) { listState ->
RenderFeedContentState(feedContentState, accountViewModel, listState, nav, routeForLastRead, scaffoldPadding) RenderFeedContentState(feedContentState, accountViewModel, listState, nav, routeForLastRead)
} }
} }
} }
@@ -95,8 +92,7 @@ fun RenderFeedContentState(
listState: LazyListState, listState: LazyListState,
nav: INav, nav: INav,
routeForLastRead: String?, routeForLastRead: String?,
scaffoldPadding: PaddingValues = PaddingValues(0.dp), onLoaded: @Composable (FeedState.Loaded) -> Unit = { FeedLoaded(it, listState, routeForLastRead, accountViewModel, nav) },
onLoaded: @Composable (FeedState.Loaded) -> Unit = { FeedLoaded(it, listState, routeForLastRead, accountViewModel, nav, scaffoldPadding) },
onEmpty: @Composable () -> Unit = { FeedEmpty(feedContentState::invalidateData) }, onEmpty: @Composable () -> Unit = { FeedEmpty(feedContentState::invalidateData) },
onError: @Composable (String) -> Unit = { FeedError(it, feedContentState::invalidateData) }, onError: @Composable (String) -> Unit = { FeedError(it, feedContentState::invalidateData) },
onLoading: @Composable () -> Unit = { LoadingFeed() }, onLoading: @Composable () -> Unit = { LoadingFeed() },
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.feeds package com.vitorpamplona.amethyst.ui.feeds
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -31,10 +30,9 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -49,12 +47,11 @@ fun FeedLoaded(
routeForLastRead: String?, routeForLastRead: String?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -26,9 +26,12 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -59,27 +62,37 @@ fun DisappearingScaffold(
) { ) {
val state = rememberDisappearingBarState() val state = rememberDisappearingBarState()
val canScroll = { // Hold the latest values in state so the NSC's captured lambda stays fresh across
allowBarHide && isActive() && accountViewModel.settings.isImmersiveScrollingActive() // recompositions without rebuilding the NSC itself.
} val latestIsActive by rememberUpdatedState(isActive)
val latestAllowBarHide by rememberUpdatedState(allowBarHide)
val latestAccountViewModel by rememberUpdatedState(accountViewModel)
val connection = val connection =
remember(state, isInvertedLayout) { remember(state, isInvertedLayout) {
DisappearingBarNestedScroll( DisappearingBarNestedScroll(
state = state, state = state,
canScroll = canScroll, canScroll = {
latestAllowBarHide &&
latestIsActive() &&
latestAccountViewModel.settings.isImmersiveScrollingActive()
},
reverseLayout = isInvertedLayout, reverseLayout = isInvertedLayout,
) )
} }
ResetBarsOnResume(state) // Only wire the lifecycle observer when the scaffold actually moves its bars.
if (allowBarHide) ResetBarsOnResume(state)
SubcomposeLayout( // When bars are pinned, skip attaching the nested-scroll connection entirely.
modifier = val rootModifier =
Modifier if (allowBarHide) {
.imePadding() Modifier.imePadding().nestedScroll(connection)
.nestedScroll(connection), } else {
) { constraints -> Modifier.imePadding()
}
SubcomposeLayout(modifier = rootModifier) { constraints ->
val layoutWidth = constraints.maxWidth val layoutWidth = constraints.maxWidth
val layoutHeight = constraints.maxHeight val layoutHeight = constraints.maxHeight
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0) val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -127,7 +140,9 @@ fun DisappearingScaffold(
val contentPlaceable = val contentPlaceable =
subcompose(DisappearingSlot.Content) { subcompose(DisappearingSlot.Content) {
mainContent(contentPadding) CompositionLocalProvider(LocalDisappearingScaffoldPadding provides contentPadding) {
mainContent(contentPadding)
}
}.firstOrNull()?.measure( }.firstOrNull()?.measure(
Constraints.fixed(layoutWidth, layoutHeight), Constraints.fixed(layoutWidth, layoutHeight),
) )
@@ -24,14 +24,24 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
/**
* The padding the surrounding [DisappearingScaffold] would like its inner scrollable
* to apply as `contentPadding`. Defaults to [PaddingValues] of 0 when no scaffold is
* providing it, so feed composables used outside a scaffold behave as before.
*
* Read it via [rememberFeedContentPadding] to merge with the list's own
* baseline padding (typically `FeedPadding`).
*/
val LocalDisappearingScaffoldPadding = compositionLocalOf { PaddingValues(0.dp) }
/** /**
* Merges two [PaddingValues] component-wise, resolving start/end against the current * Merges two [PaddingValues] component-wise, resolving start/end against the current
* [LocalLayoutDirection]. Used to combine the scaffold's bar padding with an inner * [LocalLayoutDirection].
* list's own padding (e.g. FeedPadding) into a single `contentPadding` value for a
* LazyColumn / LazyVerticalGrid.
*/ */
@Composable @Composable
fun rememberMergedPadding( fun rememberMergedPadding(
@@ -48,3 +58,10 @@ fun rememberMergedPadding(
) )
} }
} }
/**
* Convenience for inner LazyColumns/LazyVerticalGrids inside a [DisappearingScaffold]:
* merges the scaffold's reserved space with the list's own baseline padding.
*/
@Composable
fun rememberFeedContentPadding(inner: PaddingValues): PaddingValues = rememberMergedPadding(LocalDisappearingScaffoldPadding.current, inner)
@@ -21,14 +21,12 @@
package com.vitorpamplona.amethyst.ui.screen package com.vitorpamplona.amethyst.ui.screen
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
@@ -50,13 +48,12 @@ fun RefresheableFeedView(
routeForLastRead: String?, routeForLastRead: String?,
enablePullRefresh: Boolean = true, enablePullRefresh: Boolean = true,
scrollStateKey: String? = null, scrollStateKey: String? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
RefresheableBox(viewModel, enablePullRefresh) { RefresheableBox(viewModel, enablePullRefresh) {
SaveableFeedState(viewModel.feedState, scrollStateKey) { listState -> SaveableFeedState(viewModel.feedState, scrollStateKey) { listState ->
RenderFeedState(viewModel, accountViewModel, listState, nav, routeForLastRead, scaffoldPadding) RenderFeedState(viewModel, accountViewModel, listState, nav, routeForLastRead)
} }
} }
} }
@@ -104,9 +101,8 @@ fun RenderFeedState(
listState: LazyListState, listState: LazyListState,
nav: INav, nav: INav,
routeForLastRead: String?, routeForLastRead: String?,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
onLoaded: @Composable (FeedState.Loaded) -> Unit = { onLoaded: @Composable (FeedState.Loaded) -> Unit = {
FeedLoaded(it, listState, routeForLastRead, accountViewModel, nav, scaffoldPadding) FeedLoaded(it, listState, routeForLastRead, accountViewModel, nav)
}, },
onEmpty: @Composable () -> Unit = { FeedEmpty { viewModel.invalidateData() } }, onEmpty: @Composable () -> Unit = { FeedEmpty { viewModel.invalidateData() } },
onError: @Composable (String) -> Unit = { FeedError(it) { viewModel.invalidateData() } }, onError: @Composable (String) -> Unit = { FeedError(it) { viewModel.invalidateData() } },
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.screen package com.vitorpamplona.amethyst.ui.screen
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
@@ -30,14 +29,13 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
import com.vitorpamplona.amethyst.ui.feeds.FeedError import com.vitorpamplona.amethyst.ui.feeds.FeedError
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.UserCompose import com.vitorpamplona.amethyst.ui.note.UserCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -50,15 +48,13 @@ fun RefreshingFeedUserFeedView(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
enablePullRefresh: Boolean = true, enablePullRefresh: Boolean = true,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
) { ) {
RefresheableBox(viewModel, enablePullRefresh) { UserFeedView(viewModel, scaffoldPadding, accountViewModel, nav) } RefresheableBox(viewModel, enablePullRefresh) { UserFeedView(viewModel, accountViewModel, nav) }
} }
@Composable @Composable
fun UserFeedView( fun UserFeedView(
viewModel: UserFeedViewModel, viewModel: UserFeedViewModel,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -75,7 +71,7 @@ fun UserFeedView(
} }
is UserFeedState.Loaded -> { is UserFeedState.Loaded -> {
FeedLoaded(state, scaffoldPadding, accountViewModel, nav) FeedLoaded(state, accountViewModel, nav)
} }
is UserFeedState.Loading -> { is UserFeedState.Loading -> {
@@ -88,7 +84,6 @@ fun UserFeedView(
@Composable @Composable
private fun FeedLoaded( private fun FeedLoaded(
state: UserFeedState.Loaded, state: UserFeedState.Loaded,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -97,7 +92,7 @@ private fun FeedLoaded(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items, key = { _, item -> item.pubkeyHex }) { _, item -> itemsIndexed(items, key = { _, item -> item.pubkeyHex }) { _, item ->
@@ -20,7 +20,6 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.articles package com.vitorpamplona.amethyst.ui.screen.loggedIn.articles
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -30,10 +29,9 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose
@@ -45,14 +43,13 @@ import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
fun ArticlesFeedLoaded( fun ArticlesFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -77,7 +77,7 @@ fun ArticlesScreen(
NewArticleButton(nav) NewArticleButton(nav)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
RefresheableBox(articlesFeedContentState, true) { RefresheableBox(articlesFeedContentState, true) {
SaveableFeedContentState(articlesFeedContentState, scrollStateKey = ScrollStateKeys.ARTICLES_SCREEN) { listState -> SaveableFeedContentState(articlesFeedContentState, scrollStateKey = ScrollStateKeys.ARTICLES_SCREEN) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -90,7 +90,6 @@ fun ArticlesScreen(
ArticlesFeedLoaded( ArticlesFeedLoaded(
loaded = loaded, loaded = loaded,
listState = listState, listState = listState,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -125,7 +125,6 @@ private fun RenderBookmarkScreen(
RefresheableFeedView( RefresheableFeedView(
privateFeedViewModel, privateFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -135,7 +134,6 @@ private fun RenderBookmarkScreen(
RefresheableFeedView( RefresheableFeedView(
publicFeedViewModel, publicFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -159,7 +159,6 @@ private fun RenderOldBookmarkScreen(
RefresheableFeedView( RefresheableFeedView(
privateFeedViewModel, privateFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -169,7 +168,6 @@ private fun RenderOldBookmarkScreen(
RefresheableFeedView( RefresheableFeedView(
publicFeedViewModel, publicFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -31,7 +30,6 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
@@ -41,7 +39,7 @@ import com.vitorpamplona.amethyst.ui.feeds.FeedError
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose
@@ -52,13 +50,12 @@ import com.vitorpamplona.amethyst.ui.theme.FeedPadding
fun ChatroomListFeedView( fun ChatroomListFeedView(
feedContentState: FeedContentState, feedContentState: FeedContentState,
scrollStateKey: String, scrollStateKey: String,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
RefresheableBox(feedContentState, true) { RefresheableBox(feedContentState, true) {
SaveableFeedContentState(feedContentState, scrollStateKey) { listState -> SaveableFeedContentState(feedContentState, scrollStateKey) { listState ->
CrossFadeState(feedContentState, listState, scaffoldPadding, accountViewModel, nav) CrossFadeState(feedContentState, listState, accountViewModel, nav)
} }
} }
} }
@@ -67,7 +64,6 @@ fun ChatroomListFeedView(
private fun CrossFadeState( private fun CrossFadeState(
feedContentState: FeedContentState, feedContentState: FeedContentState,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -88,7 +84,7 @@ private fun CrossFadeState(
} }
is FeedState.Loaded -> { is FeedState.Loaded -> {
FeedLoaded(state, listState, scaffoldPadding, accountViewModel, nav) FeedLoaded(state, listState, accountViewModel, nav)
} }
FeedState.Loading -> { FeedState.Loading -> {
@@ -102,14 +98,13 @@ private fun CrossFadeState(
private fun FeedLoaded( private fun FeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
@@ -122,7 +121,6 @@ fun MessagesTabHeader(
fun MessagesPager( fun MessagesPager(
pagerState: PagerState, pagerState: PagerState,
tabs: List<MessagesTabItem>, tabs: List<MessagesTabItem>,
paddingValues: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -138,7 +136,6 @@ fun MessagesPager(
ChatroomListFeedView( ChatroomListFeedView(
feedContentState = tabs[page].feedContentState, feedContentState = tabs[page].feedContentState,
scrollStateKey = tabs[page].scrollStateKey, scrollStateKey = tabs[page].scrollStateKey,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -98,7 +98,6 @@ fun MessagesSinglePane(
MessagesPager( MessagesPager(
pagerState, pagerState,
tabs, tabs,
it,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -21,13 +21,11 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.twopane package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.twopane
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
@@ -44,7 +42,6 @@ import kotlinx.collections.immutable.persistentListOf
fun ChatroomList( fun ChatroomList(
knownFeedContentState: FeedContentState, knownFeedContentState: FeedContentState,
newFeedContentState: FeedContentState, newFeedContentState: FeedContentState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -76,7 +73,6 @@ fun ChatroomList(
MessagesPager( MessagesPager(
pagerState, pagerState,
tabs, tabs,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -96,7 +96,6 @@ fun MessagesTwoPane(
ChatroomList( ChatroomList(
knownFeedContentState, knownFeedContentState,
newFeedContentState, newFeedContentState,
padding,
accountViewModel, accountViewModel,
twoPaneNav, twoPaneNav,
) )
@@ -198,7 +198,6 @@ fun CommunityScreen(
RefresheableFeedView( RefresheableFeedView(
feedViewModel, feedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -208,7 +207,6 @@ fun CommunityScreen(
RefresheableFeedView( RefresheableFeedView(
modFeedViewModel, modFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -70,7 +69,7 @@ import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -277,7 +276,6 @@ private fun DiscoverPages(
routeForLastRead = tab.routeForLastRead, routeForLastRead = tab.routeForLastRead,
forceEventKind = tab.forceEventKind, forceEventKind = tab.forceEventKind,
listState = listState, listState = listState,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -289,7 +287,6 @@ private fun DiscoverPages(
routeForLastRead = tab.routeForLastRead, routeForLastRead = tab.routeForLastRead,
forceEventKind = tab.forceEventKind, forceEventKind = tab.forceEventKind,
listState = listState, listState = listState,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -307,7 +304,6 @@ private fun RenderDiscoverFeed(
routeForLastRead: String?, routeForLastRead: String?,
forceEventKind: Int?, forceEventKind: Int?,
listState: LazyGridState, listState: LazyGridState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -334,7 +330,6 @@ private fun RenderDiscoverFeed(
routeForLastRead, routeForLastRead,
listState, listState,
forceEventKind, forceEventKind,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -397,7 +392,6 @@ private fun RenderDiscoverFeed(
routeForLastRead: String?, routeForLastRead: String?,
forceEventKind: Int?, forceEventKind: Int?,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -424,7 +418,6 @@ private fun RenderDiscoverFeed(
routeForLastRead, routeForLastRead,
listState, listState,
forceEventKind, forceEventKind,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -468,14 +461,13 @@ private fun DiscoverFeedLoaded(
routeForLastRead: String?, routeForLastRead: String?,
listState: LazyListState, listState: LazyListState,
forceEventKind: Int?, forceEventKind: Int?,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item -> itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
@@ -504,7 +496,6 @@ private fun DiscoverFeedColumnsLoaded(
routeForLastRead: String?, routeForLastRead: String?,
listState: LazyGridState, listState: LazyGridState,
forceEventKind: Int?, forceEventKind: Int?,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -512,7 +503,7 @@ private fun DiscoverFeedColumnsLoaded(
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Fixed(2),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item -> itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.drafts package com.vitorpamplona.amethyst.ui.screen.loggedIn.drafts
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -54,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys.DRAFTS import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys.DRAFTS
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
@@ -146,7 +145,7 @@ private fun RenderDraftListScreen(
) )
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { scaffoldPadding -> ) {
RefresheableBox(feedState) { RefresheableBox(feedState) {
SaveableFeedState(feedState, DRAFTS) { listState -> SaveableFeedState(feedState, DRAFTS) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -155,7 +154,7 @@ private fun RenderDraftListScreen(
listState = listState, listState = listState,
nav = nav, nav = nav,
routeForLastRead = null, routeForLastRead = null,
onLoaded = { DraftFeedLoaded(it, listState, scaffoldPadding, accountViewModel, nav) }, onLoaded = { DraftFeedLoaded(it, listState, accountViewModel, nav) },
) )
} }
} }
@@ -166,14 +165,13 @@ private fun RenderDraftListScreen(
private fun DraftFeedLoaded( private fun DraftFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item -> itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -96,13 +95,13 @@ fun DvmContentDiscoveryScreen(
DvmTopBar(appDefinitionEventId, accountViewModel, nav) DvmTopBar(appDefinitionEventId, accountViewModel, nav)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note -> LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note ->
note?.let { baseNote -> note?.let { baseNote ->
WatchNoteEvent( WatchNoteEvent(
baseNote, baseNote,
onNoteEventFound = { onNoteEventFound = {
DvmContentDiscoveryScreen(baseNote, paddingValues, accountViewModel, nav) DvmContentDiscoveryScreen(baseNote, accountViewModel, nav)
}, },
onBlank = { onBlank = {
FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav) FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav)
@@ -117,7 +116,6 @@ fun DvmContentDiscoveryScreen(
@Composable @Composable
fun DvmContentDiscoveryScreen( fun DvmContentDiscoveryScreen(
appDefinition: Note, appDefinition: Note,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -152,7 +150,6 @@ fun DvmContentDiscoveryScreen(
appDefinition, appDefinition,
myRequestEventID, myRequestEventID,
onRefresh, onRefresh,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -169,7 +166,6 @@ fun ObserverContentDiscoveryResponse(
appDefinition: Note, appDefinition: Note,
dvmRequestId: Note, dvmRequestId: Note,
onRefresh: () -> Unit, onRefresh: () -> Unit,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -198,7 +194,6 @@ fun ObserverContentDiscoveryResponse(
dvmRequestId.idHex, dvmRequestId.idHex,
myResponse, myResponse,
onRefresh, onRefresh,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -251,7 +246,6 @@ fun PrepareViewContentDiscoveryModels(
dvmRequestId: String, dvmRequestId: String,
latestResponse: NIP90ContentDiscoveryResponseEvent, latestResponse: NIP90ContentDiscoveryResponseEvent,
onRefresh: () -> Unit, onRefresh: () -> Unit,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -265,14 +259,13 @@ fun PrepareViewContentDiscoveryModels(
resultFeedViewModel.invalidateData() resultFeedViewModel.invalidateData()
} }
RenderNostrNIP90ContentDiscoveryScreen(resultFeedViewModel, onRefresh, scaffoldPadding, accountViewModel, nav) RenderNostrNIP90ContentDiscoveryScreen(resultFeedViewModel, onRefresh, accountViewModel, nav)
} }
@Composable @Composable
fun RenderNostrNIP90ContentDiscoveryScreen( fun RenderNostrNIP90ContentDiscoveryScreen(
resultFeedViewModel: NIP90ContentDiscoveryFeedViewModel, resultFeedViewModel: NIP90ContentDiscoveryFeedViewModel,
onRefresh: () -> Unit, onRefresh: () -> Unit,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -284,7 +277,6 @@ fun RenderNostrNIP90ContentDiscoveryScreen(
listState, listState,
nav, nav,
null, null,
scaffoldPadding,
onEmpty = { onEmpty = {
FeedEmpty { FeedEmpty {
onRefresh() onRefresh()
@@ -179,7 +179,6 @@ fun FollowPackFeedScreen(
RefresheableFeedView( RefresheableFeedView(
newThreadFeedViewModel, newThreadFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -189,7 +188,6 @@ fun FollowPackFeedScreen(
RefresheableFeedView( RefresheableFeedView(
conversationsFeedViewModel, conversationsFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -198,7 +196,6 @@ fun FollowPackFeedScreen(
2 -> { 2 -> {
UserFeedView( UserFeedView(
membersFeedViewModel, membersFeedViewModel,
it,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -103,7 +103,6 @@ fun GeoHashScreen(
RefresheableFeedView( RefresheableFeedView(
feedViewModel, feedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -108,7 +108,6 @@ fun HashtagScreen(
RefresheableFeedView( RefresheableFeedView(
feedViewModel, feedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -24,7 +24,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -52,7 +51,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
@@ -75,7 +73,7 @@ import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -226,7 +224,6 @@ private fun HomePages(
routeForLastRead = tabs[page].routeForLastRead, routeForLastRead = tabs[page].routeForLastRead,
scrollStateKey = tabs[page].scrollStateKey, scrollStateKey = tabs[page].scrollStateKey,
liveSection = tabs[page].liveSection, liveSection = tabs[page].liveSection,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -280,7 +277,6 @@ fun HomeFeeds(
enablePullRefresh: Boolean = true, enablePullRefresh: Boolean = true,
scrollStateKey: String? = null, scrollStateKey: String? = null,
liveSection: ChannelFeedContentState? = null, liveSection: ChannelFeedContentState? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -292,7 +288,7 @@ fun HomeFeeds(
listState = listState, listState = listState,
nav = nav, nav = nav,
routeForLastRead = routeForLastRead, routeForLastRead = routeForLastRead,
onLoaded = { FeedLoaded(it, listState, routeForLastRead, liveSection, scaffoldPadding, accountViewModel, nav) }, onLoaded = { FeedLoaded(it, listState, routeForLastRead, liveSection, accountViewModel, nav) },
onEmpty = { HomeFeedEmpty(feedState::invalidateData) }, onEmpty = { HomeFeedEmpty(feedState::invalidateData) },
) )
} }
@@ -306,16 +302,13 @@ fun FeedLoaded(
listState: LazyListState, listState: LazyListState,
routeForLastRead: String?, routeForLastRead: String?,
liveSection: ChannelFeedContentState? = null, liveSection: ChannelFeedContentState? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
val listPadding = rememberMergedPadding(scaffoldPadding, FeedPadding)
LazyColumn( LazyColumn(
contentPadding = listPadding, contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
if (liveSection != null) { if (liveSection != null) {
@@ -20,7 +20,6 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -33,7 +32,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.VideoCardCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.VideoCardCompose
@@ -45,14 +44,13 @@ import com.vitorpamplona.quartz.nip71Video.VideoEvent
fun LongsFeedLoaded( fun LongsFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -77,7 +77,7 @@ fun LongsScreen(
NewLongVideoButton(accountViewModel, nav, longsFeedContentState::sendToTop) NewLongVideoButton(accountViewModel, nav, longsFeedContentState::sendToTop)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
RefresheableBox(longsFeedContentState, true) { RefresheableBox(longsFeedContentState, true) {
SaveableFeedContentState(longsFeedContentState, scrollStateKey = ScrollStateKeys.LONGS_SCREEN) { listState -> SaveableFeedContentState(longsFeedContentState, scrollStateKey = ScrollStateKeys.LONGS_SCREEN) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -90,7 +90,6 @@ fun LongsScreen(
LongsFeedLoaded( LongsFeedLoaded(
loaded = loaded, loaded = loaded,
listState = listState, listState = listState,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -26,7 +26,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -52,7 +51,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment 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.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.ui.notifications.Card import com.vitorpamplona.amethyst.commons.ui.notifications.Card
@@ -61,7 +59,7 @@ import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.feeds.FeedError import com.vitorpamplona.amethyst.ui.feeds.FeedError
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.BadgeCompose import com.vitorpamplona.amethyst.ui.note.BadgeCompose
import com.vitorpamplona.amethyst.ui.note.CloseIcon import com.vitorpamplona.amethyst.ui.note.CloseIcon
@@ -89,7 +87,6 @@ fun RenderCardFeed(
nav: INav, nav: INav,
routeForLastRead: String, routeForLastRead: String,
scrollToEventId: String? = null, scrollToEventId: String? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
headerContent: (@Composable () -> Unit)? = null, headerContent: (@Composable () -> Unit)? = null,
) { ) {
val feedState by feedContent.feedContent.collectAsStateWithLifecycle() val feedState by feedContent.feedContent.collectAsStateWithLifecycle()
@@ -118,7 +115,6 @@ fun RenderCardFeed(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
scrollToEventId = scrollToEventId, scrollToEventId = scrollToEventId,
scaffoldPadding = scaffoldPadding,
headerContent = headerContent, headerContent = headerContent,
) )
} }
@@ -153,7 +149,6 @@ private fun FeedLoaded(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
scrollToEventId: String? = null, scrollToEventId: String? = null,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
headerContent: (@Composable () -> Unit)? = null, headerContent: (@Composable () -> Unit)? = null,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
@@ -179,7 +174,7 @@ private fun FeedLoaded(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
if (headerContent != null) { if (headerContent != null) {
@@ -102,7 +102,6 @@ fun NotificationScreen(
nav = nav, nav = nav,
routeForLastRead = "Notification", routeForLastRead = "Notification",
scrollToEventId = scrollToEventId, scrollToEventId = scrollToEventId,
scaffoldPadding = it,
headerContent = { ObserveInboxRelayListAndDisplayIfNotFound(accountViewModel, nav) }, headerContent = { ObserveInboxRelayListAndDisplayIfNotFound(accountViewModel, nav) },
) )
} }
@@ -20,7 +20,6 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -33,7 +32,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -44,14 +43,13 @@ import com.vitorpamplona.quartz.nip68Picture.PictureEvent
fun PictureFeedLoaded( fun PictureFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -77,7 +77,7 @@ fun PicturesScreen(
NewPictureButton(accountViewModel, nav, picturesFeedContentState::sendToTop) NewPictureButton(accountViewModel, nav, picturesFeedContentState::sendToTop)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
RefresheableBox(picturesFeedContentState, true) { RefresheableBox(picturesFeedContentState, true) {
SaveableFeedContentState(picturesFeedContentState, scrollStateKey = ScrollStateKeys.PICTURES_SCREEN) { listState -> SaveableFeedContentState(picturesFeedContentState, scrollStateKey = ScrollStateKeys.PICTURES_SCREEN) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -90,7 +90,6 @@ fun PicturesScreen(
PictureFeedLoaded( PictureFeedLoaded(
loaded = loaded, loaded = loaded,
listState = listState, listState = listState,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -78,7 +78,6 @@ private fun RenderPinnedNotesScreen(
RefresheableFeedView( RefresheableFeedView(
pinnedNotesFeedViewModel, pinnedNotesFeedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -172,7 +172,6 @@ private fun PollsPages(
listState = listState, listState = listState,
nav = nav, nav = nav,
routeForLastRead = tabs[page].routeForLastRead, routeForLastRead = tabs[page].routeForLastRead,
scaffoldPadding = it,
) )
} }
} }
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.products
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
@@ -33,7 +32,6 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
@@ -41,7 +39,7 @@ import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
import com.vitorpamplona.amethyst.ui.feeds.FeedError import com.vitorpamplona.amethyst.ui.feeds.FeedError
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose
@@ -53,7 +51,6 @@ import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
fun RenderProductsFeed( fun RenderProductsFeed(
feedContentState: FeedContentState, feedContentState: FeedContentState,
gridState: LazyGridState, gridState: LazyGridState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -78,7 +75,6 @@ fun RenderProductsFeed(
ProductsFeedColumnsLoaded( ProductsFeedColumnsLoaded(
state, state,
gridState, gridState,
scaffoldPadding,
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -96,7 +92,6 @@ fun RenderProductsFeed(
private fun ProductsFeedColumnsLoaded( private fun ProductsFeedColumnsLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyGridState, listState: LazyGridState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -104,7 +99,7 @@ private fun ProductsFeedColumnsLoaded(
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Fixed(2),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item -> itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
@@ -76,13 +76,12 @@ fun ProductsScreen(
NewProductButton(accountViewModel, nav) NewProductButton(accountViewModel, nav)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
RefresheableBox(productsFeedContentState, true) { RefresheableBox(productsFeedContentState, true) {
SaveableGridFeedContentState(productsFeedContentState, scrollStateKey = ScrollStateKeys.PRODUCTS_SCREEN) { gridState -> SaveableGridFeedContentState(productsFeedContentState, scrollStateKey = ScrollStateKeys.PRODUCTS_SCREEN) { gridState ->
RenderProductsFeed( RenderProductsFeed(
feedContentState = productsFeedContentState, feedContentState = productsFeedContentState,
gridState = gridState, gridState = gridState,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -106,7 +106,6 @@ fun RelayFeedScreen(
RefresheableFeedView( RefresheableFeedView(
feedViewModel, feedViewModel,
null, null,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.search
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
@@ -58,7 +57,7 @@ import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -123,11 +122,10 @@ fun SearchScreen(
} }
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { scaffoldPadding -> ) {
DisplaySearchResults( DisplaySearchResults(
searchBarViewModel = searchBarViewModel, searchBarViewModel = searchBarViewModel,
scaffoldPadding = scaffoldPadding, headerContent = { ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav) },
header = { ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav) },
nav = nav, nav = nav,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) )
@@ -220,8 +218,7 @@ private fun SearchTextField(
@Composable @Composable
private fun DisplaySearchResults( private fun DisplaySearchResults(
searchBarViewModel: SearchBarViewModel, searchBarViewModel: SearchBarViewModel,
scaffoldPadding: PaddingValues, headerContent: @Composable () -> Unit,
header: @Composable () -> Unit,
nav: INav, nav: INav,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
@@ -236,10 +233,10 @@ private fun DisplaySearchResults(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = searchBarViewModel.listState, state = searchBarViewModel.listState,
) { ) {
item(key = "scaffold-header") { header() } item(key = "scaffold-header") { headerContent() }
if (!isRefreshing) return@LazyColumn if (!isRefreshing) return@LazyColumn
@@ -20,7 +20,6 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -33,7 +32,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -44,14 +43,13 @@ import com.vitorpamplona.quartz.nip71Video.VideoEvent
fun ShortsFeedLoaded( fun ShortsFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -77,7 +77,7 @@ fun ShortsScreen(
NewShortVideoButton(accountViewModel, nav, shortsFeedContentState::sendToTop) NewShortVideoButton(accountViewModel, nav, shortsFeedContentState::sendToTop)
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { paddingValues -> ) {
RefresheableBox(shortsFeedContentState, true) { RefresheableBox(shortsFeedContentState, true) {
SaveableFeedContentState(shortsFeedContentState, scrollStateKey = ScrollStateKeys.SHORTS_SCREEN) { listState -> SaveableFeedContentState(shortsFeedContentState, scrollStateKey = ScrollStateKeys.SHORTS_SCREEN) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -90,7 +90,6 @@ fun ShortsScreen(
ShortsFeedLoaded( ShortsFeedLoaded(
loaded = loaded, loaded = loaded,
listState = listState, listState = listState,
scaffoldPadding = paddingValues,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -85,7 +85,7 @@ import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
@@ -298,7 +298,6 @@ import kotlinx.coroutines.withContext
fun ThreadFeedView( fun ThreadFeedView(
noteId: String, noteId: String,
viewModel: LevelFeedViewModel, viewModel: LevelFeedViewModel,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -310,7 +309,7 @@ fun ThreadFeedView(
nav = nav, nav = nav,
routeForLastRead = null, routeForLastRead = null,
onLoaded = { onLoaded = {
RenderThreadFeed(noteId, it, viewModel.llState, viewModel, scaffoldPadding, accountViewModel, nav) RenderThreadFeed(noteId, it, viewModel.llState, viewModel, accountViewModel, nav)
}, },
) )
} }
@@ -322,7 +321,6 @@ fun RenderThreadFeed(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
viewModel: LevelFeedViewModel, viewModel: LevelFeedViewModel,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -359,7 +357,7 @@ fun RenderThreadFeed(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -69,6 +69,6 @@ fun ThreadScreen(
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { ) {
ThreadFeedView(noteId, feedViewModel, it, accountViewModel, nav) ThreadFeedView(noteId, feedViewModel, accountViewModel, nav)
} }
} }
@@ -20,7 +20,6 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.video package com.vitorpamplona.amethyst.ui.screen.loggedIn.video
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -41,7 +40,7 @@ import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -99,7 +98,6 @@ fun VideoScreen(
RenderFeed( RenderFeed(
videoFeedContentState = videoFeedContentState, videoFeedContentState = videoFeedContentState,
scrollKey = ScrollStateKeys.VIDEO_SCREEN, scrollKey = ScrollStateKeys.VIDEO_SCREEN,
scaffoldPadding = it,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -125,7 +123,6 @@ fun WatchAccountForVideoScreen(
private fun RenderFeed( private fun RenderFeed(
videoFeedContentState: FeedContentState, videoFeedContentState: FeedContentState,
scrollKey: String?, scrollKey: String?,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -141,7 +138,6 @@ private fun RenderFeed(
VideoFeedLoaded( VideoFeedLoaded(
loaded = loaded, loaded = loaded,
listState = listState, listState = listState,
scaffoldPadding = scaffoldPadding,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -155,14 +151,13 @@ private fun RenderFeed(
fun VideoFeedLoaded( fun VideoFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues = PaddingValues(0.dp),
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed( itemsIndexed(
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.webBookmarks
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -81,7 +80,7 @@ import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
@@ -155,7 +154,7 @@ private fun RenderWebBookmarksScreen(
} }
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { scaffoldPadding -> ) {
RefresheableBox(feedState) { RefresheableBox(feedState) {
SaveableFeedState(feedState, ScrollStateKeys.WEB_BOOKMARKS) { listState -> SaveableFeedState(feedState, ScrollStateKeys.WEB_BOOKMARKS) { listState ->
RenderFeedContentState( RenderFeedContentState(
@@ -164,7 +163,7 @@ private fun RenderWebBookmarksScreen(
listState = listState, listState = listState,
nav = nav, nav = nav,
routeForLastRead = null, routeForLastRead = null,
onLoaded = { WebBookmarksFeedLoaded(it, listState, scaffoldPadding, accountViewModel, nav) }, onLoaded = { WebBookmarksFeedLoaded(it, listState, accountViewModel, nav) },
) )
} }
} }
@@ -175,14 +174,13 @@ private fun RenderWebBookmarksScreen(
private fun WebBookmarksFeedLoaded( private fun WebBookmarksFeedLoaded(
loaded: FeedState.Loaded, loaded: FeedState.Loaded,
listState: LazyListState, listState: LazyListState,
scaffoldPadding: PaddingValues,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val items by loaded.feed.collectAsStateWithLifecycle() val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn( LazyColumn(
contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), contentPadding = rememberFeedContentPadding(FeedPadding),
state = listState, state = listState,
) { ) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item -> itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
@@ -0,0 +1,170 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.ui.layouts
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
class DisappearingBarNestedScrollTest {
private fun state(
topLimit: Float = 100f,
bottomLimit: Float = 50f,
) = DisappearingBarState().apply {
topHeightLimit = topLimit
bottomHeightLimit = bottomLimit
}
private fun nsc(
state: DisappearingBarState,
canScroll: Boolean = true,
reverseLayout: Boolean = false,
) = DisappearingBarNestedScroll(
state = state,
canScroll = { canScroll },
reverseLayout = reverseLayout,
)
@Test
fun `onPostScroll does not consume any delta`() {
val state = state()
val connection = nsc(state)
val consumed = connection.onPostScroll(Offset(0f, -20f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(Offset.Zero, consumed)
}
@Test
fun `scrolling content up hides both bars by the total delta`() {
val state = state(topLimit = 100f, bottomLimit = 50f)
val connection = nsc(state)
connection.onPostScroll(Offset(0f, -30f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(-30f, state.topHeightOffset)
assertEquals(-30f, state.bottomHeightOffset)
}
@Test
fun `bars clamp at their individual limits`() {
val state = state(topLimit = 100f, bottomLimit = 50f)
val connection = nsc(state)
connection.onPostScroll(Offset(0f, -200f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(-100f, state.topHeightOffset)
assertEquals(-50f, state.bottomHeightOffset)
}
@Test
fun `scrolling content down reveals both bars from a hidden state`() {
val state = state(topLimit = 100f, bottomLimit = 50f)
state.topHeightOffset = -100f
state.bottomHeightOffset = -50f
val connection = nsc(state)
connection.onPostScroll(Offset(0f, 30f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(-70f, state.topHeightOffset)
assertEquals(-20f, state.bottomHeightOffset)
}
@Test
fun `uses consumed plus available so the bars see the whole scroll attempt`() {
val state = state()
state.topHeightOffset = -50f
state.bottomHeightOffset = -50f
val connection = nsc(state)
// The list consumed 20px of a 40px reveal drag; 20 more was left as overscroll.
// The bars should move by the total 40, not just one of the halves.
connection.onPostScroll(Offset(0f, 20f), Offset(0f, 20f), NestedScrollSource.UserInput)
assertEquals(-10f, state.topHeightOffset)
assertEquals(-10f, state.bottomHeightOffset)
}
@Test
fun `canScroll returning false freezes the bars`() {
val state = state()
val connection = nsc(state, canScroll = false)
connection.onPostScroll(Offset(0f, -100f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(0f, state.topHeightOffset)
assertEquals(0f, state.bottomHeightOffset)
}
@Test
fun `reverseLayout inverts the delta sign`() {
val state = state()
val connection = nsc(state, reverseLayout = true)
// In reverse layout, a positive Y is a "hide" direction
connection.onPostScroll(Offset(0f, 20f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(-20f, state.topHeightOffset)
assertEquals(-20f, state.bottomHeightOffset)
}
@Test
fun `bar offsets never exceed zero when revealing`() {
val state = state()
// Start from a mid-hidden position
state.topHeightOffset = -10f
state.bottomHeightOffset = -10f
val connection = nsc(state)
connection.onPostScroll(Offset(0f, 100f), Offset(0f, 0f), NestedScrollSource.UserInput)
assertEquals(0f, state.topHeightOffset)
assertEquals(0f, state.bottomHeightOffset)
}
@Test
fun `setting topHeightLimit smaller than current offset clamps it in`() {
val state = state(topLimit = 100f)
state.topHeightOffset = -80f
state.topHeightLimit = 40f
assertEquals(-40f, state.topHeightOffset)
}
@Test
fun `onPostFling swallows all velocity so parents don't get a phantom fling`() =
runTest {
val state = state()
val connection = nsc(state)
val remaining =
connection.onPostFling(
consumed = Velocity(0f, 1000f),
available = Velocity(0f, 200f),
)
assertEquals(Velocity.Zero, remaining)
}
}