mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-28 21:43:19 +02:00
Fixing Ephemeral Chat interface
This commit is contained in:
@@ -70,7 +70,6 @@ import com.vitorpamplona.amethyst.service.uploads.FileHeader
|
|||||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||||
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent
|
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent
|
||||||
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
|
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.InteractiveStoryBaseEvent
|
||||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
|
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.NPub
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
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.EmojiUrlTag
|
||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
||||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
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.FollowListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
|
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
|
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.nip56Reports.ReportType
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||||
@@ -815,33 +809,7 @@ class Account(
|
|||||||
?: cache.relayHints.hintsForKey(pubkey).toSet()
|
?: cache.relayHints.hintsForKey(pubkey).toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeRelaysForChannels(event: Event): Set<NormalizedRelayUrl> {
|
private fun computeRelaysForChannels(event: Event): Set<NormalizedRelayUrl> = LocalCache.getAnyChannel(event)?.relays() ?: emptySet()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computeRelayListToBroadcast(event: Event): Set<NormalizedRelayUrl> {
|
fun computeRelayListToBroadcast(event: Event): Set<NormalizedRelayUrl> {
|
||||||
if (event is MetadataEvent || event is AdvertisedRelayListEvent) {
|
if (event is MetadataEvent || event is AdvertisedRelayListEvent) {
|
||||||
|
@@ -61,7 +61,6 @@ import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
|||||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
|
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
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.relay.normalizer.isLocalHost
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||||
@@ -416,33 +415,16 @@ object LocalCache : ILocalCache {
|
|||||||
EphemeralChatChannel(key)
|
EphemeralChatChannel(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkGetOrCreateChannel(key: String): Channel? {
|
fun checkGetOrCreatePublicChatChannel(key: String): PublicChatChannel? {
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidHex(key)) {
|
if (isValidHex(key)) {
|
||||||
return getOrCreatePublicChatChannel(key)
|
return getOrCreatePublicChatChannel(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
val address = Address.parse(key)
|
|
||||||
if (address != null) {
|
|
||||||
return getOrCreateLiveChannel(address)
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isValidHex(key: String): Boolean {
|
private fun isValidHex(key: String): Boolean {
|
||||||
if (key.isBlank()) return false
|
if (key.isBlank()) return false
|
||||||
|
if (key.length != 64) return false
|
||||||
if (key.contains(":")) return false
|
if (key.contains(":")) return false
|
||||||
|
|
||||||
return Hex.isHex(key)
|
return Hex.isHex(key)
|
||||||
@@ -1622,12 +1604,12 @@ object LocalCache : ILocalCache {
|
|||||||
if (channelId.isNullOrBlank()) return false
|
if (channelId.isNullOrBlank()) return false
|
||||||
|
|
||||||
// new event
|
// new event
|
||||||
val oldChannel = checkGetOrCreateChannel(channelId) ?: return false
|
val oldChannel = checkGetOrCreatePublicChatChannel(channelId) ?: return false
|
||||||
|
|
||||||
val author = getOrCreateUser(event.pubKey)
|
val author = getOrCreateUser(event.pubKey)
|
||||||
val isVerified =
|
val isVerified =
|
||||||
if (event.createdAt > oldChannel.updatedMetadataAt) {
|
if (event.createdAt > oldChannel.updatedMetadataAt) {
|
||||||
if (oldChannel is PublicChatChannel && (wasVerified || justVerify(event))) {
|
if (wasVerified || justVerify(event)) {
|
||||||
oldChannel.updateChannelInfo(author, event)
|
oldChannel.updateChannelInfo(author, event)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@@ -1653,44 +1635,18 @@ object LocalCache : ILocalCache {
|
|||||||
relay: NormalizedRelayUrl?,
|
relay: NormalizedRelayUrl?,
|
||||||
wasVerified: Boolean,
|
wasVerified: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val channelId = event.channelId()
|
val channelId = event.channelId() ?: return false
|
||||||
|
|
||||||
if (channelId.isNullOrBlank()) return false
|
val channel = checkGetOrCreatePublicChatChannel(channelId)
|
||||||
|
if (channel == null) {
|
||||||
val channel = checkGetOrCreateChannel(channelId) ?: return false
|
Log.w("LocalCache", "Unable to create public chat channel for event ${event.toJson()}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val note = getOrCreateNote(event.id)
|
val note = getOrCreateNote(event.id)
|
||||||
channel.addNote(note, relay)
|
channel.addNote(note, relay)
|
||||||
|
|
||||||
val author = getOrCreateUser(event.pubKey)
|
return consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consume(
|
fun consume(
|
||||||
@@ -1698,46 +1654,15 @@ object LocalCache : ILocalCache {
|
|||||||
relay: NormalizedRelayUrl?,
|
relay: NormalizedRelayUrl?,
|
||||||
wasVerified: Boolean,
|
wasVerified: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val roomId = event.roomId()
|
val roomId = event.roomId() ?: return false
|
||||||
if (roomId == null) return false
|
|
||||||
|
|
||||||
val channelId = roomId
|
|
||||||
val channel = getOrCreateEphemeralChannel(channelId) ?: return false
|
|
||||||
|
|
||||||
val note = getOrCreateNote(event.id)
|
val note = getOrCreateNote(event.id)
|
||||||
|
val channel = getOrCreateEphemeralChannel(roomId)
|
||||||
channel.addNote(note, relay)
|
channel.addNote(note, relay)
|
||||||
|
|
||||||
val author = getOrCreateUser(event.pubKey)
|
return consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consume(
|
|
||||||
event: CommentEvent,
|
|
||||||
relay: NormalizedRelayUrl?,
|
|
||||||
wasVerified: Boolean,
|
|
||||||
) = consumeRegularEvent(event, relay, wasVerified)
|
|
||||||
|
|
||||||
fun consume(
|
fun consume(
|
||||||
event: LiveActivitiesChatMessageEvent,
|
event: LiveActivitiesChatMessageEvent,
|
||||||
relay: NormalizedRelayUrl?,
|
relay: NormalizedRelayUrl?,
|
||||||
@@ -1750,36 +1675,15 @@ object LocalCache : ILocalCache {
|
|||||||
val note = getOrCreateNote(event.id)
|
val note = getOrCreateNote(event.id)
|
||||||
channel.addNote(note, relay)
|
channel.addNote(note, relay)
|
||||||
|
|
||||||
val author = getOrCreateUser(event.pubKey)
|
return consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun consume(
|
||||||
|
event: CommentEvent,
|
||||||
|
relay: NormalizedRelayUrl?,
|
||||||
|
wasVerified: Boolean,
|
||||||
|
) = consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun consume(
|
fun consume(
|
||||||
event: ChannelHideMessageEvent,
|
event: ChannelHideMessageEvent,
|
||||||
@@ -2859,18 +2763,18 @@ object LocalCache : ILocalCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is EphemeralChatEvent -> {
|
is EphemeralChatEvent -> {
|
||||||
draft.roomId()?.toKey()?.let {
|
draft.roomId()?.let {
|
||||||
checkGetOrCreateChannel(it)?.addNote(note, null)
|
getOrCreateEphemeralChannel(it).addNote(note, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ChannelMessageEvent -> {
|
is ChannelMessageEvent -> {
|
||||||
draft.channelId()?.let { channelId ->
|
draft.channelId()?.let { channelId ->
|
||||||
checkGetOrCreateChannel(channelId)?.addNote(note, null)
|
checkGetOrCreatePublicChatChannel(channelId)?.addNote(note, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is LiveActivitiesChatMessageEvent -> {
|
is LiveActivitiesChatMessageEvent -> {
|
||||||
draft.activityAddress()?.let { channelId ->
|
draft.activityAddress()?.let { channelId ->
|
||||||
checkGetOrCreateChannel(channelId.toValue())?.addNote(note, null)
|
getOrCreateLiveChannel(channelId).addNote(note, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TextNoteEvent -> {
|
is TextNoteEvent -> {
|
||||||
@@ -2937,12 +2841,17 @@ object LocalCache : ILocalCache {
|
|||||||
}
|
}
|
||||||
is ChannelMessageEvent -> {
|
is ChannelMessageEvent -> {
|
||||||
draft.channelId()?.let { channelId ->
|
draft.channelId()?.let { channelId ->
|
||||||
checkGetOrCreateChannel(channelId)?.removeNote(draftWrap)
|
getPublicChatChannelIfExists(channelId)?.removeNote(draftWrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is EphemeralChatEvent -> {
|
is EphemeralChatEvent -> {
|
||||||
draft.roomId()?.let {
|
draft.roomId()?.let {
|
||||||
getOrCreateEphemeralChannel(it).removeNote(draftWrap)
|
getEphemeralChatChannelIfExists(it)?.removeNote(draftWrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is LiveActivitiesChatMessageEvent -> {
|
||||||
|
draft.activityAddress()?.let { channelId ->
|
||||||
|
getLiveActivityChannelIfExists(channelId)?.removeNote(draftWrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TextNoteEvent -> {
|
is TextNoteEvent -> {
|
||||||
|
@@ -124,7 +124,7 @@ class HomeLiveFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sort(collection: Set<EphemeralChatChannel>): List<EphemeralChatChannel> {
|
fun sort(collection: Set<EphemeralChatChannel>): List<EphemeralChatChannel> {
|
||||||
val topFilter = account.liveDiscoveryFollowLists.value
|
val topFilter = account.liveHomeFollowLists.value
|
||||||
val topFilterAuthors =
|
val topFilterAuthors =
|
||||||
when (topFilter) {
|
when (topFilter) {
|
||||||
is AuthorsByOutboxTopNavFilter -> topFilter.authors
|
is AuthorsByOutboxTopNavFilter -> topFilter.authors
|
||||||
@@ -137,7 +137,7 @@ class HomeLiveFilter(
|
|||||||
val followingKeySet = topFilterAuthors ?: account.kind3FollowList.flow.value.authors
|
val followingKeySet = topFilterAuthors ?: account.kind3FollowList.flow.value.authors
|
||||||
|
|
||||||
val followCounts =
|
val followCounts =
|
||||||
collection.associate { it to followsThatParticipateOn(it, followingKeySet) }
|
collection.associateWith { followsThatParticipateOn(it, followingKeySet) }
|
||||||
|
|
||||||
return collection.sortedWith(
|
return collection.sortedWith(
|
||||||
compareByDescending<EphemeralChatChannel> { followCounts[it] }
|
compareByDescending<EphemeralChatChannel> { followCounts[it] }
|
||||||
|
@@ -26,8 +26,17 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
|||||||
data class RoomId(
|
data class RoomId(
|
||||||
val id: String,
|
val id: String,
|
||||||
val relayUrl: NormalizedRelayUrl,
|
val relayUrl: NormalizedRelayUrl,
|
||||||
) {
|
) : Comparable<RoomId> {
|
||||||
fun toKey() = "$id@$relayUrl"
|
fun toKey() = "$id@$relayUrl"
|
||||||
|
|
||||||
fun toDisplayKey() = id + "@" + relayUrl.displayUrl()
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ class RelayTag {
|
|||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].isNotEmpty()) { return null }
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
|
||||||
return RelayUrlNormalizer.normalizeOrNull(tag[1]) ?: return null
|
return RelayUrlNormalizer.normalizeOrNull(tag[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@@ -149,7 +149,6 @@ open class BasicRelayClient(
|
|||||||
|
|
||||||
override fun onMessage(text: String) {
|
override fun onMessage(text: String) {
|
||||||
// Log.d(logTag, "Receiving: $text")
|
// Log.d(logTag, "Receiving: $text")
|
||||||
|
|
||||||
stats.addBytesReceived(text.bytesUsedInMemory())
|
stats.addBytesReceived(text.bytesUsedInMemory())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Reference in New Issue
Block a user