feat(audio-rooms): chat ledger on AudioRoomViewModel
Adds the listener-side state for the live-chat panel (T1 #2): AudioRoomViewModel.chat: StateFlow<List<LiveActivitiesChatMessageEvent>> AudioRoomViewModel.onChatEvent(event) Same precedent as `presences` — the platform layer is the source (amethyst observes LocalCache for kind=1311 + #a=[roomATag] and pipes events here). The list is `created_at`-ascending so the chat panel auto-scrolls newest at the bottom. Dedupes by event id so a relay re-emit on reconnect doesn't double up the transcript. Tests: * onChatEventAccumulatesMessagesSortedByCreatedAt — out-of-order arrivals end up in the right place on screen * onChatEventDedupesByEventId — same event from two relays / one reconnect produces ONE row The amethyst-side wire sub + chat panel UI come next.
This commit is contained in:
+28
@@ -134,6 +134,21 @@ class AudioRoomViewModel(
|
||||
private val _presences = MutableStateFlow<Map<String, RoomPresence>>(emptyMap())
|
||||
val presences: StateFlow<Map<String, RoomPresence>> = _presences.asStateFlow()
|
||||
|
||||
/**
|
||||
* Chat ledger for the live-activities chat panel (#1) — every
|
||||
* kind-1311 ([com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent])
|
||||
* tagged with this room's `a`-pointer, ordered by `created_at`
|
||||
* ascending so the newest message is at the end (the panel
|
||||
* auto-scrolls to it).
|
||||
*
|
||||
* Dedupes by event id — a relay re-emit on reconnect can't
|
||||
* produce a duplicate row.
|
||||
*/
|
||||
private val chatById = LinkedHashMap<String, com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>()
|
||||
private val _chat =
|
||||
MutableStateFlow<List<com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>>(emptyList())
|
||||
val chat: StateFlow<List<com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>> = _chat.asStateFlow()
|
||||
|
||||
private var listener: NestsListener? = null
|
||||
private var connectJob: Job? = null
|
||||
private var stateObserverJob: Job? = null
|
||||
@@ -251,6 +266,19 @@ class AudioRoomViewModel(
|
||||
_presences.value = presenceAgg.evictOlderThan(olderThanSec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply one kind-1311 chat event to the room ledger. The platform
|
||||
* layer is the source — it filters by `a`-tag matching the current
|
||||
* room before invoking this. Same-id re-emits are deduped; the
|
||||
* resulting list is sorted by `created_at` ascending.
|
||||
*/
|
||||
fun onChatEvent(event: com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent) {
|
||||
if (closed) return
|
||||
if (chatById.containsKey(event.id)) return
|
||||
chatById[event.id] = event
|
||||
_chat.value = chatById.values.sortedBy { it.createdAt }
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this VM was constructed with capture + encoder factories. The
|
||||
* UI uses this to decide whether to render the talk button at all —
|
||||
|
||||
Reference in New Issue
Block a user