Normalizes NIP-22 viewmodel to its own entity
This commit is contained in:
@@ -30,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||
import com.vitorpamplona.quartz.nip22Comments.tags.ReplyAddressTag
|
||||
@@ -44,6 +45,7 @@ import com.vitorpamplona.quartz.nip22Comments.tags.RootIdentifierTag
|
||||
import com.vitorpamplona.quartz.nip22Comments.tags.RootKindTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.lastNotNullOfOrNull
|
||||
|
||||
@@ -172,10 +174,18 @@ class CommentEvent(
|
||||
) = eventTemplate(KIND, msg, createdAt) {
|
||||
alt(ALT + extId.toScope())
|
||||
|
||||
rootExternalIdentity(extId)
|
||||
if (extId is GeohashId) {
|
||||
GeoHashTag.geoMipMap(extId.geohash).forEach { rootExternalIdentity(GeohashId(it, extId.hint)) }
|
||||
} else {
|
||||
rootExternalIdentity(extId)
|
||||
}
|
||||
rootKind(extId)
|
||||
|
||||
replyExternalIdentity(extId)
|
||||
if (extId is GeohashId) {
|
||||
GeoHashTag.geoMipMap(extId.geohash).forEach { replyExternalIdentity(GeohashId(it, extId.hint)) }
|
||||
} else {
|
||||
replyExternalIdentity(extId)
|
||||
}
|
||||
replyKind(extId)
|
||||
|
||||
initializer()
|
||||
|
||||
@@ -48,7 +48,9 @@ fun TagArrayBuilder<CommentEvent>.rootEvent(
|
||||
pubkey: String?,
|
||||
) = addUnique(RootEventTag.assemble(eventId, relayHint, pubkey))
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.rootExternalIdentity(id: ExternalId) = addAll(RootIdentifierTag.assemble(id))
|
||||
fun TagArrayBuilder<CommentEvent>.rootExternalIdentity(id: ExternalId) = add(RootIdentifierTag.assemble(id))
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.rootExternalIdentities(ids: List<ExternalId>) = addAll(ids.map { RootIdentifierTag.assemble(it) })
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.rootKind(kind: String) = addUnique(RootKindTag.assemble(kind))
|
||||
|
||||
@@ -72,7 +74,9 @@ fun TagArrayBuilder<CommentEvent>.replyEvent(
|
||||
pubkey: String?,
|
||||
) = addUnique(ReplyEventTag.assemble(eventId, relayHint, pubkey))
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.replyExternalIdentity(id: ExternalId) = addAll(ReplyIdentifierTag.assemble(id))
|
||||
fun TagArrayBuilder<CommentEvent>.replyExternalIdentity(id: ExternalId) = add(ReplyIdentifierTag.assemble(id))
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.replyExternalIdentities(ids: List<ExternalId>) = addAll(ids.map { ReplyIdentifierTag.assemble(it) })
|
||||
|
||||
fun TagArrayBuilder<CommentEvent>.replyKind(kind: String) = addUnique(ReplyKindTag.assemble(kind))
|
||||
|
||||
|
||||
+30
-6
@@ -23,9 +23,17 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.getOrNull
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.books.BookId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.movies.MovieId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.papers.PaperId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastEpisodeId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastFeedId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastPublisherId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.urls.UrlId
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@@ -82,6 +90,26 @@ class ReplyIdentifierTag {
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseExternalId(tag: Tag): ExternalId? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == RootIdentifierTag.Companion.TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
|
||||
val value = tag[1]
|
||||
val hint = tag.getOrNull(2)
|
||||
|
||||
return BookId.parse(value, hint)
|
||||
?: HashtagId.parse(value, hint)
|
||||
?: GeohashId.parse(value, hint)
|
||||
?: MovieId.parse(value, hint)
|
||||
?: PaperId.parse(value, hint)
|
||||
?: PodcastEpisodeId.parse(value, hint)
|
||||
?: PodcastFeedId.parse(value, hint)
|
||||
?: PodcastPublisherId.parse(value, hint)
|
||||
?: UrlId.parse(value, hint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
identity: String,
|
||||
@@ -89,10 +117,6 @@ class ReplyIdentifierTag {
|
||||
) = arrayOfNotNull(TAG_NAME, identity, hint)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(id: ExternalId): List<Array<String>> =
|
||||
when (id) {
|
||||
is GeohashId -> GeoHashTag.geoMipMap(id.geohash).map { assemble(GeohashId.toScope(it), id.hint) }
|
||||
else -> listOf(assemble(id.toScope(), id.hint()))
|
||||
}
|
||||
fun assemble(id: ExternalId): Array<String> = assemble(id.toScope(), id.hint())
|
||||
}
|
||||
}
|
||||
|
||||
+30
-6
@@ -23,9 +23,17 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.getOrNull
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.books.BookId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.movies.MovieId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.papers.PaperId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastEpisodeId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastFeedId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.podcasts.PodcastPublisherId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.urls.UrlId
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@@ -81,6 +89,26 @@ class RootIdentifierTag<T : ExternalId> {
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseExternalId(tag: Tag): ExternalId? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
|
||||
val value = tag[1]
|
||||
val hint = tag.getOrNull(2)
|
||||
|
||||
return BookId.parse(value, hint)
|
||||
?: HashtagId.parse(value, hint)
|
||||
?: GeohashId.parse(value, hint)
|
||||
?: MovieId.parse(value, hint)
|
||||
?: PaperId.parse(value, hint)
|
||||
?: PodcastEpisodeId.parse(value, hint)
|
||||
?: PodcastFeedId.parse(value, hint)
|
||||
?: PodcastPublisherId.parse(value, hint)
|
||||
?: UrlId.parse(value, hint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
identity: String,
|
||||
@@ -88,10 +116,6 @@ class RootIdentifierTag<T : ExternalId> {
|
||||
) = arrayOfNotNull(TAG_NAME, identity, hint)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(id: ExternalId): List<Array<String>> =
|
||||
when (id) {
|
||||
is GeohashId -> GeoHashTag.geoMipMap(id.geohash).map { assemble(GeohashId.toScope(it), id.hint) }
|
||||
else -> listOf(assemble(id.toScope(), id.hint()))
|
||||
}
|
||||
fun assemble(id: ExternalId): Array<String> = assemble(id.toScope(), id.hint())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip73ExternalIds
|
||||
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip22Comments.tags.RootIdentifierTag
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
||||
|
||||
fun CommentEvent.scopes() = tags.mapNotNull { RootIdentifierTag.parseExternalId(it) }
|
||||
|
||||
fun CommentEvent.scope(): ExternalId? {
|
||||
val scopes = scopes()
|
||||
|
||||
if (scopes.isEmpty()) return null
|
||||
|
||||
return if (scopes.first() is GeohashId) {
|
||||
scopes.maxByOrNull { if (it is GeohashId) it.geohash else "" }
|
||||
} else {
|
||||
scopes().first()
|
||||
}
|
||||
}
|
||||
@@ -54,5 +54,13 @@ class BookId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): BookId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return BookId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,5 +53,13 @@ class GeohashId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): GeohashId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return GeohashId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,5 +80,13 @@ class MovieId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): MovieId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return MovieId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,5 +54,13 @@ class PaperId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): PaperId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return PaperId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -53,5 +53,13 @@ class PodcastEpisodeId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): PodcastEpisodeId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return PodcastEpisodeId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -54,5 +54,13 @@ class PodcastFeedId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): PodcastFeedId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return PodcastFeedId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -54,5 +54,13 @@ class PodcastPublisherId(
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return encoded.substring(PREFIX_COLON.length)
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): PodcastPublisherId? {
|
||||
ensure(encoded.startsWith(PREFIX_COLON)) { return null }
|
||||
return PodcastPublisherId(encoded.substring(PREFIX_COLON.length), hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,5 +77,16 @@ class HashtagId(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): HashtagId? {
|
||||
return if (encoded.length > 1 && encoded[0] == KIND_CHR) {
|
||||
HashtagId(encoded.substring(1), hint)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,13 @@ class UrlId(
|
||||
ensure(encoded.startsWith(PREFIX1) || encoded.startsWith(PREFIX2)) { return null }
|
||||
return encoded
|
||||
}
|
||||
|
||||
fun parse(
|
||||
encoded: String,
|
||||
hint: String?,
|
||||
): UrlId? {
|
||||
ensure(encoded.startsWith(PREFIX1) || encoded.startsWith(PREFIX2)) { return null }
|
||||
return UrlId(encoded, hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user