dynamically adding relay hints to nip19 codes.

This commit is contained in:
fiatjaf 2023-04-25 21:51:43 -03:00
parent b6207ae104
commit d912a28987
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
3 changed files with 101 additions and 42 deletions

View File

@ -22,7 +22,7 @@ object Components {
entry("canonical hex", bytes32.toHex),
"if this is a public key:",
div(
cls := "pl-2 mb-2",
cls := "mt-2 pl-2 mb-2",
entry(
"npub",
NIP19.encode(XOnlyPublicKey(bytes32))
@ -66,14 +66,13 @@ object Components {
)
def renderEventPointer(
store: Store,
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,
relayHints(store, evp.relays),
evp.author.map { pk =>
entry("author hint (pubkey hex)", pk.value.toHex)
},
@ -82,6 +81,7 @@ object Components {
)
def renderProfilePointer(
store: Store,
pp: snow.ProfilePointer,
sk: Option[PrivateKey] = None
): Resource[IO, HtmlDivElement[IO]] =
@ -90,14 +90,13 @@ object Components {
sk.map { k => entry("private key (hex)", k.value.toHex) },
sk.map { k => entry("nsec", NIP19.encode(k)) },
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,
relayHints(store, pp.relays),
entry("npub", NIP19.encode(pp.pubkey)),
nip19_21("nprofile", NIP19.encode(pp))
)
def renderAddressPointer(
store: Store,
addr: snow.AddressPointer
): Resource[IO, HtmlDivElement[IO]] =
div(
@ -105,15 +104,13 @@ object Components {
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,
relayHints(store, addr.relays),
nip19_21("naddr", NIP19.encode(addr))
)
def renderEvent(
event: Event,
store: Store
store: Store,
event: Event
): Resource[IO, HtmlDivElement[IO]] =
div(
cls := "text-md",
@ -239,5 +236,49 @@ object Components {
)
)
private def relayHints(
store: Store,
relays: List[String]
): Resource[IO, HtmlDivElement[IO]] =
SignallingRef[IO].of(false).toResource.flatMap { active =>
val value =
if relays.size > 0 then relays.reduce((a, b) => s"$a, $b") else ""
div(
cls := "flex items-center space-x-3",
span(cls := "font-bold", "relay hints "),
span(Styles.mono, cls := "max-w-xl", value),
active.map {
case true =>
div(
input.withSelf { self =>
(
onKeyPress --> (_.foreach(evt =>
evt.key match {
case "Enter" =>
self.value.get.flatMap(url =>
if url.startsWith("wss://") || url.startsWith("ws://")
then
store.input.update(
_.trim() ++ " + " ++ url
) >> active.set(false)
else IO.unit
)
case _ => IO.unit
}
))
)
}
)
case false =>
button(
Styles.buttonSmall,
"add relay hint",
onClick --> (_.foreach(_ => active.set(true)))
)
}
)
}
private val external = img(cls := "inline w-4 ml-2", src := "ext.svg")
}

View File

@ -111,15 +111,16 @@ object Main extends IOWebApp {
store.result.map {
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(event: Event) => renderEvent(store, event)
case Right(pp: ProfilePointer) => renderProfilePointer(store, pp)
case Right(evp: EventPointer) => renderEventPointer(store, evp)
case Right(sk: PrivateKey) =>
renderProfilePointer(
store,
ProfilePointer(pubkey = sk.publicKey.xonly),
Some(sk)
)
case Right(addr: AddressPointer) => renderAddressPointer(addr)
case Right(addr: AddressPointer) => renderAddressPointer(store, addr)
}
)
}

View File

@ -12,31 +12,48 @@ type Result = Either[
]
object Parser {
def parseInput(input: String): Result = if input == "" then Left("")
else
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"
val additions = raw" *\+ *".r
def parseInput(input: String): Result =
if input == "" then Left("")
else {
val spl = additions.split(input)
val result = ByteVector
.fromHex(spl.head)
.flatMap(b => Try(Right(ByteVector32(b))).toOption)
.getOrElse(
NIP19.decode(spl.head) 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"
}
}
}
}
}
)
}
}
)
val extraRelays = spl
.drop(1)
.toList
.filter(e => e.startsWith("wss://") || e.startsWith("ws://"))
result.map {
case a: AddressPointer => a.copy(relays = a.relays ::: extraRelays)
case p: ProfilePointer => p.copy(relays = p.relays ::: extraRelays)
case e: EventPointer => e.copy(relays = e.relays ::: extraRelays)
case r => r
}
}
}