Adjusts the roudabout way of making the chat screen
This commit is contained in:
+7
-17
@@ -45,13 +45,11 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.viewmodels.NestViewModel
|
import com.vitorpamplona.amethyst.commons.viewmodels.NestViewModel
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.BouncingIntentNav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.BouncingIntentNav
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.ChatroomMessageCompose
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.ChatroomMessageCompose
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel as composeViewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel as composeViewModel
|
||||||
|
|
||||||
@@ -112,6 +110,7 @@ internal fun ColumnScope.NestChatPanel(
|
|||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
onWantsToReply = nestScreenModel::reply,
|
onWantsToReply = nestScreenModel::reply,
|
||||||
|
onWantsToEditDraft = nestScreenModel::editFromDraft,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,11 +128,12 @@ internal fun ColumnScope.NestChatPanel(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NestChatMessageList(
|
private fun NestChatMessageList(
|
||||||
messages: List<LiveActivitiesChatMessageEvent>,
|
messages: List<Note>,
|
||||||
routeForLastRead: String,
|
routeForLastRead: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: BouncingIntentNav,
|
nav: BouncingIntentNav,
|
||||||
onWantsToReply: (Note) -> Unit,
|
onWantsToReply: (Note) -> Unit,
|
||||||
|
onWantsToEditDraft: (Note) -> Unit,
|
||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
@@ -150,33 +150,23 @@ private fun NestChatMessageList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// viewModel.chat is sorted ascending by created_at (oldest first).
|
|
||||||
// reverseLayout=true puts index 0 at the bottom, so reverse the
|
|
||||||
// list here to keep newest-at-bottom rendering — same behavior
|
|
||||||
// LiveStream chat ships in ChatFeedLoaded.
|
|
||||||
val reversed = remember(messages) { messages.asReversed() }
|
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = listState,
|
state = listState,
|
||||||
reverseLayout = true,
|
reverseLayout = true,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = reversed,
|
items = messages,
|
||||||
key = { it.id },
|
key = { it.idHex },
|
||||||
contentType = { it.kind },
|
) { note ->
|
||||||
) { event ->
|
|
||||||
val note = remember(event.id) { LocalCache.getOrCreateNote(event.id) }
|
|
||||||
ChatroomMessageCompose(
|
ChatroomMessageCompose(
|
||||||
baseNote = note,
|
baseNote = note,
|
||||||
routeForLastRead = routeForLastRead,
|
routeForLastRead = routeForLastRead,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
onWantsToReply = onWantsToReply,
|
onWantsToReply = onWantsToReply,
|
||||||
onWantsToEditDraft = NEST_CHAT_NO_OP_NOTE,
|
onWantsToEditDraft = onWantsToEditDraft,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val NEST_CHAT_NO_OP_NOTE: (Note) -> Unit = {}
|
|
||||||
|
|||||||
+2
-2
@@ -111,8 +111,8 @@ private fun ChatCollector(
|
|||||||
kinds = listOf(LiveActivitiesChatMessageEvent.KIND),
|
kinds = listOf(LiveActivitiesChatMessageEvent.KIND),
|
||||||
tags = mapOf("a" to listOf(roomATag)),
|
tags = mapOf("a" to listOf(roomATag)),
|
||||||
)
|
)
|
||||||
LocalCache.observeEvents<LiveActivitiesChatMessageEvent>(filter).collect { events ->
|
LocalCache.observeNotes(filter).collect { notes ->
|
||||||
events.forEach { viewModel.onChatEvent(it) }
|
notes.forEach { viewModel.onChatEvent(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-8
@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
|
|||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.Channel.Companion.DefaultFeedOrder
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.Note
|
||||||
import com.vitorpamplona.nestsclient.BroadcastHandle
|
import com.vitorpamplona.nestsclient.BroadcastHandle
|
||||||
import com.vitorpamplona.nestsclient.NestsClient
|
import com.vitorpamplona.nestsclient.NestsClient
|
||||||
import com.vitorpamplona.nestsclient.NestsListener
|
import com.vitorpamplona.nestsclient.NestsListener
|
||||||
@@ -41,6 +43,7 @@ import com.vitorpamplona.nestsclient.connectReconnectingNestsSpeaker
|
|||||||
import com.vitorpamplona.nestsclient.moq.SubscribeHandle
|
import com.vitorpamplona.nestsclient.moq.SubscribeHandle
|
||||||
import com.vitorpamplona.nestsclient.transport.WebTransportFactory
|
import com.vitorpamplona.nestsclient.transport.WebTransportFactory
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
import kotlinx.collections.immutable.persistentSetOf
|
import kotlinx.collections.immutable.persistentSetOf
|
||||||
import kotlinx.collections.immutable.toPersistentSet
|
import kotlinx.collections.immutable.toPersistentSet
|
||||||
@@ -136,7 +139,7 @@ class NestViewModel(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Chat ledger for the live-activities chat panel (#1) — every
|
* Chat ledger for the live-activities chat panel (#1) — every
|
||||||
* kind-1311 ([com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent])
|
* kind-1311 ([LiveActivitiesChatMessageEvent])
|
||||||
* tagged with this room's `a`-pointer, ordered by `created_at`
|
* tagged with this room's `a`-pointer, ordered by `created_at`
|
||||||
* ascending so the newest message is at the end (the panel
|
* ascending so the newest message is at the end (the panel
|
||||||
* auto-scrolls to it).
|
* auto-scrolls to it).
|
||||||
@@ -144,10 +147,10 @@ class NestViewModel(
|
|||||||
* Dedupes by event id — a relay re-emit on reconnect can't
|
* Dedupes by event id — a relay re-emit on reconnect can't
|
||||||
* produce a duplicate row.
|
* produce a duplicate row.
|
||||||
*/
|
*/
|
||||||
private val chatById = LinkedHashMap<String, com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>()
|
private val chatById = LinkedHashMap<String, Note>()
|
||||||
private val _chat =
|
private val _chat =
|
||||||
MutableStateFlow<List<com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>>(emptyList())
|
MutableStateFlow<List<Note>>(emptyList())
|
||||||
val chat: StateFlow<List<com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent>> = _chat.asStateFlow()
|
val chat: StateFlow<List<Note>> = _chat.asStateFlow()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recent kind-7 reactions for the floating speaker-avatar overlay
|
* Recent kind-7 reactions for the floating speaker-avatar overlay
|
||||||
@@ -388,11 +391,11 @@ class NestViewModel(
|
|||||||
* room before invoking this. Same-id re-emits are deduped; the
|
* room before invoking this. Same-id re-emits are deduped; the
|
||||||
* resulting list is sorted by `created_at` ascending.
|
* resulting list is sorted by `created_at` ascending.
|
||||||
*/
|
*/
|
||||||
fun onChatEvent(event: com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent) {
|
fun onChatEvent(chatNote: Note) {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
if (chatById.containsKey(event.id)) return
|
if (chatById.containsKey(chatNote.idHex)) return
|
||||||
chatById[event.id] = event
|
chatById[chatNote.idHex] = chatNote
|
||||||
_chat.value = chatById.values.sortedBy { it.createdAt }
|
_chat.value = chatById.values.sortedWith(DefaultFeedOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+20
-8
@@ -20,6 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.commons.viewmodels
|
package com.vitorpamplona.amethyst.commons.viewmodels
|
||||||
|
|
||||||
|
import com.sun.org.apache.xml.internal.serializer.utils.Utils.messages
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.Note
|
||||||
import com.vitorpamplona.nestsclient.NestsClient
|
import com.vitorpamplona.nestsclient.NestsClient
|
||||||
import com.vitorpamplona.nestsclient.NestsException
|
import com.vitorpamplona.nestsclient.NestsException
|
||||||
import com.vitorpamplona.nestsclient.NestsListener
|
import com.vitorpamplona.nestsclient.NestsListener
|
||||||
@@ -32,6 +34,7 @@ import com.vitorpamplona.nestsclient.transport.WebTransportFactory
|
|||||||
import com.vitorpamplona.nestsclient.transport.WebTransportSession
|
import com.vitorpamplona.nestsclient.transport.WebTransportSession
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -224,7 +227,7 @@ class NestViewModelTest {
|
|||||||
id: String,
|
id: String,
|
||||||
createdAt: Long,
|
createdAt: Long,
|
||||||
body: String,
|
body: String,
|
||||||
) = com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent(
|
) = LiveActivitiesChatMessageEvent(
|
||||||
id = id,
|
id = id,
|
||||||
pubKey = alice,
|
pubKey = alice,
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
@@ -233,15 +236,21 @@ class NestViewModelTest {
|
|||||||
sig = "0".repeat(128),
|
sig = "0".repeat(128),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun note(event: LiveActivitiesChatMessageEvent): Note {
|
||||||
|
val note = Note(event.id)
|
||||||
|
note.event = event
|
||||||
|
return note
|
||||||
|
}
|
||||||
|
|
||||||
// Out-of-order arrival on the relay must still produce a
|
// Out-of-order arrival on the relay must still produce a
|
||||||
// chronological transcript on screen.
|
// chronological transcript on screen.
|
||||||
vm.onChatEvent(chat(id = "1".repeat(64), createdAt = 200L, body = "second"))
|
vm.onChatEvent(note(chat(id = "1".repeat(64), createdAt = 200L, body = "second")))
|
||||||
vm.onChatEvent(chat(id = "2".repeat(64), createdAt = 100L, body = "first"))
|
vm.onChatEvent(note(chat(id = "2".repeat(64), createdAt = 100L, body = "first")))
|
||||||
|
|
||||||
val messages = vm.chat.value
|
val messages = vm.chat.value
|
||||||
assertEquals(2, messages.size)
|
assertEquals(2, messages.size)
|
||||||
assertEquals("first", messages[0].content)
|
assertEquals("second", messages[0].event!!.content)
|
||||||
assertEquals("second", messages[1].content)
|
assertEquals("first", messages[1].event!!.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -250,7 +259,7 @@ class NestViewModelTest {
|
|||||||
val vm = newViewModel { FakeNestsListener() }
|
val vm = newViewModel { FakeNestsListener() }
|
||||||
val alice = "a".repeat(64)
|
val alice = "a".repeat(64)
|
||||||
val msg =
|
val msg =
|
||||||
com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent(
|
LiveActivitiesChatMessageEvent(
|
||||||
id = "1".repeat(64),
|
id = "1".repeat(64),
|
||||||
pubKey = alice,
|
pubKey = alice,
|
||||||
createdAt = 100L,
|
createdAt = 100L,
|
||||||
@@ -259,10 +268,13 @@ class NestViewModelTest {
|
|||||||
sig = "0".repeat(128),
|
sig = "0".repeat(128),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val note = Note(msg.id)
|
||||||
|
note.event = msg
|
||||||
|
|
||||||
// Same id re-emitted by the relay on reconnect must not
|
// Same id re-emitted by the relay on reconnect must not
|
||||||
// produce a duplicate row.
|
// produce a duplicate row.
|
||||||
vm.onChatEvent(msg)
|
vm.onChatEvent(note)
|
||||||
vm.onChatEvent(msg)
|
vm.onChatEvent(note)
|
||||||
|
|
||||||
assertEquals(1, vm.chat.value.size)
|
assertEquals(1, vm.chat.value.size)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user