From 4d9d3ee0bd037abd021ce4fd88c7af51c5e06422 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Fri, 27 Mar 2026 17:09:07 -0300 Subject: [PATCH] Make BunkerConnectRequestScreen scrollable and improve layout structure - BunkerConnectRequestScreen: Wrap content in a Scaffold and add vertical scrolling support with a scrollbar. Move the action buttons (Cancel/Connect) into the Scaffold's bottomBar to keep them fixed while the form content scrolls. - BunkerConnectRequestScreen: Update parameters to accept `scaffoldPadding` instead of a generic `Modifier` for better layout integration. - Update `IncomingRequestScreen` and `BunkerSingleEventHomeScreen` to propagate `PaddingValues` to the updated bunker request screen. --- .../nostrsigner/ui/IncomingRequestScreen.kt | 3 + .../greenart7c3/nostrsigner/ui/MainScreen.kt | 1 + .../components/BunkerConnectRequestScreen.kt | 397 +++++++++--------- .../components/BunkerSingleEventHomeScreen.kt | 4 +- 4 files changed, 215 insertions(+), 190 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt index d8cb008c..5840f085 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt @@ -2,6 +2,7 @@ package com.greenart7c3.nostrsigner.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size @@ -33,6 +34,7 @@ import kotlinx.collections.immutable.ImmutableList fun IncomingRequestScreen( horizontalPadding: Dp, modifier: Modifier, + scaffoldPadding: PaddingValues, intents: ImmutableList, bunkerRequests: ImmutableList, packageName: String?, @@ -78,6 +80,7 @@ fun IncomingRequestScreen( BunkerSingleEventHomeScreen( horizontalPadding = horizontalPadding, modifier = modifier, + scaffoldPadding = scaffoldPadding, bunkerRequest = bunkerRequests.first(), account = account, onLoading = onLoading, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index 5577c752..82b6bda8 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -381,6 +381,7 @@ fun MainScreen( IncomingRequestScreen( horizontalPadding = verticalPadding, modifier = modifier, + scaffoldPadding = padding, intents = intents, bunkerRequests = bunkerRequests, packageName = packageName, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt index f684ebc6..82bae52c 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt @@ -5,13 +5,17 @@ import androidx.compose.foundation.clickable 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.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.BottomAppBar import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ElevatedButton @@ -62,6 +66,7 @@ import com.greenart7c3.nostrsigner.ui.RememberType import com.greenart7c3.nostrsigner.ui.SettingsRow import com.greenart7c3.nostrsigner.ui.deleteAfterToSeconds import com.greenart7c3.nostrsigner.ui.parseDeleteAfterType +import com.greenart7c3.nostrsigner.ui.verticalScrollbar import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -72,7 +77,7 @@ import kotlinx.coroutines.launch @Composable fun BunkerConnectRequestScreen( horizontalPadding: Dp, - modifier: Modifier, + scaffoldPadding: PaddingValues, shouldCloseApp: Boolean, account: Account, bunkerRequest: AmberBunkerRequest, @@ -147,220 +152,234 @@ fun BunkerConnectRequestScreen( skipPartiallyExpanded = true, ) - Column(modifier = modifier) { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp), - text = appName.value, - fontWeight = FontWeight.Bold, - fontSize = 18.sp, - textAlign = TextAlign.Center, - ) + val scrollState = rememberScrollState() - // Account selection - accounts.forEachIndexed { index, acc -> - ListItem( - modifier = Modifier - .border( - width = 1.dp, - color = if (selectedAccountIndex == index) { - MaterialTheme.colorScheme.primary - } else { - Color.Transparent - }, - shape = RoundedCornerShape(8.dp), - ) - .selectable( - selected = selectedAccountIndex == index, - onClick = { - selectedAccountIndex = index - }, + Scaffold( + modifier = Modifier.fillMaxSize().padding(scaffoldPadding), + bottomBar = { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = horizontalPadding) + .padding(vertical = 8.dp), + Arrangement.spacedBy(8.dp), + Alignment.CenterVertically, + ) { + AmberButton( + modifier = Modifier.weight(1f), + onClick = { + onReject(RememberType.NEVER) + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFFFF6B00), ), - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.background, - ), - leadingContent = { - ProfilePictureIcon( - account = acc, - ) - }, - headlineContent = { - val name by acc.name.collectAsStateWithLifecycle() - Text( - name.ifBlank { acc.npub.toShortenHex() }, - fontWeight = FontWeight.Bold, - fontSize = 16.sp, - ) - }, - ) - } + text = stringResource(R.string.cancel), + ) - // Relays with trust scores - if (connectionRelays.isNotEmpty()) { - Spacer(modifier = Modifier.height(4.dp)) + AmberButton( + modifier = Modifier.weight(1f), + onClick = { + val deleteAfter = deleteAfterToSeconds(parseDeleteAfterType(deleteAfterIndex)) + onAccept(localPermissions, selectedOption, closeApp, RememberType.ALWAYS, deleteAfter, accounts[selectedAccountIndex]) + }, + text = stringResource(R.string.connect), + ) + } + }, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .verticalScrollbar(scrollState) + .verticalScroll(scrollState) + .padding(horizontal = horizontalPadding), + ) { Text( - text = stringResource(R.string.relays_used), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + text = appName.value, fontWeight = FontWeight.Bold, - fontSize = 14.sp, - modifier = Modifier.padding(vertical = 4.dp), + fontSize = 18.sp, + textAlign = TextAlign.Center, ) - connectionRelays.forEach { relay -> - Row( + + // Account selection + accounts.forEachIndexed { index, acc -> + ListItem( modifier = Modifier - .fillMaxWidth() - .padding(vertical = 2.dp) .border( width = 1.dp, - color = MaterialTheme.colorScheme.outline, + color = if (selectedAccountIndex == index) { + MaterialTheme.colorScheme.primary + } else { + Color.Transparent + }, shape = RoundedCornerShape(8.dp), ) - .padding(8.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = relay.url, - modifier = Modifier.weight(1f), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - fontSize = 13.sp, - ) - TrustScoreBadge( - score = trustScores[relay.url], - isLoading = loadingScores[relay.url] == true, - ) - } - } - } - - HorizontalDivider( - modifier = Modifier.padding(vertical = 8.dp), - thickness = 0.5.dp, - color = MaterialTheme.colorScheme.outline, - ) - - // Close app toggle - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .clickable { - closeApp = !closeApp - }, - ) { - Text( - modifier = Modifier.weight(1f), - text = stringResource(R.string.close_application), - fontSize = 14.sp, - ) - Switch( - checked = closeApp, - onCheckedChange = { - closeApp = it - }, - ) - } - - Box( - Modifier.padding(vertical = 2.dp), - ) { - SettingsRow( - R.string.delete_after, - null, - deleteAfterItems, - deleteAfterIndex, - ) { - deleteAfterIndex = it - } - } - - ChooseSignPolicy( - selectedOption = selectedOption, - onSelected = { - selectedOption = it - }, - ) - - if (selectedOption == 1 && localPermissions.isNotEmpty()) { - Box( - Modifier.fillMaxWidth(), - contentAlignment = Alignment.Center, - ) { - ElevatedButton( - colors = ButtonDefaults.buttonColors().copy( - contentColor = Color.Black, + .selectable( + selected = selectedAccountIndex == index, + onClick = { + selectedAccountIndex = index + }, + ), + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.background, ), - shape = RoundedCornerShape(20), - content = { - Text(stringResource(R.string.permissions)) + leadingContent = { + ProfilePictureIcon( + account = acc, + ) }, - onClick = { - showModal = true + headlineContent = { + val name by acc.name.collectAsStateWithLifecycle() + Text( + name.ifBlank { acc.npub.toShortenHex() }, + fontWeight = FontWeight.Bold, + fontSize = 16.sp, + ) }, ) } - if (showModal) { - ModalBottomSheet( - sheetState = sheetState, - onDismissRequest = { - showModal = false - }, - ) { - Scaffold( - bottomBar = { - BottomAppBar { - IconRow( - center = true, - title = stringResource(R.string.go_back), - icon = ImageVector.vectorResource(R.drawable.back), - onClick = { - showModal = false - }, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, + + // Relays with trust scores + if (connectionRelays.isNotEmpty()) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(R.string.relays_used), + fontWeight = FontWeight.Bold, + fontSize = 14.sp, + modifier = Modifier.padding(vertical = 4.dp), + ) + connectionRelays.forEach { relay -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 2.dp) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outline, + shape = RoundedCornerShape(8.dp), + ) + .padding(8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, ) { - EnabledPermissions( - Modifier.padding(it), - localPermissions, + Text( + text = relay.url, + modifier = Modifier.weight(1f), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 13.sp, + ) + TrustScoreBadge( + score = trustScores[relay.url], + isLoading = loadingScores[relay.url] == true, ) } } } - } - Spacer(modifier = Modifier.height(8.dp)) - - Row( - Modifier - .fillMaxWidth() - .padding(horizontal = horizontalPadding) - .padding(vertical = 8.dp), - Arrangement.spacedBy(8.dp), - Alignment.CenterVertically, - ) { - AmberButton( - modifier = Modifier.weight(1f), - onClick = { - onReject(RememberType.NEVER) - }, - colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFFFF6B00), - ), - text = stringResource(R.string.cancel), + HorizontalDivider( + modifier = Modifier.padding(vertical = 8.dp), + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.outline, ) - AmberButton( - modifier = Modifier.weight(1f), - onClick = { - val deleteAfter = deleteAfterToSeconds(parseDeleteAfterType(deleteAfterIndex)) - onAccept(localPermissions, selectedOption, closeApp, RememberType.ALWAYS, deleteAfter, accounts[selectedAccountIndex]) + // Close app toggle + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable { + closeApp = !closeApp + }, + ) { + Text( + modifier = Modifier.weight(1f), + text = stringResource(R.string.close_application), + fontSize = 14.sp, + ) + Switch( + checked = closeApp, + onCheckedChange = { + closeApp = it + }, + ) + } + + Box( + Modifier.padding(vertical = 2.dp), + ) { + SettingsRow( + R.string.delete_after, + null, + deleteAfterItems, + deleteAfterIndex, + ) { + deleteAfterIndex = it + } + } + + ChooseSignPolicy( + selectedOption = selectedOption, + onSelected = { + selectedOption = it }, - text = stringResource(R.string.connect), ) + + if (selectedOption == 1 && localPermissions.isNotEmpty()) { + Box( + Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + ElevatedButton( + colors = ButtonDefaults.buttonColors().copy( + contentColor = Color.Black, + ), + shape = RoundedCornerShape(20), + content = { + Text(stringResource(R.string.permissions)) + }, + onClick = { + showModal = true + }, + ) + } + if (showModal) { + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = { + showModal = false + }, + ) { + Scaffold( + bottomBar = { + BottomAppBar { + IconRow( + center = true, + title = stringResource(R.string.go_back), + icon = ImageVector.vectorResource(R.drawable.back), + onClick = { + showModal = false + }, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + }, + ) { + EnabledPermissions( + Modifier.padding(it), + localPermissions, + ) + } + } + } + } + + Spacer(modifier = Modifier.height(8.dp)) } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt index 6faecd4a..eb262ca5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt @@ -3,6 +3,7 @@ package com.greenart7c3.nostrsigner.ui.components import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size @@ -61,6 +62,7 @@ import kotlinx.coroutines.launch fun BunkerSingleEventHomeScreen( horizontalPadding: Dp, modifier: Modifier, + scaffoldPadding: PaddingValues, bunkerRequest: AmberBunkerRequest, account: Account, onLoading: (Boolean) -> Unit, @@ -224,7 +226,7 @@ fun BunkerSingleEventHomeScreen( BunkerConnectRequestScreen( horizontalPadding = horizontalPadding, - modifier = modifier, + scaffoldPadding = scaffoldPadding, shouldCloseApp = applicationEntity?.application?.closeApplication ?: bunkerRequest.closeApplication, account = account, bunkerRequest = bunkerRequest,