Skip to content

Commit a9d4533

Browse files
committed
fix(ui): keep notes list at top on cold start (pinned header visible)
The staggered grid auto-scrolled from index 0 to index 1 on initial content load (Compose Foundation quirk with the full-line pinned header/body items), pushing the "Pinned" section header out of view on every fresh start. Add a short top-settle guard that holds the grid at index 0 while the layout settles right after content first appears (runs once per composition, so it never interferes with the user's in-session scroll position). Make the list/ grid scroll state non-saveable so a cold start always begins at the top while in-session navigation (note editor is a separate activity), background→ foreground and scrolling keep the position.
1 parent 1d3a5a1 commit a9d4533

1 file changed

Lines changed: 34 additions & 4 deletions

File tree

  • android/app/src/main/java/dev/dettmer/simplenotes/ui/main

android/app/src/main/java/dev/dettmer/simplenotes/ui/main/MainScreen.kt

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import androidx.compose.foundation.layout.Column
2121
import androidx.compose.foundation.layout.fillMaxSize
2222
import androidx.compose.foundation.layout.fillMaxWidth
2323
import androidx.compose.foundation.layout.padding
24-
import androidx.compose.foundation.lazy.rememberLazyListState
25-
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
24+
import androidx.compose.foundation.lazy.LazyListState
25+
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
2626
import androidx.compose.material.icons.Icons
2727
import androidx.compose.material.icons.automirrored.filled.ArrowBack
2828
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
@@ -100,6 +100,15 @@ private const val TIMESTAMP_UPDATE_INTERVAL_MS = 30_000L
100100
/** 🆕 v1.9.0 (F13): Delay before scrolling to top after manual sync, giving Compose time to recompose with new data. */
101101
private const val SYNC_SCROLL_DELAY_MS = 150L
102102

103+
/**
104+
* Grid-Top-Settle-Guard: LazyVerticalStaggeredGrid scrollt beim ersten Laden mit FullLine-Items
105+
* (Pinned-Header/-Body) spontan ein Item nach unten, wodurch der "Angeheftet"-Header oben aus dem
106+
* Viewport rutscht. Direkt nach dem ersten Erscheinen des Inhalts halten wir das Grid für ein
107+
* kurzes Fenster auf Index 0, bis sich das Layout gesetzt hat.
108+
*/
109+
private const val GRID_TOP_SETTLE_FRAMES = 20
110+
private const val GRID_TOP_SETTLE_INTERVAL_MS = 30L
111+
103112
/** 🆕 v2.7.0 (Folders): Dauer/Versatz der Ordner-Navigations-Animation (analog shared_axis_x, 200 ms). */
104113
private const val FOLDER_ANIM_DURATION_MS = 200
105114
private const val FOLDER_SLIDE_FRACTION = 0.3f
@@ -755,12 +764,33 @@ private fun NotesPane(
755764
onToggleSelection: (String) -> Unit,
756765
focusManager: FocusManager
757766
) {
758-
val listState = rememberLazyListState()
759-
val gridState = rememberLazyStaggeredGridState()
767+
// Scroll-State bewusst NICHT saveable (kein rememberSaveable): Position bleibt innerhalb
768+
// der lebenden Komposition erhalten (Notiz öffnen & zurück, Background→Foreground, da der
769+
// Editor eine eigene Activity ist und MainActivity nur gestoppt, nicht zerstört wird),
770+
// wird aber NICHT in den savedInstanceState-Bundle geschrieben. Dadurch startet ein
771+
// Kaltstart (Prozess-Tod) immer ganz oben mit sichtbarem "Angeheftet"-Header.
772+
val listState = remember(folderKey) { LazyListState() }
773+
val gridState = remember(folderKey) { LazyStaggeredGridState() }
760774
val foldersForPane = if (folderKey == null) folders else emptyList() // Ordner nur in der Root-Ansicht
761775
// 🆕 v2.7.0 (Folders): Notizen dieses Slots — eigener folderKey, nicht der gerade aktive Ordner.
762776
val paneNotes = remember(notes, folderKey) { notes.filter { it.folderName == folderKey } }
763777

778+
// Grid-Top-Settle-Guard: das Staggered-Grid scrollt beim ersten Laden spontan ein Item
779+
// nach unten (Foundation-Quirk mit FullLine-Items) → Pinned-Header verschwindet. Direkt
780+
// nach Erscheinen des Inhalts kurz auf Index 0 halten, bis das Layout sich gesetzt hat.
781+
// Läuft nur einmal pro Komposition (Key = folderKey + "hat Inhalt"), stört spätere
782+
// In-Session-Scrollposition nicht.
783+
LaunchedEffect(folderKey, paneNotes.isEmpty()) {
784+
if (isActive && displayMode == "grid" && paneNotes.isNotEmpty()) {
785+
repeat(GRID_TOP_SETTLE_FRAMES) {
786+
if (gridState.firstVisibleItemIndex != 0 || gridState.firstVisibleItemScrollOffset != 0) {
787+
gridState.scrollToItem(0)
788+
}
789+
kotlinx.coroutines.delay(GRID_TOP_SETTLE_INTERVAL_MS)
790+
}
791+
}
792+
}
793+
764794
LaunchedEffect(scrollToTop) {
765795
if (isActive && scrollToTop) {
766796
if (displayMode == "grid") gridState.animateScrollToItem(0) else listState.animateScrollToItem(0)

0 commit comments

Comments
 (0)