f3653a69dc9c

fix: Restore complete Vault List layout (v2.14.23)

time
2026-08-14 16:38 UTC · 2 months ago
machine
koh2
agent
gpt-5.6-sol
session
vault list restoration
parent
ad56fa5783bb
2 files VERSION+1 −1app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+52 −58
VERSION+1 −1
@@ -1 +1 @@1-2.14.221+2.14.23

view file

app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+52 −58
@@ -44,7 +44,9 @@4444 import androidx.compose.material.icons.rounded.Close4545 import androidx.compose.material.icons.rounded.Search4646 import androidx.compose.material.icons.rounded.Settings47+import androidx.compose.material3.SmallFloatingActionButton4748 import androidx.compose.material.icons.outlined.Lock49+import androidx.compose.material3.FloatingActionButton4850 import androidx.compose.material3.Icon4951 import androidx.compose.material3.TextButton5052 import androidx.compose.material3.Snackbar@@ -67,7 +69,6 @@6769 import androidx.compose.ui.geometry.Offset6870 import androidx.compose.ui.geometry.Size6971 import androidx.compose.ui.graphics.Color70-import androidx.compose.ui.graphics.Brush7172 import androidx.compose.ui.graphics.SolidColor7273 import androidx.compose.ui.graphics.StrokeCap7374 import androidx.compose.ui.graphics.drawscope.Stroke@@ -80,7 +81,6 @@8081 import androidx.compose.ui.input.pointer.positionChange8182 import androidx.compose.ui.focus.FocusRequester8283 import androidx.compose.ui.focus.focusRequester83-import androidx.compose.ui.focus.onFocusChanged8484 import androidx.compose.ui.platform.LocalContext8585 import androidx.compose.ui.platform.LocalDensity8686 import androidx.compose.ui.text.TextStyle@@ -170,7 +170,6 @@170170     val undoEvent by vm.undoEvent.collectAsState()171171     val proNotice by vm.proNotice.collectAsState()172172     val snackbarHostState = remember { SnackbarHostState() }173-    var searchActive by remember { mutableStateOf(initialSearchActive) }174173     // Pull-to-refresh drives a manual sync round against paired devices.175174     val syncVm: SyncViewModel = hiltViewModel()176175     val syncStatus by syncVm.status.collectAsState()@@ -210,9 +209,7 @@210209 211210     ShellBackground(modifier) {212211         Box(Modifier.fillMaxSize()) {213-        Column(214-            Modifier.fillMaxSize().then(if (isMultiSelect) Modifier else Modifier.statusBarsPadding()),215-        ) {212+        Column(Modifier.fillMaxSize()) {216213             if (isMultiSelect) {217214                 MultiSelectTopBar(218215                     count = selectedIds.size,@@ -228,6 +225,16 @@228225                 },229226                 onDismiss = vm::dismissProNotice,230227             )228+            SearchBar(229+                value = search,230+                onChange = vm::setSearch,231+                count = if (search.isNotBlank()) "${entries.size}/$total" else pluralEntries(total),232+                onCollapse = onBack,233+                topInset = !isMultiSelect,234+                requestFocus = initialSearchActive,235+                syncStatus = syncStatus,236+                pairedCount = pairedDevices.size,237+            )231238             if (allTags.isNotEmpty()) {232239                 TagFilterStrip(233240                     allTags = allTags,@@ -237,7 +244,11 @@237244             }238245             SortBar(239246                 active = sort,247+                noteOnly = noteOnly,248+                onSettings = { onNavigate(PeachNav.Settings) },240249                 onSort = vm::setSort,250+                onToggleNote = vm::toggleNoteFilter,251+                onTrash = { onNavigate(PeachNav.Trash) },241252             )242253             Box(Modifier.fillMaxWidth().weight(1f)) {243254                 PeachPullToRefresh(@@ -284,47 +295,37 @@284295                                 modifier = Modifier.animateItem(),285296                             )286297                         }287-                        // Trailing space so the last row clears the contextual288-                        // action tier and search surface.289-                        item { Spacer(Modifier.height(148.dp)) }298+                        item { Spacer(Modifier.height(88.dp)) }290299                     }291300                 }301+                Column(302+                    modifier = Modifier.align(Alignment.BottomEnd)303+                        .padding(PeachSpacing.lg)304+                        .navigationBarsPadding(),305+                    horizontalAlignment = Alignment.CenterHorizontally,306+                    verticalArrangement = Arrangement.spacedBy(PeachSpacing.md),307+                ) {308+                    SmallFloatingActionButton(309+                        onClick = vm::lock,310+                        containerColor = peachColors.S1,311+                        contentColor = LocalPeachAccent.current,312+                    ) { Icon(Icons.Outlined.Lock, contentDescription = "Lock vault") }313+                    FloatingActionButton(314+                        onClick = {315+                            if (noteOnly) vm.createNote { id -> onNavigate(PeachNav.NotesMode(id)) }316+                            else onNavigate(PeachNav.EntryEdit(null))317+                        },318+                        containerColor = LocalPeachAccent.current,319+                        contentColor = peachColors.Bg,320+                    ) {321+                        Icon(Icons.Rounded.Add, contentDescription = if (noteOnly) "new note" else "new entry")322+                    }323+                }292324             }293325         }294-        Column(295-            Modifier.align(Alignment.BottomCenter)296-                .fillMaxWidth()297-                .background(Brush.verticalGradient(listOf(Color.Transparent, peachColors.Bg), startY = 0f))298-                .padding(top = PeachSpacing.lg)299-                .navigationBarsPadding(),300-        ) {301-            if (!searchActive) {302-                VaultActionCluster(303-                    noteOnly = noteOnly,304-                    onToggleNote = vm::toggleNoteFilter,305-                    onTrash = { onNavigate(PeachNav.Trash) },306-                    onSettings = { onNavigate(PeachNav.Settings) },307-                    onAdd = {308-                        if (noteOnly) vm.createNote { id -> onNavigate(PeachNav.NotesMode(id)) }309-                        else onNavigate(PeachNav.EntryEdit(null))310-                    },311-                    onLock = vm::lock,312-                )313-            }314-            SearchBar(315-                value = search,316-                onChange = vm::setSearch,317-                count = if (search.isNotBlank()) "${entries.size}/$total" else pluralEntries(total),318-                onCollapse = onBack,319-                requestFocus = initialSearchActive,320-                onFocusChange = { searchActive = it },321-                syncStatus = syncStatus,322-                pairedCount = pairedDevices.size,323-            )324-        }325326         SnackbarHost(326327             hostState = snackbarHostState,327-            modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 148.dp).navigationBarsPadding(),328+            modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding(),328329         ) { data ->329330             Snackbar(330331                 containerColor = peachColors.SurfaceElevated,@@ -387,8 +388,8 @@387388     onChange: (String) -> Unit,388389     count: String,389390     onCollapse: (() -> Unit)? = null,391+    topInset: Boolean = false,390392     requestFocus: Boolean = false,391-    onFocusChange: (Boolean) -> Unit = {},392393     syncStatus: SyncOutcomeSummary? = null,393394     pairedCount: Int = 0,394395 ) {@@ -397,7 +398,7 @@397398     LaunchedEffect(requestFocus) {398399         if (requestFocus) focusRequester.requestFocus()399400     }400-    Column(Modifier.fillMaxWidth()) {401+    Column(Modifier.fillMaxWidth().then(if (topInset) Modifier.statusBarsPadding() else Modifier)) {401402         Row(402403             Modifier403404                 .fillMaxWidth()@@ -441,8 +442,7 @@441442                     textStyle = PeachType.searchInput.copy(color = peachColors.T0),442443                     cursorBrush = SolidColor(accent),443444                     modifier = Modifier.fillMaxWidth()444-                        .focusRequester(focusRequester)445-                        .onFocusChanged { onFocusChange(it.isFocused) },445+                        .focusRequester(focusRequester),446446                 )447447             }448448             if (syncStatus != null) {@@ -461,7 +461,11 @@461461 @Composable462462 private fun SortBar(463463     active: SortOrder,464+    noteOnly: Boolean,465+    onSettings: () -> Unit,464466     onSort: (SortOrder) -> Unit,467+    onToggleNote: () -> Unit,468+    onTrash: () -> Unit,465469 ) {466470     val hairline = peachColors.T3467471     // One row, matching the browser's single combined sort+actions bar — the@@ -488,23 +492,21 @@488492             onSelected = { key ->489493                 SortOrder.entries.firstOrNull { it.name == key }?.let(onSort)490494             },491-            modifier = Modifier.fillMaxWidth(),495+            modifier = Modifier.weight(1f),492496         )497+        ActionCluster(noteOnly, onSettings, onToggleNote, onTrash)493498     }494499 }495500 496501 @Composable497-private fun VaultActionCluster(502+private fun ActionCluster(498503     noteOnly: Boolean,499504     onSettings: () -> Unit,500505     onToggleNote: () -> Unit,501506     onTrash: () -> Unit,502-    onAdd: () -> Unit,503-    onLock: () -> Unit,504507 ) {505508     val accent = LocalPeachAccent.current506509     Surface(507-        modifier = Modifier.padding(horizontal = PeachSpacing.xl),508510         color = peachColors.S1,509511         contentColor = peachColors.T1,510512         shape = RoundedCornerShape(999.dp),@@ -535,19 +537,11 @@535537                 icon = { Icon(Icons.Outlined.Delete, contentDescription = "trash") },536538                 onClick = onTrash,537539             )538-            ClusterIcon(539-                icon = { Icon(Icons.Rounded.Add, contentDescription = if (noteOnly) "new note" else "new entry") },540-                onClick = onAdd,541-            )542540             // Settings last: it is the only app-level action in the cluster,543541             // and the extension likewise keeps it out of the content actions.544542             ClusterIcon(545543                 icon = { Icon(Icons.Outlined.Settings, contentDescription = "settings") },546544                 onClick = onSettings,547-            )548-            ClusterIcon(549-                icon = { Icon(Icons.Outlined.Lock, contentDescription = "Lock vault", tint = accent) },550-                onClick = onLock,551545             )552546         }553547     }

view file