From 4662cb75d067ec85b0e68fe029a4e568492069f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Jun 2026 20:57:41 +0000 Subject: [PATCH] refactor(roadstr): match NIP-88 polls architecture; register in LocalCache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the experimental roadstr events to mirror the nip88Polls file layout, and wire the kinds into LocalCache (previously unhandled, so they hit the "Event Not Supported" fallback and were dropped). - Split into report/ and confirmation/ sub-packages, each with XEvent.kt + TagArrayBuilderExt.kt + TagArrayExt.kt + tags/, matching nip88Polls/{poll,response}. build() now uses eventTemplate + typed builder-ext functions; accessors delegate to TagArray ext functions. - Bundle each enum into its tag class (RoadEventType in RoadEventTypeTag, RoadEventStatus in RoadEventStatusTag), like PollType in PollTypeTag. - Add RoadReportTag (a GenericETag `e`-reference) mirroring PollTag, and have RoadEventConfirmationEvent implement EventHintProvider so the referenced report is linkable — parallel to PollResponseEvent. - Shared lat/lon/geohash tag codecs live in roadstr/tags/ with generic builder/parse extensions. - LocalCache: consume kinds 1315/1316 as regular events. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Cn4Lk53CcXqYFYHMut4YfY --- .../amethyst/model/LocalCache.kt | 10 ++ .../amethyst/ui/note/NoteCompose.kt | 4 +- .../amethyst/ui/note/types/RoadEvent.kt | 8 +- .../loggedIn/threadview/ThreadFeedView.kt | 4 +- .../RoadEventConfirmationEvent.kt | 77 ++++++++------- .../confirmation/TagArrayBuilderExt.kt | 35 +++++++ .../roadstr/confirmation/TagArrayExt.kt | 29 ++++++ .../tags/RoadEventStatusTag.kt | 16 +++- .../confirmation/tags/RoadReportTag.kt | 96 +++++++++++++++++++ .../{ => report}/RoadEventReportEvent.kt | 50 ++++------ .../TagArrayBuilderExt.kt} | 17 +--- .../roadstr/report/TagArrayExt.kt | 28 ++++++ .../tags/RoadEventTypeTag.kt} | 31 +++++- .../roadstr/tags/TagArrayBuilderExt.kt | 46 +++++++++ .../experimental/roadstr/tags/TagArrayExt.kt | 27 ++++++ .../quartz/utils/EventFactory.kt | 4 +- .../experimental/roadstr/RoadEventTest.kt | 4 + 17 files changed, 400 insertions(+), 86 deletions(-) rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/{ => confirmation}/RoadEventConfirmationEvent.kt (60%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayExt.kt rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/{ => confirmation}/tags/RoadEventStatusTag.kt (80%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadReportTag.kt rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/{ => report}/RoadEventReportEvent.kt (69%) rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/{RoadEventStatus.kt => report/TagArrayBuilderExt.kt} (73%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayExt.kt rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/{RoadEventType.kt => report/tags/RoadEventTypeTag.kt} (66%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayExt.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index ff7f498420..d82d2c221a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -75,6 +75,8 @@ import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent import com.vitorpamplona.quartz.experimental.nns.NNSEvent import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent @@ -3870,6 +3872,14 @@ object LocalCache : ILocalCache, ICacheProvider { consume(event, relay, wasVerified) } + is RoadEventReportEvent -> { + consumeRegularEvent(event, relay, wasVerified) + } + + is RoadEventConfirmationEvent -> { + consumeRegularEvent(event, relay, wasVerified) + } + is RelayDiscoveryEvent -> { consumeBaseReplaceable(event, relay, wasVerified) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index cd8b844561..5202720733 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -237,8 +237,8 @@ import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAss import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.isNip82SoftwareRelease import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventConfirmationEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RoadEvent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RoadEvent.kt index 952e527412..d560d3d173 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RoadEvent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RoadEvent.kt @@ -40,10 +40,10 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.note.creators.location.LoadCityName import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.replyModifier -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventConfirmationEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventReportEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventStatus -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventType +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatus +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventType /** Emoji marker for a road event category — keeps the card icon-light and locale-independent. */ private fun RoadEventType.emoji(): String = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt index ec168e13f9..2820260a0e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt @@ -252,8 +252,8 @@ import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.Softw import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.isNip82SoftwareRelease import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventConfirmationEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventConfirmationEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/RoadEventConfirmationEvent.kt similarity index 60% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventConfirmationEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/RoadEventConfirmationEvent.kt index bec8fd80f3..9259df9d45 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventConfirmationEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/RoadEventConfirmationEvent.kt @@ -18,24 +18,25 @@ * 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.experimental.roadstr +package com.vitorpamplona.quartz.experimental.roadstr.confirmation import androidx.compose.runtime.Immutable -import com.vitorpamplona.quartz.experimental.roadstr.tags.LatitudeTag -import com.vitorpamplona.quartz.experimental.roadstr.tags.LongitudeTag -import com.vitorpamplona.quartz.experimental.roadstr.tags.RoadEventStatusTag +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatus +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadReportTag +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.tags.coordinates +import com.vitorpamplona.quartz.experimental.roadstr.tags.latitude +import com.vitorpamplona.quartz.experimental.roadstr.tags.longitude +import com.vitorpamplona.quartz.experimental.roadstr.tags.roadGeohashes import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder -import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate -import com.vitorpamplona.quartz.nip01Core.tags.events.ETag -import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash -import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.nip31Alts.alt -import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag import com.vitorpamplona.quartz.nip40Expiration.expiration import com.vitorpamplona.quartz.utils.TimeUtils @@ -46,7 +47,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils * . * * Tags (canonical order): - * - `e` — the referenced report event id. + * - `e` — the referenced report event id ([RoadReportTag]). * - `status` — [RoadEventStatus.STILL_THERE] (confirm) or [RoadEventStatus.NO_LONGER_THERE] (deny). * - `g` / `lat` / `lon` — optional copy of the report's location, so confirmations * are reachable through the same `#g` geohash queries as reports. @@ -61,11 +62,16 @@ class RoadEventConfirmationEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - /** The referenced report event id from the first `e` tag. */ - fun reportId() = tags.firstNotNullOfOrNull(ETag::parse)?.eventId +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + EventHintProvider { + override fun eventHints() = tags.mapNotNull(RoadReportTag::parseAsHint) - fun status() = tags.firstNotNullOfOrNull(RoadEventStatusTag::parse) + override fun linkedEventIds() = tags.mapNotNull(RoadReportTag::parseId) + + /** The referenced report event id from the first `e` tag. */ + fun reportId() = tags.roadReport()?.eventId + + fun status() = tags.roadEventStatus() fun isConfirmation() = status() == RoadEventStatus.STILL_THERE @@ -73,13 +79,13 @@ class RoadEventConfirmationEvent( fun geohashes() = tags.geohashes() - fun latitude() = tags.firstNotNullOfOrNull(LatitudeTag::parse) + fun latitude() = tags.latitude() - fun longitude() = tags.firstNotNullOfOrNull(LongitudeTag::parse) + fun longitude() = tags.longitude() fun alt() = tags.firstNotNullOfOrNull(AltTag::parse) - fun relayExpiration() = tags.firstNotNullOfOrNull(ExpirationTag::parse) + fun relayExpiration() = tags.expiration() companion object { const val KIND = 1316 @@ -92,21 +98,28 @@ class RoadEventConfirmationEvent( longitude: Double? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, - ): EventTemplate = - eventTemplate(KIND, "", createdAt) { - add(ETag.assemble(reportId, null, null)) - add(RoadEventStatusTag.assemble(status)) - if (latitude != null && longitude != null) { - val geohash6 = GeoHash.encode(latitude, longitude, 6).toString() - add(GeoHashTag.assembleSingle(geohash6.substring(0, 4))) - add(GeoHashTag.assembleSingle(geohash6.substring(0, 5))) - add(GeoHashTag.assembleSingle(geohash6)) - add(LatitudeTag.assemble(latitude)) - add(LongitudeTag.assemble(longitude)) - } - expiration(createdAt + RoadEventReportEvent.RELAY_TTL_SECONDS) - alt(ALT_DESCRIPTION) - initializer() + ) = eventTemplate(KIND, "", createdAt) { + report(reportId) + status(status) + if (latitude != null && longitude != null) { + roadGeohashes(latitude, longitude) + coordinates(latitude, longitude) } + expiration(createdAt + RoadEventReportEvent.RELAY_TTL_SECONDS) + alt(ALT_DESCRIPTION) + initializer() + } + + fun build( + report: EventHintBundle, + status: RoadEventStatus, + latitude: Double? = null, + longitude: Double? = null, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = build(report.event.id, status, latitude, longitude, createdAt) { + report(report) + initializer() + } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayBuilderExt.kt new file mode 100644 index 0000000000..8d5a0debd7 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayBuilderExt.kt @@ -0,0 +1,35 @@ +/* + * 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.experimental.roadstr.confirmation + +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatus +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatusTag +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadReportTag +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle + +fun TagArrayBuilder.report(reportId: HexKey) = addUnique(RoadReportTag.assemble(reportId, null, null)) + +fun TagArrayBuilder.report(reportHint: EventHintBundle) = addUnique(RoadReportTag.assemble(reportHint)) + +fun TagArrayBuilder.status(status: RoadEventStatus) = addUnique(RoadEventStatusTag.assemble(status)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayExt.kt new file mode 100644 index 0000000000..6b413829f6 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/TagArrayExt.kt @@ -0,0 +1,29 @@ +/* + * 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.experimental.roadstr.confirmation + +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatusTag +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadReportTag +import com.vitorpamplona.quartz.nip01Core.core.TagArray + +fun TagArray.roadReport() = firstNotNullOfOrNull(RoadReportTag::parse) + +fun TagArray.roadEventStatus() = firstNotNullOfOrNull(RoadEventStatusTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/RoadEventStatusTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadEventStatusTag.kt similarity index 80% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/RoadEventStatusTag.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadEventStatusTag.kt index da3c29b4e7..87ca3f0f3e 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/RoadEventStatusTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadEventStatusTag.kt @@ -18,12 +18,24 @@ * 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.experimental.roadstr.tags +package com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventStatus import com.vitorpamplona.quartz.nip01Core.core.has import com.vitorpamplona.quartz.utils.ensure +/** Confirmation status carried in the `status` tag of a Roadstr confirmation (kind 1316). */ +enum class RoadEventStatus( + val code: String, +) { + STILL_THERE("still_there"), + NO_LONGER_THERE("no_longer_there"), + ; + + companion object { + fun fromCode(code: String?): RoadEventStatus? = entries.firstOrNull { it.code == code } + } +} + /** The `status` tag of a Roadstr confirmation (kind 1316): `still_there` or `no_longer_there`. */ class RoadEventStatusTag { companion object { diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadReportTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadReportTag.kt new file mode 100644 index 0000000000..f4c3bd5597 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/confirmation/tags/RoadReportTag.kt @@ -0,0 +1,96 @@ +/* + * 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.experimental.roadstr.confirmation.tags + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer +import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag +import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent +import com.vitorpamplona.quartz.utils.arrayOfNotNull +import com.vitorpamplona.quartz.utils.ensure + +/** The `e` tag of a Roadstr confirmation (kind 1316): the referenced report event. */ +@Immutable +data class RoadReportTag( + override val eventId: HexKey, +) : GenericETag { + override var relay: NormalizedRelayUrl? = null + override var author: HexKey? = null + + constructor(eventId: HexKey, relayHint: NormalizedRelayUrl? = null, authorPubKeyHex: HexKey? = null) : this(eventId) { + this.relay = relayHint + this.author = authorPubKeyHex + } + + fun toNEvent(): String = NEvent.create(eventId, author, RoadEventReportEvent.KIND, relay) + + override fun toTagArray() = assemble(eventId, relay, author) + + companion object { + const val TAG_NAME = "e" + + fun isTagged(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64 + + fun parse(tag: Array): RoadReportTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + + val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) } + + return RoadReportTag(tag[1], hint, tag.getOrNull(3)) + } + + fun parseId(tag: Array): String? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + return tag[1] + } + + fun parseAsHint(tag: Array): EventIdHint? { + ensure(tag.has(2)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + ensure(tag[2].isNotEmpty()) { return null } + + val hint = RelayUrlNormalizer.normalizeOrNull(tag[2]) + + ensure(hint != null) { return null } + + return EventIdHint(tag[1], hint) + } + + fun assemble( + eventId: HexKey, + relay: NormalizedRelayUrl?, + author: HexKey?, + ) = arrayOfNotNull(TAG_NAME, eventId, relay?.url, author) + + fun assemble(eventHint: EventHintBundle) = assemble(eventHint.event.id, eventHint.relay, eventHint.event.pubKey) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventReportEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/RoadEventReportEvent.kt similarity index 69% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventReportEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/RoadEventReportEvent.kt index 1a07d710eb..1d7eb8747c 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventReportEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/RoadEventReportEvent.kt @@ -18,23 +18,21 @@ * 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.experimental.roadstr +package com.vitorpamplona.quartz.experimental.roadstr.report import androidx.compose.runtime.Immutable -import com.vitorpamplona.quartz.experimental.roadstr.tags.LatitudeTag -import com.vitorpamplona.quartz.experimental.roadstr.tags.LongitudeTag +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventType +import com.vitorpamplona.quartz.experimental.roadstr.tags.coordinates +import com.vitorpamplona.quartz.experimental.roadstr.tags.latitude +import com.vitorpamplona.quartz.experimental.roadstr.tags.longitude +import com.vitorpamplona.quartz.experimental.roadstr.tags.roadGeohashes import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder -import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate -import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash -import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes -import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.nip31Alts.alt -import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag import com.vitorpamplona.quartz.nip40Expiration.expiration import com.vitorpamplona.quartz.utils.TimeUtils @@ -64,22 +62,22 @@ class RoadEventReportEvent( content: String, sig: HexKey, ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - /** The raw event-type code from the first `t` tag (may be a value outside [RoadEventType]). */ - fun roadEventTypeCode() = tags.firstNotNullOfOrNull(HashtagTag::parse) - /** The parsed [RoadEventType], or null when the `t` tag is missing/unknown. */ - fun roadEventType() = RoadEventType.fromCode(roadEventTypeCode()) + fun roadEventType() = tags.roadEventType() - /** All `g` geohash tags, finest-to-coarsest as published. */ + /** The raw event-type code from the first `t` tag (may be outside [RoadEventType]). */ + fun roadEventTypeCode() = tags.roadEventTypeCode() + + /** All `g` geohash tags, coarse-to-fine as published. */ fun geohashes() = tags.geohashes() - fun latitude() = tags.firstNotNullOfOrNull(LatitudeTag::parse) + fun latitude() = tags.latitude() - fun longitude() = tags.firstNotNullOfOrNull(LongitudeTag::parse) + fun longitude() = tags.longitude() fun alt() = tags.firstNotNullOfOrNull(AltTag::parse) - fun relayExpiration() = tags.firstNotNullOfOrNull(ExpirationTag::parse) + fun relayExpiration() = tags.expiration() /** Client-side effective expiry (`created_at + ` the type's TTL), or null if the type is unknown. */ fun effectiveExpirationAt(): Long? = roadEventType()?.let { createdAt + it.effectiveTtlSeconds } @@ -103,19 +101,13 @@ class RoadEventReportEvent( comment: String = "", createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, - ): EventTemplate { - val geohash6 = GeoHash.encode(latitude, longitude, 6).toString() - return eventTemplate(KIND, comment, createdAt) { - add(HashtagTag.assemble(type.code)) - add(GeoHashTag.assembleSingle(geohash6.substring(0, 4))) - add(GeoHashTag.assembleSingle(geohash6.substring(0, 5))) - add(GeoHashTag.assembleSingle(geohash6)) - add(LatitudeTag.assemble(latitude)) - add(LongitudeTag.assemble(longitude)) - expiration(createdAt + RELAY_TTL_SECONDS) - alt(altDescription(type)) - initializer() - } + ) = eventTemplate(KIND, comment, createdAt) { + roadEventType(type) + roadGeohashes(latitude, longitude) + coordinates(latitude, longitude) + expiration(createdAt + RELAY_TTL_SECONDS) + alt(altDescription(type)) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventStatus.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayBuilderExt.kt similarity index 73% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventStatus.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayBuilderExt.kt index d8f4bfb63d..ff4713a50a 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventStatus.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayBuilderExt.kt @@ -18,17 +18,10 @@ * 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.experimental.roadstr +package com.vitorpamplona.quartz.experimental.roadstr.report -/** Confirmation status carried in the `status` tag of a Roadstr confirmation (kind 1316). */ -enum class RoadEventStatus( - val code: String, -) { - STILL_THERE("still_there"), - NO_LONGER_THERE("no_longer_there"), - ; +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventType +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventTypeTag +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder - companion object { - fun fromCode(code: String?): RoadEventStatus? = entries.firstOrNull { it.code == code } - } -} +fun TagArrayBuilder.roadEventType(type: RoadEventType) = add(RoadEventTypeTag.assemble(type)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayExt.kt new file mode 100644 index 0000000000..7f6f40ab1f --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/TagArrayExt.kt @@ -0,0 +1,28 @@ +/* + * 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.experimental.roadstr.report + +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventTypeTag +import com.vitorpamplona.quartz.nip01Core.core.TagArray + +fun TagArray.roadEventType() = firstNotNullOfOrNull(RoadEventTypeTag::parse) + +fun TagArray.roadEventTypeCode() = firstNotNullOfOrNull(RoadEventTypeTag::parseCode) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventType.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/tags/RoadEventTypeTag.kt similarity index 66% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventType.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/tags/RoadEventTypeTag.kt index 70ffc66bd9..48188fe5ca 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventType.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/report/tags/RoadEventTypeTag.kt @@ -18,7 +18,10 @@ * 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.experimental.roadstr +package com.vitorpamplona.quartz.experimental.roadstr.report.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure /** * Road event categories carried in the `t` tag of a Roadstr report (kind 1315). @@ -51,3 +54,29 @@ enum class RoadEventType( fun fromCode(code: String?): RoadEventType? = entries.firstOrNull { it.code == code } } } + +/** The `t` tag of a Roadstr report (kind 1315): the [RoadEventType] code. */ +class RoadEventTypeTag { + companion object { + const val TAG_NAME = "t" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + /** Parses the tag into a known [RoadEventType], or null when missing/unknown. */ + fun parse(tag: Array): RoadEventType? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + return RoadEventType.fromCode(tag[1]) + } + + /** Returns the raw `t` value even when it is not a known [RoadEventType]. */ + fun parseCode(tag: Array): 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(type: RoadEventType) = arrayOf(TAG_NAME, type.code) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayBuilderExt.kt new file mode 100644 index 0000000000..a124f24cb4 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayBuilderExt.kt @@ -0,0 +1,46 @@ +/* + * 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.experimental.roadstr.tags + +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash +import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag + +/** Adds `lat`/`lon` tags (7-decimal strings) shared by Roadstr reports and confirmations. */ +fun TagArrayBuilder.coordinates( + latitude: Double, + longitude: Double, +) = apply { + add(LatitudeTag.assemble(latitude)) + add(LongitudeTag.assemble(longitude)) +} + +/** Adds the three `g` geohash tags (precision 4, 5 and 6) derived from [latitude]/[longitude]. */ +fun TagArrayBuilder.roadGeohashes( + latitude: Double, + longitude: Double, +) = apply { + val geohash6 = GeoHash.encode(latitude, longitude, 6).toString() + add(GeoHashTag.assembleSingle(geohash6.substring(0, 4))) + add(GeoHashTag.assembleSingle(geohash6.substring(0, 5))) + add(GeoHashTag.assembleSingle(geohash6)) +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayExt.kt new file mode 100644 index 0000000000..1368797d18 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/roadstr/tags/TagArrayExt.kt @@ -0,0 +1,27 @@ +/* + * 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.experimental.roadstr.tags + +import com.vitorpamplona.quartz.nip01Core.core.TagArray + +fun TagArray.latitude() = firstNotNullOfOrNull(LatitudeTag::parse) + +fun TagArray.longitude() = firstNotNullOfOrNull(LongitudeTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt index 88a320c30f..cdf987c70f 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt @@ -53,8 +53,8 @@ import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent import com.vitorpamplona.quartz.experimental.nns.NNSEvent import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventConfirmationEvent -import com.vitorpamplona.quartz.experimental.roadstr.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageEvent import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventTest.kt index 0ff2c4493a..16a7f82706 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/roadstr/RoadEventTest.kt @@ -20,6 +20,10 @@ */ package com.vitorpamplona.quartz.experimental.roadstr +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.RoadEventConfirmationEvent +import com.vitorpamplona.quartz.experimental.roadstr.confirmation.tags.RoadEventStatus +import com.vitorpamplona.quartz.experimental.roadstr.report.RoadEventReportEvent +import com.vitorpamplona.quartz.experimental.roadstr.report.tags.RoadEventType import com.vitorpamplona.quartz.experimental.roadstr.tags.RoadCoordinate import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash