From da6dae31855dc35af8dfe1963adb113548712fe4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 15 Apr 2026 19:55:15 +0000 Subject: [PATCH] fix(amethyst): show caller display name in WebRTC call notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The incoming call push notification was falling back to the raw caller hex (or short npub) whenever the caller's User object hadn't been created or their metadata hadn't been loaded yet — which is the common case when the app is woken up by a push notification with a fresh, empty in-memory cache. Switch notifyIncomingCall to use LocalCache.getOrCreateUser, and if the caller's metadata hasn't been loaded, briefly subscribe via UserFinderQueryState (same pattern used by wakeUpFor) and wait up to 5 seconds for metadata to arrive before building the notification. This lets toBestDisplayName() resolve to the user's real name in the notification popup. Also tighten NotificationUtils.showIncomingCallNotification to use getOrCreateUser so that in-app calls fall back to the user's npub display rather than a raw hex prefix when metadata is unavailable. --- .../EventNotificationConsumer.kt | 29 +++++++++++++++++-- .../notifications/NotificationUtils.kt | 6 ++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt index 739a71ed9..8216d9b68 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt @@ -71,8 +71,10 @@ import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull import java.math.BigDecimal import kotlin.coroutines.cancellation.CancellationException @@ -699,11 +701,32 @@ class EventNotificationConsumer( if (TimeUtils.now() - event.createdAt > CallManager.MAX_EVENT_AGE_SECONDS) return - val callerUser = LocalCache.getUserIfExists(event.pubKey) - val callerName = callerUser?.toBestDisplayName() ?: event.pubKey.take(8) + "..." + val callerUser = LocalCache.getOrCreateUser(event.pubKey) + + // If the caller's metadata hasn't been loaded yet (e.g. fresh process from + // a push notification), briefly subscribe to the user finder so we can + // resolve the user's display name instead of showing the raw pubkey. + if (callerUser.metadataOrNull()?.bestName() == null) { + val authorState = UserFinderQueryState(callerUser, account) + try { + Amethyst.instance.sources.userFinder + .subscribe(authorState) + withTimeoutOrNull(5_000L) { + callerUser + .metadata() + .flow + .first { it?.info?.bestName() != null } + } + } finally { + Amethyst.instance.sources.userFinder + .unsubscribe(authorState) + } + } + + val callerName = callerUser.toBestDisplayName() val callerBitmap = - callerUser?.profilePicture()?.let { pictureUrl -> + callerUser.profilePicture()?.let { pictureUrl -> kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { try { val request = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationUtils.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationUtils.kt index 2c6f66497..53ffd2e60 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationUtils.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationUtils.kt @@ -630,12 +630,12 @@ object NotificationUtils { callerPubKey: String, context: Context, ) { - val callerUser = LocalCache.getUserIfExists(callerPubKey) - val callerName = callerUser?.toBestDisplayName() ?: callerPubKey.take(8) + "..." + val callerUser = LocalCache.getOrCreateUser(callerPubKey) + val callerName = callerUser.toBestDisplayName() val uri = "nostr:${callerPubKey.hexToByteArray().toNpub()}" val callerBitmap = - callerUser?.profilePicture()?.let { pictureUrl -> + callerUser.profilePicture()?.let { pictureUrl -> withContext(Dispatchers.IO) { try { val request =