From 76275cc9e1dce1f40dae7bf9237e127f56c23eba Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Thu, 19 Mar 2026 14:24:58 -0400 Subject: [PATCH 1/3] add username to plaintext notifications push title --- go/bind/keybase.go | 4 ++-- go/bind/notifications.go | 7 ++++++- .../io/keybase/ossifrage/KBPushNotifier.kt | 5 +++-- shared/ios/Keybase/Pusher.swift | 20 ++++++++++++++----- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/go/bind/keybase.go b/go/bind/keybase.go index 46a598215680..c5bd8f3aceeb 100644 --- a/go/bind/keybase.go +++ b/go/bind/keybase.go @@ -70,7 +70,7 @@ func log(format string, args ...interface{}) { } type PushNotifier interface { - LocalNotification(ident string, msg string, badgeCount int, soundName string, convID string, typ string) + LocalNotification(ident string, title string, msg string, badgeCount int, soundName string, convID string, typ string) DisplayChatNotification(notification *ChatNotification) } @@ -688,7 +688,7 @@ func pushPendingMessageFailure(obrs []chat1.OutboxRecord, pusher PushNotifier) { for _, obr := range obrs { if topicType := obr.Msg.ClientHeader.Conv.TopicType; obr.Msg.IsBadgableType() && topicType == chat1.TopicType_CHAT { kbCtx.Log.Debug("pushPendingMessageFailure: pushing convID: %s", obr.ConvID) - pusher.LocalNotification("failedpending", + pusher.LocalNotification("failedpending", "", "Heads up! Your message hasn't sent yet, tap here to retry.", -1, "default", obr.ConvID.String(), "chat.failedpending") return diff --git a/go/bind/notifications.go b/go/bind/notifications.go index 5ce690763a44..3362ae707ef2 100644 --- a/go/bind/notifications.go +++ b/go/bind/notifications.go @@ -55,6 +55,8 @@ type ChatNotification struct { IsPlaintext bool SoundName string BadgeCount int + // Title is the notification title, e.g. "username@keybase" + Title string } func HandlePostTextReply(strConvID, tlfName string, intMessageID int, body string) (err error) { @@ -144,6 +146,7 @@ func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender str return err } + currentUsername := string(kbCtx.Env.GetUsername()) chatNotification := ChatNotification{ IsPlaintext: displayPlaintext, Message: &Message{ @@ -156,10 +159,12 @@ func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender str TopicName: conv.Info.TopicName, TlfName: conv.Info.TlfName, IsGroupConversation: len(conv.Info.Participants) > 2, - ConversationName: utils.FormatConversationName(conv.Info, string(kbCtx.Env.GetUsername())), + ConversationName: utils.FormatConversationName(conv.Info, currentUsername), SoundName: soundName, BadgeCount: badgeCount, + Title: fmt.Sprintf("%s@keybase", currentUsername), } + kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: title=%s", chatNotification.Title) msgUnboxed, err := mp.UnboxPushNotification(ctx, uid, convID, membersType, body) if err == nil && msgUnboxed.IsValid() { diff --git a/shared/android/app/src/main/java/io/keybase/ossifrage/KBPushNotifier.kt b/shared/android/app/src/main/java/io/keybase/ossifrage/KBPushNotifier.kt index 2d071a144bfa..b3c8f2a4d9fa 100644 --- a/shared/android/app/src/main/java/io/keybase/ossifrage/KBPushNotifier.kt +++ b/shared/android/app/src/main/java/io/keybase/ossifrage/KBPushNotifier.kt @@ -122,6 +122,7 @@ class KBPushNotifier internal constructor(private val context: Context, private val convData = ConvData(chatNotification.convID, chatNotification.tlfName ?: "", chatNotification.message.id) val builder = NotificationCompat.Builder(context, KeybasePushNotificationListenerService.CHAT_CHANNEL_ID) .setSmallIcon(R.drawable.ic_notif) + .setContentTitle(chatNotification.title ?: "") .setContentIntent(pending_intent) .setAutoCancel(true) var notificationDefaults = NotificationCompat.DEFAULT_LIGHTS or NotificationCompat.DEFAULT_VIBRATE @@ -228,9 +229,9 @@ class KBPushNotifier internal constructor(private val context: Context, private notificationManager.notify(uniqueTag, 0, builder.build()) } - override fun localNotification(ident: String, msg: String, badgeCount: Long, soundName: String, convID: String, + override fun localNotification(ident: String, title: String, msg: String, badgeCount: Long, soundName: String, convID: String, typ: String) { - genericNotification(ident, "", msg, bundle, KeybasePushNotificationListenerService.GENERAL_CHANNEL_ID) + genericNotification(ident, title, msg, bundle, KeybasePushNotificationListenerService.GENERAL_CHANNEL_ID) } companion object { diff --git a/shared/ios/Keybase/Pusher.swift b/shared/ios/Keybase/Pusher.swift index 75650447a745..d6c6b84ff37d 100644 --- a/shared/ios/Keybase/Pusher.swift +++ b/shared/ios/Keybase/Pusher.swift @@ -1,17 +1,22 @@ import Foundation -import UserNotifications import Keybasego +import UserNotifications class PushNotifier: NSObject, Keybasego.KeybasePushNotifierProtocol { - func localNotification(_ ident: String?, msg: String?, badgeCount: Int, soundName: String?, convID: String?, typ: String?) { + func localNotification( + _ ident: String?, title: String?, msg: String?, badgeCount: Int, soundName: String?, + convID: String?, typ: String? + ) { let content = UNMutableNotificationContent() if let soundName = soundName { content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName)) } content.badge = (badgeCount >= 0) ? NSNumber(value: badgeCount) : nil + content.title = title ?? "" content.body = msg ?? "" content.userInfo = ["convID": convID ?? "", "type": typ ?? ""] - let request = UNNotificationRequest(identifier: ident ?? UUID().uuidString, content: content, trigger: nil) + let request = UNNotificationRequest( + identifier: ident ?? UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) { error in if let error = error { NSLog("local notification failed: %@", error.localizedDescription) @@ -27,12 +32,17 @@ class PushNotifier: NSObject, Keybasego.KeybasePushNotifierProtocol { if notification.isPlaintext && !message.plaintext.isEmpty { let username = message.from?.keybaseUsername ?? "" let convName = notification.conversationName - msg = (username == convName || convName.isEmpty) + msg = + (username == convName || convName.isEmpty) ? "\(username): \(message.plaintext)" : "\(username) (\(convName)): \(message.plaintext)" } else { msg = message.serverMessage } - localNotification(ident, msg: msg, badgeCount: notification.badgeCount, soundName: notification.soundName, convID: notification.convID, typ: "chat.newmessage") + let title = notification.title + NSLog("PushNotifier display: title=%@", title ?? "") + localNotification( + ident, title: title, msg: msg, badgeCount: notification.badgeCount, + soundName: notification.soundName, convID: notification.convID, typ: "chat.newmessage") } } From 03e3d9150e90888d03e8366c1eda6d1798f0c1ef Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Tue, 24 Mar 2026 11:26:35 -0400 Subject: [PATCH 2/3] condition on multiple logged in accounts --- go/bind/notifications.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/go/bind/notifications.go b/go/bind/notifications.go index 3362ae707ef2..f8d103f8312b 100644 --- a/go/bind/notifications.go +++ b/go/bind/notifications.go @@ -26,8 +26,25 @@ import ( var ( seenNotificationsMtx sync.Mutex seenNotifications, _ = lru.New(100) + + multipleAccountsMtx sync.Mutex + multipleAccountsCached *bool ) +func hasMultipleLoggedInAccounts(ctx context.Context) bool { + multipleAccountsMtx.Lock() + defer multipleAccountsMtx.Unlock() + if multipleAccountsCached != nil { + return *multipleAccountsCached + } + result := false + if users, err := kbCtx.GetUsersWithStoredSecrets(ctx); err == nil { + result = len(users) > 1 + } + multipleAccountsCached = &result + return result +} + type Person struct { KeybaseUsername string KeybaseAvatar string @@ -147,6 +164,10 @@ func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender str } currentUsername := string(kbCtx.Env.GetUsername()) + title := "Keybase" + if hasMultipleLoggedInAccounts(ctx) { + title = fmt.Sprintf("%s@keybase", currentUsername) + } chatNotification := ChatNotification{ IsPlaintext: displayPlaintext, Message: &Message{ @@ -162,7 +183,7 @@ func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender str ConversationName: utils.FormatConversationName(conv.Info, currentUsername), SoundName: soundName, BadgeCount: badgeCount, - Title: fmt.Sprintf("%s@keybase", currentUsername), + Title: title, } kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: title=%s", chatNotification.Title) From 05ac4885e5f03a5c2e58e576b8c94c719f7b3753 Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Tue, 24 Mar 2026 12:36:35 -0400 Subject: [PATCH 3/3] don't cache errors --- go/bind/notifications.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/go/bind/notifications.go b/go/bind/notifications.go index f8d103f8312b..48aa41f494e1 100644 --- a/go/bind/notifications.go +++ b/go/bind/notifications.go @@ -37,10 +37,12 @@ func hasMultipleLoggedInAccounts(ctx context.Context) bool { if multipleAccountsCached != nil { return *multipleAccountsCached } - result := false - if users, err := kbCtx.GetUsersWithStoredSecrets(ctx); err == nil { - result = len(users) > 1 + users, err := kbCtx.GetUsersWithStoredSecrets(ctx) + if err != nil { + // Don't cache on error; retry next time. + return false } + result := len(users) > 1 multipleAccountsCached = &result return result }