mirror of
https://github.com/fiatjaf/nak.git
synced 2024-11-22 08:19:06 -05:00
nip19 parsing.
This commit is contained in:
parent
b966c8a1ac
commit
d54b776c38
86
src/main/scala/Components.scala
Normal file
86
src/main/scala/Components.scala
Normal file
|
@ -0,0 +1,86 @@
|
|||
import cats.data.*
|
||||
import cats.effect.*
|
||||
import cats.effect.syntax.all.*
|
||||
import cats.syntax.all.*
|
||||
import fs2.concurrent.*
|
||||
import fs2.dom.{Event => _, *}
|
||||
import io.circe.parser.*
|
||||
import io.circe.syntax.*
|
||||
import calico.*
|
||||
import calico.html.io.{*, given}
|
||||
import calico.syntax.*
|
||||
import scoin.*
|
||||
import snow.*
|
||||
|
||||
object Components {
|
||||
def renderEventPointer(
|
||||
evp: snow.EventPointer
|
||||
): Resource[IO, HtmlDivElement[IO]] =
|
||||
div(
|
||||
cls := "text-md",
|
||||
entry("event id (hex)", evp.id),
|
||||
if evp.relays.size > 0 then
|
||||
Some(entry("relay hints", evp.relays.reduce((a, b) => s"$a, $b")))
|
||||
else None,
|
||||
evp.author.map { pk =>
|
||||
entry("author hint (pubkey hex)", pk.value.toHex)
|
||||
}
|
||||
)
|
||||
|
||||
def renderProfilePointer(
|
||||
pp: snow.ProfilePointer,
|
||||
sk: Option[PrivateKey] = None
|
||||
): Resource[IO, HtmlDivElement[IO]] =
|
||||
div(
|
||||
cls := "text-md",
|
||||
sk.map { k => entry("private key (hex)", k.value.toHex) },
|
||||
entry("public key (hex)", pp.pubkey.value.toHex),
|
||||
if pp.relays.size > 0 then
|
||||
Some(entry("relay hints", pp.relays.reduce((a, b) => s"$a, $b")))
|
||||
else None
|
||||
)
|
||||
|
||||
def renderAddressPointer(
|
||||
addr: snow.AddressPointer
|
||||
): Resource[IO, HtmlDivElement[IO]] =
|
||||
div(
|
||||
cls := "text-md",
|
||||
entry("author (pubkey hex)", addr.author.value.toHex),
|
||||
entry("identifier", addr.d),
|
||||
entry("kind", addr.kind.toString),
|
||||
if addr.relays.size > 0 then
|
||||
Some(entry("relay hints", addr.relays.reduce((a, b) => s"$a, $b")))
|
||||
else None
|
||||
)
|
||||
|
||||
def renderEvent(event: Event): Resource[IO, HtmlDivElement[IO]] =
|
||||
div(
|
||||
cls := "text-md",
|
||||
List(("pubkey", event.pubkey), ("id", event.id), ("sig", event.sig))
|
||||
.filter((_, v) => v.isEmpty)
|
||||
.map { (label, _) => entry("property missing", label) },
|
||||
entry("serialized event", event.serialized),
|
||||
entry("implied event id", event.hash.toHex),
|
||||
entry(
|
||||
"does the implied event id match the given event id?",
|
||||
event.id == Some(event.hash.toHex) match {
|
||||
case true => "yes"; case false => "no"
|
||||
}
|
||||
),
|
||||
entry(
|
||||
"is signature valid?",
|
||||
event.isValid match {
|
||||
case true => "yes"; case false => "no"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
private def entry(
|
||||
key: String,
|
||||
value: String
|
||||
): Resource[IO, HtmlDivElement[IO]] =
|
||||
div(
|
||||
span(cls := "font-bold", key + " "),
|
||||
span(Styles.mono, value)
|
||||
)
|
||||
}
|
|
@ -13,6 +13,7 @@ import scoin.*
|
|||
import snow.*
|
||||
|
||||
import Utils.*
|
||||
import Components.*
|
||||
|
||||
object Store {
|
||||
def apply(window: Window[IO]): Resource[IO, Store] = {
|
||||
|
@ -105,6 +106,7 @@ object Main extends IOWebApp {
|
|||
(
|
||||
cls := "w-full max-h-96 p-3 rounded",
|
||||
styleAttr := "min-height: 280px; font-family: monospace",
|
||||
spellCheck := false,
|
||||
placeholder := "paste something nostric",
|
||||
onInput --> (_.foreach(_ =>
|
||||
self.value.get.flatMap(store.input.set)
|
||||
|
@ -121,47 +123,18 @@ object Main extends IOWebApp {
|
|||
store.input.map { input =>
|
||||
if input.trim() == "" then div("")
|
||||
else
|
||||
decode[Event](input) match {
|
||||
case Left(err: io.circe.ParsingFailure) =>
|
||||
div("not valid JSON")
|
||||
case Left(err: io.circe.DecodingFailure) =>
|
||||
err.pathToRootString match {
|
||||
case None => div(s"decoding ${err.pathToRootString}")
|
||||
case Some(path) => div(s"field $path is missing or wrong")
|
||||
}
|
||||
case Right(event) =>
|
||||
div(
|
||||
cls := "text-md",
|
||||
div(
|
||||
span(cls := "font-bold", "serialized event "),
|
||||
span(Styles.mono, event.serialized)
|
||||
),
|
||||
div(
|
||||
span(cls := "font-bold", "implied event id "),
|
||||
span(Styles.mono, event.hash.toHex)
|
||||
),
|
||||
div(
|
||||
span(
|
||||
cls := "font-bold",
|
||||
"does the implied event id match the given event id? "
|
||||
),
|
||||
span(
|
||||
Styles.mono,
|
||||
event.id == event.hash.toHex match {
|
||||
case true => "yes"; case false => "no"
|
||||
}
|
||||
)
|
||||
),
|
||||
div(
|
||||
span(cls := "font-bold", "is signature valid? "),
|
||||
span(
|
||||
Styles.mono,
|
||||
event.isValid match {
|
||||
case true => "yes"; case false => "no"
|
||||
}
|
||||
)
|
||||
)
|
||||
Parser.parseInput(input) match {
|
||||
case Left(msg) => div(msg)
|
||||
case Right(event: Event) =>
|
||||
renderEvent(event)
|
||||
case Right(pp: ProfilePointer) => renderProfilePointer(pp)
|
||||
case Right(evp: EventPointer) => renderEventPointer(evp)
|
||||
case Right(sk: PrivateKey) =>
|
||||
renderProfilePointer(
|
||||
ProfilePointer(pubkey = sk.publicKey.xonly),
|
||||
Some(sk)
|
||||
)
|
||||
case Right(addr: AddressPointer) => renderAddressPointer(addr)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
33
src/main/scala/Parser.scala
Normal file
33
src/main/scala/Parser.scala
Normal file
|
@ -0,0 +1,33 @@
|
|||
import io.circe.parser.*
|
||||
import cats.syntax.all.*
|
||||
import scoin.*
|
||||
import snow.*
|
||||
|
||||
import Components.*
|
||||
|
||||
object Parser {
|
||||
def parseInput(input: String): Either[
|
||||
String,
|
||||
Event | PrivateKey | AddressPointer | EventPointer | ProfilePointer
|
||||
] =
|
||||
NIP19.decode(input) match {
|
||||
case Right(pp: ProfilePointer) => Right(pp)
|
||||
case Right(evp: EventPointer) => Right(evp)
|
||||
case Right(sk: PrivateKey) => Right(sk)
|
||||
case Right(addr: AddressPointer) => Right(addr)
|
||||
case Left(_) =>
|
||||
parse(input) match {
|
||||
case Left(err: io.circe.ParsingFailure) =>
|
||||
Left("not valid JSON or NIP-19 code")
|
||||
case Right(json) =>
|
||||
json
|
||||
.as[Event]
|
||||
.leftMap { err =>
|
||||
err.pathToRootString match {
|
||||
case None => s"decoding ${err.pathToRootString}"
|
||||
case Some(path) => s"field $path is missing or wrong"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user