From 76ca99a73b720887578063a13df61d9968db4d72 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 20 Jun 2023 14:49:56 -0300 Subject: [PATCH] clicking on edit button to fill in the input. --- edit.svg | 7 ++ ext.svg | 2 +- src/main/scala/Components.scala | 118 +++++++++++++++++++++++++++----- src/main/scala/Main.scala | 4 +- 4 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 edit.svg diff --git a/edit.svg b/edit.svg new file mode 100644 index 0000000..abfcd8b --- /dev/null +++ b/edit.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ext.svg b/ext.svg index 6e1a739..09048c0 100644 --- a/ext.svg +++ b/ext.svg @@ -1 +1 @@ - + diff --git a/src/main/scala/Components.scala b/src/main/scala/Components.scala index 23bf25d..bcd5005 100644 --- a/src/main/scala/Components.scala +++ b/src/main/scala/Components.scala @@ -16,7 +16,10 @@ import snow.* import Utils.* object Components { - def render32Bytes(bytes32: ByteVector32): Resource[IO, HtmlDivElement[IO]] = + def render32Bytes( + store: Store, + bytes32: ByteVector32 + ): Resource[IO, HtmlDivElement[IO]] = div( cls := "text-md", entry("canonical hex", bytes32.toHex), @@ -25,9 +28,16 @@ object Components { cls := "mt-2 pl-2 mb-2", entry( "npub", - NIP19.encode(XOnlyPublicKey(bytes32)) + NIP19.encode(XOnlyPublicKey(bytes32)), + Some( + editable( + store, + NIP19.encode(XOnlyPublicKey(bytes32)) + ) + ) ), nip19_21( + store, "nprofile", NIP19.encode(ProfilePointer(XOnlyPublicKey(bytes32))) ) @@ -37,13 +47,26 @@ object Components { cls := "pl-2 mb-2", entry( "nsec", - NIP19.encode(PrivateKey(bytes32)) + NIP19.encode(PrivateKey(bytes32)), + Some( + editable( + store, + NIP19.encode(PrivateKey(bytes32)) + ) + ) ), entry( "npub", - NIP19.encode(PrivateKey(bytes32).publicKey.xonly) + NIP19.encode(PrivateKey(bytes32).publicKey.xonly), + Some( + editable( + store, + NIP19.encode(PrivateKey(bytes32).publicKey.xonly) + ) + ) ), nip19_21( + store, "nprofile", NIP19.encode(ProfilePointer(PrivateKey(bytes32).publicKey.xonly)) ) @@ -52,6 +75,7 @@ object Components { div( cls := "pl-2 mb-2", nip19_21( + store, "nevent", NIP19.encode(EventPointer(bytes32.toHex)) ) @@ -60,7 +84,13 @@ object Components { cls := "pl-2 mb-2", entry( "note", - NIP19.encode(bytes32) + NIP19.encode(bytes32), + Some( + editable( + store, + NIP19.encode(bytes32) + ) + ) ) ) ) @@ -71,13 +101,21 @@ object Components { ): Resource[IO, HtmlDivElement[IO]] = div( cls := "text-md", - entry("event id (hex)", evp.id), + entry( + "event id (hex)", + evp.id, + Some(editable(store, evp.id)) + ), relayHints(store, evp.relays), evp.author.map { pk => entry("author hint (pubkey hex)", pk.value.toHex) }, - nip19_21("nevent", NIP19.encode(evp)), - entry("note", NIP19.encode(ByteVector32.fromValidHex(evp.id))) + nip19_21(store, "nevent", NIP19.encode(evp)), + entry( + "note", + NIP19.encode(ByteVector32.fromValidHex(evp.id)), + Some(editable(store, NIP19.encode(ByteVector32.fromValidHex(evp.id)))) + ) ) def renderProfilePointer( @@ -87,16 +125,36 @@ object Components { ): Resource[IO, HtmlDivElement[IO]] = div( cls := "text-md", - 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), + sk.map { k => + entry( + "private key (hex)", + k.value.toHex, + Some(editable(store, k.value.toHex)) + ) + }, + sk.map { k => + entry( + "nsec", + NIP19.encode(k), + Some(editable(store, NIP19.encode(k))) + ) + }, + entry( + "public key (hex)", + pp.pubkey.value.toHex, + Some(editable(store, pp.pubkey.value.toHex)) + ), relayHints( store, pp.relays, dynamic = if sk.isDefined then false else true ), - entry("npub", NIP19.encode(pp.pubkey)), - nip19_21("nprofile", NIP19.encode(pp)) + entry( + "npub", + NIP19.encode(pp.pubkey), + Some(editable(store, NIP19.encode(pp.pubkey))) + ), + nip19_21(store, "nprofile", NIP19.encode(pp)) ) def renderAddressPointer( @@ -109,7 +167,7 @@ object Components { entry("identifier", addr.d), entry("kind", addr.kind.toString), relayHints(store, addr.relays), - nip19_21("naddr", NIP19.encode(addr)) + nip19_21(store, "naddr", NIP19.encode(addr)) ) def renderEvent( @@ -205,6 +263,7 @@ object Components { ), event.id.map(id => nip19_21( + store, "nevent", NIP19.encode(EventPointer(id, author = event.pubkey)) ) @@ -212,28 +271,33 @@ object Components { event.id.map(id => entry( "note", - NIP19.encode(ByteVector32.fromValidHex(id)) + NIP19.encode(ByteVector32.fromValidHex(id)), + Some(editable(store, NIP19.encode(ByteVector32.fromValidHex(id)))) ) ) ) private def entry( key: String, - value: String + value: String, + editLink: Option[Resource[IO, HtmlSpanElement[IO]]] = None ): Resource[IO, HtmlDivElement[IO]] = div( cls := "flex items-center space-x-3", span(cls := "font-bold", key + " "), - span(Styles.mono, cls := "max-w-xl", value) + span(Styles.mono, cls := "max-w-xl", value), + editLink ) private def nip19_21( + store: Store, key: String, code: String ): Resource[IO, HtmlDivElement[IO]] = div( span(cls := "font-bold", key + " "), span(Styles.mono, cls := "break-all", code), + editable(store, code), a( href := "nostr:" + code, external @@ -364,5 +428,25 @@ object Components { ) } + private def editable( + store: Store, + code: String + ): Resource[IO, HtmlSpanElement[IO]] = + span( + store.input.map(current => + if current == code then a("") + else + a( + href := "#/" + code, + onClick --> (_.foreach(evt => + evt.preventDefault >> + store.input.set(code) + )), + edit + ) + ) + ) + + private val edit = img(cls := "inline w-4 ml-2", src := "edit.svg") private val external = img(cls := "inline w-4 ml-2", src := "ext.svg") } diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index d91cbbc..7f3c914 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -18,7 +18,7 @@ object Main extends IOWebApp { def render: Resource[IO, HtmlDivElement[IO]] = Store(window).flatMap { store => div( - cls := "flex w-full h-full flex-col items-center justify-center", + cls := "flex w-full flex-col items-center justify-center", div( cls := "w-4/5", h1( @@ -131,7 +131,7 @@ object Main extends IOWebApp { cls := "w-full flex my-5", store.result.map { case Left(msg) => div(msg) - case Right(bytes: ByteVector32) => render32Bytes(bytes) + case Right(bytes: ByteVector32) => render32Bytes(store, bytes) case Right(event: Event) => renderEvent(store, event) case Right(pp: ProfilePointer) => renderProfilePointer(store, pp) case Right(evp: EventPointer) => renderEventPointer(store, evp)