Fixing Ephemeral Chat interface

This commit is contained in:
Vitor Pamplona
2025-07-10 19:42:18 -04:00
parent e3acf4c250
commit a94c8673d5
6 changed files with 46 additions and 161 deletions

View File

@@ -70,7 +70,6 @@ import com.vitorpamplona.amethyst.service.uploads.FileHeader
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
@@ -145,9 +144,6 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
@@ -163,8 +159,6 @@ import com.vitorpamplona.quartz.nip51Lists.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.FollowListEvent
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip56Reports.ReportType
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
@@ -815,33 +809,7 @@ class Account(
?: cache.relayHints.hintsForKey(pubkey).toSet()
}
private fun computeRelaysForChannels(event: Event): Set<NormalizedRelayUrl> {
val isInChannel =
if (
event is ChannelMessageEvent ||
event is ChannelMetadataEvent ||
event is ChannelCreateEvent ||
event is LiveActivitiesChatMessageEvent ||
event is LiveActivitiesEvent ||
event is EphemeralChatEvent
) {
(event as? ChannelMessageEvent)?.channelId()
?: (event as? ChannelMetadataEvent)?.channelId()
?: (event as? ChannelCreateEvent)?.id
?: (event as? LiveActivitiesChatMessageEvent)?.activity()?.toTag()
?: (event as? LiveActivitiesEvent)?.aTag()?.toTag()
?: (event as? EphemeralChatEvent)?.roomId()?.toKey()
} else {
null
}
return if (isInChannel != null) {
val channel = LocalCache.checkGetOrCreateChannel(isInChannel)
channel?.relays() ?: emptySet()
} else {
emptySet()
}
}
private fun computeRelaysForChannels(event: Event): Set<NormalizedRelayUrl> = LocalCache.getAnyChannel(event)?.relays() ?: emptySet()
fun computeRelayListToBroadcast(event: Event): Set<NormalizedRelayUrl> {
if (event is MetadataEvent || event is AdvertisedRelayListEvent) {

View File

@@ -61,7 +61,6 @@ import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.isLocalHost
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
@@ -416,33 +415,16 @@ object LocalCache : ILocalCache {
EphemeralChatChannel(key)
}
fun checkGetOrCreateChannel(key: String): Channel? {
checkNotInMainThread()
if (key.contains("@")) {
val idParts = key.split("@")
val relay = RelayUrlNormalizer.normalizeOrNull(idParts[1])
if (relay == null) {
return null
} else {
getOrCreateEphemeralChannel(RoomId(idParts[0], relay))
}
}
fun checkGetOrCreatePublicChatChannel(key: String): PublicChatChannel? {
if (isValidHex(key)) {
return getOrCreatePublicChatChannel(key)
}
val address = Address.parse(key)
if (address != null) {
return getOrCreateLiveChannel(address)
}
return null
}
private fun isValidHex(key: String): Boolean {
if (key.isBlank()) return false
if (key.length != 64) return false
if (key.contains(":")) return false
return Hex.isHex(key)
@@ -1622,12 +1604,12 @@ object LocalCache : ILocalCache {
if (channelId.isNullOrBlank()) return false
// new event
val oldChannel = checkGetOrCreateChannel(channelId) ?: return false
val oldChannel = checkGetOrCreatePublicChatChannel(channelId) ?: return false
val author = getOrCreateUser(event.pubKey)
val isVerified =
if (event.createdAt > oldChannel.updatedMetadataAt) {
if (oldChannel is PublicChatChannel && (wasVerified || justVerify(event))) {
if (wasVerified || justVerify(event)) {
oldChannel.updateChannelInfo(author, event)
true
} else {
@@ -1653,44 +1635,18 @@ object LocalCache : ILocalCache {
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
): Boolean {
val channelId = event.channelId()
val channelId = event.channelId() ?: return false
if (channelId.isNullOrBlank()) return false
val channel = checkGetOrCreateChannel(channelId) ?: return false
val channel = checkGetOrCreatePublicChatChannel(channelId)
if (channel == null) {
Log.w("LocalCache", "Unable to create public chat channel for event ${event.toJson()}")
return false
}
val note = getOrCreateNote(event.id)
channel.addNote(note, relay)
val author = getOrCreateUser(event.pubKey)
if (relay != null) {
author.addRelayBeingUsed(relay, event.createdAt)
note.addRelay(relay)
}
// Already processed this event.
if (note.event != null) return false
if (antiSpam.isSpam(event, relay)) {
return false
}
if (wasVerified || justVerify(event)) {
val replyTo = computeReplyTo(event)
note.loadEvent(event, author, replyTo)
// Log.d("CM", "New Chat Note (${note.author?.toBestDisplayName()} ${note.event?.content}
// ${formattedDateTime(event.createdAt)}")
// Counts the replies
replyTo.forEach { it.addReply(note) }
refreshObservers(note)
}
return true
return consumeRegularEvent(event, relay, wasVerified)
}
fun consume(
@@ -1698,46 +1654,15 @@ object LocalCache : ILocalCache {
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
): Boolean {
val roomId = event.roomId()
if (roomId == null) return false
val channelId = roomId
val channel = getOrCreateEphemeralChannel(channelId) ?: return false
val roomId = event.roomId() ?: return false
val note = getOrCreateNote(event.id)
val channel = getOrCreateEphemeralChannel(roomId)
channel.addNote(note, relay)
val author = getOrCreateUser(event.pubKey)
if (relay != null) {
author.addRelayBeingUsed(relay, event.createdAt)
note.addRelay(relay)
}
// Already processed this event.
if (note.event != null) return false
if (antiSpam.isSpam(event, relay)) {
return false
}
if (wasVerified || justVerify(event)) {
note.loadEvent(event, author, emptyList())
refreshObservers(note)
return true
}
return false
return consumeRegularEvent(event, relay, wasVerified)
}
fun consume(
event: CommentEvent,
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
) = consumeRegularEvent(event, relay, wasVerified)
fun consume(
event: LiveActivitiesChatMessageEvent,
relay: NormalizedRelayUrl?,
@@ -1750,36 +1675,15 @@ object LocalCache : ILocalCache {
val note = getOrCreateNote(event.id)
channel.addNote(note, relay)
val author = getOrCreateUser(event.pubKey)
if (relay != null) {
author.addRelayBeingUsed(relay, event.createdAt)
note.addRelay(relay)
}
// Already processed this event.
if (note.event != null) return false
if (antiSpam.isSpam(event, relay)) {
return false
}
if (wasVerified || justVerify(event)) {
val replyTo = computeReplyTo(event)
note.loadEvent(event, author, replyTo)
// Counts the replies
replyTo.forEach { it.addReply(note) }
refreshObservers(note)
return true
}
return false
return consumeRegularEvent(event, relay, wasVerified)
}
fun consume(
event: CommentEvent,
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
) = consumeRegularEvent(event, relay, wasVerified)
@Suppress("UNUSED_PARAMETER")
fun consume(
event: ChannelHideMessageEvent,
@@ -2859,18 +2763,18 @@ object LocalCache : ILocalCache {
}
}
is EphemeralChatEvent -> {
draft.roomId()?.toKey()?.let {
checkGetOrCreateChannel(it)?.addNote(note, null)
draft.roomId()?.let {
getOrCreateEphemeralChannel(it).addNote(note, null)
}
}
is ChannelMessageEvent -> {
draft.channelId()?.let { channelId ->
checkGetOrCreateChannel(channelId)?.addNote(note, null)
checkGetOrCreatePublicChatChannel(channelId)?.addNote(note, null)
}
}
is LiveActivitiesChatMessageEvent -> {
draft.activityAddress()?.let { channelId ->
checkGetOrCreateChannel(channelId.toValue())?.addNote(note, null)
getOrCreateLiveChannel(channelId).addNote(note, null)
}
}
is TextNoteEvent -> {
@@ -2937,12 +2841,17 @@ object LocalCache : ILocalCache {
}
is ChannelMessageEvent -> {
draft.channelId()?.let { channelId ->
checkGetOrCreateChannel(channelId)?.removeNote(draftWrap)
getPublicChatChannelIfExists(channelId)?.removeNote(draftWrap)
}
}
is EphemeralChatEvent -> {
draft.roomId()?.let {
getOrCreateEphemeralChannel(it).removeNote(draftWrap)
getEphemeralChatChannelIfExists(it)?.removeNote(draftWrap)
}
}
is LiveActivitiesChatMessageEvent -> {
draft.activityAddress()?.let { channelId ->
getLiveActivityChannelIfExists(channelId)?.removeNote(draftWrap)
}
}
is TextNoteEvent -> {

View File

@@ -124,7 +124,7 @@ class HomeLiveFilter(
}
fun sort(collection: Set<EphemeralChatChannel>): List<EphemeralChatChannel> {
val topFilter = account.liveDiscoveryFollowLists.value
val topFilter = account.liveHomeFollowLists.value
val topFilterAuthors =
when (topFilter) {
is AuthorsByOutboxTopNavFilter -> topFilter.authors
@@ -137,7 +137,7 @@ class HomeLiveFilter(
val followingKeySet = topFilterAuthors ?: account.kind3FollowList.flow.value.authors
val followCounts =
collection.associate { it to followsThatParticipateOn(it, followingKeySet) }
collection.associateWith { followsThatParticipateOn(it, followingKeySet) }
return collection.sortedWith(
compareByDescending<EphemeralChatChannel> { followCounts[it] }

View File

@@ -26,8 +26,17 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
data class RoomId(
val id: String,
val relayUrl: NormalizedRelayUrl,
) {
) : Comparable<RoomId> {
fun toKey() = "$id@$relayUrl"
fun toDisplayKey() = id + "@" + relayUrl.displayUrl()
override fun compareTo(other: RoomId): Int {
val result = id.compareTo(other.id)
return if (result == 0) {
relayUrl.url.compareTo(other.relayUrl.url)
} else {
result
}
}
}

View File

@@ -39,7 +39,7 @@ class RelayTag {
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return RelayUrlNormalizer.normalizeOrNull(tag[1]) ?: return null
return RelayUrlNormalizer.normalizeOrNull(tag[1])
}
@JvmStatic

View File

@@ -149,7 +149,6 @@ open class BasicRelayClient(
override fun onMessage(text: String) {
// Log.d(logTag, "Receiving: $text")
stats.addBytesReceived(text.bytesUsedInMemory())
try {