mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
fix(qr): correct the bunker message, and pin what the scanner sheet has to explain
While looking into routing more scanned payloads I found the reverse: the scanner's explanation sheet told users to add a bunker:// address "from the NIP-46 signer screen", which cannot accept one. Amethyst *publishes* bunker addresses — it is the remote signer — and the signer screen pairs nostrconnect:// offers only; remote-signer login exists on Desktop and even there it generates a nostrconnect:// rather than consuming a bunker URI. Split the string so bunker and nostrconnect each say something true. The investigation also showed raw nostr+walletconnect:// URIs already route, via the fallback at the end of uriToRoute rather than the dlnwc?value= branch above it. Both behaviours are now pinned by tests, because what uriToRoute returns null for is exactly what that sheet has to explain. Also set LANG/LC_ALL to C.UTF-8 for agent sessions. Without a UTF-8 locale sun.jnu.encoding is ASCII, and `./gradlew test` fails while *writing its HTML report* — after every test has passed — because eleven test names across four modules contain an em dash. It cost a full 12-minute run this session and would cost one in every future session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134jvyriixNTHST4WRbbqbX
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"env": {
|
||||
"LANG": "C.UTF-8",
|
||||
"LC_ALL": "C.UTF-8"
|
||||
},
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
|
||||
+6
-1
@@ -40,6 +40,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.qr_scanner_copy
|
||||
import com.vitorpamplona.amethyst.commons.resources.qr_scanner_kind_bunker
|
||||
import com.vitorpamplona.amethyst.commons.resources.qr_scanner_kind_cashu
|
||||
import com.vitorpamplona.amethyst.commons.resources.qr_scanner_kind_lightning
|
||||
import com.vitorpamplona.amethyst.commons.resources.qr_scanner_kind_nostr_unsupported
|
||||
@@ -137,7 +138,11 @@ private fun explain(payload: ScannedPayload): String =
|
||||
}
|
||||
|
||||
is ScannedPayload.WalletConnect -> stringRes(Res.string.qr_scanner_kind_wallet)
|
||||
is ScannedPayload.Bunker, is ScannedPayload.NostrConnect -> stringRes(Res.string.qr_scanner_kind_signer)
|
||||
// Split deliberately: Amethyst PUBLISHES bunker:// addresses (it is the signer) and has no
|
||||
// screen that consumes one, so pointing the user at the signer screen -- as this used to --
|
||||
// sends them somewhere that cannot accept it.
|
||||
is ScannedPayload.Bunker -> stringRes(Res.string.qr_scanner_kind_bunker)
|
||||
is ScannedPayload.NostrConnect -> stringRes(Res.string.qr_scanner_kind_signer)
|
||||
is ScannedPayload.Lightning -> stringRes(Res.string.qr_scanner_kind_lightning)
|
||||
is ScannedPayload.Cashu -> stringRes(Res.string.qr_scanner_kind_cashu)
|
||||
is ScannedPayload.Web -> stringRes(Res.string.qr_scanner_kind_web)
|
||||
|
||||
@@ -58,6 +58,50 @@ class UriToRouteTest {
|
||||
assertEquals(Route.Hashtag("foo"), uriToRoute("nostr:hashtag?id=foo", account))
|
||||
}
|
||||
|
||||
// The QR scanner shows a "can't open this" sheet for anything uriToRoute returns null for, so
|
||||
// what does and does not route here decides what that sheet ever has to explain.
|
||||
|
||||
@Test
|
||||
fun rawWalletConnectUrisRouteWithoutTheDlnwcWrapper() {
|
||||
// Not just the `dlnwc?value=` deep-link form: a wallet's QR code holds the bare URI, and
|
||||
// the fallback at the end of uriToRoute is what catches it.
|
||||
assertEquals(
|
||||
Route.WalletAddNwc(NWC_URI),
|
||||
uriToRoute(NWC_URI, account),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun everyWalletConnectSchemeSpellingRoutes() {
|
||||
assertEquals(
|
||||
Route.WalletAddNwc(NWC_URI_NO_PLUS),
|
||||
uriToRoute(NWC_URI_NO_PLUS, account),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun walletConnectDeepLinksStillUnwrapTheValueParameter() {
|
||||
assertEquals(
|
||||
Route.WalletAddNwc(NWC_URI),
|
||||
uriToRoute("dlnwc?value=$NWC_URI", account),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bunkerUrisDoNotRouteAnywhere() {
|
||||
// Amethyst *publishes* bunker:// addresses (it is the remote signer); it has no screen that
|
||||
// consumes one. The NIP-46 signer screen pairs nostrconnect:// offers only. Until that
|
||||
// changes, a scanned bunker:// belongs in the scanner's explanation sheet, and the sheet
|
||||
// must not tell the user to take it somewhere that cannot accept it.
|
||||
assertNull(uriToRoute("bunker://$PUBKEY_HEX?relay=wss%3A%2F%2Frelay.example&secret=abc", account))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PUBKEY_HEX = "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
|
||||
private const val NWC_URI = "nostr+walletconnect://$PUBKEY_HEX?relay=wss%3A%2F%2Frelay.example&secret=$PUBKEY_HEX"
|
||||
private const val NWC_URI_NO_PLUS = "nostrwalletconnect://$PUBKEY_HEX?relay=wss%3A%2F%2Frelay.example&secret=$PUBKEY_HEX"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nostrConnectOfferRoutesToTheSignerScreenCarryingTheUri() {
|
||||
val offer = "nostrconnect://" + "b".repeat(64) + "?relay=wss%3A%2F%2Frelay.example.com&secret=abc123"
|
||||
|
||||
@@ -218,7 +218,8 @@
|
||||
<string name="qr_scanner_unsupported_secret">This code carries a private key or a pairing secret, so its contents are not shown here. Use the screen that expects it.</string>
|
||||
<string name="qr_scanner_kind_nsec">This is a private key. Paste it on the login screen instead.</string>
|
||||
<string name="qr_scanner_kind_wallet">This is a wallet connection. Add it from Wallet settings.</string>
|
||||
<string name="qr_scanner_kind_signer">This is a remote signer. Add it from the NIP-46 signer screen.</string>
|
||||
<string name="qr_scanner_kind_bunker">This is a remote-signer address. Amethyst hands these out so other apps can ask it to sign — it cannot sign in with one.</string>
|
||||
<string name="qr_scanner_kind_signer">This is an app asking to connect to your signer. Open it from the NIP-46 signer screen.</string>
|
||||
<string name="qr_scanner_kind_lightning">This is a Lightning invoice.</string>
|
||||
<string name="qr_scanner_kind_cashu">This is a Cashu token.</string>
|
||||
<string name="qr_scanner_kind_web">This is a web link.</string>
|
||||
|
||||
Reference in New Issue
Block a user