feat(meetingrooms): show parent MeetingSpace link on kind:30313

A NIP-53 kind-30313 [MeetingRoomEvent] references its parent kind-30312
[MeetingSpaceEvent] via the standard `["a", "30312:<host>:<d>"]` tag.
Without surfacing it, a meeting card in a feed lost the context of
which Nest the meeting belonged to — users couldn't tell if "Office
Hours Q3" lived inside their company nest or someone else's.

Render a small "In <Space Name>" label above the meeting's title
inside [RenderMeetingRoomEventInner]:

  - Resolves the parent address via `MeetingRoomEvent.interactiveRoom()`
    → `MeetingSpaceTag.address`. Bails when the address points at
    something other than kind:30312 (no broken links).
  - Loads the parent through the existing `LoadAddressableNote`
    helper. When the kind:30312 hasn't propagated to LocalCache yet,
    the row falls back to a generic "In a Nest" label and tapping
    routes through the standard `routeFor` thread path which
    auto-fetches.
  - Tap navigates to the parent space's thread view via `routeFor`
    + `nav.nav` — the same path Discovery / NoteCompose use for
    quote-into-thread navigation.

Two new strings:
  - meeting_room_in_space ("In %1$s")
  - meeting_room_in_space_unknown ("a Nest" — fallback when the
    parent space isn't yet resolvable)

https://claude.ai/code/session_01RDpuki4t8StSg1CZcXnV5b
This commit is contained in:
Claude
2026-04-27 19:10:12 +00:00
parent 0ceeafe90c
commit 8d53c9025d
2 changed files with 64 additions and 0 deletions
@@ -249,6 +249,15 @@ fun RenderMeetingRoomEventInner(
val status = remember(eventUpdates) { noteEvent.status() }
val starts = remember(eventUpdates) { noteEvent.starts() }
val participants = remember(eventUpdates) { noteEvent.participants() }
val interactiveRoom = remember(eventUpdates) { noteEvent.interactiveRoom() }
interactiveRoom?.let { spaceTag ->
ParentMeetingSpaceLink(
spaceAddress = spaceTag.address,
accountViewModel = accountViewModel,
nav = nav,
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -293,6 +302,59 @@ fun RenderMeetingRoomEventInner(
RenderParticipants(participants, accountViewModel, nav)
}
/**
* Small "In <Space Name>" row rendered above a kind-30313
* [MeetingRoomEvent] so users see which kind-30312 space the
* meeting belongs to. Resolves the addressable note for the parent
* space; if the kind-30312 hasn't been seen yet the link still
* renders with a placeholder name and tapping navigates to its
* thread (which auto-fetches the event). When the parent address
* doesn't resolve to a kind-30312 we render nothing — keeping the
* card free of broken links.
*/
@Composable
private fun ParentMeetingSpaceLink(
spaceAddress: com.vitorpamplona.quartz.nip01Core.core.Address,
accountViewModel: AccountViewModel,
nav: INav,
) {
if (spaceAddress.kind != MeetingSpaceEvent.KIND) return
com.vitorpamplona.amethyst.ui.note.LoadAddressableNote(
address = spaceAddress,
accountViewModel = accountViewModel,
) { spaceNote ->
spaceNote ?: return@LoadAddressableNote
val spaceEvent = spaceNote.event as? MeetingSpaceEvent
val spaceName =
spaceEvent?.room()?.ifBlank { null }
?: stringRes(R.string.meeting_room_in_space_unknown)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable {
nav.nav {
com.vitorpamplona.amethyst.ui.navigation.routes.routeFor(
spaceNote,
accountViewModel.account,
)
}
}.padding(vertical = 4.dp),
) {
Text(
text = stringRes(R.string.meeting_room_in_space, spaceName),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
/**
* Inline renderer for kind-10312 [MeetingRoomPresenceEvent] events.
* The event's `content` is empty; the interesting bits are the
+2
View File
@@ -489,6 +489,8 @@
<string name="follow_packs">Follow Packs</string>
<string name="live_streams">Live Streams</string>
<string name="nests">Nests</string>
<string name="meeting_room_in_space">In %1$s</string>
<string name="meeting_room_in_space_unknown">a Nest</string>
<string name="nest_stage">Stage</string>
<string name="nest_audience">Audience</string>
<string name="nest_raise_hand">Raise hand</string>