feat: implement full NIP-52 calendar spec compliance
- CalendarDateSlotEvent (31922): Add title, summary, image, geohash, hashtags, participants, references, multiple locations accessors and build() method with DSL builder pattern - CalendarTimeSlotEvent (31923): Fix timezone bug (startTzId/endTzId were parsed as Long instead of String), add all accessors like DateSlot, rename startTmz/endTmz to startTzId/endTzId, add build() - CalendarRSVPEvent (31925): Remove incorrect location/start/end accessors (not in spec), add status/freebusy/calendarEventAddress/ calendarEventId/calendarEventAuthor accessors, update from outdated L/l label format to current status/fb tag format, add build() - CalendarEvent (31924): Add title and calendarEventAddresses accessors, add build() method - Create NIP-52 tag classes: LocationTag, RSVPStatusTag (with enum), FreeBusyTag (with enum) - Create TagArrayBuilderExt.kt with DSL builder extensions for all four calendar event types https://claude.ai/code/session_01UMXgE3tNftmqNZqcYwF2kL
This commit is contained in:
+45
-10
@@ -23,10 +23,22 @@ package com.vitorpamplona.quartz.nip52Calendar
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.LocationTag
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class CalendarDateSlotEvent(
|
class CalendarDateSlotEvent(
|
||||||
@@ -37,25 +49,48 @@ class CalendarDateSlotEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
fun location() = tags.firstTagValue("location")
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun location() = tags.firstNotNullOfOrNull(LocationTag::parse)
|
||||||
|
|
||||||
|
fun locations() = tags.mapNotNull(LocationTag::parse)
|
||||||
|
|
||||||
fun start() = tags.firstTagValue("start")
|
fun start() = tags.firstTagValue("start")
|
||||||
|
|
||||||
fun end() = tags.firstTagValue("end")
|
fun end() = tags.firstTagValue("end")
|
||||||
|
|
||||||
// ["start", "<YYYY-MM-DD>"],
|
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||||
// ["end", "<YYYY-MM-DD>"],
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun geohash() = tags.firstNotNullOfOrNull(GeoHashTag::parse)
|
||||||
|
|
||||||
|
fun hashtags() = tags.hashtags()
|
||||||
|
|
||||||
|
fun participants() = tags.mapNotNull(PTag::parse)
|
||||||
|
|
||||||
|
fun references() = tags.references()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KIND = 31922
|
const val KIND = 31922
|
||||||
const val ALT = "Full-day calendar event"
|
const val ALT = "Full-day calendar event"
|
||||||
|
|
||||||
suspend fun create(
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
signer: NostrSigner,
|
fun build(
|
||||||
|
title: String,
|
||||||
|
start: String,
|
||||||
|
content: String = "",
|
||||||
|
end: String? = null,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): CalendarDateSlotEvent {
|
initializer: TagArrayBuilder<CalendarDateSlotEvent>.() -> Unit = {},
|
||||||
val tags = arrayOf(AltTag.assemble(ALT))
|
) = eventTemplate(KIND, content, createdAt) {
|
||||||
return signer.sign(createdAt, KIND, tags, "")
|
dTag(dTag)
|
||||||
|
title(title)
|
||||||
|
startDate(start)
|
||||||
|
end?.let { endDate(it) }
|
||||||
|
alt(ALT)
|
||||||
|
initializer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-7
@@ -23,9 +23,15 @@ package com.vitorpamplona.quartz.nip52Calendar
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.aTag.taggedAddresses
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class CalendarEvent(
|
class CalendarEvent(
|
||||||
@@ -36,16 +42,26 @@ class CalendarEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun calendarEventAddresses() = taggedAddresses()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KIND = 31924
|
const val KIND = 31924
|
||||||
const val ALT = "Calendar"
|
const val ALT = "Calendar"
|
||||||
|
|
||||||
suspend fun create(
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
signer: NostrSigner,
|
fun build(
|
||||||
|
title: String,
|
||||||
|
content: String = "",
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): CalendarEvent {
|
initializer: TagArrayBuilder<CalendarEvent>.() -> Unit = {},
|
||||||
val tags = arrayOf(AltTag.assemble(ALT))
|
) = eventTemplate(KIND, content, createdAt) {
|
||||||
return signer.sign(createdAt, KIND, tags, "")
|
dTag(dTag)
|
||||||
|
title(title)
|
||||||
|
alt(ALT)
|
||||||
|
initializer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-15
@@ -23,10 +23,21 @@ package com.vitorpamplona.quartz.nip52Calendar
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.aTag.aTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.aTag.firstTaggedAddress
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.FreeBusyTag
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.RSVPStatusTag
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class CalendarRSVPEvent(
|
class CalendarRSVPEvent(
|
||||||
@@ -37,27 +48,42 @@ class CalendarRSVPEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
fun location() = tags.firstTagValue("location")
|
fun status() = tags.firstNotNullOfOrNull(RSVPStatusTag::parse)
|
||||||
|
|
||||||
fun start() = tags.firstTagValue("start")
|
fun statusValue() = tags.firstNotNullOfOrNull(RSVPStatusTag::parseValue)
|
||||||
|
|
||||||
fun end() = tags.firstTagValue("end")
|
fun freebusy() = tags.firstNotNullOfOrNull(FreeBusyTag::parse)
|
||||||
|
|
||||||
// ["L", "status"],
|
fun calendarEventAddress() = firstTaggedAddress()
|
||||||
// ["l", "<accepted/declined/tentative>", "status"],
|
|
||||||
// ["L", "freebusy"],
|
fun calendarEventId() = firstTaggedEvent()
|
||||||
// ["l", "<free/busy>", "freebusy"]
|
|
||||||
|
fun calendarEventAuthor() = tags.firstNotNullOfOrNull(PTag::parse)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KIND = 31925
|
const val KIND = 31925
|
||||||
const val ALT = "Calendar event's invitation response"
|
const val ALT = "Calendar event's invitation response"
|
||||||
|
|
||||||
suspend fun create(
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
signer: NostrSigner,
|
fun build(
|
||||||
|
calendarEventAddress: ATag,
|
||||||
|
status: RSVPStatusTag.STATUS,
|
||||||
|
content: String = "",
|
||||||
|
calendarEventId: ETag? = null,
|
||||||
|
calendarEventAuthor: PTag? = null,
|
||||||
|
freebusy: FreeBusyTag.STATUS? = null,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): CalendarRSVPEvent {
|
initializer: TagArrayBuilder<CalendarRSVPEvent>.() -> Unit = {},
|
||||||
val tags = arrayOf(AltTag.assemble(ALT))
|
) = eventTemplate(KIND, content, createdAt) {
|
||||||
return signer.sign(createdAt, KIND, tags, "")
|
dTag(dTag)
|
||||||
|
aTag(calendarEventAddress)
|
||||||
|
status(status)
|
||||||
|
calendarEventId?.let { add(it.toTagArray()) }
|
||||||
|
calendarEventAuthor?.let { add(it.toTagArray()) }
|
||||||
|
freebusy?.let { freebusy(it) }
|
||||||
|
alt(ALT)
|
||||||
|
initializer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-14
@@ -23,11 +23,23 @@ package com.vitorpamplona.quartz.nip52Calendar
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueAsLong
|
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueAsLong
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.LocationTag
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class CalendarTimeSlotEvent(
|
class CalendarTimeSlotEvent(
|
||||||
@@ -38,31 +50,56 @@ class CalendarTimeSlotEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
fun location() = tags.firstTagValue("location")
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun location() = tags.firstNotNullOfOrNull(LocationTag::parse)
|
||||||
|
|
||||||
|
fun locations() = tags.mapNotNull(LocationTag::parse)
|
||||||
|
|
||||||
fun start() = tags.firstTagValueAsLong("start")
|
fun start() = tags.firstTagValueAsLong("start")
|
||||||
|
|
||||||
fun end() = tags.firstTagValueAsLong("end")
|
fun end() = tags.firstTagValueAsLong("end")
|
||||||
|
|
||||||
fun startTmz() = tags.firstTagValueAsLong("start_tzid")
|
fun startTzId() = tags.firstTagValue("start_tzid")
|
||||||
|
|
||||||
fun endTmz() = tags.firstTagValueAsLong("end_tzid")
|
fun endTzId() = tags.firstTagValue("end_tzid")
|
||||||
|
|
||||||
// ["start", "<Unix timestamp in seconds>"],
|
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||||
// ["end", "<Unix timestamp in seconds>"],
|
|
||||||
// ["start_tzid", "<IANA Time Zone Database identifier>"],
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
// ["end_tzid", "<IANA Time Zone Database identifier>"],
|
|
||||||
|
fun geohash() = tags.firstNotNullOfOrNull(GeoHashTag::parse)
|
||||||
|
|
||||||
|
fun hashtags() = tags.hashtags()
|
||||||
|
|
||||||
|
fun participants() = tags.mapNotNull(PTag::parse)
|
||||||
|
|
||||||
|
fun references() = tags.references()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KIND = 31923
|
const val KIND = 31923
|
||||||
const val ALT = "Calendar time-slot event"
|
const val ALT = "Calendar time-slot event"
|
||||||
|
|
||||||
suspend fun create(
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
signer: NostrSigner,
|
fun build(
|
||||||
|
title: String,
|
||||||
|
start: Long,
|
||||||
|
content: String = "",
|
||||||
|
end: Long? = null,
|
||||||
|
startTzId: String? = null,
|
||||||
|
endTzId: String? = null,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): CalendarTimeSlotEvent {
|
initializer: TagArrayBuilder<CalendarTimeSlotEvent>.() -> Unit = {},
|
||||||
val tags = arrayOf(AltTag.assemble(ALT))
|
) = eventTemplate(KIND, content, createdAt) {
|
||||||
return signer.sign(createdAt, KIND, tags, "")
|
dTag(dTag)
|
||||||
|
title(title)
|
||||||
|
startTimestamp(start)
|
||||||
|
end?.let { endTimestamp(it) }
|
||||||
|
startTzId?.let { startTzId(it) }
|
||||||
|
endTzId?.let { endTzId(it) }
|
||||||
|
alt(ALT)
|
||||||
|
initializer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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.nip52Calendar
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||||
|
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.FreeBusyTag
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.LocationTag
|
||||||
|
import com.vitorpamplona.quartz.nip52Calendar.tags.RSVPStatusTag
|
||||||
|
|
||||||
|
// CalendarDateSlotEvent builder extensions
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.startDate(date: String) = addUnique(arrayOf("start", date))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.endDate(date: String) = addUnique(arrayOf("end", date))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.location(location: String) = add(LocationTag.assemble(location))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.locations(locations: List<String>) = addAll(locations.map { LocationTag.assemble(it) })
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.geohash(geohash: String) = addAll(GeoHashTag.assemble(geohash).toList())
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.participant(p: PTag) = add(p.toTagArray())
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarDateSlotEvent>.participants(ps: List<PTag>) = addAll(ps.map { it.toTagArray() })
|
||||||
|
|
||||||
|
// CalendarTimeSlotEvent builder extensions
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.startTimestamp(timestamp: Long) = addUnique(arrayOf("start", timestamp.toString()))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.endTimestamp(timestamp: Long) = addUnique(arrayOf("end", timestamp.toString()))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.startTzId(tzId: String) = addUnique(arrayOf("start_tzid", tzId))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.endTzId(tzId: String) = addUnique(arrayOf("end_tzid", tzId))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.location(location: String) = add(LocationTag.assemble(location))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.locations(locations: List<String>) = addAll(locations.map { LocationTag.assemble(it) })
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.geohash(geohash: String) = addAll(GeoHashTag.assemble(geohash).toList())
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.participant(p: PTag) = add(p.toTagArray())
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarTimeSlotEvent>.participants(ps: List<PTag>) = addAll(ps.map { it.toTagArray() })
|
||||||
|
|
||||||
|
// CalendarEvent builder extensions
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
// CalendarRSVPEvent builder extensions
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarRSVPEvent>.status(status: RSVPStatusTag.STATUS) = addUnique(status.toTagArray())
|
||||||
|
|
||||||
|
fun TagArrayBuilder<CalendarRSVPEvent>.freebusy(fb: FreeBusyTag.STATUS) = addUnique(fb.toTagArray())
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.nip52Calendar.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class FreeBusyTag {
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "fb"
|
||||||
|
|
||||||
|
fun parse(tag: Array<String>): STATUS? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return STATUS.entries.firstOrNull { it.value == tag[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class STATUS(
|
||||||
|
val value: String,
|
||||||
|
) {
|
||||||
|
FREE("free"),
|
||||||
|
BUSY("busy"),
|
||||||
|
;
|
||||||
|
|
||||||
|
fun toTagArray() = arrayOf(TAG_NAME, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -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.nip52Calendar.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class LocationTag {
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "location"
|
||||||
|
|
||||||
|
fun parse(tag: Array<String>): String? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return tag[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assemble(locationName: String) = arrayOf(TAG_NAME, locationName)
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.nip52Calendar.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class RSVPStatusTag {
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "status"
|
||||||
|
|
||||||
|
fun parse(tag: Array<String>): STATUS? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return STATUS.entries.firstOrNull { it.value == tag[1] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseValue(tag: Array<String>): String? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return tag[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class STATUS(
|
||||||
|
val value: String,
|
||||||
|
) {
|
||||||
|
ACCEPTED("accepted"),
|
||||||
|
DECLINED("declined"),
|
||||||
|
TENTATIVE("tentative"),
|
||||||
|
;
|
||||||
|
|
||||||
|
fun toTagArray() = arrayOf(TAG_NAME, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user