From a403f44d8b3df1d5b048119c4c1e07dbdd942e37 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 27 May 2026 15:36:30 +0000 Subject: [PATCH] fix(music): observe playlist and track notes so cards update on relay arrival MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The playlist header and each PlaylistTrackRow were reading note.event synchronously, so when a fresh playlist (or a track referenced by the playlist) arrived from a relay after first composition, the row stayed on its 'Unknown track' / stale-snapshot placeholder until the parent recomposed for some other reason. Switch both to observeNoteEvent, which subscribes to the note's metadata flow AND drives the EventFinderFilterAssembler — so a relay delivering the event both updates the local cache and triggers recomposition on the same call. --- .../amethyst/ui/note/types/MusicPlaylist.kt | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt index 8298f10081..3fdfe4f347 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt @@ -40,6 +40,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -59,7 +60,7 @@ import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent import com.vitorpamplona.amethyst.ui.components.LoadNote import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer @@ -94,10 +95,15 @@ fun RenderMusicPlaylist( accountViewModel: AccountViewModel, nav: INav, ) { - val noteEvent = note.event as? MusicPlaylistEvent ?: return + // Observe so the card recomposes when a newer version of this playlist arrives from + // a relay (e.g. tracks added/removed, title changed). observeNoteEvent also drives + // the underlying EventFinderFilterAssembler subscription, so the playlist body itself + // gets fetched from relays if only the address was known. + val noteEvent by observeNoteEvent(note, accountViewModel) + val event = noteEvent ?: return MusicPlaylistHeader( - noteEvent = noteEvent, + noteEvent = event, note = note, makeItShort = makeItShort, canPreview = canPreview, @@ -222,13 +228,6 @@ fun MusicPlaylistHeader( } LoadAddressableNote(address, accountViewModel) { trackNote -> if (trackNote != null) { - // Ask relays for the track event itself (and its - // reactions/replies) so a playlist whose tracks aren't - // already in cache populates instead of sitting on the - // "Loading…" placeholder. The subscription is keyed on - // the AddressableNote and lifecycle-aware, so it stops - // when the playlist scrolls off-screen. - EventFinderFilterAssemblerSubscription(trackNote, accountViewModel) PlaylistTrackRow( position = index + 1, trackNote = trackNote, @@ -325,7 +324,12 @@ private fun PlaylistTrackRow( accountViewModel: AccountViewModel, nav: INav, ) { - val trackEvent = trackNote.event as? MusicTrackEvent + // Observe the track note so each row recomposes when the track event arrives from a + // relay (cards switch from "Unknown track" placeholder to the real title/artist/cover) + // or a newer revision replaces the cached one. observeNoteEvent also drives the + // EventFinderFilterAssembler subscription, so the row pulls the track itself if only + // the address is known. + val trackEvent by observeNoteEvent(trackNote, accountViewModel) val title = trackEvent?.title() ?: stringRes(R.string.music_playlist_unknown_track) val artist = trackEvent?.artist() val duration = trackEvent?.duration()