mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-30 19:46:17 +00:00
fix: attribute reply-notification parent to its real author, not always "Me"
The parent message shown above a reply in the MessagingStyle notification was unconditionally labeled "Me" with the account's avatar. That's wrong whenever the account is notified as the *root* author of a thread but the direct parent belongs to someone else — e.g. Vitor starts a thread, fiatjaf replies, a third person replies to fiatjaf: fiatjaf's note was rendered as Vitor. This surfaced through the NIP-22 comment and public-chat reply paths, which notify on root-authorship, not just direct-parent authorship. ReplyNotification now receives the parent Note (not a bare content string) and resolves its actual author: attributed to the MessagingStyle `me` Person only when the parent truly is the account's, otherwise shown as the real author with their name + avatar, observed for enrichment like the replier. postConversation reuses the `me` Person for a self-authored parent so it still renders as you. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0122uQ8BLHLeHDni81RBP26r
This commit is contained in:
+5
-9
@@ -286,7 +286,7 @@ class EventNotificationConsumer(
|
||||
val repliedNote = LocalCache.getNoteIfExists(replyTargetId)
|
||||
if (repliedNote?.author?.pubkeyHex == account.signer.pubKey) {
|
||||
val threadRoot = event.markedRoot()?.eventId ?: event.unmarkedRoot()?.eventId ?: replyTargetId
|
||||
ReplyNotification.notify(applicationContext, account, event, repliedNote.event?.content, threadRoot)
|
||||
ReplyNotification.notify(applicationContext, account, event, repliedNote, threadRoot)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -301,14 +301,14 @@ class EventNotificationConsumer(
|
||||
val isTarget = event.replyAuthorKeys().contains(pubKey) || event.rootAuthorKeys().contains(pubKey)
|
||||
if (!isTarget) return
|
||||
|
||||
val parentContent = event.replyingTo()?.let { LocalCache.getNoteIfExists(it)?.event?.content }
|
||||
val parentNote = event.replyingTo()?.let { LocalCache.getNoteIfExists(it) }
|
||||
val threadRoot =
|
||||
event.rootEventIds().firstOrNull()
|
||||
?: event.rootAddressIds().firstOrNull()
|
||||
?: event.replyingToAddressOrEvent()
|
||||
?: event.id
|
||||
|
||||
ReplyNotification.notify(applicationContext, account, event, parentContent, threadRoot)
|
||||
ReplyNotification.notify(applicationContext, account, event, parentNote, threadRoot)
|
||||
}
|
||||
|
||||
private suspend fun notifyChannelMessage(
|
||||
@@ -318,13 +318,9 @@ class EventNotificationConsumer(
|
||||
val note = LocalCache.getNoteIfExists(event.id) ?: return
|
||||
|
||||
if (NotificationFeedFilter.isNotifiablePublicChatReply(note, account.signer.pubKey)) {
|
||||
val parentContent =
|
||||
note.replyTo
|
||||
?.lastOrNull()
|
||||
?.event
|
||||
?.content
|
||||
val parentNote = note.replyTo?.lastOrNull()
|
||||
val threadRoot = event.channelId() ?: event.id
|
||||
ReplyNotification.notify(applicationContext, account, event, parentContent, threadRoot)
|
||||
ReplyNotification.notify(applicationContext, account, event, parentNote, threadRoot)
|
||||
} else {
|
||||
MentionNotification.notify(applicationContext, account, event)
|
||||
}
|
||||
|
||||
+19
-6
@@ -152,6 +152,13 @@ object NotificationUtils {
|
||||
val senderName: String,
|
||||
val body: String,
|
||||
val pictureUrl: String?,
|
||||
/**
|
||||
* True when the parent was authored by the logged-in account, so it is
|
||||
* attributed to the MessagingStyle `me` Person (avatar + "you") rather than
|
||||
* shown as a separate participant. False for a third party's note — e.g. a
|
||||
* reply to someone else's reply in a thread the account started.
|
||||
*/
|
||||
val isFromMe: Boolean = false,
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -284,13 +291,19 @@ object NotificationUtils {
|
||||
val messagingStyle = NotificationCompat.MessagingStyle(me)
|
||||
|
||||
if (parent != null) {
|
||||
val parentAvatar = parent.pictureUrl?.let { loadBitmap(it, applicationContext) }?.let { circleCrop(it) }
|
||||
val parentSender =
|
||||
Person
|
||||
.Builder()
|
||||
.setName(parent.senderName)
|
||||
.apply { parentAvatar?.let { setIcon(IconCompat.createWithBitmap(it)) } }
|
||||
.build()
|
||||
if (parent.isFromMe) {
|
||||
// The account authored the parent — reuse the `me` Person so it
|
||||
// renders as self with the account's avatar, not a stranger.
|
||||
me
|
||||
} else {
|
||||
val parentAvatar = parent.pictureUrl?.let { loadBitmap(it, applicationContext) }?.let { circleCrop(it) }
|
||||
Person
|
||||
.Builder()
|
||||
.setName(parent.senderName)
|
||||
.apply { parentAvatar?.let { setIcon(IconCompat.createWithBitmap(it)) } }
|
||||
.build()
|
||||
}
|
||||
messagingStyle.addMessage(parent.body, (time - 1) * 1000, parentSender)
|
||||
}
|
||||
messagingStyle.addMessage(messageBody, time * 1000, sender)
|
||||
|
||||
+22
-5
@@ -24,6 +24,7 @@ import android.content.Context
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationCategory
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationContent
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationEnricher
|
||||
@@ -51,7 +52,7 @@ object ReplyNotification {
|
||||
context: Context,
|
||||
account: Account,
|
||||
event: Event,
|
||||
parentContent: String?,
|
||||
parentNote: Note?,
|
||||
threadRootId: String,
|
||||
) {
|
||||
val replyNote = LocalCache.getNoteIfExists(event.id) ?: return
|
||||
@@ -63,17 +64,27 @@ object ReplyNotification {
|
||||
|
||||
val citedUsers = NotificationContent.resolveMentions(event.content).citedUsers
|
||||
|
||||
// The parent may be the account's own note (a direct reply) OR a third
|
||||
// party's — e.g. someone replying to another reply in a thread the account
|
||||
// started. Attribute it to whoever actually wrote it, and observe that
|
||||
// author so their name/avatar fill in like the replier's do.
|
||||
val parentContent = parentNote?.event?.content
|
||||
val parentAuthor = parentNote?.author
|
||||
val parentIsFromMe = parentAuthor?.pubkeyHex == account.signer.pubKey
|
||||
val observedParentAuthor = parentAuthor?.takeUnless { parentIsFromMe }
|
||||
|
||||
val nm = context.notificationManager()
|
||||
|
||||
NotificationEnricher.enrichAndPost(
|
||||
context = context,
|
||||
account = account,
|
||||
notificationId = event.id,
|
||||
users = listOf(author) + citedUsers,
|
||||
users = listOf(author) + citedUsers + listOfNotNull(observedParentAuthor),
|
||||
notes = listOf(replyNote),
|
||||
isComplete = {
|
||||
author.metadataOrNull()?.bestName() != null &&
|
||||
citedUsers.all { it.metadataOrNull()?.bestName() != null }
|
||||
citedUsers.all { it.metadataOrNull()?.bestName() != null } &&
|
||||
(observedParentAuthor == null || observedParentAuthor.metadataOrNull()?.bestName() != null)
|
||||
},
|
||||
) {
|
||||
val user = author.toBestDisplayName()
|
||||
@@ -82,9 +93,15 @@ object ReplyNotification {
|
||||
val parent =
|
||||
parentExcerpt?.let {
|
||||
ParentMessage(
|
||||
senderName = stringRes(context, R.string.app_notification_me),
|
||||
senderName =
|
||||
if (parentIsFromMe || parentAuthor == null) {
|
||||
stringRes(context, R.string.app_notification_me)
|
||||
} else {
|
||||
parentAuthor.toBestDisplayName()
|
||||
},
|
||||
body = it,
|
||||
pictureUrl = account.userProfile().profilePicture(),
|
||||
pictureUrl = if (parentIsFromMe) account.userProfile().profilePicture() else parentAuthor?.profilePicture(),
|
||||
isFromMe = parentIsFromMe,
|
||||
)
|
||||
}
|
||||
nm.postConversation(
|
||||
|
||||
Reference in New Issue
Block a user