perf(nests): index room presence separately from chat for O(speakers) scans

LiveActivitiesChannel.notes is mixed-kind (chat, zaps, raids, clips, presence),
and chat dominates by volume in active rooms. The Nests feed filter scanned it
twice per room per recompute -- once for any-fresh-presence, once for fresh
on-stage presence -- doing an `is MeetingRoomPresenceEvent` cast on every chat
message just to find the speakers.

Add a presenceNotes index on LiveActivitiesChannel keyed by author pubkey
(presence is replaceable per author, so the key auto-collapses heartbeats).
Populate it from LocalCache.consume(MeetingRoomPresenceEvent). Switch
NestsFeedFilter to a single hasFreshSpeakers() pass over the index, dropping
the now-redundant isLiveByPresence() check (hasFreshSpeakers implies it).
Migrate NestsFeedLoaded's latest-presence flow to the same index.
This commit is contained in:
Claude
2026-04-30 00:02:13 +00:00
parent 7c61eaf4e3
commit d87fc36d21
4 changed files with 42 additions and 42 deletions
@@ -26,9 +26,11 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.utils.cache.LargeCache
@Stable
class LiveActivitiesChannel(
@@ -40,6 +42,24 @@ class LiveActivitiesChannel(
// Important to keep this long-term reference because LocalCache uses WeakReferences.
var infoNote: Note? = null
/**
* Audio-room presence index (NIP-53 kind-10312) keyed by author
* pubkey. Presence is replaceable (one per author), so keying on
* the author auto-collapses heartbeat versions instead of growing
* unbounded the way `notes` does. Empty for streaming channels
* (kind-30311) — only kind-30312 rooms publish presence.
*
* Lets feeds answer "is anyone live on stage in this room?"
* without scanning the chat-dominated `notes` map. See
* [com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.dal.NestsFeedFilter].
*/
val presenceNotes = LargeCache<HexKey, Note>()
fun addPresenceNote(note: Note) {
val author = note.author?.pubkeyHex ?: return
presenceNotes.put(author, note)
}
fun address() = address
override fun relays() = info?.allRelayUrls()?.toSet()?.ifEmpty { null } ?: super.relays()