From 9b409723bd74d561875006bc330b2bfdf405d83c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 14:59:57 +0000 Subject: [PATCH] fix(ui): thread scaffold padding through Messages, DVMs, Search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit follow-up to the previous padding migration: four more `DisappearingScaffold` consumers were still using the old `HorizontalPager(contentPadding = it)` or `Modifier.padding(it)` patterns, so their feeds didn't extend behind the chrome. - Messages single-pane: `MessagesPager` no longer applies the scaffold padding to the pager's `contentPadding` (which shrinks pages); it threads it down to `ChatroomListFeedView` instead, which now accepts `scaffoldPadding` and applies it on its inner `LazyColumn` via `rememberMergedPadding(scaffoldPadding, FeedPadding)`. - Messages two-pane: drops the outer `Modifier.padding(padding) .consumeWindowInsets(padding)` on the `TwoPane` and threads the padding through `ChatroomList` → `MessagesPager` → `ChatroomListFeedView`. - `DvmContentDiscoveryScreen`: drops the wrapping `Column(Modifier .padding(paddingValues))` and threads the scaffold padding through `DvmContentDiscoveryScreen` → `ObserverContentDiscoveryResponse` → `PrepareViewContentDiscoveryModels` → `RenderNostrNIP90Content DiscoveryScreen` → `RenderFeedState`'s default `FeedLoaded`. - `SearchScreen`: drops the wrapping `Column(Modifier.padding(it).consumeWindowInsets(it))` and turns the inbox-relay warning card into a `headerContent` slot rendered as the first item of the search-results `LazyColumn`. The list now also always exists (with the early `return` moved into the lazy-list builder via `return@LazyColumn`) so the header is visible even when no search is in progress. Settings (non-scrollable) and chat detail screens (inverted layout plus a custom input field at the bottom — these need a more careful refactor to handle IME + nav inset together) intentionally left unchanged. https://claude.ai/code/session_01M3Bj24jLc9aVhMuvn55jXa --- .../chats/rooms/feed/ChatroomListFeedView.kt | 12 +++- .../chats/rooms/feed/ChatroomListTabs.kt | 2 +- .../chats/rooms/twopane/ChatroomListPane.kt | 3 +- .../chats/rooms/twopane/MessagesTwoPane.kt | 4 +- .../dvms/DvmContentDiscoveryScreen.kt | 67 ++++++++++--------- .../ui/screen/loggedIn/search/SearchScreen.kt | 32 +++++---- 6 files changed, 67 insertions(+), 53 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt index 60d6fc142..a1c12f74d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn @@ -30,6 +31,7 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState @@ -39,6 +41,7 @@ import com.vitorpamplona.amethyst.ui.feeds.FeedError import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState +import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose @@ -49,12 +52,13 @@ import com.vitorpamplona.amethyst.ui.theme.FeedPadding fun ChatroomListFeedView( feedContentState: FeedContentState, scrollStateKey: String, + scaffoldPadding: PaddingValues = PaddingValues(0.dp), accountViewModel: AccountViewModel, nav: INav, ) { RefresheableBox(feedContentState, true) { SaveableFeedContentState(feedContentState, scrollStateKey) { listState -> - CrossFadeState(feedContentState, listState, accountViewModel, nav) + CrossFadeState(feedContentState, listState, scaffoldPadding, accountViewModel, nav) } } } @@ -63,6 +67,7 @@ fun ChatroomListFeedView( private fun CrossFadeState( feedContentState: FeedContentState, listState: LazyListState, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { @@ -83,7 +88,7 @@ private fun CrossFadeState( } is FeedState.Loaded -> { - FeedLoaded(state, listState, accountViewModel, nav) + FeedLoaded(state, listState, scaffoldPadding, accountViewModel, nav) } FeedState.Loading -> { @@ -97,13 +102,14 @@ private fun CrossFadeState( private fun FeedLoaded( loaded: FeedState.Loaded, listState: LazyListState, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { val items by loaded.feed.collectAsStateWithLifecycle() LazyColumn( - contentPadding = FeedPadding, + contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), state = listState, ) { itemsIndexed( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt index 8168e1c64..f0c52e4a0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt @@ -127,7 +127,6 @@ fun MessagesPager( nav: INav, ) { HorizontalPager( - contentPadding = paddingValues, state = pagerState, userScrollEnabled = true, modifier = @@ -139,6 +138,7 @@ fun MessagesPager( ChatroomListFeedView( feedContentState = tabs[page].feedContentState, scrollStateKey = tabs[page].scrollStateKey, + scaffoldPadding = paddingValues, accountViewModel = accountViewModel, nav = nav, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/ChatroomListPane.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/ChatroomListPane.kt index 14928b247..ae1e24fe1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/ChatroomListPane.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/ChatroomListPane.kt @@ -44,6 +44,7 @@ import kotlinx.collections.immutable.persistentListOf fun ChatroomList( knownFeedContentState: FeedContentState, newFeedContentState: FeedContentState, + scaffoldPadding: PaddingValues = PaddingValues(0.dp), accountViewModel: AccountViewModel, nav: INav, ) { @@ -75,7 +76,7 @@ fun ChatroomList( MessagesPager( pagerState, tabs, - PaddingValues(0.dp), + scaffoldPadding, accountViewModel, nav, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt index 1e187d614..7fd398d2a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.twopane import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding @@ -97,6 +96,7 @@ fun MessagesTwoPane( ChatroomList( knownFeedContentState, newFeedContentState, + padding, accountViewModel, twoPaneNav, ) @@ -134,7 +134,7 @@ fun MessagesTwoPane( strategy = strategy, displayFeatures = displayFeatures, foldAwareConfiguration = FoldAwareConfiguration.VerticalFoldsOnly, - modifier = Modifier.padding(padding).consumeWindowInsets(padding).fillMaxSize(), + modifier = Modifier.fillMaxSize(), ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt index 0d81a4cc8..0f1488fab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt @@ -23,8 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button @@ -97,20 +97,18 @@ fun DvmContentDiscoveryScreen( }, accountViewModel = accountViewModel, ) { paddingValues -> - Column(Modifier.padding(paddingValues)) { - LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note -> - note?.let { baseNote -> - WatchNoteEvent( - baseNote, - onNoteEventFound = { - DvmContentDiscoveryScreen(baseNote, accountViewModel, nav) - }, - onBlank = { - FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav) - }, - accountViewModel, - ) - } + LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note -> + note?.let { baseNote -> + WatchNoteEvent( + baseNote, + onNoteEventFound = { + DvmContentDiscoveryScreen(baseNote, paddingValues, accountViewModel, nav) + }, + onBlank = { + FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav) + }, + accountViewModel, + ) } } } @@ -119,6 +117,7 @@ fun DvmContentDiscoveryScreen( @Composable fun DvmContentDiscoveryScreen( appDefinition: Note, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { @@ -153,6 +152,7 @@ fun DvmContentDiscoveryScreen( appDefinition, myRequestEventID, onRefresh, + scaffoldPadding, accountViewModel, nav, ) @@ -169,6 +169,7 @@ fun ObserverContentDiscoveryResponse( appDefinition: Note, dvmRequestId: Note, onRefresh: () -> Unit, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { @@ -197,6 +198,7 @@ fun ObserverContentDiscoveryResponse( dvmRequestId.idHex, myResponse, onRefresh, + scaffoldPadding, accountViewModel, nav, ) @@ -249,6 +251,7 @@ fun PrepareViewContentDiscoveryModels( dvmRequestId: String, latestResponse: NIP90ContentDiscoveryResponseEvent, onRefresh: () -> Unit, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { @@ -262,32 +265,32 @@ fun PrepareViewContentDiscoveryModels( resultFeedViewModel.invalidateData() } - RenderNostrNIP90ContentDiscoveryScreen(resultFeedViewModel, onRefresh, accountViewModel, nav) + RenderNostrNIP90ContentDiscoveryScreen(resultFeedViewModel, onRefresh, scaffoldPadding, accountViewModel, nav) } @Composable fun RenderNostrNIP90ContentDiscoveryScreen( resultFeedViewModel: NIP90ContentDiscoveryFeedViewModel, onRefresh: () -> Unit, + scaffoldPadding: PaddingValues, accountViewModel: AccountViewModel, nav: INav, ) { - Column(Modifier.fillMaxHeight()) { - SaveableFeedState(resultFeedViewModel.feedState, null) { listState -> - // TODO (Optional) Instead of a like reaction, do a Kind 31989 NIP89 App recommendation - RenderFeedState( - resultFeedViewModel, - accountViewModel, - listState, - nav, - null, - onEmpty = { - FeedEmpty { - onRefresh() - } - }, - ) - } + SaveableFeedState(resultFeedViewModel.feedState, null) { listState -> + // TODO (Optional) Instead of a like reaction, do a Kind 31989 NIP89 App recommendation + RenderFeedState( + resultFeedViewModel, + accountViewModel, + listState, + nav, + null, + scaffoldPadding, + onEmpty = { + FeedEmpty { + onRefresh() + } + }, + ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt index 2dffe5d4c..42b8623c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt @@ -22,9 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.search import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth @@ -59,6 +58,7 @@ import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold +import com.vitorpamplona.amethyst.ui.layouts.rememberMergedPadding import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route @@ -123,13 +123,14 @@ fun SearchScreen( } }, accountViewModel = accountViewModel, - ) { - Column( - modifier = Modifier.padding(it).consumeWindowInsets(it), - ) { - ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav) - DisplaySearchResults(searchBarViewModel, nav, accountViewModel) - } + ) { scaffoldPadding -> + DisplaySearchResults( + searchBarViewModel = searchBarViewModel, + scaffoldPadding = scaffoldPadding, + header = { ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav) }, + nav = nav, + accountViewModel = accountViewModel, + ) } } @@ -219,13 +220,12 @@ private fun SearchTextField( @Composable private fun DisplaySearchResults( searchBarViewModel: SearchBarViewModel, + scaffoldPadding: PaddingValues, + header: @Composable () -> Unit, nav: INav, accountViewModel: AccountViewModel, ) { - if (!searchBarViewModel.isRefreshing.value) { - return - } - + val isRefreshing by searchBarViewModel.isRefreshing val hashTags by searchBarViewModel.hashtagResults.collectAsStateWithLifecycle() val relays by searchBarViewModel.relayResults.collectAsStateWithLifecycle() val users by searchBarViewModel.searchResultsUsers.collectAsStateWithLifecycle() @@ -236,9 +236,13 @@ private fun DisplaySearchResults( LazyColumn( modifier = Modifier.fillMaxHeight(), - contentPadding = FeedPadding, + contentPadding = rememberMergedPadding(scaffoldPadding, FeedPadding), state = searchBarViewModel.listState, ) { + item(key = "scaffold-header") { header() } + + if (!isRefreshing) return@LazyColumn + itemsIndexed( hashTags, key = { _, item -> "#$item" },