From 0d8936e8d3cc7c181b46bac887ce7c8e6c249d1e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 23:06:11 +0000 Subject: [PATCH] refactor(git): index PR updates via LocalCache.observeEvents GitPullRequestUpdateIndex now subscribes to a kind-1619-filtered LocalCache.observeEvents instead of LocalCache.live.newEventBundles. The indexed observable seeds its matching set from the cache index (via init()) and re-emits the full list on each new PR update, so the manual onStart full-cache scan and the per-bundle type filtering over every event of every kind are both gone. The collector just reduces the list to the latest-per-parent map. PR updates are rare, so recomputing the whole map per emission is cheaper than scanning every bundle. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr --- .../model/GitPullRequestUpdateIndex.kt | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/GitPullRequestUpdateIndex.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/GitPullRequestUpdateIndex.kt index f937515040..f4b80eecce 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/GitPullRequestUpdateIndex.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/GitPullRequestUpdateIndex.kt @@ -20,7 +20,9 @@ */ package com.vitorpamplona.amethyst.model +import com.vitorpamplona.amethyst.model.LocalCache.observeEvents import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestUpdateEvent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -28,17 +30,24 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicBoolean /** * Cross-screen index of the most recent NIP-34 pull-request update event * (kind 1619) per parent pull-request id, kept up to date from - * [LocalCache.live.newEventBundles]. A PR update *revises* its parent PR with a + * [LocalCache.observeEvents]. A PR update *revises* its parent PR with a * newer commit / merge base, so the UI folds the latest one into the PR rather * than listing updates separately. Like [GitStatusIndex], updates aren't tracked * in `Note.replies`, so a per-row cache scan would otherwise be required. + * + * The kind-indexed [observeEvents] subscription replaces both the full-cache + * `onStart` scan and the per-bundle type filtering the old + * `LocalCache.live.newEventBundles` flow needed: the observable's `init()` seeds + * the matching set from the index and re-emits the whole list on every new 1619, + * so we just reduce it to the latest-per-parent map each time. PR updates are + * rare, so recomputing the full map per emission is cheaper than it scanning + * every event of every kind. */ object GitPullRequestUpdateIndex { private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) @@ -50,35 +59,21 @@ object GitPullRequestUpdateIndex { fun startIfNeeded() { if (!started.compareAndSet(false, true)) return scope.launch { - LocalCache.live.newEventBundles - .onStart { - val initial = HashMap() - LocalCache.notes.forEach { _, note -> - val event = note.event as? GitPullRequestUpdateEvent ?: return@forEach - val target = event.parentPullRequestId() ?: return@forEach - val current = initial[target] - if (current == null || event.createdAt > current.createdAt) { - initial[target] = event - } - } - mutableLatestByPullRequest.value = initial - }.collect { bundle -> processBundle(bundle) } + LocalCache + .observeEvents(Filter(kinds = listOf(GitPullRequestUpdateEvent.KIND))) + .collect { events -> mutableLatestByPullRequest.value = latestByParent(events) } } } - private fun processBundle(bundle: Set) { - val snapshot = mutableLatestByPullRequest.value ?: emptyMap() - var modified: HashMap? = null - for (note in bundle) { - val event = note.event as? GitPullRequestUpdateEvent ?: continue + private fun latestByParent(events: List): Map { + val latest = HashMap() + for (event in events) { val target = event.parentPullRequestId() ?: continue - val map = modified ?: snapshot - val current = map[target] + val current = latest[target] if (current == null || event.createdAt > current.createdAt) { - if (modified == null) modified = HashMap(snapshot) - modified[target] = event + latest[target] = event } } - modified?.let { mutableLatestByPullRequest.value = it } + return latest } }