fix(dvm-favorites): star click is a no-op + star missing from DVM detail top bar

Two distinct bugs with the same symptom ("clicking the star doesn't
seem to do anything"):

1. LocalCache didn't know how to route kind 10090. FavoriteDvmListEvent
   was missing from LocalCache's event-type dispatch, so after
   Account.followFavoriteDvm signs and publishes the event, the
   cache silently dropped it — never updated favoriteDvmListNote,
   so account.favoriteDvmList.flow never re-emitted, so the star's
   filled/outlined state (and the spinner chip) never flipped. Add
   `is FavoriteDvmListEvent -> consumeBaseReplaceable(...)`.

2. DvmTopBar's toggle never rendered. The Home feed passes the DVM's
   hex event id via Route.ContentDiscovery, so LoadNote(baseNoteHex)
   returns a plain Note, not the AddressableNote the toggle needs
   (the `is AddressableNote` guard was always false). Derive the
   AddressableNote from the loaded AppDefinitionEvent.address() so
   the toggle shows up with the correct backing object.
This commit is contained in:
Claude
2026-04-19 14:34:07 +00:00
parent 9ba2d2f5cc
commit d8b313088d
2 changed files with 22 additions and 6 deletions
@@ -149,6 +149,7 @@ import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.PinListEvent import com.vitorpamplona.quartz.nip51Lists.PinListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.favoriteDvmList.FavoriteDvmListEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent
@@ -2635,6 +2636,7 @@ object LocalCache : ILocalCache, ICacheProvider {
is LiveChessGameEndEvent -> consumeBaseReplaceable(event, relay, wasVerified) is LiveChessGameEndEvent -> consumeBaseReplaceable(event, relay, wasVerified)
is LiveChessDrawOfferEvent -> consumeBaseReplaceable(event, relay, wasVerified) is LiveChessDrawOfferEvent -> consumeBaseReplaceable(event, relay, wasVerified)
is HashtagListEvent -> consumeBaseReplaceable(event, relay, wasVerified) is HashtagListEvent -> consumeBaseReplaceable(event, relay, wasVerified)
is FavoriteDvmListEvent -> consumeBaseReplaceable(event, relay, wasVerified)
is HighlightEvent -> consumeRegularEvent(event, relay, wasVerified) is HighlightEvent -> consumeRegularEvent(event, relay, wasVerified)
is IndexerRelayListEvent -> consumeBaseReplaceable(event, relay, wasVerified) is IndexerRelayListEvent -> consumeBaseReplaceable(event, relay, wasVerified)
is InteractiveStoryPrologueEvent -> consumeBaseReplaceable(event, relay, wasVerified) is InteractiveStoryPrologueEvent -> consumeBaseReplaceable(event, relay, wasVerified)
@@ -24,10 +24,12 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap
import com.vitorpamplona.amethyst.ui.components.LoadNote import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -37,6 +39,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.SimpleImage35Modifier import com.vitorpamplona.amethyst.ui.theme.SimpleImage35Modifier
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
@Composable @Composable
fun DvmTopBar( fun DvmTopBar(
@@ -87,12 +90,23 @@ fun DvmTopBar(
}, },
navigationIcon = { IconButton(onClick = nav::popBack) { ArrowBackIcon() } }, navigationIcon = { IconButton(onClick = nav::popBack) { ArrowBackIcon() } },
actions = { actions = {
// The route passes the event's hex id, so LoadNote returns a plain Note,
// not the AddressableNote the toggle needs. Derive the AddressableNote
// from the loaded AppDefinitionEvent's address() once the event exists.
LoadNote(baseNoteHex = appDefinitionId, accountViewModel = accountViewModel) { appDefinitionNote -> LoadNote(baseNoteHex = appDefinitionId, accountViewModel = accountViewModel) { appDefinitionNote ->
if (appDefinitionNote is AddressableNote) { if (appDefinitionNote != null) {
FavoriteDvmToggle( val addressableNote by
appDefinitionNote = appDefinitionNote, observeNoteAndMap(appDefinitionNote, accountViewModel) { note ->
accountViewModel = accountViewModel, (note.event as? AppDefinitionEvent)?.let {
) LocalCache.getOrCreateAddressableNote(it.address())
}
}
addressableNote?.let { target ->
FavoriteDvmToggle(
appDefinitionNote = target,
accountViewModel = accountViewModel,
)
}
} }
} }
}, },