diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/AppModules.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/AppModules.kt index 2f1f3b9e8..29c888d45 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/AppModules.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/AppModules.kt @@ -69,7 +69,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import java.io.File -import kotlin.run class AppModules( val appContext: Context, @@ -178,9 +177,6 @@ class AppModules( // Caches all events in Memory val cache: LocalCache = LocalCache - // Organizes cache clearing - val trimmingService = MemoryTrimmingService(cache) - // Provides a relay pool val client: INostrClient = NostrClient(websocketBuilder, applicationDefaultScope) @@ -215,6 +211,9 @@ class AppModules( client = client, ) + // Organizes cache clearing + val trimmingService = MemoryTrimmingService(cache) + // as new accounts are loaded, updates the state of the TorRelaySettings, which produces new TorRelayEvaluator // and reconnects relays if the configuration has been changed. val accountsTorStateConnector = AccountsTorStateConnector(accountsCache, torEvaluatorFlow, applicationDefaultScope) @@ -283,7 +282,8 @@ class AppModules( fun trim() { applicationDefaultScope.launch { - trimmingService.run(null, LocalPreferences.allSavedAccounts()) + val loggedIn = accountsCache.accounts.value.values + trimmingService.run(loggedIn, LocalPreferences.allSavedAccounts()) } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt index 7d3d0719b..9224b615d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt @@ -106,6 +106,42 @@ fun debugState(context: Context) { " / " + LocalCache.users.size(), ) + Log.d( + "STATE DUMP", + "Public Chat Channels: " + + LocalCache.publicChatChannels.filter { _, it -> it.flowSet != null }.size + + " / " + + LocalCache.publicChatChannels.size() + + " / " + + LocalCache.publicChatChannels.values().sumOf { it.notes.size() }, + ) + Log.d( + "STATE DUMP", + "Live Chat Channels: " + + LocalCache.liveChatChannels.filter { _, it -> it.flowSet != null }.size + + " / " + + LocalCache.liveChatChannels.size() + + " / " + + LocalCache.liveChatChannels.values().sumOf { it.notes.size() }, + ) + Log.d( + "STATE DUMP", + "Ephemeral Chat Channels: " + + LocalCache.ephemeralChannels.filter { _, it -> it.flowSet != null }.size + + " / " + + LocalCache.ephemeralChannels.size() + + " / " + + LocalCache.ephemeralChannels.values().sumOf { it.notes.size() }, + ) + LocalCache.chatroomList.forEach { key, room -> + Log.d( + "STATE DUMP", + "Private Chats $key: " + + room.rooms.size() + + " / " + + room.rooms.sumOf { key, value -> value.messages.size }, + ) + } Log.d( "STATE DUMP", "Deletion Events: " + @@ -128,7 +164,7 @@ fun debugState(context: Context) { Log.d( "STATE DUMP", "Memory used by Events: " + - LocalCache.notes.sumOfLong { _, note -> note.event?.countMemory() ?: 0L } / (1024 * 1024) + + LocalCache.notes.sumOf { _, note -> note.event?.countMemory() ?: 0 } / (1024 * 1024) + " MB", ) @@ -137,10 +173,10 @@ fun debugState(context: Context) { val bytesNotes = LocalCache.notes - .sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory() ?: 0L }) + .sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory()?.toLong() ?: 0L }) val bytesAddressables = LocalCache.addressables - .sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory() ?: 0L }) + .sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory()?.toLong() ?: 0L }) qttNotes.toList().sortedByDescending { bytesNotes[it.first] }.forEach { (kind, qtt) -> Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesNotes[kind]?.div((1024 * 1024))}MB ") diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt index 3c3268fd7..c23628065 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt @@ -97,6 +97,9 @@ val GLOBAL_FOLLOWS = " Global " // This has spaces to avoid mixing with a potential NIP-51 list with the same name. val ALL_FOLLOWS = " All Follows " +// This has spaces to avoid mixing with a potential NIP-51 list with the same name. +val ALL_USER_FOLLOWS = " All User Follows " + // This has spaces to avoid mixing with a potential NIP-51 list with the same name. val AROUND_ME = " Around Me " diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt index eb59a304c..e711e8fe1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt @@ -43,7 +43,7 @@ abstract class Channel : NotesGatherer { fun changesFlow(): MutableSharedFlow> { val current = changesFlow.get() if (current != null) return current - val new = MutableSharedFlow>(0, 100, BufferOverflow.DROP_OLDEST) + val new = MutableSharedFlow>(0, 10, BufferOverflow.DROP_OLDEST) changesFlow = WeakReference(new) return new } @@ -96,7 +96,7 @@ abstract class Channel : NotesGatherer { notes.put(note.idHex, note) note.addGatherer(this) - if ((note.createdAt() ?: 0) > (lastNote?.createdAt() ?: 0)) { + if ((note.createdAt() ?: 0L) > (lastNote?.createdAt() ?: 0L)) { lastNote = note } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 6f632d358..2834116eb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -700,7 +700,7 @@ object LocalCache : ILocalCache { if (isVerified || justVerify(event)) { val replyTo = computeReplyTo(event) - if (event.createdAt > (note.createdAt() ?: 0)) { + if (event.createdAt > (note.createdAt() ?: 0L)) { note.loadEvent(event, author, replyTo) refreshNewNoteObservers(note) @@ -743,7 +743,7 @@ object LocalCache : ILocalCache { } if (isVerified || justVerify(event)) { - if (event.createdAt > (note.createdAt() ?: 0)) { + if (event.createdAt > (note.createdAt() ?: 0L)) { val replyTo = computeReplyTo(event) note.loadEvent(event, author, replyTo) @@ -845,7 +845,7 @@ object LocalCache : ILocalCache { if (note.event?.id == event.id) return false - if (event.createdAt > (note.createdAt() ?: 0) && (isVerified || justVerify(event))) { + if (event.createdAt > (note.createdAt() ?: 0L) && (isVerified || justVerify(event))) { note.loadEvent(event, author, emptyList()) val channel = getOrCreateLiveChannel(note.address) @@ -1073,7 +1073,7 @@ object LocalCache : ILocalCache { // Already processed this event. if (note.event?.id == event.id) return false - if (event.createdAt > (note.createdAt() ?: 0) && (isVerified || justVerify(event))) { + if (event.createdAt > (note.createdAt() ?: 0L) && (isVerified || justVerify(event))) { note.loadEvent(event, author, emptyList()) author.flowSet?.statuses?.invalidateData() @@ -1196,7 +1196,7 @@ object LocalCache : ILocalCache { // Already processed this event. if (replaceableNote.event?.id == event.id) return isVerified - if (event.createdAt > (replaceableNote.createdAt() ?: 0) && (isVerified || justVerify(event))) { + if (event.createdAt > (replaceableNote.createdAt() ?: 0L) && (isVerified || justVerify(event))) { // clear index from previous tags replaceableNote.replyTo?.forEach { it.removeNote(replaceableNote) @@ -1257,7 +1257,7 @@ object LocalCache : ILocalCache { val deleteNoteEvent = deleteNote.event if (deleteNoteEvent is AddressableEvent) { val addressableNote = getAddressableNoteIfExists(deleteNoteEvent.addressTag()) - if (addressableNote?.author?.pubkeyHex == event.pubKey && (addressableNote.createdAt() ?: 0) <= event.createdAt) { + if (addressableNote?.author?.pubkeyHex == event.pubKey && (addressableNote.createdAt() ?: 0L) <= event.createdAt) { // Counts the replies deleteNote(addressableNote) @@ -1279,7 +1279,7 @@ object LocalCache : ILocalCache { .mapNotNull { getAddressableNoteIfExists(it) } .forEach { deleteNote -> // must be the same author - if (deleteNote.author?.pubkeyHex == event.pubKey && (deleteNote.createdAt() ?: 0) <= event.createdAt) { + if (deleteNote.author?.pubkeyHex == event.pubKey && (deleteNote.createdAt() ?: 0L) <= event.createdAt) { // Counts the replies deleteNote(deleteNote) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index dceb0d3dd..b203748b1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -746,7 +746,7 @@ open class Note( val dayAgo = TimeUtils.oneDayAgo() return reports.isNotEmpty() || ( - author?.reports?.any { it.value.firstOrNull { (it.createdAt() ?: 0) > dayAgo } != null } + author?.reports?.any { it.value.firstOrNull { (it.createdAt() ?: 0L) > dayAgo } != null } ?: false ) } @@ -778,14 +778,14 @@ open class Note( fun hasBoostedInTheLast5Minutes(loggedIn: User): Boolean { val fiveMinsAgo = TimeUtils.fiveMinutesAgo() return boosts.any { - it.author == loggedIn && (it.createdAt() ?: 0) > fiveMinsAgo + it.author == loggedIn && (it.createdAt() ?: 0L) > fiveMinsAgo } } fun hasBoostedInTheLast5Minutes(loggedIn: HexKey): Boolean { val fiveMinsAgo = TimeUtils.fiveMinutesAgo() return boosts.any { - (it.createdAt() ?: 0) > fiveMinsAgo && it.author?.pubkeyHex == loggedIn + (it.createdAt() ?: 0L) > fiveMinsAgo && it.author?.pubkeyHex == loggedIn } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/privateChats/Chatroom.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/privateChats/Chatroom.kt index ed669a73a..9219c2b92 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/privateChats/Chatroom.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/privateChats/Chatroom.kt @@ -70,14 +70,14 @@ class Chatroom : NotesGatherer { } } - val createdAt = msg.createdAt() ?: 0 - if (createdAt > (newestMessage?.createdAt() ?: 0)) { + val createdAt = msg.createdAt() ?: 0L + if (createdAt > (newestMessage?.createdAt() ?: 0L)) { newestMessage = msg } val newSubject = msg.event?.subject() - if (newSubject != null && (msg.createdAt() ?: 0) > (subjectCreatedAt ?: 0)) { + if (newSubject != null && (msg.createdAt() ?: 0L) > (subjectCreatedAt ?: 0)) { subject.tryEmit(newSubject) subjectCreatedAt = msg.createdAt() } @@ -96,7 +96,7 @@ class Chatroom : NotesGatherer { msg.removeGatherer(this) if (msg == newestMessage) { - newestMessage = messages.maxByOrNull { it.createdAt() ?: 0 } + newestMessage = messages.maxByOrNull { it.createdAt() ?: 0L } } if (msg.event?.subject() == subject.value) { @@ -127,7 +127,7 @@ class Chatroom : NotesGatherer { val sorted = messages.sortedWith(DefaultFeedOrder) val toKeep = - if ((sorted.firstOrNull()?.createdAt() ?: 0) > TimeUtils.oneWeekAgo()) { + if ((sorted.firstOrNull()?.createdAt() ?: 0L) > TimeUtils.oneWeekAgo()) { // Recent messages, keep last 100 sorted.take(100).toSet() } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/FeedTopNavFilterState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/FeedTopNavFilterState.kt index a825fed96..efc97e01c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/FeedTopNavFilterState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/FeedTopNavFilterState.kt @@ -21,11 +21,13 @@ package com.vitorpamplona.amethyst.model.topNavFeeds import com.vitorpamplona.amethyst.model.ALL_FOLLOWS +import com.vitorpamplona.amethyst.model.ALL_USER_FOLLOWS import com.vitorpamplona.amethyst.model.AROUND_ME import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsFeedFlow +import com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows.AllUserFollowsFeedFlow import com.vitorpamplona.amethyst.model.topNavFeeds.aroundMe.AroundMeFeedFlow import com.vitorpamplona.amethyst.model.topNavFeeds.global.GlobalFeedFlow import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.NoteFeedFlow @@ -60,6 +62,7 @@ class FeedTopNavFilterState( when (listName) { GLOBAL_FOLLOWS -> GlobalFeedFlow(followsRelays, proxyRelays) ALL_FOLLOWS -> AllFollowsFeedFlow(allFollows, followsRelays, blockedRelays, proxyRelays) + ALL_USER_FOLLOWS -> AllUserFollowsFeedFlow(allFollows, followsRelays, blockedRelays, proxyRelays) AROUND_ME -> AroundMeFeedFlow(locationFlow, followsRelays, proxyRelays) else -> { val note = LocalCache.checkGetOrCreateAddressableNote(listName) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByOutboxTopNavFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByOutboxTopNavFilter.kt new file mode 100644 index 000000000..b3bf7538e --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByOutboxTopNavFilter.kt @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter +import com.vitorpamplona.amethyst.model.topNavFeeds.OutboxRelayLoader +import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilter +import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip22Comments.CommentEvent +import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine + +/** + * This is a big OR filter on all fields. + */ +@Immutable +class AllUserFollowsByOutboxTopNavFilter( + val authors: Set, + val defaultRelays: StateFlow>, + val blockedRelays: StateFlow>, +) : IFeedTopNavFilter { + override fun matchAuthor(pubkey: HexKey): Boolean = pubkey in authors + + override fun match(noteEvent: Event): Boolean = + when (noteEvent) { + is LiveActivitiesEvent -> { + noteEvent.participantsIntersect(authors) + } + + is CommentEvent -> { + // ignore follows and checks only the root scope + noteEvent.pubKey in authors + } + + else -> { + noteEvent.pubKey in authors + } + } + + override fun toPerRelayFlow(cache: LocalCache): Flow { + val authorsPerRelay = OutboxRelayLoader().toAuthorsPerRelayFlow(authors, cache) { it } + + return combine(authorsPerRelay, defaultRelays, blockedRelays) { perRelayAuthors, default, blockedRelays -> + val allRelays = perRelayAuthors.keys.filter { it !in blockedRelays }.ifEmpty { default } + + AuthorsTopNavPerRelayFilterSet( + allRelays.associateWith { + AuthorsTopNavPerRelayFilter( + authors = perRelayAuthors[it] ?: emptySet(), + ) + }, + ) + } + } + + override fun startValue(cache: LocalCache): AuthorsTopNavPerRelayFilterSet { + val authorsPerRelay = OutboxRelayLoader().authorsPerRelaySnapshot(authors, cache) { it } + + val allRelays = authorsPerRelay.keys.filter { it !in blockedRelays.value }.ifEmpty { defaultRelays.value } + + return AuthorsTopNavPerRelayFilterSet( + allRelays.associateWith { + AuthorsTopNavPerRelayFilter( + authors = authorsPerRelay[it] ?: emptySet(), + ) + }, + ) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByProxyTopNavFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByProxyTopNavFilter.kt new file mode 100644 index 000000000..6064ce5b2 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsByProxyTopNavFilter.kt @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter +import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilter +import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip22Comments.CommentEvent +import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * This is a big OR filter on all fields. + */ +@Immutable +class AllUserFollowsByProxyTopNavFilter( + val authors: Set, + val proxyRelays: Set, +) : IFeedTopNavFilter { + override fun matchAuthor(pubkey: HexKey): Boolean = pubkey in authors + + override fun match(noteEvent: Event): Boolean = + when (noteEvent) { + is LiveActivitiesEvent -> { + noteEvent.participantsIntersect(authors) + } + + is CommentEvent -> { + // ignore follows and checks only the root scope + noteEvent.pubKey in authors + } + + else -> { + noteEvent.pubKey in authors + } + } + + // forces the use of the Proxy on all connections, replacing the outbox model. + override fun toPerRelayFlow(cache: LocalCache): Flow = + MutableStateFlow( + AuthorsTopNavPerRelayFilterSet( + proxyRelays.associateWith { + AuthorsTopNavPerRelayFilter( + authors = authors, + ) + }, + ), + ) + + override fun startValue(cache: LocalCache): AuthorsTopNavPerRelayFilterSet { + // forces the use of the Proxy on all connections, replacing the outbox model. + return AuthorsTopNavPerRelayFilterSet( + proxyRelays.associateWith { + AuthorsTopNavPerRelayFilter( + authors = authors, + ) + }, + ) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsFeedFlow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsFeedFlow.kt new file mode 100644 index 000000000..9ad834025 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/allUserFollows/AllUserFollowsFeedFlow.kt @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows + +import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState +import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedFlowsType +import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine + +class AllUserFollowsFeedFlow( + val allFollows: StateFlow, + val followsRelays: StateFlow>, + val blockedRelays: StateFlow>, + val proxyRelays: StateFlow>, +) : IFeedFlowsType { + fun convert( + kind3: FollowListState.Kind3Follows?, + proxyRelays: Set, + ): IFeedTopNavFilter = + if (kind3 != null) { + if (proxyRelays.isEmpty()) { + AllUserFollowsByOutboxTopNavFilter( + authors = kind3.authors, + defaultRelays = followsRelays, + blockedRelays = blockedRelays, + ) + } else { + AllUserFollowsByProxyTopNavFilter( + authors = kind3.authors, + proxyRelays = proxyRelays, + ) + } + } else { + AllUserFollowsByOutboxTopNavFilter( + authors = emptySet(), + defaultRelays = followsRelays, + blockedRelays = blockedRelays, + ) + } + + override fun flow() = combine(allFollows, proxyRelays, ::convert) + + override fun startValue(): IFeedTopNavFilter = convert(allFollows.value, proxyRelays.value) + + override suspend fun startValue(collector: FlowCollector) { + collector.emit(startValue()) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/eventCache/MemoryTrimmingService.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/eventCache/MemoryTrimmingService.kt index 6d31d5578..f41639f2b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/eventCache/MemoryTrimmingService.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/eventCache/MemoryTrimmingService.kt @@ -33,13 +33,13 @@ class MemoryTrimmingService( var isTrimmingMemoryMutex = AtomicBoolean(false) private suspend fun doTrim( - account: Account? = null, + account: Collection, otherAccounts: List, ) { cache.cleanMemory() cache.cleanObservers() - account?.let { + account.forEach { cache.pruneHiddenEvents(it) cache.pruneHiddenMessages(it) } @@ -53,7 +53,7 @@ class MemoryTrimmingService( } suspend fun run( - account: Account?, + account: Collection, otherAccounts: List, ) { if (isTrimmingMemoryMutex.compareAndSet(false, true)) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/okhttp/OkHttpWebSocket.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/okhttp/OkHttpWebSocket.kt index e139631dd..f570d9b57 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/okhttp/OkHttpWebSocket.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/okhttp/OkHttpWebSocket.kt @@ -72,7 +72,7 @@ class OkHttpWebSocket( webSocket: okhttp3.WebSocket, response: Response, ) = out.onOpen( - response.receivedResponseAtMillis - response.sentRequestAtMillis, + (response.receivedResponseAtMillis - response.sentRequestAtMillis).toInt(), response.headers["Sec-WebSocket-Extensions"]?.contains("permessage-deflate") ?: false, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/loaders/FilterMissingAddressables.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/loaders/FilterMissingAddressables.kt index f228907e0..89b222886 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/loaders/FilterMissingAddressables.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/loaders/FilterMissingAddressables.kt @@ -76,10 +76,6 @@ fun filterMissingAddressables(keys: List): List add(relayUrl, key.note.address) } - - key.account.searchRelayList.flow.value.forEach { relayUrl -> - add(relayUrl, key.note.address) - } } // loads threading that is event-based diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/FrameStat.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/FrameStat.kt index 6be8af457..745bc3f80 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/FrameStat.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/FrameStat.kt @@ -35,7 +35,7 @@ class FrameStat { kind: Int, subId: String, relayUrl: NormalizedRelayUrl, - memory: Long, + memory: Int, ) { eventCount.incrementAndGet() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/KindGroup.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/KindGroup.kt index 4fa495f17..b648047f3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/KindGroup.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/speedLogger/KindGroup.kt @@ -24,22 +24,21 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl import com.vitorpamplona.quartz.utils.LargeCache import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong import kotlin.concurrent.atomics.ExperimentalAtomicApi @OptIn(ExperimentalAtomicApi::class) class KindGroup( var count: AtomicInteger = AtomicInteger(0), - var memory: AtomicLong = AtomicLong(0L), + var memory: AtomicInteger = AtomicInteger(0), val subs: LargeCache = LargeCache(), val relays: LargeCache = LargeCache(), ) { companion object { - const val MB: Long = 1024 + const val MB: Int = 1024 } fun increment( - mem: Long, + mem: Int, subId: String, relayUrl: NormalizedRelayUrl, ) { @@ -63,7 +62,7 @@ class KindGroup( fun reset() { count.set(0) - memory.set(0L) + memory.set(0) subs.forEach { key, value -> value.set(0) } relays.forEach { key, value -> value.set(0) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt index d39042564..fa72f5264 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt @@ -195,7 +195,7 @@ fun RenderRelayIcon( iconUrl: String?, loadProfilePicture: Boolean, loadRobohash: Boolean, - pingInMs: Long, + pingInMs: Int, iconModifier: Modifier = MaterialTheme.colorScheme.relayIconModifier, ) { val green = MaterialTheme.colorScheme.allGoodColor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt index 197f09d62..9e224b999 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt @@ -63,7 +63,7 @@ fun NormalTimeAgo( val nowStr = stringRes(id = R.string.now) val time by - remember(baseNote) { derivedStateOf { timeAgoShort(baseNote.createdAt() ?: 0, nowStr) } } + remember(baseNote) { derivedStateOf { timeAgoShort(baseNote.createdAt() ?: 0L, nowStr) } } Text( text = time, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt index 28efb1f97..e1f60d664 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt @@ -104,7 +104,6 @@ fun RenderTextEvent( note, accountViewModel, ) { body -> - val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null } val newBody = if (editState.value is GenericLoadable.Loaded) { (editState.value as? GenericLoadable.Loaded) @@ -118,10 +117,13 @@ fun RenderTextEvent( } val eventContent = - if (!subject.isNullOrBlank() && !newBody.startsWith(subject)) { - "### $subject\n$newBody" - } else { - newBody + remember(newBody) { + val subject = (note.event as? TextNoteEvent)?.subject()?.ifBlank { null } + if (!subject.isNullOrBlank() && !newBody.contains(subject, ignoreCase = true)) { + "$subject\n\n$newBody" + } else { + newBody + } } if (makeItShort && accountViewModel.isLoggedUser(note.author)) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt index 361988be8..5ccdd707d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.ALL_FOLLOWS +import com.vitorpamplona.amethyst.model.ALL_USER_FOLLOWS import com.vitorpamplona.amethyst.model.AROUND_ME import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AddressableNote @@ -82,6 +83,15 @@ class FollowListState( unpackList = listOf(ContactListEvent.blockListFor(account.signer.pubKey)), ) + val kind3FollowUsers = + PeopleListOutBoxFeedDefinition( + code = ALL_USER_FOLLOWS, + name = ResourceName(R.string.follow_list_kind3follows_users_only), + type = CodeNameType.HARDCODED, + kinds = DEFAULT_FEED_KINDS, + unpackList = listOf(ContactListEvent.blockListFor(account.signer.pubKey)), + ) + val globalFollow = GlobalFeedDefinition( code = GLOBAL_FOLLOWS, @@ -107,7 +117,7 @@ class FollowListState( unpackList = listOf(MuteListEvent.blockListFor(account.userProfile().pubkeyHex)), ) - val defaultLists = persistentListOf(kind3Follow, aroundMe, globalFollow, muteListFollow) + val defaultLists = persistentListOf(kind3Follow, kind3FollowUsers, aroundMe, globalFollow, muteListFollow) fun getPeopleLists(): List = account @@ -218,7 +228,7 @@ class FollowListState( checkNotInMainThread() emit( listOf( - listOf(kind3Follow, aroundMe, globalFollow), + listOf(kind3Follow, kind3FollowUsers, aroundMe, globalFollow), myLivePeopleListsFlow, myLiveKind3FollowsFlow, listOf(muteListFollow), @@ -234,7 +244,7 @@ class FollowListState( checkNotInMainThread() emit( listOf( - listOf(kind3Follow, aroundMe, globalFollow), + listOf(kind3Follow, kind3FollowUsers, aroundMe, globalFollow), myLivePeopleListsFlow, listOf(muteListFollow), ).flatten().toImmutableList(), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 896f98841..87f3ba2f6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -1084,6 +1084,13 @@ class AccountViewModel( account.markAsRead("Channel/${noteEvent.channelId()}", noteEvent.createdAt) } else if (noteEvent is ChatroomKeyable) { account.markAsRead("Room/${noteEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt) + } else if (noteEvent is DraftWrapEvent) { + val innerEvent = account.draftsDecryptionCache.preCachedDraft(noteEvent) + if (innerEvent is IsInPublicChatChannel) { + account.markAsRead("Channel/${innerEvent.channelId()}", noteEvent.createdAt) + } else if (innerEvent is ChatroomKeyable) { + account.markAsRead("Room/${innerEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt) + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt index edef82313..2a6f7e060 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt @@ -34,7 +34,7 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText @Composable fun ChatTimeAgo(baseNote: Note) { val nowStr = stringRes(id = R.string.now) - val time = remember(baseNote) { timeAgoShort(baseNote.createdAt() ?: 0, nowStr) } + val time = remember(baseNote) { timeAgoShort(baseNote.createdAt() ?: 0L, nowStr) } Text( text = time, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt index 09bc8a4b3..4a786a408 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt @@ -105,7 +105,7 @@ class ChatroomListKnownFeedFilter( val channelId = (oldNote.event as? ChannelMessageEvent)?.channelId() if (newNotePair.key == channelId) { hasUpdated = true - if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) { + if ((newNotePair.value.createdAt() ?: 0L) > (oldNote.createdAt() ?: 0L)) { myNewList = myNewList.replace(oldNote, newNotePair.value) } } @@ -121,7 +121,7 @@ class ChatroomListKnownFeedFilter( val noteEvent = (oldNote.event as? EphemeralChatEvent)?.roomId() if (newNotePair.key == noteEvent) { hasUpdated = true - if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) { + if ((newNotePair.value.createdAt() ?: 0L) > (oldNote.createdAt() ?: 0L)) { myNewList = myNewList.replace(oldNote, newNotePair.value) } } @@ -138,7 +138,7 @@ class ChatroomListKnownFeedFilter( if (newNotePair.key == oldRoom) { hasUpdated = true - if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) { + if ((newNotePair.value.createdAt() ?: 0L) > (oldNote.createdAt() ?: 0L)) { myNewList = myNewList.replace(oldNote, newNotePair.value) } } @@ -179,7 +179,7 @@ class ChatroomListKnownFeedFilter( if (channelId in followingChannels && account.isAcceptable(newNote)) { val lastNote = newRelevantPublicMessages.get(channelId) if (lastNote != null) { - if ((newNote.createdAt() ?: 0) > (lastNote.createdAt() ?: 0)) { + if ((newNote.createdAt() ?: 0L) > (lastNote.createdAt() ?: 0L)) { newRelevantPublicMessages.put(channelId, newNote) } } else { @@ -205,7 +205,7 @@ class ChatroomListKnownFeedFilter( if (room != null && room in followingEphemeralChats && account.isAcceptable(newNote)) { val lastNote = newRelevantEphemeralChats.get(room) if (lastNote != null) { - if ((newNote.createdAt() ?: 0) > (lastNote.createdAt() ?: 0)) { + if ((newNote.createdAt() ?: 0L) > (lastNote.createdAt() ?: 0L)) { newRelevantEphemeralChats.put(room, newNote) } } else { @@ -241,7 +241,7 @@ class ChatroomListKnownFeedFilter( ) { val lastNote = newRelevantPrivateMessages.get(roomKey) if (lastNote != null) { - if ((newNote.createdAt() ?: 0) > (lastNote.createdAt() ?: 0)) { + if ((newNote.createdAt() ?: 0L) > (lastNote.createdAt() ?: 0L)) { newRelevantPrivateMessages.put(roomKey, newNote) } } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListNewFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListNewFeedFilter.kt index ffc24da83..5e9426ee1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListNewFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListNewFeedFilter.kt @@ -72,7 +72,7 @@ class ChatroomListNewFeedFilter( if (newNotePair.key == oldRoom) { hasUpdated = true - if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) { + if ((newNotePair.value.createdAt() ?: 0L) > (oldNote.createdAt() ?: 0L)) { myNewList = myNewList.replace(oldNote, newNotePair.value) } } @@ -120,7 +120,7 @@ class ChatroomListNewFeedFilter( ) { val lastNote = newRelevantPrivateMessages.get(roomKey) if (lastNote != null) { - if ((newNote.createdAt() ?: 0) > (lastNote.createdAt() ?: 0)) { + if ((newNote.createdAt() ?: 0L) > (lastNote.createdAt() ?: 0L)) { newRelevantPrivateMessages.put(roomKey, newNote) } } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt index ab004dfc0..c3b6a5da7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt @@ -106,12 +106,12 @@ open class DiscoverChatFeedFilter( // precache to avoid breaking the contract val lastNote = items.associateWith { note -> - LocalCache.getPublicChatChannelIfExists(note.idHex)?.lastNote?.createdAt() ?: 0 + LocalCache.getPublicChatChannelIfExists(note.idHex)?.lastNote?.createdAt() ?: 0L } val createdNote = items.associateWith { note -> - note.createdAt() ?: 0 + note.createdAt() ?: 0L } val comparator: Comparator = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt index e45a11bed..1e9299271 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt @@ -127,7 +127,7 @@ open class DiscoverCommunityFeedFilter( val createdNote = items.associateWith { note -> - note.createdAt() ?: 0 + note.createdAt() ?: 0L } val comparator: Comparator = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/subassemblies/FilterCommunitiesGlobal.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/subassemblies/FilterCommunitiesGlobal.kt index 334aeb33f..2fdc48c33 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/subassemblies/FilterCommunitiesGlobal.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/subassemblies/FilterCommunitiesGlobal.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent +import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.utils.TimeUtils fun filterCommunitiesGlobal( @@ -41,8 +42,8 @@ fun filterCommunitiesGlobal( relay = it.key, filter = Filter( - kinds = CommunityPostApprovalEvent.KIND_LIST, - limit = 100, + kinds = listOf(CommunityDefinitionEvent.KIND), + limit = 500, since = since, ), ), @@ -52,7 +53,7 @@ fun filterCommunitiesGlobal( Filter( kinds = CommunityPostApprovalEvent.KIND_LIST, limit = 100, - since = since ?: TimeUtils.oneWeekAgo(), + since = since ?: TimeUtils.oneMonthAgo(), ), ), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt index efb752dc9..5e1102248 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt @@ -80,9 +80,9 @@ open class NIP90ContentDiscoveryResponseFilter( protected open fun innerApplyFilter(collection: Collection): Set { // val params = buildFilterParams(account) - val maxNote = collection.filter { acceptableEvent(it) }.maxByOrNull { it.createdAt() ?: 0 } ?: return emptySet() + val maxNote = collection.filter { acceptableEvent(it) }.maxByOrNull { it.createdAt() ?: 0L } ?: return emptySet() - if ((maxNote.createdAt() ?: 0) > (latestNote?.createdAt() ?: 0)) { + if ((maxNote.createdAt() ?: 0L) > (latestNote?.createdAt() ?: 0L)) { latestNote = maxNote } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt index 686837fb4..62f55d898 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt @@ -115,7 +115,7 @@ class HomeLiveFilter( oldList.filter { channel -> val channelTime = (channel as? LiveActivitiesChannel)?.info?.createdAt (channelTime == null || channelTime > fifteenMinsAgo) || - (channel.lastNote?.createdAt() ?: 0) > fifteenMinsAgo + (channel.lastNote?.createdAt() ?: 0L) > fifteenMinsAgo } val newItemsToBeAdded = applyFilter(newItems) @@ -194,7 +194,7 @@ class HomeLiveFilter( return collection.sortedWith( compareByDescending { followCounts[it] } - .thenByDescending { it.lastNote?.createdAt() ?: 0 } + .thenByDescending { it.lastNote?.createdAt() ?: 0L } .thenBy { it.hashCode() }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt index 175e0bec9..4ee5b7973 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt @@ -239,7 +239,7 @@ class CardFeedContentState( (boostsInCard + zapsInCard.map { it.response } + reactionsInCard).groupBy { sdf.format( Instant - .ofEpochSecond(it.createdAt() ?: 0) + .ofEpochSecond(it.createdAt() ?: 0L) .atZone(ZoneId.systemDefault()) .toLocalDateTime(), ) @@ -273,7 +273,7 @@ class CardFeedContentState( user.value.groupBy { sdf.format( Instant - .ofEpochSecond(it.createdAt() ?: 0) + .ofEpochSecond(it.createdAt() ?: 0L) .atZone(ZoneId.systemDefault()) .toLocalDateTime(), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedState.kt index 67063f904..7baa1fee9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedState.kt @@ -43,7 +43,7 @@ abstract class Card { class BadgeCard( val note: Note, ) : Card() { - override fun createdAt(): Long = note.createdAt() ?: 0 + override fun createdAt(): Long = note.createdAt() ?: 0L override fun id() = note.idHex } @@ -52,7 +52,7 @@ class BadgeCard( class NoteCard( val note: Note, ) : Card() { - override fun createdAt(): Long = note.createdAt() ?: 0 + override fun createdAt(): Long = note.createdAt() ?: 0L override fun id() = note.idHex } @@ -62,7 +62,7 @@ class ZapUserSetCard( val user: User, val zapEvents: ImmutableList, ) : Card() { - val createdAt = zapEvents.maxOf { it.createdAt() ?: 0 } + val createdAt = zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L override fun createdAt(): Long = createdAt @@ -78,9 +78,9 @@ class MultiSetCard( ) : Card() { val maxCreatedAt = maxOf( - zapEvents.maxOfOrNull { it.createdAt() ?: 0 } ?: 0, - likeEvents.maxOfOrNull { it.createdAt() ?: 0 } ?: 0, - boostEvents.maxOfOrNull { it.createdAt() ?: 0 } ?: 0, + zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L, + likeEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L, + boostEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L, ) val minCreatedAt = @@ -109,7 +109,7 @@ class MultiSetCard( class MessageSetCard( val note: Note, ) : Card() { - override fun createdAt(): Long = note.createdAt() ?: 0 + override fun createdAt(): Long = note.createdAt() ?: 0L override fun id() = note.idHex } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 30a5ecc1d..b8fcf86d4 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -502,6 +502,7 @@ Follow List All Follows + All User Follows Follows via Proxy Around Me Global diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 0a0d3f2ec..20c9b19cd 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -215,10 +215,8 @@ private fun TranslationMessage( } }, onClick = { - scope.launch(Dispatchers.IO) { - accountViewModel.account.toggleDontTranslateFrom(source) - langSettingsPopupExpanded = false - } + accountViewModel.toggleDontTranslateFrom(source) + langSettingsPopupExpanded = false }, ) HorizontalDivider(thickness = DividerThickness) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/experimental/interactiveStories/tags/RootSceneTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/experimental/interactiveStories/tags/RootSceneTag.kt index ab50b9071..7586b058d 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/experimental/interactiveStories/tags/RootSceneTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/experimental/interactiveStories/tags/RootSceneTag.kt @@ -48,9 +48,9 @@ data class RootSceneTag( this.relay = relayHint } - fun countMemory(): Long = + fun countMemory(): Int = 5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit) - 8L + // kind + 8 + // kind pubKeyHex.bytesUsedInMemory() + dTag.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/core/Event.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/core/Event.kt index 09ea417cf..7a6a2f8d6 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/core/Event.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/core/Event.kt @@ -47,9 +47,9 @@ open class Event( open fun extraIndexableTagNames() = emptySet() - open fun countMemory(): Long = + open fun countMemory(): Int = 7 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit) - 12L + // createdAt + kind + 12 + // createdAt + kind id.bytesUsedInMemory() + pubKey.bytesUsedInMemory() + tags.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/hints/EventHintBundle.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/hints/EventHintBundle.kt index 56961814c..61a95097c 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/hints/EventHintBundle.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/hints/EventHintBundle.kt @@ -45,7 +45,7 @@ data class EventHintBundle( this.authorHomeRelay = authorHomeRelay } - fun countMemory(): Long = + fun countMemory(): Int = 2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit) event.countMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt index 54c1f7e5e..e80d553db 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt @@ -157,7 +157,7 @@ open class BasicRelayClient( val onConnected: () -> Unit, ) : WebSocketListener { override fun onOpen( - pingMillis: Long, + pingMillis: Int, compression: Boolean, ) { Log.d(logTag, "OnOpen (ping: ${pingMillis}ms${if (compression) ", using compression" else ""})") @@ -260,7 +260,7 @@ open class BasicRelayClient( } fun markConnectionAsReady( - pingInMs: Long, + pingInMs: Int, usingCompression: Boolean, ) { this.resetEOSEStatuses() @@ -459,7 +459,7 @@ open class BasicRelayClient( ) } socket?.let { - // Log.d(logTag, "Sending: $str") + Log.d(logTag, "Sending: $str") val result = it.send(str) listener.onSend(this@BasicRelayClient, str, result) stats.addBytesSent(str.bytesUsedInMemory()) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStat.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStat.kt index fd266e0ff..13be36066 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStat.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStat.kt @@ -24,11 +24,11 @@ import androidx.collection.LruCache import com.vitorpamplona.quartz.utils.TimeUtils class RelayStat( - var receivedBytes: Long = 0L, - var sentBytes: Long = 0L, - var spamCounter: Long = 0L, - var errorCounter: Long = 0L, - var pingInMs: Long = 0L, + var receivedBytes: Int = 0, + var sentBytes: Int = 0, + var spamCounter: Int = 0, + var errorCounter: Int = 0, + var pingInMs: Int = 0, ) { val messages = LruCache(100) @@ -54,11 +54,11 @@ class RelayStat( messages.put(debugMessage, debugMessage) } - fun addBytesReceived(bytesUsedInMemory: Long) { + fun addBytesReceived(bytesUsedInMemory: Int) { receivedBytes += bytesUsedInMemory } - fun addBytesSent(bytesUsedInMemory: Long) { + fun addBytesSent(bytesUsedInMemory: Int) { sentBytes += bytesUsedInMemory } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStats.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStats.kt index b12e95e2c..a282c9e49 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStats.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/stats/RelayStats.kt @@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl object RelayStats { private val innerCache = - object : LruCache(1000) { + object : LruCache(100) { override fun create(key: NormalizedRelayUrl?) = RelayStat() } @@ -33,14 +33,14 @@ object RelayStats { fun addBytesReceived( url: NormalizedRelayUrl, - bytesUsedInMemory: Long, + bytesUsedInMemory: Int, ) { get(url).addBytesReceived(bytesUsedInMemory) } fun addBytesSent( url: NormalizedRelayUrl, - bytesUsedInMemory: Long, + bytesUsedInMemory: Int, ) { get(url).addBytesSent(bytesUsedInMemory) } @@ -61,7 +61,7 @@ object RelayStats { fun setPing( url: NormalizedRelayUrl, - pingInMs: Long, + pingInMs: Int, ) { get(url).pingInMs = pingInMs } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/WebSocketListener.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/WebSocketListener.kt index acebf8d6b..99a099464 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/WebSocketListener.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/WebSocketListener.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.relay.sockets interface WebSocketListener { fun onOpen( - pingMillis: Long, + pingMillis: Int, compression: Boolean, ) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/okhttp/BasicOkHttpWebSocket.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/okhttp/BasicOkHttpWebSocket.kt index f61060469..12b1f07fa 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/okhttp/BasicOkHttpWebSocket.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/sockets/okhttp/BasicOkHttpWebSocket.kt @@ -46,7 +46,7 @@ class BasicOkHttpWebSocket( webSocket: okhttp3.WebSocket, response: Response, ) = out.onOpen( - response.receivedResponseAtMillis - response.sentRequestAtMillis, + (response.receivedResponseAtMillis - response.sentRequestAtMillis).toInt(), response.headers["Sec-WebSocket-Extensions"]?.contains("permessage-deflate") ?: false, ) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt index 74a0ac49b..f4ad7a243 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/ATag.kt @@ -40,9 +40,9 @@ data class ATag( ) { constructor(address: Address, relayHint: NormalizedRelayUrl? = null) : this(address.kind, address.pubKeyHex, address.dTag, relayHint) - fun countMemory(): Long = + fun countMemory(): Int = 5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit) - 8L + // kind + 8 + // kind pubKeyHex.bytesUsedInMemory() + dTag.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt index b055aab09..0f24ffd4f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt @@ -39,9 +39,9 @@ data class Address( Parcelable { fun toValue() = assemble(kind, pubKeyHex, dTag) - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + - 8L + // kind + 8 + // kind pubKeyHex.bytesUsedInMemory() + dTag.bytesUsedInMemory() diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/dTags/DTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/dTags/DTag.kt index e439d00d7..b92452f38 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/dTags/DTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/dTags/DTag.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes class DTag( val dId: String, ) { - fun countMemory(): Long = 1 * pointerSizeInBytes + dId.bytesUsedInMemory() + fun countMemory(): Int = 1 * pointerSizeInBytes + dId.bytesUsedInMemory() fun toTagArray() = assemble(dId) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt index 1a793e857..fe4672f1f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/events/ETag.kt @@ -44,7 +44,7 @@ data class ETag( this.author = authorPubKeyHex } - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) eventId.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt index 1b712af7a..92e0f48ed 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip01Core/tags/people/PTag.kt @@ -40,7 +40,7 @@ data class PTag( override val pubKey: HexKey, override val relayHint: NormalizedRelayUrl? = null, ) : PubKeyReferenceTag { - fun countMemory(): Long = + fun countMemory(): Int = 2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit) pubKey.bytesUsedInMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt index b67f628b7..548b020a3 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip02FollowList/tags/ContactTag.kt @@ -50,7 +50,7 @@ data class ContactTag( this.petname = petname } - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + pubKey.bytesUsedInMemory() + (relayUri?.url?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip13Pow/tags/PoWTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip13Pow/tags/PoWTag.kt index e19494610..f8f241b5f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip13Pow/tags/PoWTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip13Pow/tags/PoWTag.kt @@ -30,7 +30,7 @@ class PoWTag( val nonce: String, val commitment: Int?, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + nonce.bytesUsedInMemory() + (commitment?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + nonce.bytesUsedInMemory() + (commitment?.bytesUsedInMemory() ?: 0) fun toTagArray() = assemble(nonce, commitment) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QAddressableTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QAddressableTag.kt index b25b6ebfa..db1b5eb14 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QAddressableTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QAddressableTag.kt @@ -53,7 +53,7 @@ data class QAddressableTag( this.relay = relayHint } - fun countMemory(): Long = + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QEventTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QEventTag.kt index 662c6f9b9..aa8344b74 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QEventTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip18Reposts/quotes/QEventTag.kt @@ -42,7 +42,7 @@ data class QEventTag( this.author = authorPubKeyHex } - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) eventId.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt index f561afb53..809e39c2d 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip23LongContent/LongTextNoteEvent.kt @@ -132,7 +132,18 @@ class LongTextNoteEvent( fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse) - override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + override fun publishedAt(): Long? { + val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + + if (publishedAt == null) return null + + // removes posts in the future. + return if (publishedAt <= createdAt) { + publishedAt + } else { + null + } + } companion object { const val KIND = 30023 diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip28PublicChat/list/tags/ChannelTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip28PublicChat/list/tags/ChannelTag.kt index 88aafdff8..122a9d7b0 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip28PublicChat/list/tags/ChannelTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip28PublicChat/list/tags/ChannelTag.kt @@ -38,7 +38,7 @@ class ChannelTag( val relay: NormalizedRelayUrl? = null, val author: HexKey? = null, ) { - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) eventId.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerMessage.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerMessage.kt index bb2823521..fe7007f79 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerMessage.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerMessage.kt @@ -26,7 +26,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer abstract class BunkerMessage { - abstract fun countMemory(): Long + abstract fun countMemory(): Int class BunkerMessageDeserializer : StdDeserializer(BunkerMessage::class.java) { override fun deserialize( diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequest.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequest.kt index 7e84eb24d..8b5a4bbe6 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequest.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerRequest.kt @@ -36,7 +36,7 @@ open class BunkerRequest( val method: String, val params: Array = emptyArray(), ) : BunkerMessage() { - override fun countMemory(): Long = + override fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) id.bytesUsedInMemory() + method.bytesUsedInMemory() + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerResponse.kt index 6d04c807b..d6272a6aa 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/BunkerResponse.kt @@ -36,7 +36,7 @@ open class BunkerResponse( val result: String?, val error: String?, ) : BunkerMessage() { - override fun countMemory(): Long = + override fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) id.bytesUsedInMemory() + (result?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt index b653d897b..9f5373365 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt @@ -28,7 +28,7 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes abstract class Response( @field:JsonProperty("result_type") val resultType: String, ) { - abstract fun countMemory(): Long + abstract fun countMemory(): Int } // PayInvoice Call @@ -39,10 +39,10 @@ class PayInvoiceSuccessResponse( class PayInvoiceResultParams( val preimage: String? = null, ) { - fun countMemory(): Long = pointerSizeInBytes + (preimage?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = pointerSizeInBytes + (preimage?.bytesUsedInMemory() ?: 0) } - override fun countMemory(): Long = pointerSizeInBytes + (result?.countMemory() ?: 0) + override fun countMemory(): Int = pointerSizeInBytes + (result?.countMemory() ?: 0) } class PayInvoiceErrorResponse( @@ -52,10 +52,10 @@ class PayInvoiceErrorResponse( val code: ErrorType? = null, val message: String? = null, ) { - fun countMemory(): Long = pointerSizeInBytes + pointerSizeInBytes + (message?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = pointerSizeInBytes + pointerSizeInBytes + (message?.bytesUsedInMemory() ?: 0) } - override fun countMemory(): Long = pointerSizeInBytes + (error?.countMemory() ?: 0) + override fun countMemory(): Int = pointerSizeInBytes + (error?.countMemory() ?: 0) enum class ErrorType { @JsonProperty(value = "RATE_LIMITED") @@ -69,6 +69,10 @@ class PayInvoiceErrorResponse( @JsonProperty(value = "INSUFFICIENT_BALANCE") INSUFFICIENT_BALANCE, + // The command is not known or is intentionally not implemented. + @JsonProperty(value = "PAYMENT_FAILED") + PAYMENT_FAILED, + // The wallet does not have enough funds to cover a fee reserve or the payment amount. @JsonProperty(value = "QUOTA_EXCEEDED") QUOTA_EXCEEDED, diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip48ProxyTags/ProxyTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip48ProxyTags/ProxyTag.kt index 44496d1ea..e2bb5591f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip48ProxyTags/ProxyTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip48ProxyTags/ProxyTag.kt @@ -31,7 +31,7 @@ data class ProxyTag( val id: String, val protocol: String, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + id.bytesUsedInMemory() + protocol.bytesUsedInMemory() + fun countMemory(): Int = 2 * pointerSizeInBytes + id.bytesUsedInMemory() + protocol.bytesUsedInMemory() fun toTagArray() = assemble(id, protocol) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/AddressBookmark.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/AddressBookmark.kt index ede0ece45..02e0fbd10 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/AddressBookmark.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/AddressBookmark.kt @@ -35,7 +35,7 @@ class AddressBookmark( val address: Address, val relayHint: NormalizedRelayUrl? = null, ) : BookmarkIdTag { - fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/EventBookmark.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/EventBookmark.kt index 8bfeb5a59..82de81214 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/EventBookmark.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/bookmarkList/tags/EventBookmark.kt @@ -38,7 +38,7 @@ class EventBookmark( val relay: NormalizedRelayUrl? = null, val author: HexKey? = null, ) : BookmarkIdTag { - fun countMemory(): Long = + fun countMemory(): Int = 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) eventId.bytesUsedInMemory() + (relay?.url?.bytesUsedInMemory() ?: 0) + diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/UserTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/UserTag.kt index 2558e6d54..69fd21af1 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/UserTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/UserTag.kt @@ -38,7 +38,7 @@ class UserTag( val pubKey: HexKey, val relayHint: NormalizedRelayUrl? = null, ) : MuteTag { - fun countMemory(): Long = + fun countMemory(): Int = 2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit) pubKey.bytesUsedInMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/WordTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/WordTag.kt index 1204b5d42..9a1010c89 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/WordTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip51Lists/muteList/tags/WordTag.kt @@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes class WordTag( val word: String, ) : MuteTag { - fun countMemory(): Long = + fun countMemory(): Int = 1 * pointerSizeInBytes + // 1 fields, 4 bytes each reference (32bit) word.bytesUsedInMemory() diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/MeetingSpaceTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/MeetingSpaceTag.kt index e7237ca0c..cfe346311 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/MeetingSpaceTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/MeetingSpaceTag.kt @@ -35,7 +35,7 @@ class MeetingSpaceTag( val address: Address, val relayHint: NormalizedRelayUrl? = null, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/presence/tags/MeetingRoomTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/presence/tags/MeetingRoomTag.kt index c59d1d714..c5c9062c7 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/presence/tags/MeetingRoomTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/presence/tags/MeetingRoomTag.kt @@ -35,7 +35,7 @@ class MeetingRoomTag( val address: Address, val relayHint: NormalizedRelayUrl? = null, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt index e35cbafef..a33a6c2ad 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt @@ -46,6 +46,8 @@ import com.vitorpamplona.quartz.nip19Bech32.eventHints import com.vitorpamplona.quartz.nip19Bech32.eventIds import com.vitorpamplona.quartz.nip19Bech32.pubKeyHints import com.vitorpamplona.quartz.nip19Bech32.pubKeys +import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag +import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag.Companion.parse import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.nip50Search.SearchableEvent import com.vitorpamplona.quartz.utils.TimeUtils @@ -129,12 +131,18 @@ class WikiNoteEvent( fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1) - override fun publishedAt() = - try { - tags.firstOrNull { it.size > 1 && it[0] == "published_at" }?.get(1)?.toLongOrNull() - } catch (_: Exception) { + override fun publishedAt(): Long? { + val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + + if (publishedAt == null) return null + + // removes posts in the future. + return if (publishedAt <= createdAt) { + publishedAt + } else { null } + } companion object { const val KIND = 30818 diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/LnZapEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/LnZapEvent.kt index 5393d2b4d..bd7299f9f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/LnZapEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/LnZapEvent.kt @@ -72,7 +72,7 @@ class LnZapEvent( } } - override fun countMemory(): Long = + override fun countMemory(): Int = super.countMemory() + pointerSizeInBytes + (zapRequest?.countMemory() ?: 0) + // rough calculation pointerSizeInBytes + 36 // bigdecimal size diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/zapraiser/ZapRaiserTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/zapraiser/ZapRaiserTag.kt index fdc831fb6..a86d4215c 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/zapraiser/ZapRaiserTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip57Zaps/zapraiser/ZapRaiserTag.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes class ZapRaiserTag( val amountInSats: Long, ) { - fun countMemory(): Long = 1 * pointerSizeInBytes + amountInSats.bytesUsedInMemory() + fun countMemory(): Int = 1 * pointerSizeInBytes + amountInSats.bytesUsedInMemory() fun toTagArray() = assemble(amountInSats) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/seals/SealedRumorEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/seals/SealedRumorEvent.kt index a2def5540..210cc13f9 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/seals/SealedRumorEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/seals/SealedRumorEvent.kt @@ -44,7 +44,7 @@ class SealedRumorEvent( ) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var innerEventId: HexKey? = null - override fun countMemory(): Long = + override fun countMemory(): Int = super.countMemory() + pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt index 01a72f03d..8f2e65cf3 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt @@ -48,7 +48,7 @@ class GiftWrapEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var innerEventId: HexKey? = null - override fun countMemory(): Long = + override fun countMemory(): Int = super.countMemory() + pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/ReplaceableVideoEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/ReplaceableVideoEvent.kt index f57e4cc43..645eb87a5 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/ReplaceableVideoEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/ReplaceableVideoEvent.kt @@ -53,7 +53,18 @@ abstract class ReplaceableVideoEvent( override fun title() = tags.firstNotNullOfOrNull(TitleTag::parse) - override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + override fun publishedAt(): Long? { + val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + + if (publishedAt == null) return null + + // removes posts in the future. + return if (publishedAt <= createdAt) { + publishedAt + } else { + null + } + } override fun duration() = tags.firstNotNullOfOrNull(DurationTag::parse) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/tags/TextTrackTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/tags/TextTrackTag.kt index 671c77ca1..d1cd9cf23 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/tags/TextTrackTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip71Video/tags/TextTrackTag.kt @@ -31,7 +31,7 @@ data class TextTrackTag( val eventId: HexKey, var relay: String? = null, ) { - fun countMemory(): Long = + fun countMemory(): Int = 2 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit) eventId.bytesUsedInMemory() + (relay?.bytesUsedInMemory() ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/approval/tags/ApprovedAddressTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/approval/tags/ApprovedAddressTag.kt index 727993df6..1e2a4582f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/approval/tags/ApprovedAddressTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/approval/tags/ApprovedAddressTag.kt @@ -36,7 +36,7 @@ class ApprovedAddressTag( val address: Address, val relayHint: NormalizedRelayUrl? = null, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/follow/tags/CommunityTag.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/follow/tags/CommunityTag.kt index 153482351..262d201e2 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/follow/tags/CommunityTag.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip72ModCommunities/follow/tags/CommunityTag.kt @@ -36,7 +36,7 @@ class CommunityTag( val address: Address, val relayHint: NormalizedRelayUrl? = null, ) { - fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) + fun countMemory(): Int = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0) fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppDefinitionEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppDefinitionEvent.kt index 76db75a2a..b025dc79a 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppDefinitionEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppDefinitionEvent.kt @@ -49,7 +49,7 @@ class AppDefinitionEvent( sig: HexKey, ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig), PublishedAtProvider { - override fun countMemory(): Long = super.countMemory() + (cachedMetadata?.countMemory() ?: 8L) + override fun countMemory(): Int = super.countMemory() + (cachedMetadata?.countMemory() ?: 8) @Transient private var cachedMetadata: AppMetadata? = null @@ -79,7 +79,18 @@ class AppDefinitionEvent( fun includeKind(kind: Int) = tags.isTaggedKind(kind) - override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + override fun publishedAt(): Long? { + val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + + if (publishedAt == null) return null + + // removes posts in the future. + return if (publishedAt <= createdAt) { + publishedAt + } else { + null + } + } companion object { const val KIND = 31990 diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppMetadata.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppMetadata.kt index 9ee0072ce..f47606412 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppMetadata.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip89AppHandlers/definition/AppMetadata.kt @@ -50,25 +50,25 @@ class AppMetadata { var lud06: String? = null var lud16: String? = null - fun countMemory(): Long = + fun countMemory(): Int = 20 * pointerSizeInBytes + // 20 fields, 4 bytes for each reference - (name?.bytesUsedInMemory() ?: 0L) + - (username?.bytesUsedInMemory() ?: 0L) + - (displayName?.bytesUsedInMemory() ?: 0L) + - (picture?.bytesUsedInMemory() ?: 0L) + - (banner?.bytesUsedInMemory() ?: 0L) + - (image?.bytesUsedInMemory() ?: 0L) + - (website?.bytesUsedInMemory() ?: 0L) + - (about?.bytesUsedInMemory() ?: 0L) + - (subscription?.bytesUsedInMemory() ?: 0L) + - (acceptsNutZaps?.bytesUsedInMemory() ?: 0L) + - (supportsEncryption?.bytesUsedInMemory() ?: 0L) + - (personalized?.bytesUsedInMemory() ?: 0L) + // A Boolean has 8 bytes of header, plus 1 byte of payload, for a total of 9 bytes of information. The JVM then rounds it up to the next multiple of 8. so the one instance of java.lang.Boolean takes up 16 bytes of memory. - (amount?.bytesUsedInMemory() ?: 0L) + - (nip05?.bytesUsedInMemory() ?: 0L) + - (domain?.bytesUsedInMemory() ?: 0L) + - (lud06?.bytesUsedInMemory() ?: 0L) + - (lud16?.bytesUsedInMemory() ?: 0L) + (name?.bytesUsedInMemory() ?: 0) + + (username?.bytesUsedInMemory() ?: 0) + + (displayName?.bytesUsedInMemory() ?: 0) + + (picture?.bytesUsedInMemory() ?: 0) + + (banner?.bytesUsedInMemory() ?: 0) + + (image?.bytesUsedInMemory() ?: 0) + + (website?.bytesUsedInMemory() ?: 0) + + (about?.bytesUsedInMemory() ?: 0) + + (subscription?.bytesUsedInMemory() ?: 0) + + (acceptsNutZaps?.bytesUsedInMemory() ?: 0) + + (supportsEncryption?.bytesUsedInMemory() ?: 0) + + (personalized?.bytesUsedInMemory() ?: 0) + // A Boolean has 8 bytes of header, plus 1 byte of payload, for a total of 9 bytes of information. The JVM then rounds it up to the next multiple of 8. so the one instance of java.lang.Boolean takes up 16 bytes of memory. + (amount?.bytesUsedInMemory() ?: 0) + + (nip05?.bytesUsedInMemory() ?: 0) + + (domain?.bytesUsedInMemory() ?: 0) + + (lud06?.bytesUsedInMemory() ?: 0) + + (lud16?.bytesUsedInMemory() ?: 0) fun anyName(): String? = displayName ?: name ?: username diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt index d7ca0d2ac..ea94b7466 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt @@ -43,7 +43,7 @@ class NIP90ContentDiscoveryResponseEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var events: List? = null - override fun countMemory(): Long = + override fun countMemory(): Int = super.countMemory() + pointerSizeInBytes + (events?.sumOf { it.bytesUsedInMemory() } ?: 0) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip99Classifieds/ClassifiedsEvent.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip99Classifieds/ClassifiedsEvent.kt index b0222fca9..6e80ba713 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip99Classifieds/ClassifiedsEvent.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip99Classifieds/ClassifiedsEvent.kt @@ -70,7 +70,18 @@ class ClassifiedsEvent( fun location() = tags.firstNotNullOfOrNull(LocationTag::parse) - override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + override fun publishedAt(): Long? { + val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) + + if (publishedAt == null) return null + + // removes posts in the future. + return if (publishedAt <= createdAt) { + publishedAt + } else { + null + } + } fun categories() = tags.hashtags() diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/StringUtils.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/StringUtils.kt index 557ca3fdb..c88a46dfd 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/StringUtils.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/StringUtils.kt @@ -24,13 +24,13 @@ import kotlin.math.min val pointerSizeInBytes = 4 -fun String.bytesUsedInMemory(): Long = (8 * (((this.length * 2L) + 45) / 8)) +fun String.bytesUsedInMemory(): Int = (8 * (((this.length * 2) + 45) / 8)) -fun Long.bytesUsedInMemory(): Long = 8 +fun Long.bytesUsedInMemory(): Int = 8 -fun Int.bytesUsedInMemory(): Long = 4 +fun Int.bytesUsedInMemory(): Int = 4 -fun Boolean.bytesUsedInMemory(): Long = 8 +fun Boolean.bytesUsedInMemory(): Int = 8 fun String.containsIgnoreCase(term: String): Boolean { if (term.isEmpty()) return true // Empty string is contained