diff --git a/src/main/scala/Components.scala b/src/main/scala/Components.scala index 08ff3ab..4b8126b 100644 --- a/src/main/scala/Components.scala +++ b/src/main/scala/Components.scala @@ -9,12 +9,55 @@ import io.circe.syntax.* import calico.* import calico.html.io.{*, given} import calico.syntax.* +import scodec.bits.ByteVector import scoin.* import snow.* import Utils.* object Components { + def render32Bytes(bytes32: ByteVector32): Resource[IO, HtmlDivElement[IO]] = + div( + cls := "text-md", + entry("canonical hex", bytes32.toHex), + "if this is a public key:", + div( + cls := "pl-2 mb-2", + entry( + "npub", + NIP19.encode(XOnlyPublicKey(bytes32)) + ), + entry( + "nprofile", + NIP19.encode(ProfilePointer(XOnlyPublicKey(bytes32))) + ) + ), + "if this is a private key:", + div( + cls := "pl-2 mb-2", + entry( + "nsec", + NIP19.encode(PrivateKey(bytes32)) + ), + entry( + "npub", + NIP19.encode(XOnlyPublicKey(bytes32)) + ), + entry( + "nprofile", + NIP19.encode(ProfilePointer(XOnlyPublicKey(bytes32))) + ) + ), + "if this is an event id:", + div( + cls := "pl-2 mb-2", + entry( + "nevent", + NIP19.encode(EventPointer(bytes32.toHex)) + ) + ) + ) + def renderEventPointer( evp: snow.EventPointer ): Resource[IO, HtmlDivElement[IO]] = diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 06bf8f3..fe17d60 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -90,10 +90,11 @@ object Main extends IOWebApp { div( cls := "w-full flex my-5", store.result.map { - case Left(msg) => div(msg) - case Right(event: Event) => renderEvent(event, store) - case Right(pp: ProfilePointer) => renderProfilePointer(pp) - case Right(evp: EventPointer) => renderEventPointer(evp) + case Left(msg) => div(msg) + case Right(bytes: ByteVector32) => render32Bytes(bytes) + case Right(event: Event) => renderEvent(event, store) + case Right(pp: ProfilePointer) => renderProfilePointer(pp) + case Right(evp: EventPointer) => renderEventPointer(evp) case Right(sk: PrivateKey) => renderProfilePointer( ProfilePointer(pubkey = sk.publicKey.xonly), diff --git a/src/main/scala/Parser.scala b/src/main/scala/Parser.scala index cb30d19..2de17f4 100644 --- a/src/main/scala/Parser.scala +++ b/src/main/scala/Parser.scala @@ -1,33 +1,41 @@ +import scala.util.Try import io.circe.parser.* import cats.syntax.all.* +import scodec.bits.ByteVector import scoin.* import snow.* type Result = Either[ String, - Event | PrivateKey | AddressPointer | EventPointer | ProfilePointer + Event | PrivateKey | AddressPointer | EventPointer | ProfilePointer | + ByteVector32 ] object Parser { def parseInput(input: String): Result = - 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" - } - } + ByteVector + .fromHex(input) + .flatMap(b => Try(Right(ByteVector32(b))).toOption) + .getOrElse( + 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" + } + } + } } - } + ) } diff --git a/src/main/scala/Store.scala b/src/main/scala/Store.scala index 7817602..35dadd8 100644 --- a/src/main/scala/Store.scala +++ b/src/main/scala/Store.scala @@ -37,7 +37,7 @@ object Store { _ <- input.discrete .evalTap(input => IO.cede *> window.localStorage.setItem(key, input)) - .evalTap(input => result.set(Parser.parseInput(input))) + .evalTap(input => result.set(Parser.parseInput(input.trim()))) .compile .drain .background