Merge pull request #2538 from vitorpamplona/claude/add-reply-notifications-GvMOn
Add reply and mention notifications for public notes
This commit is contained in:
+17
@@ -61,6 +61,23 @@ class WakeUpEvent(
|
||||
|
||||
fun kinds() = tags.kinds()
|
||||
|
||||
/**
|
||||
* A WakeUpEvent's `p` tags point to the authors of the referenced subject
|
||||
* events (see [authorKeys]), **not** to the account the wake-up should be
|
||||
* delivered to. Example: Bob reacts to Alice's note, a WakeUpEvent about
|
||||
* Bob's reaction p-tags Bob — but it's Alice's device that needs to wake
|
||||
* up to process the reaction.
|
||||
*
|
||||
* WakeUpEvents reach this device through transport-level routing (push,
|
||||
* relay subscription). By the time one lands in [LocalCache], it is
|
||||
* already "for us" — so every logged-in signing account is a valid
|
||||
* recipient to kick the relay wakeup on behalf of. Returning true here
|
||||
* means the dispatcher invokes [com.vitorpamplona.quartz.experimental.
|
||||
* notifications.wake.WakeUpEvent]-handling for each logged-in account,
|
||||
* which is fine because keeping relay connections alive is idempotent.
|
||||
*/
|
||||
override fun notifies(userHex: HexKey): Boolean = true
|
||||
|
||||
companion object {
|
||||
const val KIND = 23903
|
||||
const val ALT_DESCRIPTION = "WakeUp"
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.kotlinSerialization.EventKSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -45,6 +46,24 @@ open class Event(
|
||||
*/
|
||||
open fun isContentEncoded() = false
|
||||
|
||||
/**
|
||||
* Returns true when this event is intended to notify [userHex].
|
||||
*
|
||||
* The default delegates to
|
||||
* [com.vitorpamplona.quartz.nip01Core.tags.people.PTag.isNotifying],
|
||||
* i.e. "any lowercase `p` tag addresses the user" — the convention for
|
||||
* most kinds that address a single recipient or a set of mentions
|
||||
* (NIP-01 mentions, NIP-04/17 DMs, NIP-25 reactions, NIP-28 chat
|
||||
* messages, NIP-34 git issues/patches, NIP-57 zap receipts, NIP-68
|
||||
* pictures, NIP-71 videos, NIP-84 highlights, NIP-AC calls, chess,
|
||||
* wiki/long-form/poll mentions).
|
||||
*
|
||||
* Subclasses override when the NIP defines additional notification tags
|
||||
* — e.g. NIP-22 comments use uppercase `P` for the root author in
|
||||
* addition to lowercase `p` for the direct-reply author.
|
||||
*/
|
||||
open fun notifies(userHex: HexKey): Boolean = PTag.isNotifying(tags, userHex)
|
||||
|
||||
fun toJson(): String = OptimizedJsonMapper.toJson(this)
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
|
||||
@@ -54,6 +55,24 @@ data class PTag(
|
||||
key: HexKey,
|
||||
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == key
|
||||
|
||||
/**
|
||||
* Returns true if any `p` tag inside [tags] addresses [userHex].
|
||||
* This is the canonical check for "does this event notify this user
|
||||
* under the lowercase-`p` convention" and is the default used by
|
||||
* [Event.notifies]. Kinds that address recipients through other tag
|
||||
* names (e.g. NIP-22 uppercase `P` for root authors) override
|
||||
* [Event.notifies] and may combine this with their own checks.
|
||||
*/
|
||||
fun isNotifying(
|
||||
tags: TagArray,
|
||||
userHex: HexKey,
|
||||
): Boolean {
|
||||
for (tag in tags) {
|
||||
if (isTagged(tag, userHex)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun parse(tag: Tag): PTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
|
||||
@@ -197,6 +197,15 @@ class CommentEvent(
|
||||
|
||||
override fun unmarkedReplyTos() = emptyList<String>()
|
||||
|
||||
/**
|
||||
* NIP-22 addresses two distinct recipients: the direct-reply author
|
||||
* (lowercase `p` via [ReplyAuthorTag]) and the root-scope author
|
||||
* (uppercase `P` via [RootAuthorTag]). A comment several levels deep
|
||||
* only tags the root author with uppercase `P`, so the base-class
|
||||
* lowercase-only default would miss them.
|
||||
*/
|
||||
override fun notifies(userHex: HexKey): Boolean = super.notifies(userHex) || rootAuthorKeys().contains(userHex)
|
||||
|
||||
override fun replyingTo(): HexKey? =
|
||||
tags.lastNotNullOfOrNull(ReplyEventTag::parseKey)
|
||||
?: tags.lastNotNullOfOrNull(RootEventTag::parseKey)
|
||||
|
||||
Reference in New Issue
Block a user