refactor(nip29): finish the datasource rename — Open threads + consistent filter-builder names

Continues the scope+role naming pass over the group-chat datasource layer:

- RelayGroupThreadFeed -> RelayGroupOpenThreads: it's the open group's threads
  tab, so it now sits in the "Open" family beside RelayGroupOpenChatTail/History.
- filterMetadataToRelayGroup -> filterRelayGroupState: matches the "state"
  concept (39000-39005 + pinned-id back-fill) the always-on state sub is named for.
- makeRelayGroupsDiscoveryFilter -> filterRelayGroupsDiscovery: consistent
  `filter…` verb with the rest of the relay-group filter builders.

The RelayGroupsOnRelay / RelayGroupsDiscovery assemblers keep their PLURAL prefix
on purpose: it marks the "browse/discover many groups" surfaces apart from the
singular in-a-specific-group ones (RelayGroupJoined*/Open*/Card*). The already-
consistent filterRelayGroupsBy* discovery builders are left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDK63toGbE7DQxKxrQnhMU
This commit is contained in:
Claude
2026-07-18 14:33:56 +00:00
parent 54b2b42d87
commit ee5dc25414
10 changed files with 25 additions and 25 deletions
@@ -44,7 +44,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupJoinedStateFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenChatHistoryFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenChatTailFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupThreadFeedFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenThreadsFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsDiscoveryFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsOnRelayFilterAssembler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssembler
@@ -129,7 +129,7 @@ class RelaySubscriptionsCoordinator(
// assembler above (same as NIP-28 public chats), so only the group-specific surfaces get their
// own here.
val relayGroupsOnRelay = RelayGroupsOnRelayFilterAssembler(client) // browsing one relay's channel list
val relayGroupThreadFeed = RelayGroupThreadFeedFilterAssembler(client) // a group's forum-threads tab
val relayGroupOpenThreads = RelayGroupOpenThreadsFilterAssembler(client) // a group's forum-threads tab
val relayGroupCardWarmup = RelayGroupCardWarmupFilterAssembler(client) // prefetching a group before it's opened
val relayGroupsDiscovery = RelayGroupsDiscoveryFilterAssembler(client) // the cross-relay Discover feed
@@ -210,7 +210,7 @@ class RelaySubscriptionsCoordinator(
val all =
listOf(
relayGroupsOnRelay,
relayGroupThreadFeed,
relayGroupOpenThreads,
relayGroupCardWarmup,
relayGroupsDiscovery,
relayGroupJoinedState,
@@ -61,7 +61,7 @@ fun ChannelFilterAssemblerSubscription(
}
// Relay groups: when the kind-39005 pin list changes, re-invalidate so the id-based back-fill
// for pinned message bodies (see filterMetadataToRelayGroup) picks up the new ids. Keyed on the
// for pinned message bodies (see filterRelayGroupState) picks up the new ids. Keyed on the
// pin list alone, so unrelated roster/metadata churn doesn't force a re-subscribe.
if (channel is RelayGroupChannel) {
val metadataState by channel
@@ -63,7 +63,7 @@ class ChannelPublicFilterSubAssembler(
// the relay-signed metadata + pinned-id back-fill so an OPEN group — including a non-joined
// one not covered by the always-on joined-groups state sub — resolves its name/roster/pins.
// See amethyst/plans/2026-07-18-nip29-group-chat-subscriptions.md.
filterMetadataToRelayGroup(channel, since)
filterRelayGroupState(channel, since)
}
else -> {
@@ -46,7 +46,7 @@ private val RELAY_GROUP_METADATA_KINDS =
* group's host relay. The relay signs these with its own key, so a single-relay
* query scoped by `#d` returns exactly this group's directory.
*/
fun filterMetadataToRelayGroup(
fun filterRelayGroupState(
channel: RelayGroupChannel,
since: SincePerRelayMap?,
): List<RelayBasedFilter> {
@@ -58,7 +58,7 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBack
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupThreadFeedSubscription
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenThreadsSubscription
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.quartz.nip01Core.core.HexKey
@@ -69,7 +69,7 @@ import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent
/**
* A group's forum-style threads (kind 11) — the secondary content type kept out of
* the kind-9 chat feed. Streams the group's threads + their comments via
* [RelayGroupThreadFeedSubscription]; tapping a thread opens the generic thread view
* [RelayGroupOpenThreadsSubscription]; tapping a thread opens the generic thread view
* ([Route.Note]) with its comment tree. Members can start a new thread.
*/
@Composable
@@ -93,7 +93,7 @@ private fun RelayGroupThreads(
accountViewModel: AccountViewModel,
nav: INav,
) {
RelayGroupThreadFeedSubscription(channel, accountViewModel.dataSources().relayGroupThreadFeed, accountViewModel)
RelayGroupOpenThreadsSubscription(channel, accountViewModel.dataSources().relayGroupOpenThreads, accountViewModel)
val threads by channel.threads.collectAsStateWithLifecycle()
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUniqueIdEoseManager
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.subassemblies.filterMetadataToRelayGroup
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.subassemblies.filterRelayGroupState
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
@@ -99,7 +99,7 @@ class RelayGroupCardWarmupSubAssembler(
if (joined.any { it.groupId == groupId.id && RelayUrlNormalizer.normalizeOrNull(it.relayUrl) == groupId.relayUrl }) {
return emptyList()
}
val metadata = if (key.contentOnly) emptyList() else filterMetadataToRelayGroup(key.channel, since)
val metadata = if (key.contentOnly) emptyList() else filterRelayGroupState(key.channel, since)
return metadata +
RelayBasedFilter(
relay = groupId.relayUrl,
@@ -32,7 +32,7 @@ import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent
/** One threads-screen's request for a single group's kind-11 threads. */
class RelayGroupThreadFeedQueryState(
class RelayGroupOpenThreadsQueryState(
val channel: RelayGroupChannel,
)
@@ -43,12 +43,12 @@ class RelayGroupThreadFeedQueryState(
* kind-9 chat, so we don't pay for them until asked). Fetching the comments here
* too means opening a thread from the list has its replies already cached.
*/
class RelayGroupThreadFeedFilterAssembler(
class RelayGroupOpenThreadsFilterAssembler(
client: INostrClient,
) : ComposeSubscriptionManager<RelayGroupThreadFeedQueryState>() {
) : ComposeSubscriptionManager<RelayGroupOpenThreadsQueryState>() {
val group =
listOf(
RelayGroupThreadFeedSubAssembler(client, ::allKeys),
RelayGroupOpenThreadsSubAssembler(client, ::allKeys),
)
override fun invalidateKeys() = invalidateFilters()
@@ -58,12 +58,12 @@ class RelayGroupThreadFeedFilterAssembler(
override fun destroy() = group.forEach { it.destroy() }
}
class RelayGroupThreadFeedSubAssembler(
class RelayGroupOpenThreadsSubAssembler(
client: INostrClient,
allKeys: () -> Set<RelayGroupThreadFeedQueryState>,
) : PerUniqueIdEoseManager<RelayGroupThreadFeedQueryState, GroupId>(client, allKeys) {
allKeys: () -> Set<RelayGroupOpenThreadsQueryState>,
) : PerUniqueIdEoseManager<RelayGroupOpenThreadsQueryState, GroupId>(client, allKeys) {
override fun updateFilter(
key: RelayGroupThreadFeedQueryState,
key: RelayGroupOpenThreadsQueryState,
since: SincePerRelayMap?,
): List<RelayBasedFilter> {
val groupId = key.channel.groupId
@@ -80,5 +80,5 @@ class RelayGroupThreadFeedSubAssembler(
)
}
override fun id(key: RelayGroupThreadFeedQueryState) = key.channel.groupId
override fun id(key: RelayGroupOpenThreadsQueryState) = key.channel.groupId
}
@@ -28,14 +28,14 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
/** Mount on a group's Threads screen to stream its kind-11 threads + 1111 comments. */
@Composable
fun RelayGroupThreadFeedSubscription(
fun RelayGroupOpenThreadsSubscription(
channel: RelayGroupChannel,
dataSource: RelayGroupThreadFeedFilterAssembler,
dataSource: RelayGroupOpenThreadsFilterAssembler,
accountViewModel: AccountViewModel,
) {
val state =
remember(channel.groupId) {
RelayGroupThreadFeedQueryState(channel)
RelayGroupOpenThreadsQueryState(channel)
}
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
@@ -42,7 +42,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsGlobal
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
fun makeRelayGroupsDiscoveryFilter(
fun filterRelayGroupsDiscovery(
feedSettings: IFeedTopNavPerRelayFilterSet,
since: SincePerRelayMap?,
defaultSince: Long? = null,
@@ -55,7 +55,7 @@ class RelayGroupsDiscoverySubAssembler(
val feedSettings = key.followsPerRelay()
val defaultSince = key.feedStates.relayGroupsDiscoveryFeed.lastNoteCreatedAtIfFilled()
val base = makeRelayGroupsDiscoveryFilter(feedSettings, since, defaultSince)
val base = filterRelayGroupsDiscovery(feedSettings, since, defaultSince)
// The follow-list filter sets resolve their relays via the outbox model (a follow's own
// publish relays), but a NIP-29 roster (39001/39002) lives ONLY on the group's host relay.