From 9a0eee3414d3098c6f5ebf08f6726e913b9f32f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Apr 2026 18:42:12 +0000 Subject: [PATCH 1/2] fix(ui): pass FeedDefinition through FeedFilterSpinner.onSelect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dialog used to hand the caller an integer index into the latest options list, but the indexes were captured from a snapshot taken at remember time. If options changed (a new community/list arrived) between dialog open and tap, the user could pick "Community A" and have an unrelated entry selected. Pass the resolved FeedDefinition directly so the picked item can never drift. Other audit fixes folded into the same composable: - Match the placeholder by both subclass and code string so TopFilter variants that share an Address-derived code (PeopleList vs MuteList) no longer collide. - Drop the local mutableStateOf for `selected` and the derivedStateOf-in- remember for `currentText` — both were redundant with the StateFlow round-trip and caused an extra recomposition per pick. - De-duplicate RenderOption with Name.name(context) (also fixes the accessibility text disagreeing with the visible label for Geohash). - Pre-compute the ordered (group, items) list once per options change. - Drop IndexedFeedDefinition (no longer needed), use Spacer.width instead of a Spacer with start padding. --- .../navigation/topbars/FeedFilterSpinner.kt | 223 +++++++----------- .../loggedIn/articles/ArticlesTopBar.kt | 2 +- .../loggedIn/audiorooms/AudioRoomsTopBar.kt | 2 +- .../ui/screen/loggedIn/badges/BadgesTopBar.kt | 2 +- .../communities/list/CommunitiesTopBar.kt | 2 +- .../loggedIn/discover/DiscoveryTopBar.kt | 2 +- .../browse/BrowseEmojiSetsTopBar.kt | 2 +- .../followPacks/list/FollowPacksTopBar.kt | 2 +- .../ui/screen/loggedIn/home/HomeTopBar.kt | 2 +- .../loggedIn/livestreams/LiveStreamsTopBar.kt | 2 +- .../ui/screen/loggedIn/longs/LongsTopBar.kt | 2 +- .../notifications/NotificationTopBar.kt | 2 +- .../loggedIn/pictures/PicturesTopBar.kt | 2 +- .../ui/screen/loggedIn/polls/PollsTopBar.kt | 2 +- .../loggedIn/products/ProductsTopBar.kt | 2 +- .../loggedIn/publicChats/PublicChatsTopBar.kt | 2 +- .../ui/screen/loggedIn/shorts/ShortsTopBar.kt | 2 +- .../ui/screen/loggedIn/video/StoriesTopBar.kt | 2 +- 18 files changed, 101 insertions(+), 156 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/FeedFilterSpinner.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/FeedFilterSpinner.kt index 710259e15..4761b28b3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/FeedFilterSpinner.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/FeedFilterSpinner.kt @@ -34,15 +34,14 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -69,7 +68,6 @@ import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols -import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.TopFilter import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote @@ -92,10 +90,6 @@ import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Size20Modifier import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.placeholderText -import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent -import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent -import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent -import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import kotlinx.collections.immutable.ImmutableList @OptIn(ExperimentalPermissionsApi::class) @@ -104,32 +98,27 @@ fun FeedFilterSpinner( placeholderCode: TopFilter, explainer: String, options: ImmutableList, - onSelect: (Int) -> Unit, + onSelect: (FeedDefinition) -> Unit, modifier: Modifier = Modifier, accountViewModel: AccountViewModel, ) { var optionsShowing by remember { mutableStateOf(false) } val context = LocalContext.current - val selectAnOption = - stringRes( - id = R.string.select_an_option, - ) + val selectAnOption = stringRes(id = R.string.select_an_option) - var selected by + val selected = remember(placeholderCode, options) { - mutableStateOf( - options.firstOrNull { it.code.code == placeholderCode.code }, - ) - } - - val currentText by - remember(placeholderCode, options) { - derivedStateOf { - selected?.name?.name(context) ?: selectAnOption + // Match by both subclass and code string to avoid collisions between + // TopFilter variants that derive `code` from the same Address (e.g. + // PeopleList vs MuteList). + options.firstOrNull { + it.code::class == placeholderCode::class && it.code.code == placeholderCode.code } } + val currentText = selected?.name?.name(context) ?: selectAnOption + val accessibilityDescription = if (selected != null) { stringRes(R.string.feed_filter_selected, currentText) @@ -282,10 +271,9 @@ fun FeedFilterSpinner( title = explainer, options = options, onDismiss = { optionsShowing = false }, - onSelect = { - selected = options[it] + onSelect = { definition -> optionsShowing = false - onSelect(it) + onSelect(definition) }, ) { RenderOption(it.name, accountViewModel) @@ -298,6 +286,7 @@ fun RenderOption( option: Name, accountViewModel: AccountViewModel, ) { + val context = LocalContext.current when (option) { is GeoHashName -> { LoadCityName(option.geoHashTag) { @@ -305,74 +294,35 @@ fun RenderOption( } } - is HashtagName -> { - Text(text = option.name(), fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface) - } - - is ResourceName -> { - Text( - text = stringRes(id = option.resourceId), - fontSize = Font14SP, - color = MaterialTheme.colorScheme.onSurface, - ) - } - + // Note-backed names: subscribe to the note so the displayed title updates as + // the corresponding event arrives from relays. The displayed string itself is + // produced by Name.name(), which already has the right precedence rules. is PeopleListName -> { val noteState by observeNote(option.note, accountViewModel) - - val noteEvent = noteState.note.event - val name = - when (noteEvent) { - is PeopleListEvent -> { - noteEvent.titleOrName() ?: option.note.dTag() - } - - is FollowListEvent -> { - noteEvent.title() ?: option.note.dTag() - } - - else -> { - option.note.dTag() - } - } - + val name = remember(noteState) { option.name(context) } Text(text = name, fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface) } is CommunityName -> { - val it by observeNote(option.note, accountViewModel) - - val addressable = it.note as? AddressableNote - val definition = addressable?.event as? CommunityDefinitionEvent - val label = definition?.name()?.ifBlank { null } ?: addressable?.dTag() ?: "" - Text(text = "/n/$label", fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface) - } - - is RelayName -> { - Text( - text = option.name(), - fontSize = Font14SP, - color = MaterialTheme.colorScheme.onSurface, - ) + val noteState by observeNote(option.note, accountViewModel) + val name = remember(noteState) { option.name(context) } + Text(text = name, fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface) } is FavoriteAlgoFeedName -> { val noteState by observeNote(option.note, accountViewModel) - val name = - (noteState.note.event as? AppDefinitionEvent) - ?.appMetaData() - ?.name - ?.takeIf { it.isNotBlank() } ?: option.note.dTag() - Text( - text = name, - fontSize = Font14SP, - color = MaterialTheme.colorScheme.onSurface, - ) + val name = remember(noteState) { option.name(context) } + Text(text = name, fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface) } - is InterestSetName -> { + // Pure names: no relay subscription needed. + is HashtagName, + is ResourceName, + is RelayName, + is InterestSetName, + -> { Text( - text = option.name(), + text = option.name(context), fontSize = Font14SP, color = MaterialTheme.colorScheme.onSurface, ) @@ -380,12 +330,6 @@ fun RenderOption( } } -@Immutable -private data class IndexedFeedDefinition( - val originalIndex: Int, - val item: FeedDefinition, -) - private enum class FeedGroup( @param:androidx.annotation.StringRes val labelRes: Int, ) { @@ -399,48 +343,51 @@ private enum class FeedGroup( RELAYS(R.string.feed_group_relays), } -private fun groupFeedDefinitions(options: ImmutableList): Map> { - val indexed = options.mapIndexed { index, item -> IndexedFeedDefinition(index, item) } - return indexed.groupBy { entry -> - when (entry.item.name) { - is HashtagName -> { - FeedGroup.HASHTAGS - } +private fun FeedDefinition.group(): FeedGroup = + when (name) { + is HashtagName -> { + FeedGroup.HASHTAGS + } - is CommunityName -> { - FeedGroup.COMMUNITIES - } + is CommunityName -> { + FeedGroup.COMMUNITIES + } - is PeopleListName -> { - FeedGroup.LISTS - } + is PeopleListName -> { + FeedGroup.LISTS + } - is RelayName -> { - FeedGroup.RELAYS - } + is RelayName -> { + FeedGroup.RELAYS + } - is GeoHashName -> { - FeedGroup.LOCATIONS - } + is GeoHashName -> { + FeedGroup.LOCATIONS + } - is FavoriteAlgoFeedName -> { - FeedGroup.DVMS - } + is FavoriteAlgoFeedName -> { + FeedGroup.DVMS + } - is InterestSetName -> { - FeedGroup.INTEREST_SETS - } + is InterestSetName -> { + FeedGroup.INTEREST_SETS + } - is ResourceName -> { - when (entry.item.code) { - is TopFilter.AroundMe -> FeedGroup.LOCATIONS - is TopFilter.Global -> FeedGroup.RELAYS - is TopFilter.AllFavoriteAlgoFeeds -> FeedGroup.DVMS - else -> FeedGroup.FEEDS - } + is ResourceName -> { + when (code) { + is TopFilter.AroundMe -> FeedGroup.LOCATIONS + is TopFilter.Global -> FeedGroup.RELAYS + is TopFilter.AllFavoriteAlgoFeeds -> FeedGroup.DVMS + else -> FeedGroup.FEEDS } } } + +private fun groupFeedDefinitions(options: ImmutableList): List>> { + val grouped = options.groupBy { it.group() } + return FeedGroup.entries.mapNotNull { group -> + grouped[group]?.takeIf { it.isNotEmpty() }?.let { group to it } + } } @OptIn(ExperimentalLayoutApi::class) @@ -448,7 +395,7 @@ private fun groupFeedDefinitions(options: ImmutableList): Map, - onSelect: (Int) -> Unit, + onSelect: (FeedDefinition) -> Unit, onDismiss: () -> Unit, onRenderItem: @Composable (FeedDefinition) -> Unit, ) { @@ -472,18 +419,15 @@ private fun GroupedFeedFilterDialog( ) } - FeedGroup.entries.forEach { group -> - val items = grouped[group] - if (!items.isNullOrEmpty()) { - item { - GroupSection( - label = stringRes(group.labelRes), - items = items, - isChipLayout = group == FeedGroup.HASHTAGS, - onSelect = onSelect, - onRenderItem = onRenderItem, - ) - } + grouped.forEach { (group, items) -> + item(key = group) { + GroupSection( + label = stringRes(group.labelRes), + items = items, + isChipLayout = group == FeedGroup.HASHTAGS, + onSelect = onSelect, + onRenderItem = onRenderItem, + ) } } } @@ -495,11 +439,12 @@ private fun GroupedFeedFilterDialog( @Composable private fun GroupSection( label: String, - items: List, + items: List, isChipLayout: Boolean, - onSelect: (Int) -> Unit, + onSelect: (FeedDefinition) -> Unit, onRenderItem: @Composable (FeedDefinition) -> Unit, ) { + val context = LocalContext.current Surface( modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp), shape = RoundedCornerShape(16.dp), @@ -523,13 +468,13 @@ private fun GroupSection( ) { items.forEach { entry -> Surface( - modifier = Modifier.clickable { onSelect(entry.originalIndex) }, + modifier = Modifier.clickable { onSelect(entry) }, shape = RoundedCornerShape(18.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline), color = Color.Transparent, ) { Text( - text = entry.item.name.name(), + text = entry.name.name(context), fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.padding(horizontal = 14.dp, vertical = 7.dp), @@ -545,15 +490,15 @@ private fun GroupSection( modifier = Modifier .fillMaxWidth() - .clickable { onSelect(entry.originalIndex) } + .clickable { onSelect(entry) } .padding(horizontal = 16.dp, vertical = 6.dp), ) { FeedIcon( - item = entry.item, + item = entry, modifier = Size20Modifier, ) - Spacer(modifier = Modifier.padding(start = 12.dp)) - Column(modifier = Modifier.weight(1f)) { onRenderItem(entry.item) } + Spacer(modifier = Modifier.width(12.dp)) + Column(modifier = Modifier.weight(1f)) { onRenderItem(entry) } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/ArticlesTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/ArticlesTopBar.kt index 80365369c..53bc0b952 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/ArticlesTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/ArticlesTopBar.kt @@ -64,7 +64,7 @@ private fun ArticlesTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/audiorooms/AudioRoomsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/audiorooms/AudioRoomsTopBar.kt index c76315864..9fc96bb5b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/audiorooms/AudioRoomsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/audiorooms/AudioRoomsTopBar.kt @@ -64,7 +64,7 @@ private fun AudioRoomsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/BadgesTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/BadgesTopBar.kt index 522a6d765..9a9974166 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/BadgesTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/BadgesTopBar.kt @@ -64,7 +64,7 @@ private fun BadgesTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/CommunitiesTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/CommunitiesTopBar.kt index 5c8957f05..641c1db90 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/CommunitiesTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/CommunitiesTopBar.kt @@ -64,7 +64,7 @@ private fun CommunitiesTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/DiscoveryTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/DiscoveryTopBar.kt index 4c3b57af3..668606b31 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/DiscoveryTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/DiscoveryTopBar.kt @@ -64,7 +64,7 @@ private fun TopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/BrowseEmojiSetsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/BrowseEmojiSetsTopBar.kt index abde6ead6..8bc6e8460 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/BrowseEmojiSetsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/BrowseEmojiSetsTopBar.kt @@ -64,7 +64,7 @@ private fun BrowseEmojiSetsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/FollowPacksTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/FollowPacksTopBar.kt index a29cf8434..bf4d82d3d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/FollowPacksTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/FollowPacksTopBar.kt @@ -64,7 +64,7 @@ private fun FollowPacksTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeTopBar.kt index 59e23c43c..3140f5880 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/HomeTopBar.kt @@ -69,7 +69,7 @@ private fun TopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/LiveStreamsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/LiveStreamsTopBar.kt index 656465430..147830473 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/LiveStreamsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/LiveStreamsTopBar.kt @@ -64,7 +64,7 @@ private fun LiveStreamsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/LongsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/LongsTopBar.kt index eac3b6b4f..dfcf802bf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/LongsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/LongsTopBar.kt @@ -64,7 +64,7 @@ private fun LongsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationTopBar.kt index 59cd27aef..2e377f37c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationTopBar.kt @@ -64,7 +64,7 @@ private fun TopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PicturesTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PicturesTopBar.kt index 5acf6cd50..bef51851b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PicturesTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PicturesTopBar.kt @@ -64,7 +64,7 @@ private fun PicturesTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollsTopBar.kt index dd05a7922..66437b1d3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/PollsTopBar.kt @@ -64,7 +64,7 @@ private fun PollsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/ProductsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/ProductsTopBar.kt index 12a404836..7ea108939 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/ProductsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/ProductsTopBar.kt @@ -64,7 +64,7 @@ private fun ProductsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsTopBar.kt index 98142ef97..a46e7f9e2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsTopBar.kt @@ -64,7 +64,7 @@ private fun PublicChatsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/ShortsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/ShortsTopBar.kt index 2a50c7c69..4c0ccc852 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/ShortsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/ShortsTopBar.kt @@ -64,7 +64,7 @@ private fun ShortsTopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/StoriesTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/StoriesTopBar.kt index d74ab4e67..5f3bffbf5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/StoriesTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/StoriesTopBar.kt @@ -65,7 +65,7 @@ private fun TopNavFilterBar( placeholderCode = listName, explainer = stringRes(R.string.select_list_to_filter), options = allLists, - onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.allFollows) }, + onSelect = onChange, accountViewModel = accountViewModel, ) } From e00da8c52d363818b949915769b068100ffa3615 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Apr 2026 19:10:03 +0000 Subject: [PATCH 2/2] fix(relay): fetch InterestSetEvent (kind 30015) for the account MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither AccountInfoAndListsFromKeyKinds2 nor BasicAccountInfoKinds2 included InterestSetEvent.KIND, so a fresh login on a new device wouldn't pull the user's existing interest sets — they only showed up if the device already had them in cache or the user re-created them locally. The spinner's INTEREST_SETS group would silently be empty. Also bump the AccountInfoAndListsFromKeyKinds2 limit from 20 to 80 so the combined list of NIP-51 lists (10 kinds, now 11) actually fits. --- .../account/metadata/FilterAccountInfoAndListsFromKey.kt | 4 +++- .../account/metadata/FilterBasicAccountInfoFromKeys.kt | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterAccountInfoAndListsFromKey.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterAccountInfoAndListsFromKey.kt index 6e336cb68..8a2f31603 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterAccountInfoAndListsFromKey.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterAccountInfoAndListsFromKey.kt @@ -35,6 +35,7 @@ import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent +import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent @@ -73,6 +74,7 @@ val AccountInfoAndListsFromKeyKinds2 = TrustProviderListEvent.KIND, PaymentTargetsEvent.KIND, RelayFeedsListEvent.KIND, + InterestSetEvent.KIND, ) val AmethystMetadataKinds = listOf(AppSpecificDataEvent.KIND) @@ -102,7 +104,7 @@ fun filterAccountInfoAndListsFromKey( Filter( kinds = AccountInfoAndListsFromKeyKinds2, authors = listOf(pubkey), - limit = 20, + limit = 80, since = since, ), ), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterBasicAccountInfoFromKeys.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterBasicAccountInfoFromKeys.kt index 41e08a87b..e946849a0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterBasicAccountInfoFromKeys.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/metadata/FilterBasicAccountInfoFromKeys.kt @@ -31,6 +31,7 @@ import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent +import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent @@ -65,6 +66,7 @@ val BasicAccountInfoKinds2 = GeohashListEvent.KIND, TrustProviderListEvent.KIND, RelayFeedsListEvent.KIND, + InterestSetEvent.KIND, ) fun filterBasicAccountInfoFromKeys(