Split nip30303 compound metadata tags into per-field tags

The artifact/dialect/chapter/translation-version events packed several
independent attributes into single positional tags
(["artifact", url, visibility, license], ["dialect", name, country,
language], etc.). Positional packing made every field mandatory (a
missing or malformed field nulled the whole metadata blob), blocked
extension, and forced ArtifactMetadataTag and
TranslationArtifactVersionMetadataTag to share TAG_NAME "artifact" with
field 1 meaning url vs name.

Replace the four compound tags with seven shared single-field tags
(NameTag, UrlTag, VisibilityTag, LicenseTag, CountryTag, LanguageTag,
OriginalTextTag), following the NIP-23 one-tag-per-field convention.
Event accessors now read each field independently and build()/toXEvent()
emit one tag per field; from...Event() readers let-chain the per-field
accessors. This also removes the duplicate "artifact" tag-name collision.

Note: breaking wire-format change for kinds 30300/30302/30304/30306;
safe now since these events are not published yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-07-26 00:06:12 +02:00
parent c45a310641
commit 8e3c1cffe2
19 changed files with 276 additions and 288 deletions

View File

@@ -11,8 +11,10 @@ import com.vitorpamplona.quartz.nip31Alts.AltTag
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.ArtifactEvent
import press.mantra.compose.nostr.nip30303.tags.ArtifactMetadataTag
import press.mantra.compose.nostr.nip30303.tags.DialectIdTag
import press.mantra.compose.nostr.nip30303.tags.LicenseTag
import press.mantra.compose.nostr.nip30303.tags.UrlTag
import press.mantra.compose.nostr.nip30303.tags.VisibilityTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -69,13 +71,9 @@ data class MantraArtifact(
pubKey = publicKey,
createdAt = createdAt.epochSeconds,
tags = TagArrayBuilder<ArtifactEvent>()
.addUnique(
ArtifactMetadataTag.assemble(
url = url,
visibility = visibility,
license = license
)
)
.addUnique(UrlTag.assemble(url))
.addUnique(VisibilityTag.assemble(visibility))
.addUnique(LicenseTag.assemble(license))
.addUnique(
DialectIdTag.assemble(dialectId)
)
@@ -109,20 +107,24 @@ data class MantraArtifact(
)
}
fun fromArtifactEvent(artifactEvent: ArtifactEvent, chatRoomId: HexKey, ): MantraArtifact? {
return artifactEvent.artifactMetadata?.let { artifactMetadata ->
artifactEvent.dialectId()?.let { dialectId ->
MantraArtifact(
id = artifactEvent.id,
publicKey = artifactEvent.pubKey,
dialectId = dialectId,
createdAt = Instant.fromEpochSeconds(artifactEvent.createdAt),
url = artifactMetadata.url,
license = artifactMetadata.license,
visibility = artifactMetadata.visibility,
name = artifactEvent.content,
signature = artifactEvent.sig,
chatRoomId = chatRoomId
)
return artifactEvent.url()?.let { url ->
artifactEvent.visibility()?.let { visibility ->
artifactEvent.license()?.let { license ->
artifactEvent.dialectId()?.let { dialectId ->
MantraArtifact(
id = artifactEvent.id,
publicKey = artifactEvent.pubKey,
dialectId = dialectId,
createdAt = Instant.fromEpochSeconds(artifactEvent.createdAt),
url = url,
license = license,
visibility = visibility,
name = artifactEvent.content,
signature = artifactEvent.sig,
chatRoomId = chatRoomId
)
}
}
}
}
}

View File

@@ -10,8 +10,9 @@ import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.ArtifactVersionEvent
import press.mantra.compose.nostr.nip30303.ChapterEvent
import press.mantra.compose.nostr.nip30303.tags.ArtifactVersionIdTag
import press.mantra.compose.nostr.nip30303.tags.ChapterMetadataTag
import press.mantra.compose.nostr.nip30303.tags.IndexTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
import press.mantra.compose.nostr.nip30303.tags.OriginalTextTag
import press.mantra.compose.nostr.nip30303.tags.WordStatisticsTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -74,12 +75,8 @@ data class MantraChapter(
.addUnique(
ArtifactVersionIdTag.assemble(artifactVersionId)
)
.addUnique(
ChapterMetadataTag.assemble(
name = name,
originalText = originalText,
)
)
.addUnique(NameTag.assemble(name))
.addUnique(OriginalTextTag.assemble(originalText))
.addUnique(
IndexTag.assemble(
index
@@ -96,23 +93,25 @@ data class MantraChapter(
companion object {
fun fromChapterEvent(chapterEvent: ChapterEvent, chatRoomId: HexKey): MantraChapter? {
return chapterEvent.chapterMetadata?.let { chapterMetadata ->
chapterEvent.wordStatisticsTag?.let { wordStatisticsTag ->
chapterEvent.artifactVersionId()?.let { artifactId ->
chapterEvent.index()?.let { index ->
MantraChapter(
id = chapterEvent.id,
publicKey = chapterEvent.pubKey,
createdAt = Instant.fromEpochSeconds(chapterEvent.createdAt),
artifactVersionId = artifactId,
name = chapterMetadata.name,
originalText = chapterMetadata.originalText,
index = index,
wordCount = wordStatisticsTag.wordCount,
characterCount = wordStatisticsTag.characterCount,
signature = chapterEvent.sig,
chatRoomId = chatRoomId
)
return chapterEvent.name()?.let { name ->
chapterEvent.originalText()?.let { originalText ->
chapterEvent.wordStatisticsTag?.let { wordStatisticsTag ->
chapterEvent.artifactVersionId()?.let { artifactId ->
chapterEvent.index()?.let { index ->
MantraChapter(
id = chapterEvent.id,
publicKey = chapterEvent.pubKey,
createdAt = Instant.fromEpochSeconds(chapterEvent.createdAt),
artifactVersionId = artifactId,
name = name,
originalText = originalText,
index = index,
wordCount = wordStatisticsTag.wordCount,
characterCount = wordStatisticsTag.characterCount,
signature = chapterEvent.sig,
chatRoomId = chatRoomId
)
}
}
}
}

View File

@@ -10,7 +10,9 @@ import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.ChunkEvent
import press.mantra.compose.nostr.nip30303.DialectEvent
import press.mantra.compose.nostr.nip30303.tags.ChapterIdTag
import press.mantra.compose.nostr.nip30303.tags.DialectMetadataTag
import press.mantra.compose.nostr.nip30303.tags.CountryTag
import press.mantra.compose.nostr.nip30303.tags.LanguageTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -56,13 +58,9 @@ data class MantraDialect(
pubKey = publicKey,
createdAt = createdAt.epochSeconds,
tags = TagArrayBuilder<ChunkEvent>()
.addUnique(
DialectMetadataTag.assemble(
name = name,
country = country,
language = language
)
)
.addUnique(NameTag.assemble(name))
.addUnique(CountryTag.assemble(country))
.addUnique(LanguageTag.assemble(language))
.build(),
content = language, // TODO: Consider using the isoCode... as content?
sig = signature

View File

@@ -11,7 +11,9 @@ import press.mantra.compose.nostr.nip30303.ArtifactVersionEvent
import press.mantra.compose.nostr.nip30303.TranslationArtifactVersionEvent
import press.mantra.compose.nostr.nip30303.tags.ArtifactVersionIdTag
import press.mantra.compose.nostr.nip30303.tags.DialectIdTag
import press.mantra.compose.nostr.nip30303.tags.TranslationArtifactVersionMetadataTag
import press.mantra.compose.nostr.nip30303.tags.LicenseTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
import press.mantra.compose.nostr.nip30303.tags.VisibilityTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -80,13 +82,9 @@ data class MantraTranslationArtifactVersion(
.addUnique(
DialectIdTag.assemble(dialectId)
)
.addUnique(
TranslationArtifactVersionMetadataTag.assemble(
name = name,
visibility = visibility,
license = license
)
)
.addUnique(NameTag.assemble(name))
.addUnique(VisibilityTag.assemble(visibility))
.addUnique(LicenseTag.assemble(license))
.build(),
content = "",
sig = signature
@@ -98,19 +96,23 @@ data class MantraTranslationArtifactVersion(
return translationArtifactVersionEvent.artifactVersionId()?.let { artifactVersionId ->
translationArtifactVersionEvent.dialectId()?.let { dialectId ->
translationArtifactVersionEvent.translationArtifactVersionMetadata?.let { translationArtifactVersionMetadata ->
MantraTranslationArtifactVersion(
id = translationArtifactVersionEvent.id,
publicKey = translationArtifactVersionEvent.pubKey,
createdAt = Instant.fromEpochSeconds(translationArtifactVersionEvent.createdAt),
artifactVersionId = artifactVersionId,
dialectId = dialectId,
name = translationArtifactVersionMetadata.name,
visibility = translationArtifactVersionMetadata.visibility,
license = translationArtifactVersionMetadata.license,
signature = translationArtifactVersionEvent.sig,
chatRoomId = chatRoomId
)
translationArtifactVersionEvent.name()?.let { name ->
translationArtifactVersionEvent.visibility()?.let { visibility ->
translationArtifactVersionEvent.license()?.let { license ->
MantraTranslationArtifactVersion(
id = translationArtifactVersionEvent.id,
publicKey = translationArtifactVersionEvent.pubKey,
createdAt = Instant.fromEpochSeconds(translationArtifactVersionEvent.createdAt),
artifactVersionId = artifactVersionId,
dialectId = dialectId,
name = name,
visibility = visibility,
license = license,
signature = translationArtifactVersionEvent.sig,
chatRoomId = chatRoomId
)
}
}
}
}
}

View File

@@ -9,8 +9,10 @@ import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip50Search.SearchableEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.nostr.nip30303.tags.ArtifactMetadataTag
import press.mantra.compose.nostr.nip30303.tags.DialectIdTag
import press.mantra.compose.nostr.nip30303.tags.LicenseTag
import press.mantra.compose.nostr.nip30303.tags.UrlTag
import press.mantra.compose.nostr.nip30303.tags.VisibilityTag
@Immutable
@@ -22,11 +24,9 @@ class ArtifactEvent(
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
val artifactMetadata = tags.firstNotNullOfOrNull(ArtifactMetadataTag::parse)
fun url() = artifactMetadata?.url
fun visibility() = artifactMetadata?.visibility
fun license() = artifactMetadata?.license
fun url() = tags.firstNotNullOfOrNull(UrlTag::parse)?.url
fun visibility() = tags.firstNotNullOfOrNull(VisibilityTag::parse)?.visibility
fun license() = tags.firstNotNullOfOrNull(LicenseTag::parse)?.license
fun dialectIdReference() = tags.firstNotNullOfOrNull(DialectIdTag::parse)?.ref
fun dialectId() = dialectIdReference()?.eventId
@@ -45,13 +45,9 @@ class ArtifactEvent(
initializer: TagArrayBuilder<ArtifactEvent>.() -> Unit = {},
) = eventTemplate(KIND, name, createdAt) {
alt(ALT_DESCRIPTION)
addUnique(
ArtifactMetadataTag.assemble(
url = url,
visibility = visibility,
license = license
)
)
addUnique(UrlTag.assemble(url))
addUnique(VisibilityTag.assemble(visibility))
addUnique(LicenseTag.assemble(license))
addUnique(
DialectIdTag.assemble(dialectId)
)

View File

@@ -8,8 +8,9 @@ import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.nostr.nip30303.tags.ArtifactVersionIdTag
import press.mantra.compose.nostr.nip30303.tags.ChapterMetadataTag
import press.mantra.compose.nostr.nip30303.tags.IndexTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
import press.mantra.compose.nostr.nip30303.tags.OriginalTextTag
import press.mantra.compose.nostr.nip30303.tags.WordStatisticsTag
@@ -22,14 +23,13 @@ class ChapterEvent(
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
val chapterMetadata = tags.firstNotNullOfOrNull(ChapterMetadataTag::parse)
val wordStatisticsTag = tags.firstNotNullOfOrNull(WordStatisticsTag::parse)
fun artifactVersionIdReference() = tags.firstNotNullOfOrNull(ArtifactVersionIdTag::parse)?.ref
fun artifactVersionId() = artifactVersionIdReference()?.eventId
fun name() = chapterMetadata?.name
fun originalText() = chapterMetadata?.originalText
fun name() = tags.firstNotNullOfOrNull(NameTag::parse)?.name
fun originalText() = tags.firstNotNullOfOrNull(OriginalTextTag::parse)?.originalText
fun index() = tags.firstNotNullOfOrNull(IndexTag::parse)?.index
fun wordCount() = wordStatisticsTag?.wordCount

View File

@@ -9,7 +9,9 @@ import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip50Search.SearchableEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.nostr.nip30303.tags.DialectMetadataTag
import press.mantra.compose.nostr.nip30303.tags.CountryTag
import press.mantra.compose.nostr.nip30303.tags.LanguageTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
@Immutable
@@ -21,11 +23,9 @@ class DialectEvent(
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
val dialectMetadata = tags.firstNotNullOfOrNull(DialectMetadataTag::parse)
fun name() = dialectMetadata?.name
fun country() = dialectMetadata?.country
fun language() = dialectMetadata?.language
fun name() = tags.firstNotNullOfOrNull(NameTag::parse)?.name
fun country() = tags.firstNotNullOfOrNull(CountryTag::parse)?.country
fun language() = tags.firstNotNullOfOrNull(LanguageTag::parse)?.language
companion object {
const val KIND = 30304

View File

@@ -9,7 +9,9 @@ import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.nostr.nip30303.tags.ArtifactVersionIdTag
import press.mantra.compose.nostr.nip30303.tags.DialectIdTag
import press.mantra.compose.nostr.nip30303.tags.TranslationArtifactVersionMetadataTag
import press.mantra.compose.nostr.nip30303.tags.LicenseTag
import press.mantra.compose.nostr.nip30303.tags.NameTag
import press.mantra.compose.nostr.nip30303.tags.VisibilityTag
@Immutable
@@ -22,7 +24,9 @@ class TranslationArtifactVersionEvent(
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
val translationArtifactVersionMetadata = tags.firstNotNullOfOrNull(TranslationArtifactVersionMetadataTag::parse)
fun name() = tags.firstNotNullOfOrNull(NameTag::parse)?.name
fun visibility() = tags.firstNotNullOfOrNull(VisibilityTag::parse)?.visibility
fun license() = tags.firstNotNullOfOrNull(LicenseTag::parse)?.license
fun artifactVersionIdReference() = tags.firstNotNullOfOrNull(ArtifactVersionIdTag::parse)?.ref
fun artifactVersionId() = artifactVersionIdReference()?.eventId

View File

@@ -1,49 +0,0 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ArtifactMetadataTag(
val url: String,
val visibility: String,
val license: String,
) {
fun toTagArray() = assemble(
url = url,
visibility = visibility,
license = license
)
companion object {
const val TAG_NAME = "artifact"
fun parse(tag: Array<String>): ArtifactMetadataTag? {
ensure(tag.has(3)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return ArtifactMetadataTag(
url = tag[1],
visibility = tag[2],
license = tag[3],
)
}
fun assemble(
url: String,
visibility: String,
license: String,
): Array<String> = arrayOf(
TAG_NAME,
url,
visibility,
license
)
fun assemble(artifactMetadataTag: ArtifactMetadataTag) = assemble(
url = artifactMetadataTag.url,
visibility = artifactMetadataTag.visibility,
license = artifactMetadataTag.license
)
}
}

View File

@@ -1,42 +0,0 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ChapterMetadataTag(
val name: String,
val originalText: String,
) {
fun toTagArray() = assemble(
name = name,
originalText = originalText,
)
companion object {
const val TAG_NAME = "chapter"
fun parse(tag: Array<String>): ChapterMetadataTag? {
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return ChapterMetadataTag(
name = tag[1],
originalText = tag[2],
)
}
fun assemble(
name: String,
originalText: String,
): Array<String> = arrayOf(
TAG_NAME,
name,
originalText,
)
fun assemble(chapterMetadataTag: ChapterMetadataTag) = assemble(
name = chapterMetadataTag.name,
originalText = chapterMetadataTag.originalText,
)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class CountryTag(
val country: String,
) {
fun toTagArray() = assemble(country = country)
companion object {
const val TAG_NAME = "country"
fun parse(tag: Array<String>): CountryTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return CountryTag(country = tag[1])
}
fun assemble(country: String): Array<String> = arrayOf(TAG_NAME, country)
fun assemble(countryTag: CountryTag) = assemble(country = countryTag.country)
}
}

View File

@@ -1,48 +0,0 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DialectMetadataTag(
val name: String,
val country: String,
val language: String,
) {
fun toTagArray() = assemble(
name = name,
country = country,
language = language
)
companion object {
const val TAG_NAME = "dialect"
fun parse(tag: Array<String>): DialectMetadataTag? {
ensure(tag.has(3)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return DialectMetadataTag(
name = tag[1],
country = tag[2],
language = tag[3],
)
}
fun assemble(
name: String,
country: String,
language: String,
): Array<String> = arrayOf(
TAG_NAME,
name,
country,
language
)
fun assemble(dialectMetadataTag: DialectMetadataTag) = assemble(
name = dialectMetadataTag.name,
country = dialectMetadataTag.country,
language = dialectMetadataTag.language
)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class LanguageTag(
val language: String,
) {
fun toTagArray() = assemble(language = language)
companion object {
const val TAG_NAME = "language"
fun parse(tag: Array<String>): LanguageTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return LanguageTag(language = tag[1])
}
fun assemble(language: String): Array<String> = arrayOf(TAG_NAME, language)
fun assemble(languageTag: LanguageTag) = assemble(language = languageTag.language)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class LicenseTag(
val license: String,
) {
fun toTagArray() = assemble(license = license)
companion object {
const val TAG_NAME = "license"
fun parse(tag: Array<String>): LicenseTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return LicenseTag(license = tag[1])
}
fun assemble(license: String): Array<String> = arrayOf(TAG_NAME, license)
fun assemble(licenseTag: LicenseTag) = assemble(license = licenseTag.license)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class NameTag(
val name: String,
) {
fun toTagArray() = assemble(name = name)
companion object {
const val TAG_NAME = "name"
fun parse(tag: Array<String>): NameTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return NameTag(name = tag[1])
}
fun assemble(name: String): Array<String> = arrayOf(TAG_NAME, name)
fun assemble(nameTag: NameTag) = assemble(name = nameTag.name)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class OriginalTextTag(
val originalText: String,
) {
fun toTagArray() = assemble(originalText = originalText)
companion object {
const val TAG_NAME = "original_text"
fun parse(tag: Array<String>): OriginalTextTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return OriginalTextTag(originalText = tag[1])
}
fun assemble(originalText: String): Array<String> = arrayOf(TAG_NAME, originalText)
fun assemble(originalTextTag: OriginalTextTag) = assemble(originalText = originalTextTag.originalText)
}
}

View File

@@ -1,49 +0,0 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class TranslationArtifactVersionMetadataTag(
val name: String,
val visibility: String,
val license: String,
) {
fun toTagArray() = assemble(
name = name,
visibility = visibility,
license = license
)
companion object {
const val TAG_NAME = "artifact"
fun parse(tag: Array<String>): TranslationArtifactVersionMetadataTag? {
ensure(tag.has(3)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return TranslationArtifactVersionMetadataTag(
name = tag[1],
visibility = tag[2],
license = tag[3],
)
}
fun assemble(
name: String,
visibility: String,
license: String,
): Array<String> = arrayOf(
TAG_NAME,
name,
visibility,
license
)
fun assemble(translationArtifactVersionMetadataTag: TranslationArtifactVersionMetadataTag) = assemble(
name = translationArtifactVersionMetadataTag.name,
visibility = translationArtifactVersionMetadataTag.visibility,
license = translationArtifactVersionMetadataTag.license
)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class UrlTag(
val url: String,
) {
fun toTagArray() = assemble(url = url)
companion object {
const val TAG_NAME = "url"
fun parse(tag: Array<String>): UrlTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return UrlTag(url = tag[1])
}
fun assemble(url: String): Array<String> = arrayOf(TAG_NAME, url)
fun assemble(urlTag: UrlTag) = assemble(url = urlTag.url)
}
}

View File

@@ -0,0 +1,25 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class VisibilityTag(
val visibility: String,
) {
fun toTagArray() = assemble(visibility = visibility)
companion object {
const val TAG_NAME = "visibility"
fun parse(tag: Array<String>): VisibilityTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return VisibilityTag(visibility = tag[1])
}
fun assemble(visibility: String): Array<String> = arrayOf(TAG_NAME, visibility)
fun assemble(visibilityTag: VisibilityTag) = assemble(visibility = visibilityTag.visibility)
}
}