Merge remote-tracking branch 'origin/main' into claude/audio-rooms-android-ui-PIpFN

This commit is contained in:
Claude
2026-04-27 17:57:03 +00:00
42 changed files with 108 additions and 48 deletions
@@ -98,6 +98,8 @@ class BouncingIntentNav(
override fun navBottomBar(route: Route) = nav(route)
override fun canPop(): Boolean = false
override fun popBack() {
// No-op: this nav is for in-Activity chat navigation that
// exclusively forwards to MainActivity. There's no back stack
@@ -46,6 +46,8 @@ class EmptyNav : INav {
override fun navBottomBar(route: Route) {}
override fun canPop(): Boolean = false
override fun popBack() {}
override fun <T : Route> popUpTo(
@@ -43,6 +43,8 @@ interface INav {
fun navBottomBar(route: Route)
fun canPop(): Boolean
fun popBack()
fun <T : Route> popUpTo(
@@ -89,6 +89,8 @@ class Nav(
}
}
override fun canPop(): Boolean = controller.previousBackStackEntry != null
override fun popBack() {
navigationScope.launch {
controller.navigateUp()
@@ -25,13 +25,14 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopBarWithBackButton(
caption: String,
popBack: () -> Unit,
nav: INav,
) {
ShorterTopAppBar(
title = {
@@ -42,8 +43,13 @@ fun TopBarWithBackButton(
)
},
navigationIcon = {
IconButton(popBack) {
ArrowBackIcon()
// Suppress the back arrow when this is the bottom of the back stack
// (i.e. the user landed here via the bottom nav, which clears the stack
// with popUpTo(route) { inclusive = true }).
if (nav.canPop()) {
IconButton(nav::popBack) {
ArrowBackIcon()
}
}
},
)
@@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUse
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.note.SearchIcon
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
@@ -62,7 +63,16 @@ fun UserDrawerSearchTopBar(
}
},
navigationIcon = {
LoggedInUserPictureDrawer(accountViewModel, nav::openDrawer)
// When this screen sits on top of a back stack (entered via the drawer
// or any deep link), show a back arrow. When it's the root (entered via
// the bottom nav, which clears the stack), show the drawer opener.
if (nav.canPop()) {
IconButton(onClick = nav::popBack) {
ArrowBackIcon()
}
} else {
LoggedInUserPictureDrawer(accountViewModel, nav::openDrawer)
}
},
actions = {
IconButton(onClick = { nav.nav(Route.Search) }) {
@@ -359,7 +359,7 @@ fun PayViaIntentScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.manual_zaps), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.manual_zaps), nav)
},
) { pad ->
val list = accountViewModel.tempManualPaymentCache.get(paymentId)
@@ -143,7 +143,7 @@ fun ProfileBadgesScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.profile_badges_title), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.profile_badges_title), nav)
},
) { pad ->
Column(Modifier.padding(pad).fillMaxSize()) {
@@ -129,7 +129,7 @@ private fun RenderBookmarkScreen(
isInvertedLayout = false,
topBar = {
Column {
TopBarWithBackButton(stringRes(id = R.string.bookmarks_title), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.bookmarks_title), nav)
SecondaryTabRow(
containerColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
@@ -109,7 +109,7 @@ fun ListOfBookmarkGroupsFeed(
) {
Scaffold(
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.bookmark_lists), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.bookmark_lists), nav)
},
floatingActionButton = {
BookmarkGroupFab(onAddGroup = addBookmarkGroup)
@@ -77,7 +77,7 @@ private fun ListManagementView(
Scaffold(
modifier = modifier,
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.article_bookmark_management_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.article_bookmark_management_title), nav)
},
floatingActionButton = {
NewListButton { nav.nav(Route.BookmarkGroupMetadataEdit()) }
@@ -76,7 +76,7 @@ private fun ListManagementView(
Scaffold(
modifier = modifier,
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.post_bookmark_management_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.post_bookmark_management_title), nav)
},
floatingActionButton = {
NewListButton { nav.nav(Route.BookmarkGroupMetadataEdit()) }
@@ -137,7 +137,7 @@ private fun RenderOldBookmarkScreen(
isInvertedLayout = false,
topBar = {
Column {
TopBarWithBackButton(stringRes(id = R.string.old_bookmarks_title), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.old_bookmarks_title), nav)
SecondaryTabRow(
containerColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
@@ -129,8 +129,10 @@ private fun RenderDraftListScreen(
)
},
navigationIcon = {
IconButton(onClick = nav::popBack) {
ArrowBackIcon()
if (nav.canPop()) {
IconButton(onClick = nav::popBack) {
ArrowBackIcon()
}
}
},
actions = {
@@ -77,7 +77,7 @@ fun FavoriteAlgoFeedsListScreen(
topBar = {
TopBarWithBackButton(
caption = stringRes(R.string.favorite_dvms_title),
popBack = nav::popBack,
nav = nav,
)
},
) { padding ->
@@ -105,7 +105,7 @@ fun ListOfEmojiPacksFeed(
) {
Scaffold(
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.emoji_packs_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.emoji_packs_title), nav)
},
floatingActionButton = {
EmojiPackFab(onAddPack = addEmojiPack)
@@ -88,7 +88,7 @@ private fun EmojiPackSelectionView(
Scaffold(
modifier = modifier,
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.emoji_pack_management_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.emoji_pack_management_title), nav)
},
) { contentPadding ->
Column(
@@ -97,7 +97,7 @@ private fun MyEmojiListView(
) {
Scaffold(
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.my_emoji_list_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.my_emoji_list_title), nav)
},
) { contentPadding ->
val packAddresses by observeNoteEventAndMap<EmojiPackSelectionEvent, List<Address>>(
@@ -67,7 +67,7 @@ fun InterestSetScreen(
Scaffold(
topBar = {
TopBarWithBackButton(caption = set?.title ?: stringRes(R.string.interest_sets_title), nav::popBack)
TopBarWithBackButton(caption = set?.title ?: stringRes(R.string.interest_sets_title), nav)
},
) { paddingValues ->
if (set == null) {
@@ -63,7 +63,7 @@ fun ListOfInterestSetsScreen(
Scaffold(
topBar = {
TopBarWithBackButton(caption = stringRes(R.string.interest_sets_title), nav::popBack)
TopBarWithBackButton(caption = stringRes(R.string.interest_sets_title), nav)
},
floatingActionButton = {
InterestSetFab(onAdd = { nav.nav(Route.InterestSetMetadataEdit()) })
@@ -85,6 +85,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.note.authenticate
@@ -110,7 +111,7 @@ fun AccountBackupScreen(
accountViewModel: AccountViewModel,
nav: INav,
) {
AccountBackupScreenContent(accountViewModel, nav::popBack)
AccountBackupScreenContent(accountViewModel, nav)
}
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
@@ -119,7 +120,8 @@ fun AccountBackupScreenPreview() {
ThemeComparisonRow {
AccountBackupScreenContent(
mockAccountViewModel(),
) {}
EmptyNav(),
)
}
}
@@ -127,13 +129,13 @@ fun AccountBackupScreenPreview() {
@Composable
private fun AccountBackupScreenContent(
accountViewModel: AccountViewModel,
onClose: () -> Unit,
nav: INav,
) {
Scaffold(
topBar = {
TopBarWithBackButton(
stringRes(R.string.backup_keys),
popBack = onClose,
nav = nav,
)
},
) {
@@ -62,7 +62,7 @@ fun ListOfPeopleListsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(R.string.my_lists), nav::popBack)
TopBarWithBackButton(stringRes(R.string.my_lists), nav)
},
) { paddingValues ->
Column(
@@ -81,7 +81,7 @@ fun FollowListAndPackAndUserScreen(
val userName by observeUserName(userToAddOrRemove, accountViewModel)
TopBarWithBackButton(
caption = stringRes(id = R.string.follow_set_man_dialog_title2, userName),
popBack = nav::popBack,
nav = nav,
)
},
) { contentPadding ->
@@ -93,7 +93,7 @@ private fun RenderPinnedNotesScreen(
DisappearingScaffold(
isInvertedLayout = false,
topBar = {
TopBarWithBackButton(stringRes(id = R.string.pinned_notes), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.pinned_notes), nav)
},
accountViewModel = accountViewModel,
) { paddingValues ->
@@ -67,6 +67,7 @@ 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
@@ -93,6 +94,33 @@ fun ProfileHeader(
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
@@ -143,7 +143,7 @@ fun ShowQRScreen(
topBar = {
TopBarWithBackButton(
"",
nav::popBack,
nav,
)
},
) { pad ->
@@ -93,7 +93,7 @@ fun EventSyncScreen(
topBar = {
TopBarWithBackButton(
caption = stringRes(R.string.event_sync_title),
popBack = nav::popBack,
nav = nav,
)
},
) { padding ->
@@ -149,7 +149,7 @@ fun RequestToVanishScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.request_to_vanish), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.request_to_vanish), nav)
},
) { padding ->
Column(
@@ -77,7 +77,7 @@ fun VanishEventsScreen(
topBar = {
TopBarWithBackButton(
stringRes(id = R.string.vanish_events_title),
nav::popBack,
nav,
)
},
) { padding ->
@@ -93,7 +93,7 @@ fun AllSettingsScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.settings), nav)
},
) { padding ->
Column(Modifier.padding(padding).verticalScroll(rememberScrollState())) {
@@ -93,7 +93,7 @@ fun SettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.application_preferences), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.application_preferences), nav)
},
) {
Column(Modifier.padding(it)) {
@@ -91,7 +91,7 @@ fun BottomBarSettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.bottom_bar_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.bottom_bar_settings), nav)
},
) { padding ->
Column(Modifier.padding(padding)) {
@@ -69,7 +69,7 @@ fun CallSettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.call_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.call_settings), nav)
},
) { padding ->
Column(
@@ -64,7 +64,7 @@ fun NamecoinSettingsScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.namecoin_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.namecoin_settings), nav)
},
) {
Column(
@@ -65,7 +65,7 @@ fun OtsSettingsScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.ots_explorer_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.ots_explorer_settings), nav)
},
) {
Column(
@@ -90,7 +90,7 @@ fun ReactionsSettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.reactions_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.reactions_settings), nav)
},
) { padding ->
Column(Modifier.padding(padding)) {
@@ -157,7 +157,7 @@ fun SecurityFiltersScreen(
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.security_filters), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.security_filters), nav)
},
) {
Column(
@@ -93,7 +93,7 @@ fun UserSettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.user_preferences), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.user_preferences), nav)
},
) {
Column(Modifier.padding(it)) {
@@ -91,7 +91,7 @@ fun VideoPlayerSettingsScreen(
) {
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.video_player_settings), nav::popBack)
TopBarWithBackButton(stringRes(id = R.string.video_player_settings), nav)
},
) { padding ->
Column(Modifier.padding(padding)) {
@@ -107,7 +107,7 @@ fun NewHlsVideoScreen(
topBar = {
TopBarWithBackButton(
caption = stringResource(R.string.share_hls_video),
popBack = nav::popBack,
nav = nav,
)
},
) { padding ->
@@ -88,11 +88,13 @@ fun WalletScreen(
TopAppBar(
title = { Text(stringRes(R.string.wallet)) },
navigationIcon = {
IconButton(onClick = { nav.popBack() }) {
Icon(
symbol = MaterialSymbols.AutoMirrored.ArrowBack,
contentDescription = stringRes(R.string.back),
)
if (nav.canPop()) {
IconButton(onClick = { nav.popBack() }) {
Icon(
symbol = MaterialSymbols.AutoMirrored.ArrowBack,
contentDescription = stringRes(R.string.back),
)
}
}
},
actions = {
@@ -130,8 +130,10 @@ private fun RenderWebBookmarksScreen(
Text(text = stringRes(id = R.string.web_bookmarks))
},
navigationIcon = {
IconButton(onClick = nav::popBack) {
ArrowBackIcon()
if (nav.canPop()) {
IconButton(onClick = nav::popBack) {
ArrowBackIcon()
}
}
},
)