Migrates Desktop App to the new metadata cache.

This commit is contained in:
Vitor Pamplona
2026-02-04 15:38:16 -05:00
parent d2f77ed521
commit e6f7a543cd
6 changed files with 37 additions and 27 deletions
@@ -54,7 +54,7 @@ class ThreadAssembler(
// Check to see if there is an error in the tag and the root has replies // Check to see if there is an error in the tag and the root has replies
val rootNote = cache.getNoteIfExists(markedAsRoot) as? Note val rootNote = cache.getNoteIfExists(markedAsRoot) as? Note
if (rootNote?.replyTo?.isEmpty() == true) { if (rootNote?.replyTo?.isEmpty() == true) {
return cache.checkGetOrCreateNote(markedAsRoot) as? Note return cache.checkGetOrCreateNote(markedAsRoot)
} }
} }
@@ -88,7 +88,7 @@ class ThreadAssembler(
) )
fun findRoot(noteId: String): Note? { fun findRoot(noteId: String): Note? {
val note = cache.checkGetOrCreateNote(noteId) as? Note ?: return null val note = cache.checkGetOrCreateNote(noteId) ?: return null
return if (note.event != null) { return if (note.event != null) {
val thread = OnlyLatestVersionSet() val thread = OnlyLatestVersionSet()
@@ -102,7 +102,7 @@ class ThreadAssembler(
fun findThreadFor(noteId: String): ThreadInfo? { fun findThreadFor(noteId: String): ThreadInfo? {
checkNotInMainThread() checkNotInMainThread()
val note = cache.checkGetOrCreateNote(noteId) as? Note ?: return null val note = cache.checkGetOrCreateNote(noteId) ?: return null
return if (note.event != null) { return if (note.event != null) {
val thread = OnlyLatestVersionSet() val thread = OnlyLatestVersionSet()
@@ -41,7 +41,11 @@ class MetadataPreloader(
*/ */
fun preloadForUsers(users: Collection<User>) { fun preloadForUsers(users: Collection<User>) {
users.forEach { user -> users.forEach { user ->
val metadata = user.info val metadata =
user
.metadata()
.flow.value
?.info
if (metadata != null) { if (metadata != null) {
// Already have metadata, prefetch avatar // Already have metadata, prefetch avatar
metadata.picture?.let { avatarUrl -> metadata.picture?.let { avatarUrl ->
@@ -58,7 +62,11 @@ class MetadataPreloader(
* Queue a single user for metadata preloading. * Queue a single user for metadata preloading.
*/ */
fun preloadForUser(user: User) { fun preloadForUser(user: User) {
val metadata = user.info val metadata =
user
.metadata()
.flow.value
?.info
if (metadata != null) { if (metadata != null) {
metadata.picture?.let { avatarUrl -> metadata.picture?.let { avatarUrl ->
imagePrefetcher?.prefetch(avatarUrl) imagePrefetcher?.prefetch(avatarUrl)
@@ -73,7 +81,7 @@ class MetadataPreloader(
* Triggers avatar image prefetch. * Triggers avatar image prefetch.
*/ */
fun onMetadataReceived(user: User) { fun onMetadataReceived(user: User) {
user.info?.picture?.let { avatarUrl -> user.metadata().flow.value?.info?.picture?.let { avatarUrl ->
imagePrefetcher?.prefetch(avatarUrl) imagePrefetcher?.prefetch(avatarUrl)
} }
} }
@@ -83,7 +91,7 @@ class MetadataPreloader(
*/ */
fun prefetchAvatars(users: Collection<User>) { fun prefetchAvatars(users: Collection<User>) {
users.forEach { user -> users.forEach { user ->
user.info?.picture?.let { avatarUrl -> user.metadata().flow.value?.info?.picture?.let { avatarUrl ->
imagePrefetcher?.prefetch(avatarUrl) imagePrefetcher?.prefetch(avatarUrl)
} }
} }
@@ -107,11 +107,11 @@ class DesktopLocalCache : ICacheProvider {
fun consumeMetadata(event: MetadataEvent) { fun consumeMetadata(event: MetadataEvent) {
val user = getOrCreateUser(event.pubKey) val user = getOrCreateUser(event.pubKey)
// Only update if newer if (user.metadata().shouldUpdateWith(event)) {
val currentMetadata = user.latestMetadata val newUserMetadata = event.contactMetaData()
if (currentMetadata == null || event.createdAt > currentMetadata.createdAt) { if (newUserMetadata != null) {
user.latestMetadata = event user.updateUserInfo(newUserMetadata, event, null)
user.info = event.contactMetaData() }
} }
} }
@@ -333,7 +333,11 @@ fun FeedScreen(
// Only fetch metadata for users we don't have yet // Only fetch metadata for users we don't have yet
val missingPubkeys = val missingPubkeys =
zapSenderPubkeys.filter { pubkey -> zapSenderPubkeys.filter { pubkey ->
localCache.getUserIfExists(pubkey)?.info == null localCache
.getUserIfExists(pubkey)
?.metadataOrNull()
?.flow
?.value == null
} }
if (missingPubkeys.isEmpty()) { if (missingPubkeys.isEmpty()) {
return@rememberSubscription null return@rememberSubscription null
@@ -449,7 +453,11 @@ fun FeedScreen(
// Only fetch metadata for users we don't have yet // Only fetch metadata for users we don't have yet
val missingPubkeys = val missingPubkeys =
authorPubkeys.filter { pubkey -> authorPubkeys.filter { pubkey ->
localCache.getUserIfExists(pubkey)?.info == null localCache
.getUserIfExists(pubkey)
?.metadataOrNull()
?.flow
?.value == null
} }
if (missingPubkeys.isEmpty()) { if (missingPubkeys.isEmpty()) {
return@rememberSubscription null return@rememberSubscription null
@@ -70,14 +70,12 @@ import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache
import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager
import com.vitorpamplona.amethyst.desktop.nwc.NwcPaymentHandler import com.vitorpamplona.amethyst.desktop.nwc.NwcPaymentHandler
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArrayOrNull
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.nip19Bech32.entities.NNote import com.vitorpamplona.quartz.nip19Bech32.entities.NNote
import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
@@ -149,12 +147,8 @@ fun getDisplayName(
pubKey: String, pubKey: String,
localCache: DesktopLocalCache, localCache: DesktopLocalCache,
): String { ): String {
val user = localCache.getUserIfExists(pubKey) val user = localCache.getUserIfExists(pubKey) ?: return pubKey.take(12)
return user?.info?.bestName() return user.toBestDisplayName()
?: pubKey.hexToByteArrayOrNull()?.toNpub()?.let { npub ->
npub.take(12) + "..." + npub.takeLast(6)
}
?: pubKey.take(12) + "..."
} }
/** /**
@@ -308,7 +302,7 @@ fun ZapReceiptsDialog(
.distinct() .distinct()
.filter { pubKey -> .filter { pubKey ->
val user = localCache.getUserIfExists(pubKey) val user = localCache.getUserIfExists(pubKey)
user?.info == null user?.metadataOrNull()?.flow?.value == null
} }
if (pubKeysNeedingMetadata.isNotEmpty()) { if (pubKeysNeedingMetadata.isNotEmpty()) {
@@ -925,7 +919,7 @@ private suspend fun zapNote(
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
// Get author's lightning address from cache // Get author's lightning address from cache
var user = localCache.getUserIfExists(event.pubKey) var user = localCache.getUserIfExists(event.pubKey)
var lnAddress = user?.info?.lud16 ?: user?.info?.lud06 var lnAddress = user?.lnAddress()
// TODO: Use UserFinderFilterAssemblerSubscription pattern from Amethyst // TODO: Use UserFinderFilterAssemblerSubscription pattern from Amethyst
// to proactively load metadata when zap button is displayed. // to proactively load metadata when zap button is displayed.
@@ -1068,7 +1062,7 @@ private suspend fun fetchUserLightningAddress(
if (event is MetadataEvent && !resumed) { if (event is MetadataEvent && !resumed) {
localCache.consumeMetadata(event) localCache.consumeMetadata(event)
val user = localCache.getUserIfExists(pubKey) val user = localCache.getUserIfExists(pubKey)
val lnAddress = user?.info?.lud16 ?: user?.info?.lud06 val lnAddress = user?.lnAddress()
if (lnAddress != null && !resumed) { if (lnAddress != null && !resumed) {
resumed = true resumed = true
timeoutJob.cancel() timeoutJob.cancel()
@@ -84,8 +84,8 @@ fun LongFormCard(
onAuthorClick: (String) -> Unit = {}, onAuthorClick: (String) -> Unit = {},
onClick: () -> Unit = {}, onClick: () -> Unit = {},
) { ) {
val author = localCache.getUserIfExists(event.pubKey) val author = localCache.getOrCreateUser(event.pubKey)
val authorName = author?.info?.bestName() ?: event.pubKey.take(8) val authorName = author.toBestDisplayName()
val publishedAt = event.publishedAt() ?: event.createdAt val publishedAt = event.publishedAt() ?: event.createdAt
Card( Card(