diff --git a/amethyst/src/main/AndroidManifest.xml b/amethyst/src/main/AndroidManifest.xml index 3b878e3cb..4c34d20dd 100644 --- a/amethyst/src/main/AndroidManifest.xml +++ b/amethyst/src/main/AndroidManifest.xml @@ -89,6 +89,7 @@ + @@ -103,6 +104,7 @@ + diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index a310b7465..f6b9baf5b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -50,6 +50,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile import com.vitorpamplona.quartz.nip19Bech32.entities.NPub import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect import com.vitorpamplona.quartz.utils.Log +import com.vitorpamplona.quartz.utils.UriParser import kotlinx.coroutines.CancellationException import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers @@ -126,20 +127,28 @@ class MainActivity : AppCompatActivity() { } } +fun isNotificationRoute(uri: String) = uri.startsWith("notifications", true) || uri.startsWith("nostr:notifications", true) + +fun isHashtagRoute(uri: String) = uri.startsWith("hashtag?id=") || uri.startsWith("nostr:hashtag?id=") + +fun isWalletConnectRoute(uri: String) = uri.startsWith("dlnwc?value=") || uri.startsWith("amethyst+walletconnect:dlnwc?value=") || uri.startsWith("amethyst+walletconnect://dlnwc?value=") + fun uriToRoute( - uri: String?, + uri: String, account: Account, -): Route? = - if (uri?.startsWith("notifications", true) == true || uri?.startsWith("nostr:notifications", true) == true) { - Route.Notification - } else { - if (uri?.startsWith("hashtag?id=") == true || uri?.startsWith("nostr:hashtag?id=") == true) { - Route.Hashtag(uri.removePrefix("nostr:").removePrefix("hashtag?id=")) - } else { - val nip19 = Nip19Parser.uriToRoute(uri)?.entity - if (nip19 != null) { - LocalCache.consume(nip19) - } +): Route? { + if (isNotificationRoute(uri)) { + return Route.Notification + } + if (isHashtagRoute(uri)) { + return Route.Hashtag(uri.removePrefix("nostr:").removePrefix("hashtag?id=")) + } + + val nip19 = Nip19Parser.uriToRoute(uri)?.entity + if (nip19 != null) { + LocalCache.consume(nip19) + + val route = when (nip19) { is NPub -> Route.Profile(nip19.hex) is NProfile -> Route.Profile(nip19.hex) @@ -175,14 +184,27 @@ fun uriToRoute( else -> null } + + if (route != null) { + return route } - ?: try { - uri?.let { - Nip47WalletConnect.parse(it) - Route.Nip47NWCSetup(it) - } - } catch (e: Exception) { - if (e is CancellationException) throw e - null - } } + + if (isWalletConnectRoute(uri)) { + val url = UriParser(uri) + val nip47Uri = url.getQueryParameter("value") + if (nip47Uri != null) { + Nip47WalletConnect.parse(nip47Uri) + return Route.Nip47NWCSetup(nip47Uri) + } + } + + try { + Nip47WalletConnect.parse(uri) + return Route.Nip47NWCSetup(uri) + } catch (e: Exception) { + if (e is CancellationException) throw e + } + + return null +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt index 68ab2cfbd..d62753d7c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt @@ -46,6 +46,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.ContentPaste import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material.icons.outlined.VisibilityOff @@ -328,14 +329,19 @@ fun UpdateZapAmountContent( IconButton( onClick = { onClose() - runCatching { uri.openUri("https://nwc.getalby.com/apps/new?c=Amethyst") } + + try { + uri.openUri("nostrnwc://connect?appname=Amethyst&appicon=https%3A%2F%2Fraw.githubusercontent.com%2Fvitorpamplona%2Famethyst%2Frefs%2Fheads%2Fmain%2Ficon.png&callback=amethyst%2Bwalletconnect%3A%2F%2Fdlnwc") + } catch (_: IllegalArgumentException) { + accountViewModel.toastManager.toast(R.string.couldnt_find_nwc_wallets, R.string.couldnt_find_nwc_wallets_description) + } }, ) { Icon( - painter = painterRes(R.drawable.alby, 1), - contentDescription = stringRes(id = R.string.accessibility_navigate_to_alby), + imageVector = Icons.Outlined.Add, + contentDescription = stringRes(id = R.string.connect_to_new_nwc_wallet), modifier = Size24Modifier, - tint = Color.Unspecified, + tint = MaterialTheme.colorScheme.primary, ) } @@ -352,7 +358,7 @@ fun UpdateZapAmountContent( }, ) { Icon( - Icons.Outlined.ContentPaste, + imageVector = Icons.Outlined.ContentPaste, contentDescription = stringRes(id = R.string.paste_from_clipboard), modifier = Size24Modifier, tint = MaterialTheme.colorScheme.primary, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/QrCodeScanner.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/QrCodeScanner.kt index a1324e358..d160cb813 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/QrCodeScanner.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/QrCodeScanner.kt @@ -41,7 +41,9 @@ fun NIP19QrCodeScanner( ) { SimpleQrCodeScanner { try { - onScan(uriToRoute(it, accountViewModel.account)) + if (it != null) { + onScan(uriToRoute(it, accountViewModel.account)) + } } catch (e: Throwable) { if (e is CancellationException) throw e Log.e("NIP19 Scanner", "Error parsing $it", e) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 81fec61e1..030288809 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -841,6 +841,17 @@ Invalid NIP-47 URI The URI %1$s is not a valid NIP-47 login URI. + Connect to new NWC wallet + Wallet not found + Amethyst couldn\'t find any wallet apps that support Nostr Wallet Connect (NWC). +\n\nWhat you can do: +\n\t1. Verify NWC Support: Double-check that your preferred wallet app is compatible with Nostr Wallet Connect. +\n\t2. Create Connection: Go to your wallet app and create a new NWC connection. +\n\t3. Link to Amethyst: Your wallet will usually give you three options: +\n\t\t3.1 Automatic Link: A button to automatically open and connect to Amethyst. If so, click that button, open Amethyst and hit save on the new screen. +\n\t\t3.2 Manual Link: A Connection URI you can copy and click the paste icon on this screen to directly input it. +\n\t\t3.3 QR Code: The wallet creates a QR code which you can read from the QR code button in this screen. + For the App\'s Interface Dark, Light or System theme diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Nip47WalletConnect.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Nip47WalletConnect.kt index c906515d6..406d285eb 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Nip47WalletConnect.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/Nip47WalletConnect.kt @@ -38,7 +38,7 @@ class Nip47WalletConnect { val url = UriParser(uri) - if (url.scheme() != "nostrwalletconnect" && url.scheme() != "nostr+walletconnect") { + if (url.scheme() != "nostrwalletconnect" && url.scheme() != "nostr+walletconnect" && url.scheme() != "amethyst+walletconnect") { throw IllegalArgumentException("Not a Wallet Connect QR Code") }