From 4c82011a25d06f69bc2667945e8d53607dcd74eb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 14:11:06 +0000 Subject: [PATCH] fix(interest-sets): consume kind 30015 locally + empty-state polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LocalCache.consume dispatcher now handles InterestSetEvent via consumeBaseReplaceable, so the event created by the user is stored and flows through newEventBundles → interestSets.newNotes → listFeedFlow refresh. Without this, the just-signed event sat in the sendMyPublicAndPrivateOutbox call but the UI never saw the update. - List screen: icon + centered text for the empty state and dividers between rows. --- .../amethyst/model/LocalCache.kt | 2 + .../list/ListOfInterestSetsScreen.kt | 45 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index aed8edf69..8512b8d51 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -153,6 +153,7 @@ import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFee import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent 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.labeledBookmarkList.LabeledBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent @@ -2642,6 +2643,7 @@ object LocalCache : ILocalCache, ICacheProvider { is InteractiveStoryPrologueEvent -> consumeBaseReplaceable(event, relay, wasVerified) is InteractiveStorySceneEvent -> consumeBaseReplaceable(event, relay, wasVerified) is InteractiveStoryReadingStateEvent -> consumeBaseReplaceable(event, relay, wasVerified) + is InterestSetEvent -> consumeBaseReplaceable(event, relay, wasVerified) is LabeledBookmarkListEvent -> consumeBaseReplaceable(event, relay, wasVerified) is LiveActivitiesEvent -> consume(event, relay, wasVerified) is LiveActivitiesChatMessageEvent -> consume(event, relay, wasVerified) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/interestSets/list/ListOfInterestSetsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/interestSets/list/ListOfInterestSetsScreen.kt index f803b1f05..005d45881 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/interestSets/list/ListOfInterestSetsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/interestSets/list/ListOfInterestSetsScreen.kt @@ -24,13 +24,17 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.PlaylistAdd +import androidx.compose.material.icons.outlined.Tag import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -39,6 +43,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.navigation.navs.INav @@ -46,6 +52,8 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.amethyst.ui.theme.DividerThickness +import com.vitorpamplona.amethyst.ui.theme.FeedPadding @Composable fun ListOfInterestSetsScreen( @@ -71,19 +79,15 @@ fun ListOfInterestSetsScreen( ).fillMaxHeight(), ) { if (sets.isEmpty()) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(stringRes(R.string.interest_sets_empty)) - } + EmptyInterestSets() } else { LazyColumn( modifier = Modifier.fillMaxSize(), + contentPadding = FeedPadding, ) { - items(sets, key = { it.identifier }) { set -> + itemsIndexed(sets, key = { _, it -> it.identifier }) { _, set -> InterestSetItem( + modifier = Modifier.fillMaxSize().animateItem(), interestSet = set, onClick = { nav.nav(Route.InterestSetView(set.identifier)) }, onRename = { nav.nav(Route.InterestSetMetadataEdit(set.identifier)) }, @@ -105,6 +109,7 @@ fun ListOfInterestSetsScreen( } }, ) + HorizontalDivider(thickness = DividerThickness) } } } @@ -112,6 +117,28 @@ fun ListOfInterestSetsScreen( } } +@Composable +private fun EmptyInterestSets() { + Column( + modifier = Modifier.fillMaxSize().padding(32.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = Icons.Outlined.Tag, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(64.dp), + ) + Text( + text = stringRes(R.string.interest_sets_empty), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.fillMaxWidth().padding(top = 16.dp), + ) + } +} + @Composable fun InterestSetFab(onAdd: () -> Unit) { ExtendedFloatingActionButton(