mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-09-14 00:55:08 +00:00
fix(search): initialise the list before the collector that scrolls it
Opening search from any seeded screen crashed the app on the spot:
NullPointerException: Attempt to invoke virtual method
LazyListState.scrollToItem(...) on a null object reference
at SearchBarViewModel.updateDataSource(SearchBarViewModel.kt:479)
at SearchBarViewModel.<init>(SearchBarViewModel.kt:131)
`sourceWatcher` shares its flow `Eagerly`, so `onEach { updateDataSource(...) }`
runs while the constructor is still executing, and `updateDataSource` scrolls
`listState` -- which was declared *after* it. Kotlin initialises properties in
declaration order, so at that moment the field is still null.
It was latent until this branch. `updateDataSource` returns early on a blank
term, and the box always opened blank, so the scroll was never reached during
construction. Seeding the field with the screen's own filter makes the term
non-blank on the very first pass, which turns the ordering bug into a crash the
moment search opens from any seeded feed.
Moving the declaration above the collector fixes it, and the comment says why it
has to stay there -- the next person to tidy these fields alphabetically would
put it back.
Reproduced deterministically before the change (Reads -> search, fresh crash
buffer, one FATAL) and confirmed gone after, on the same tap sequence. The
feature it was blocking now works: search opened from Reads seeds `kind:article`
as a chip, tapping the chip offers Change/Remove, and Change cuts it to `kind:`
and opens the new kind picker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab13369fd1
commit
6f83f6c5ed
+8
-2
@@ -124,14 +124,20 @@ class SearchBarViewModel(
|
||||
followPlusAllMineWithSearchRelays = account.followPlusAllMineWithSearch.flow,
|
||||
)
|
||||
|
||||
// Declared before [sourceWatcher], and it must stay there. That collector is Eagerly
|
||||
// shared, so it runs `updateDataSource` during construction, and `updateDataSource` scrolls
|
||||
// this list -- Kotlin initialises properties in declaration order, so from below it would
|
||||
// still be null. It never showed while the box opened empty, because a blank term returns
|
||||
// before the scroll; seeding the field from the screen's filter made the term non-blank on
|
||||
// the very first pass and turned that into an NPE the moment search opened.
|
||||
val listState: LazyListState = LazyListState(0, 0)
|
||||
|
||||
@Suppress("unused")
|
||||
val sourceWatcher =
|
||||
source
|
||||
.onEach { updateDataSource(searchValue) }
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, SearchSource.RELAYS)
|
||||
|
||||
val listState: LazyListState = LazyListState(0, 0)
|
||||
|
||||
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
|
||||
val directNip05Resolver: Flow<User?> =
|
||||
searchTerm
|
||||
|
||||
Reference in New Issue
Block a user