Move profile back/more buttons into Scaffold topBar

The back button and the more-options menu used to float as
absolute-positioned overlays inside ProfileHeader. They are now
hosted in a transparent ShorterTopAppBar wired into the Scaffold's
topBar slot, so the banner can still render edge-to-edge as the
background by setting contentWindowInsets to zero and only applying
the bottom padding from Scaffold to the body.
This commit is contained in:
Claude
2026-04-27 23:09:57 +00:00
parent 3c3e327bcb
commit 152900688d
3 changed files with 115 additions and 73 deletions
@@ -86,6 +86,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal.UserPro
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags.FollowedTagsTabHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags.TabFollowedTags
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileTopBar
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.identity.UserExternalIdentitiesViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.TabMutualConversations
@@ -300,6 +301,10 @@ fun ProfileScreen(
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
ProfileTopBar(baseUser, accountViewModel, nav)
},
contentWindowInsets = WindowInsets(0),
bottomBar = {
AppBottomBar(
Route.Profile(accountViewModel.userProfile().pubkeyHex),
@@ -314,7 +319,7 @@ fun ProfileScreen(
}
},
) { padding ->
Box(modifier = Modifier.padding(padding)) {
Box(modifier = Modifier.padding(bottom = padding.calculateBottomPadding())) {
RenderSurface(scrollState) { tabRowModifier: Modifier, pagerModifier: Modifier ->
RenderScreen(
baseUser,
@@ -31,10 +31,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -67,17 +64,13 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle
import com.vitorpamplona.amethyst.ui.components.LoadingAnimation
import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.identity.UserExternalIdentitiesViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.Size100dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.userProfileBorderModifier
import kotlinx.coroutines.launch
@@ -89,74 +82,9 @@ fun ProfileHeader(
nav: INav,
accountViewModel: AccountViewModel,
) {
var popupExpanded by remember { mutableStateOf(false) }
Box {
DrawBanner(baseUser, accountViewModel)
if (nav.canPop()) {
Box(
modifier =
Modifier
.statusBarsPadding()
.padding(start = 10.dp, end = 10.dp, top = 10.dp)
.size(40.dp)
.align(Alignment.TopStart),
) {
Button(
modifier =
Modifier
.size(30.dp)
.align(Alignment.Center),
onClick = nav::popBack,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.background,
),
contentPadding = ZeroPadding,
) {
ArrowBackIcon(tint = MaterialTheme.colorScheme.placeholderText)
}
}
}
Box(
modifier =
Modifier
.statusBarsPadding()
.padding(start = 10.dp, end = 10.dp, top = 10.dp)
.size(40.dp)
.align(Alignment.TopEnd),
) {
Button(
modifier =
Modifier
.size(30.dp)
.align(Alignment.Center),
onClick = { popupExpanded = true },
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.background,
),
contentPadding = ZeroPadding,
) {
Icon(
tint = MaterialTheme.colorScheme.placeholderText,
symbol = MaterialSymbols.MoreVert,
contentDescription = stringRes(R.string.more_options),
)
UserProfileDropDownMenu(
baseUser,
popupExpanded,
{ popupExpanded = false },
accountViewModel,
)
}
}
Column(
modifier =
Modifier
@@ -0,0 +1,109 @@
/*
* 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.screen.loggedIn.profile.header
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileTopBar(
baseUser: User,
accountViewModel: AccountViewModel,
nav: INav,
) {
ShorterTopAppBar(
title = {},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent,
),
navigationIcon = {
if (nav.canPop()) {
Button(
modifier = Modifier.padding(start = 10.dp).size(30.dp),
onClick = nav::popBack,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.background,
),
contentPadding = ZeroPadding,
) {
ArrowBackIcon(tint = MaterialTheme.colorScheme.placeholderText)
}
}
},
actions = {
var popupExpanded by remember { mutableStateOf(false) }
Button(
modifier = Modifier.padding(end = 10.dp).size(30.dp),
onClick = { popupExpanded = true },
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.background,
),
contentPadding = ZeroPadding,
) {
Icon(
tint = MaterialTheme.colorScheme.placeholderText,
symbol = MaterialSymbols.MoreVert,
contentDescription = stringRes(R.string.more_options),
)
UserProfileDropDownMenu(
baseUser,
popupExpanded,
{ popupExpanded = false },
accountViewModel,
)
}
},
)
}