diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index ee83c10a5..c7421c35f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -1202,7 +1202,7 @@ class Account( Client.send(signedEvent, relayList = relayList) LocalCache.consume(signedEvent, null) - return LocalCache.notes[signedEvent.id] + return LocalCache.getNoteIfExists(signedEvent.id) } fun consumeNip95( @@ -1212,7 +1212,7 @@ class Account( LocalCache.consume(data, null) LocalCache.consume(signedEvent, null) - return LocalCache.notes[signedEvent.id] + return LocalCache.getNoteIfExists(signedEvent.id) } fun sendNip95( @@ -1232,7 +1232,7 @@ class Account( Client.send(signedEvent, relayList = relayList) LocalCache.consume(signedEvent, null) - LocalCache.notes[signedEvent.id]?.let { onReady(it) } + LocalCache.getNoteIfExists(signedEvent.id)?.let { onReady(it) } } fun createHeader( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index e21342a4f..3b45fd55a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -102,6 +102,7 @@ import com.vitorpamplona.quartz.events.TextNoteEvent import com.vitorpamplona.quartz.events.VideoHorizontalEvent import com.vitorpamplona.quartz.events.VideoVerticalEvent import com.vitorpamplona.quartz.events.WikiNoteEvent +import com.vitorpamplona.quartz.utils.TimeUtils import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList @@ -118,18 +119,31 @@ import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.concurrent.ConcurrentHashMap +import kotlin.time.measureTimedValue object LocalCache { val antiSpam = AntiSpamFilter() - val users = ConcurrentHashMap(5000) - val notes = ConcurrentHashMap(5000) + private val users = ConcurrentHashMap(5000) + private val notes = ConcurrentHashMap(5000) val channels = ConcurrentHashMap() val addressables = ConcurrentHashMap(100) val awaitingPaymentRequests = ConcurrentHashMap Unit>>(10) + var noteListCache: List = emptyList() + var userListCache: List = emptyList() + + fun updateListCache() { + val (value, elapsed) = + measureTimedValue { + noteListCache = ArrayList(notes.values) + userListCache = ArrayList(users.values) + } + Log.d("LocalCache", "UpdateListCache $elapsed") + } + fun checkGetOrCreateUser(key: String): User? { // checkNotInMainThread() @@ -910,7 +924,7 @@ object LocalCache { event .deleteEvents() - .mapNotNull { notes[it] } + .mapNotNull { getNoteIfExists(it) } .forEach { deleteNote -> // must be the same author if (deleteNote.author?.pubkeyHex == event.pubKey) { @@ -1547,11 +1561,14 @@ object LocalCache { val key = decodePublicKeyAsHexOrNull(username) - if (key != null && users[key] != null) { - return listOfNotNull(users[key]) + if (key != null) { + val user = getUserIfExists(key) + if (user != null) { + return listOfNotNull(user) + } } - return users.values.filter { + return userListCache.filter { (it.anyNameStartsWith(username)) || it.pubkeyHex.startsWith(username, true) || it.pubkeyNpub().startsWith(username, true) @@ -1569,11 +1586,14 @@ object LocalCache { null } - if (key != null && (notes[key] ?: addressables[key]) != null) { - return listOfNotNull(notes[key] ?: addressables[key]) + if (key != null) { + val note = getNoteIfExists(key) + if (note != null) { + return listOfNotNull(note) + } } - return notes.values.filter { + return noteListCache.filter { ( it.event !is GenericRepostEvent && it.event !is RepostEvent && @@ -1652,28 +1672,29 @@ object LocalCache { suspend fun findEarliestOtsForNote(note: Note): Long? { checkNotInMainThread() - val validOts = - notes - .mapNotNull { - val noteEvent = it.value.event - if ((noteEvent is OtsEvent && noteEvent.isTaggedEvent(note.idHex) && !noteEvent.isExpired())) { - noteEvent.verifiedTime - } else { - null + var minTime: Long? = null + val time = TimeUtils.now() + + noteListCache.forEach { item -> + val noteEvent = item.event + if ((noteEvent is OtsEvent && noteEvent.isTaggedEvent(note.idHex) && !noteEvent.isExpirationBefore(time))) { + noteEvent.verifiedTime?.let { stampedTime -> + if (minTime == null || stampedTime < (minTime ?: Long.MAX_VALUE)) { + minTime = stampedTime } } + } + } - if (validOts.isEmpty()) return null - - return validOts.minBy { it } + return minTime } fun cleanObservers() { - notes.forEach { it.value.clearLive() } + noteListCache.forEach { it.clearLive() } addressables.forEach { it.value.clearLive() } - users.forEach { it.value.clearLive() } + userListCache.forEach { it.clearLive() } } fun pruneOldAndHiddenMessages(account: Account) { @@ -1699,8 +1720,8 @@ object LocalCache { } } - users.forEach { userPair -> - userPair.value.privateChatrooms.values.map { + userListCache.forEach { userPair -> + userPair.privateChatrooms.values.map { val toBeRemoved = it.pruneMessagesToTheLatestOnly() val childrenToBeRemoved = mutableListOf() @@ -1715,7 +1736,7 @@ object LocalCache { if (toBeRemoved.size > 1) { println( - "PRUNE: ${toBeRemoved.size} private messages with ${userPair.value.toBestDisplayName()} removed. ${it.roomMessages.size} kept", + "PRUNE: ${toBeRemoved.size} private messages with ${userPair.toBestDisplayName()} removed. ${it.roomMessages.size} kept", ) } } @@ -1724,9 +1745,9 @@ object LocalCache { fun prunePastVersionsOfReplaceables() { val toBeRemoved = - notes + noteListCache .filter { - val noteEvent = it.value.event + val noteEvent = it.event if (noteEvent is AddressableEvent) { noteEvent.createdAt() < (addressables[noteEvent.address().toTag()]?.event?.createdAt() ?: 0) @@ -1734,7 +1755,6 @@ object LocalCache { false } } - .values val childrenToBeRemoved = mutableListOf() @@ -1759,24 +1779,23 @@ object LocalCache { checkNotInMainThread() val toBeRemoved = - notes + noteListCache .filter { ( - (it.value.event is TextNoteEvent && !it.value.isNewThread()) || - it.value.event is ReactionEvent || - it.value.event is LnZapEvent || - it.value.event is LnZapRequestEvent || - it.value.event is ReportEvent || - it.value.event is GenericRepostEvent + (it.event is TextNoteEvent && !it.isNewThread()) || + it.event is ReactionEvent || + it.event is LnZapEvent || + it.event is LnZapRequestEvent || + it.event is ReportEvent || + it.event is GenericRepostEvent ) && - it.value.replyTo?.any { it.liveSet?.isInUse() == true } != true && - it.value.liveSet?.isInUse() != true && // don't delete if observing. - it.value.author?.pubkeyHex !in + it.replyTo?.any { it.liveSet?.isInUse() == true } != true && + it.liveSet?.isInUse() != true && // don't delete if observing. + it.author?.pubkeyHex !in accounts && // don't delete if it is the logged in account - it.value.event?.isTaggedUsers(accounts) != + it.event?.isTaggedUsers(accounts) != true // don't delete if it's a notification to the logged in user } - .values val childrenToBeRemoved = mutableListOf() @@ -1842,7 +1861,8 @@ object LocalCache { fun pruneExpiredEvents() { checkNotInMainThread() - val toBeRemoved = notes.filter { it.value.event?.isExpired() == true }.values + val now = TimeUtils.now() + val toBeRemoved = noteListCache.filter { it.event?.isExpirationBefore(now) == true } val childrenToBeRemoved = mutableListOf() @@ -1868,7 +1888,7 @@ object LocalCache { ?.hiddenUsers ?.map { userHex -> ( - notes.values.filter { it.event?.pubKey() == userHex } + + noteListCache.filter { it.event?.pubKey() == userHex } + addressables.values.filter { it.event?.pubKey() == userHex } ) .toSet() @@ -1890,7 +1910,7 @@ object LocalCache { checkNotInMainThread() var removingContactList = 0 - users.values.forEach { + userListCache.forEach { if ( it.pubkeyHex !in loggedIn && (it.liveSet == null || it.liveSet?.isInUse() == false) && @@ -2044,6 +2064,10 @@ class LocalCacheLiveData { private val bundler = BundledInsert(1000, Dispatchers.IO) fun invalidateData(newNote: Note) { - bundler.invalidateList(newNote) { bundledNewNotes -> _newEventBundles.emit(bundledNewNotes) } + bundler.invalidateList(newNote) { + bundledNewNotes -> + _newEventBundles.emit(bundledNewNotes) + LocalCache.updateListCache() + } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt index 7a60c6d04..30ded640a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt @@ -139,10 +139,10 @@ class User(val pubkeyHex: String) { // Update Followers of the past user list // Update Followers of the new contact list (oldContactListEvent)?.unverifiedFollowKeySet()?.forEach { - LocalCache.users[it]?.liveSet?.innerFollowers?.invalidateData() + LocalCache.getUserIfExists(it)?.liveSet?.innerFollowers?.invalidateData() } (latestContactList)?.unverifiedFollowKeySet()?.forEach { - LocalCache.users[it]?.liveSet?.innerFollowers?.invalidateData() + LocalCache.getUserIfExists(it)?.liveSet?.innerFollowers?.invalidateData() } liveSet?.innerRelays?.invalidateData() @@ -363,7 +363,7 @@ class User(val pubkeyHex: String) { } suspend fun transientFollowerCount(): Int { - return LocalCache.users.count { it.value.latestContactList?.isTaggedUser(pubkeyHex) ?: false } + return LocalCache.userListCache.count { it.latestContactList?.isTaggedUser(pubkeyHex) ?: false } } fun cachedFollowingKeySet(): Set { @@ -387,7 +387,7 @@ class User(val pubkeyHex: String) { } suspend fun cachedFollowerCount(): Int { - return LocalCache.users.count { it.value.latestContactList?.isTaggedUser(pubkeyHex) ?: false } + return LocalCache.userListCache.count { it.latestContactList?.isTaggedUser(pubkeyHex) ?: false } } fun hasSentMessagesTo(key: ChatroomKey?): Boolean { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt index 5dddfe075..274bb5c87 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt @@ -112,7 +112,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { event.pubKey != acc.userProfile().pubkeyHex ) { // from the user - val chatNote = LocalCache.notes[event.id] ?: return + val chatNote = LocalCache.getNoteIfExists(event.id) ?: return val chatRoom = event.chatroomKey(acc.keyPair.pubKey.toHexKey()) val followingKeySet = acc.followingKeySet() @@ -145,7 +145,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { event: PrivateDmEvent, acc: Account, ) { - val note = LocalCache.notes[event.id] ?: return + val note = LocalCache.getNoteIfExists(event.id) ?: return // old event being re-broadcast if (event.createdAt < TimeUtils.fiveMinutesAgo()) return @@ -184,7 +184,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { event: LnZapEvent, acc: Account, ) { - val noteZapEvent = LocalCache.notes[event.id] ?: return + val noteZapEvent = LocalCache.getNoteIfExists(event.id) ?: return // old event being re-broadcast if (event.createdAt < TimeUtils.fiveMinutesAgo()) return diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/MarkdownParser.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/MarkdownParser.kt index a4714f773..4fe9d706b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/MarkdownParser.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/MarkdownParser.kt @@ -66,19 +66,19 @@ class MarkdownParser { private fun getDisplayNameFromNip19(nip19: Nip19Bech32.Return): Pair? { if (nip19.type == Nip19Bech32.Type.USER) { - LocalCache.users[nip19.hex]?.let { + LocalCache.getUserIfExists(nip19.hex)?.let { return Pair(it.toBestDisplayName(), it.pubkeyNpub()) } } else if (nip19.type == Nip19Bech32.Type.NOTE) { - LocalCache.notes[nip19.hex]?.let { + LocalCache.getNoteIfExists(nip19.hex)?.let { return Pair(it.idDisplayNote(), it.toNEvent()) } } else if (nip19.type == Nip19Bech32.Type.ADDRESS) { - LocalCache.addressables[nip19.hex]?.let { + LocalCache.getAddressableNoteIfExists(nip19.hex)?.let { return Pair(it.idDisplayNote(), it.toNEvent()) } } else if (nip19.type == Nip19Bech32.Type.EVENT) { - LocalCache.notes[nip19.hex]?.let { + LocalCache.getNoteIfExists(nip19.hex)?.let { return Pair(it.idDisplayNote(), it.toNEvent()) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/CommunityFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/CommunityFeedFilter.kt index 5a6cc7288..767d31f51 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/CommunityFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/CommunityFeedFilter.kt @@ -33,7 +33,7 @@ class CommunityFeedFilter(val note: AddressableNote, val account: Account) : } override fun feed(): List { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt index b902abbb6..65d249e5a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt @@ -36,7 +36,7 @@ class GeoHashFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil } override fun feed(): List { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt index b51d4c2a4..4eaa3778f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt @@ -36,7 +36,7 @@ class HashtagFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil } override fun feed(): List { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeConversationsFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeConversationsFeedFilter.kt index 178b970f7..15d977db7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeConversationsFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeConversationsFeedFilter.kt @@ -45,7 +45,7 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeNewThreadFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeNewThreadFeedFilter.kt index 07cc506ed..ac87c3507 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeNewThreadFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/HomeNewThreadFeedFilter.kt @@ -50,7 +50,7 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter() } override fun feed(): List { - val notes = innerApplyFilter(LocalCache.notes.values, true) + val notes = innerApplyFilter(LocalCache.noteListCache, true) val longFormNotes = innerApplyFilter(LocalCache.addressables.values, false) return sort(notes + longFormNotes) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt index d4acf44f0..a92a0d4a5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/NotificationFeedFilter.kt @@ -52,7 +52,7 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter() } override fun feed(): List { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileConversationsFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileConversationsFeedFilter.kt index 1a4171d1c..81953dd82 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileConversationsFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileConversationsFeedFilter.kt @@ -36,7 +36,7 @@ class UserProfileConversationsFeedFilter(val user: User, val account: Account) : } override fun feed(): List { - return sort(innerApplyFilter(LocalCache.notes.values)) + return sort(innerApplyFilter(LocalCache.noteListCache)) } override fun applyFilter(collection: Set): Set { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileFollowersFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileFollowersFeedFilter.kt index 3c7a11e79..616be35e7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileFollowersFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileFollowersFeedFilter.kt @@ -30,7 +30,7 @@ class UserProfileFollowersFeedFilter(val user: User, val account: Account) : Fee } override fun feed(): List { - return LocalCache.users.values.filter { it.isFollowing(user) && !account.isHidden(it) } + return LocalCache.userListCache.filter { it.isFollowing(user) && !account.isHidden(it) } } override fun limit() = 400 diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileNewThreadFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileNewThreadFeedFilter.kt index 04dac8a9d..9acff0d9b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileNewThreadFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileNewThreadFeedFilter.kt @@ -41,7 +41,7 @@ class UserProfileNewThreadFeedFilter(val user: User, val account: Account) : } override fun feed(): List { - val notes = innerApplyFilter(LocalCache.notes.values) + val notes = innerApplyFilter(LocalCache.noteListCache) val longFormNotes = innerApplyFilter(LocalCache.addressables.values) return sort(notes + longFormNotes) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/VideoFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/VideoFeedFilter.kt index 721cb736e..17f3dc24e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/VideoFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/VideoFeedFilter.kt @@ -43,7 +43,7 @@ class VideoFeedFilter(val account: Account) : AdditiveFeedFilter() { } override fun feed(): List { - val notes = innerApplyFilter(LocalCache.notes.values) + val notes = innerApplyFilter(LocalCache.noteListCache) return sort(notes) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt index 3e314bfbc..c5dfcf639 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt @@ -979,11 +979,11 @@ fun debugState(context: Context) { Log.d( "STATE DUMP", "Notes: " + - LocalCache.notes.filter { it.value.liveSet != null }.size + + LocalCache.noteListCache.filter { it.liveSet != null }.size + " / " + - LocalCache.notes.filter { it.value.event != null }.size + + LocalCache.noteListCache.filter { it.event != null }.size + " / " + - LocalCache.notes.size, + LocalCache.noteListCache.size, ) Log.d( "STATE DUMP", @@ -997,21 +997,21 @@ fun debugState(context: Context) { Log.d( "STATE DUMP", "Users: " + - LocalCache.users.filter { it.value.liveSet != null }.size + + LocalCache.userListCache.filter { it.liveSet != null }.size + " / " + - LocalCache.users.filter { it.value.info?.latestMetadata != null }.size + + LocalCache.userListCache.filter { it.info?.latestMetadata != null }.size + " / " + - LocalCache.users.size, + LocalCache.userListCache.size, ) Log.d( "STATE DUMP", "Memory used by Events: " + - LocalCache.notes.values.sumOf { it.event?.countMemory() ?: 0 } / (1024 * 1024) + + LocalCache.noteListCache.sumOf { it.event?.countMemory() ?: 0 } / (1024 * 1024) + " MB", ) - LocalCache.notes.values + LocalCache.noteListCache .groupBy { it.event?.kind() } .forEach { Log.d("STATE DUMP", "Kind ${it.key}: \t${it.value.size} elements ") } LocalCache.addressables.values diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt index 5358eab70..0d396edf5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt @@ -232,7 +232,7 @@ class UserReactionsViewModel(val account: Account) : ViewModel() { val replies = mutableMapOf() val takenIntoAccount = mutableSetOf() - LocalCache.notes.values.forEach { + LocalCache.noteListCache.forEach { val noteEvent = it.event if (noteEvent != null && !takenIntoAccount.contains(noteEvent.id())) { if (noteEvent is ReactionEvent) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index d80b7f326..f35f36ff6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -179,7 +179,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { val event = (zapEvent.event as LnZapEvent) val author = event.zappedAuthor().firstNotNullOfOrNull { - LocalCache.users[it] // don't create user if it doesn't exist + LocalCache.getUserIfExists(it) // don't create user if it doesn't exist } if (author != null) { val zapRequest = author.zaps.filter { it.value == zapEvent }.keys.firstOrNull() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 1f53559a5..52329ea95 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -96,6 +96,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import java.util.Locale import kotlin.coroutines.resume @@ -897,18 +898,22 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View return LocalCache.addressables[key] } - fun findStatusesForUser( + suspend fun findStatusesForUser( myUser: User, onResult: (ImmutableList) -> Unit, ) { - viewModelScope.launch(Dispatchers.IO) { onResult(LocalCache.findStatusesForUser(myUser)) } + withContext(Dispatchers.IO) { + onResult(LocalCache.findStatusesForUser(myUser)) + } } - fun findOtsEventsForNote( + suspend fun findOtsEventsForNote( note: Note, onResult: (Long?) -> Unit, ) { - viewModelScope.launch(Dispatchers.IO) { onResult(LocalCache.findEarliestOtsForNote(note)) } + withContext(Dispatchers.IO) { + onResult(LocalCache.findEarliestOtsForNote(note)) + } } private suspend fun checkGetOrCreateChannel(key: HexKey): Channel? { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Lud06.kt b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Lud06.kt index c2cbc21b9..d4d2bb178 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Lud06.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Lud06.kt @@ -33,11 +33,14 @@ class Lud06 { val url = toLnUrlp(str) val matcher = LNURLP_PATTERN.matcher(url) - matcher.find() - val domain = matcher.group(2) - val username = matcher.group(3) + if (matcher.find()) { + val domain = matcher.group(2) + val username = matcher.group(3) - "$username@$domain" + "$username@$domain" + } else { + null + } } catch (t: Throwable) { t.printStackTrace() Log.w("Lud06ToLud16", "Fail to convert LUD06 to LUD16", t) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt index dc8461040..aba51ab92 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt @@ -207,6 +207,8 @@ open class Event( override fun isExpired() = (expiration() ?: Long.MAX_VALUE) < TimeUtils.now() + override fun isExpirationBefore(time: Long) = (expiration() ?: Long.MAX_VALUE) < time + override fun getTagOfAddressableKind(kind: Int): ATag? { val kindStr = kind.toString() val aTag = diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt index a847eae0b..55f0b4b2b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt @@ -137,5 +137,7 @@ interface EventInterface { fun isExpired(): Boolean + fun isExpirationBefore(time: Long): Boolean + fun hasZapSplitSetup(): Boolean }