Showing ephemeral chats in the messages list.

Fixing a bug of the last message with unloaded event
This commit is contained in:
Vitor Pamplona
2025-07-22 20:12:43 -04:00
parent 6d4708bbeb
commit 95f463b0aa
3 changed files with 68 additions and 5 deletions

View File

@@ -322,8 +322,6 @@ object LocalCache : ILocalCache {
fun getNoteIfExists(key: ETag): Note? = notes.get(key.eventId)
fun getChatroomListIfExists(key: String): ChatroomList? = chatroomList.get(key)
fun getPublicChatChannelIfExists(key: String): PublicChatChannel? = publicChatChannels.get(key)
fun getEphemeralChatChannelIfExists(key: RoomId): EphemeralChatChannel? = ephemeralChannels.get(key)
@@ -1571,6 +1569,8 @@ object LocalCache : ILocalCache {
): Boolean {
val channelId = event.channelId() ?: return false
val new = consumeRegularEvent(event, relay, wasVerified)
val channel = checkGetOrCreatePublicChatChannel(channelId)
if (channel == null) {
Log.w("LocalCache", "Unable to create public chat channel for event ${event.toJson()}")
@@ -1580,7 +1580,7 @@ object LocalCache : ILocalCache {
val note = getOrCreateNote(event.id)
channel.addNote(note, relay)
return consumeRegularEvent(event, relay, wasVerified)
return new
}
fun consume(
@@ -1590,11 +1590,13 @@ object LocalCache : ILocalCache {
): Boolean {
val roomId = event.roomId() ?: return false
val new = consumeRegularEvent(event, relay, wasVerified)
val note = getOrCreateNote(event.id)
val channel = getOrCreateEphemeralChannel(roomId)
channel.addNote(note, relay)
return consumeRegularEvent(event, relay, wasVerified)
return new
}
fun consume(
@@ -1604,12 +1606,14 @@ object LocalCache : ILocalCache {
): Boolean {
val activityAddress = event.activityAddress() ?: return false
val new = consumeRegularEvent(event, relay, wasVerified)
val channel = getOrCreateLiveChannel(activityAddress)
val note = getOrCreateNote(event.id)
channel.addNote(note, relay)
return consumeRegularEvent(event, relay, wasVerified)
return new
}
fun consume(

View File

@@ -33,12 +33,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.model.nip53LiveActivities.LiveActivitiesChannel
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteOts
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserStatuses
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.Dispatchers
@@ -172,6 +174,20 @@ fun LoadPublicChatChannel(
channel.value?.let { content(it) }
}
@Composable
fun LoadEphemeralChatChannel(
id: RoomId,
accountViewModel: AccountViewModel,
content: @Composable (EphemeralChatChannel) -> Unit,
) {
val channel =
produceStateIfNotNull(accountViewModel.getEphemeralChatChannelIfExists(id), id) {
value = accountViewModel.checkGetOrCreateEphemeralChatChannel(id)
}
channel.value?.let { content(it) }
}
@Composable
fun LoadLiveActivityChannel(
id: Address,

View File

@@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteHasEvent
@@ -72,11 +73,14 @@ import com.vitorpamplona.amethyst.ui.note.ObserveDraftEvent
import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.RoomNameDisplay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.LoadEphemeralChatChannel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.AccountPictureModifier
import com.vitorpamplona.amethyst.ui.theme.Size55dp
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
@@ -146,6 +150,13 @@ private fun ChatroomEntry(
val room = baseNoteEvent.chatroomKey(accountViewModel.userProfile().pubkeyHex)
UserRoomCompose(room, lastMessage, accountViewModel, nav)
}
is EphemeralChatEvent -> {
baseNoteEvent.roomId()?.let {
LoadEphemeralChatChannel(it, accountViewModel) { channel ->
ChannelRoomCompose(lastMessage, channel, accountViewModel, nav)
}
}
}
else -> BlankNote()
}
}
@@ -191,6 +202,38 @@ private fun ChannelRoomCompose(
)
}
@Composable
private fun ChannelRoomCompose(
lastMessage: Note,
channel: EphemeralChatChannel,
accountViewModel: AccountViewModel,
nav: INav,
) {
val authorName by observeUserName(lastMessage.author!!, accountViewModel)
val channelState by observeChannel(channel, accountViewModel)
val channel = channelState?.channel as? EphemeralChatChannel ?: return
val relayInfo by loadRelayInfo(channel.roomId.relayUrl, accountViewModel)
val noteEvent = lastMessage.event
val description = noteEvent?.content?.take(200)
val lastReadTime by accountViewModel.account.loadLastReadFlow("Channel/${channel.roomId.toKey()}").collectAsStateWithLifecycle()
ChannelName(
channelIdHex = channel.roomId.toKey(),
channelPicture = relayInfo.icon,
channelTitle = { modifier -> ChannelTitleWithLabelInfo(channel.toBestDisplayName(), R.string.ephemeral_relay_chat, modifier) },
channelLastTime = lastMessage.createdAt(),
channelLastContent = "$authorName: $description",
hasNewMessages = (noteEvent?.createdAt ?: Long.MIN_VALUE) > lastReadTime,
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
onClick = { nav.nav(routeFor(channel)) },
)
}
@Composable
private fun ChannelTitleWithLabelInfo(
channelName: String,