mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-15 11:48:21 +00:00
feat: 1-tap connection
This commit is contained in:
@ -29,6 +29,13 @@ void runZapStream() {
|
||||
TextTheme(),
|
||||
),
|
||||
routerConfig: GoRouter(
|
||||
redirect: (context, state) {
|
||||
// redirect back to the wallet settings page
|
||||
if (state.uri.scheme == "zswc") {
|
||||
return "/settings/wallet";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
ShellRoute(
|
||||
observers: [routeObserver],
|
||||
|
@ -35,6 +35,7 @@ const defaultRelays = [
|
||||
"wss://relay.fountain.fm",
|
||||
];
|
||||
const searchRelays = ["wss://relay.nostr.band", "wss://search.nos.today"];
|
||||
const nwcRelays = ["wss://relay.getalby.com/v1"];
|
||||
|
||||
final loginData = LoginData();
|
||||
final RouteObserver<ModalRoute<void>> routeObserver =
|
||||
|
@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 28
|
||||
/// Strings: 1988 (71 per locale)
|
||||
/// Strings: 1991 (71 per locale)
|
||||
///
|
||||
/// Built on 2025-05-26 at 12:50 UTC
|
||||
/// Built on 2025-05-26 at 15:39 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
@ -285,8 +285,10 @@ class TranslationsSettingsWalletEn {
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
String get connect_wallet => 'Connect Wallet (NWC nwc://)';
|
||||
String get connect_wallet => 'Connect Wallet (NWC nostr+walletconnect://)';
|
||||
String get disconnect_wallet => 'Disconnect Wallet';
|
||||
String get connect_1tap => '1-Tap Connection';
|
||||
String get paste => 'Paste URL';
|
||||
late final TranslationsSettingsWalletErrorEn error = TranslationsSettingsWalletErrorEn.internal(_root);
|
||||
}
|
||||
|
||||
@ -366,6 +368,7 @@ class TranslationsSettingsWalletErrorEn {
|
||||
|
||||
// Translations
|
||||
String get logged_out => 'Cant connect wallet when logged out';
|
||||
String get nwc_auth_event_not_found => 'No wallet auth event found';
|
||||
}
|
||||
|
||||
/// Flat map(s) containing all translations.
|
||||
@ -449,9 +452,12 @@ extension on Translations {
|
||||
case 'settings.profile.nip05': return 'Nostr Address';
|
||||
case 'settings.profile.lud16': return 'Lightning Address';
|
||||
case 'settings.profile.error.logged_out': return 'Cant edit profile when logged out';
|
||||
case 'settings.wallet.connect_wallet': return 'Connect Wallet (NWC nwc://)';
|
||||
case 'settings.wallet.connect_wallet': return 'Connect Wallet (NWC nostr+walletconnect://)';
|
||||
case 'settings.wallet.disconnect_wallet': return 'Disconnect Wallet';
|
||||
case 'settings.wallet.connect_1tap': return '1-Tap Connection';
|
||||
case 'settings.wallet.paste': return 'Paste URL';
|
||||
case 'settings.wallet.error.logged_out': return 'Cant connect wallet when logged out';
|
||||
case 'settings.wallet.error.nwc_auth_event_not_found': return 'No wallet auth event found';
|
||||
case 'login.username': return 'Username';
|
||||
case 'login.amber': return 'Login with Amber';
|
||||
case 'login.key': return 'Login with Key';
|
||||
|
@ -120,10 +120,13 @@ settings:
|
||||
error:
|
||||
logged_out: Cant edit profile when logged out
|
||||
wallet:
|
||||
connect_wallet: Connect Wallet (NWC nwc://)
|
||||
connect_wallet: Connect Wallet (NWC nostr+walletconnect://)
|
||||
disconnect_wallet: Disconnect Wallet
|
||||
connect_1tap: 1-Tap Connection
|
||||
paste: Paste URL
|
||||
error:
|
||||
logged_out: Cant connect wallet when logged out
|
||||
nwc_auth_event_not_found: No wallet auth event found
|
||||
login:
|
||||
username: "Username"
|
||||
amber: Login with Amber
|
||||
|
@ -1,5 +1,14 @@
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:ndk/ndk.dart';
|
||||
import 'package:ndk/shared/nips/nip01/bip340.dart';
|
||||
import 'package:ndk/shared/nips/nip01/key_pair.dart';
|
||||
import 'package:protocol_handler/protocol_handler.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:zap_stream_flutter/const.dart';
|
||||
import 'package:zap_stream_flutter/i18n/strings.g.dart';
|
||||
import 'package:zap_stream_flutter/login.dart';
|
||||
@ -13,16 +22,93 @@ class SettingsWalletPage extends StatefulWidget {
|
||||
State<StatefulWidget> createState() => _Inner();
|
||||
}
|
||||
|
||||
class _Inner extends State<SettingsWalletPage> {
|
||||
const nwaHandlerUrl = "zswc://handler";
|
||||
|
||||
class _Inner extends State<SettingsWalletPage> with ProtocolListener {
|
||||
late final TextEditingController _uri;
|
||||
String? _error;
|
||||
|
||||
KeyPair? _nwaKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_uri = TextEditingController();
|
||||
protocolHandler.addListener(this);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
protocolHandler.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onProtocolUrlReceived(String url) async {
|
||||
developer.log("NWA: $url");
|
||||
|
||||
if (url == nwaHandlerUrl && _nwaKey != null) {
|
||||
final walletInfos = ndk.requests
|
||||
.query(
|
||||
filters: [
|
||||
Filter(kinds: [13194], pTags: [_nwaKey!.publicKey], limit: 5),
|
||||
],
|
||||
explicitRelays: nwcRelays,
|
||||
)
|
||||
.stream
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
final walletInfo =
|
||||
(await walletInfos.toList())
|
||||
.sortedBy((e) => e.createdAt)
|
||||
.reversed
|
||||
.firstOrNull;
|
||||
if (walletInfo == null) {
|
||||
setState(() {
|
||||
_error = t.settings.wallet.error.nwc_auth_event_not_found;
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
final nwcUrl = Uri(
|
||||
scheme: "nostr+walletconnect",
|
||||
host: walletInfo.pubKey,
|
||||
queryParameters: {"relay": nwcRelays, "secret": _nwaKey!.privateKey},
|
||||
);
|
||||
_setWallet(WalletConfig(type: WalletType.nwc, data: nwcUrl.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _start1TapFlow() async {
|
||||
final key = Bip340.generatePrivateKey();
|
||||
final url = Uri(
|
||||
scheme: "nostr+walletauth",
|
||||
host: key.publicKey,
|
||||
queryParameters: {
|
||||
"relay": nwcRelays,
|
||||
"name": "zap.stream",
|
||||
"request_methods": "pay_invoice",
|
||||
"icon": "https://zap.stream/logo.png",
|
||||
"return_to": nwaHandlerUrl,
|
||||
},
|
||||
);
|
||||
setState(() {
|
||||
_error = null;
|
||||
_nwaKey = key;
|
||||
});
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
|
||||
_setWallet(WalletConfig? cfg) {
|
||||
loginData.value = LoginAccount(
|
||||
type: loginData.value!.type,
|
||||
pubkey: loginData.value!.pubkey,
|
||||
privateKey: loginData.value!.privateKey,
|
||||
signerRelays: loginData.value!.signerRelays,
|
||||
wallet: cfg,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pubkey = ndk.accounts.getPublicKey();
|
||||
@ -33,11 +119,34 @@ class _Inner extends State<SettingsWalletPage> {
|
||||
builder: (context, state, child) {
|
||||
if (state?.wallet == null) {
|
||||
return Column(
|
||||
spacing: 8,
|
||||
spacing: 16,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (Platform.isAndroid) ...[
|
||||
Text(
|
||||
t.settings.wallet.connect_1tap,
|
||||
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
|
||||
),
|
||||
BasicButton.text(
|
||||
t.button.connect,
|
||||
onTap: (context) {
|
||||
_start1TapFlow().onError((e, _) {
|
||||
setState(() {
|
||||
_error = e.toString();
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
Text(
|
||||
"Paste URL",
|
||||
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
|
||||
),
|
||||
TextField(
|
||||
controller: _uri,
|
||||
decoration: InputDecoration(labelText: t.settings.wallet.connect_wallet),
|
||||
decoration: InputDecoration(
|
||||
labelText: t.settings.wallet.connect_wallet,
|
||||
),
|
||||
),
|
||||
BasicButton.text(
|
||||
t.button.connect,
|
||||
@ -48,13 +157,8 @@ class _Inner extends State<SettingsWalletPage> {
|
||||
type: WalletType.nwc,
|
||||
data: _uri.text,
|
||||
);
|
||||
loginData.value = LoginAccount(
|
||||
type: loginData.value!.type,
|
||||
pubkey: loginData.value!.pubkey,
|
||||
privateKey: loginData.value!.privateKey,
|
||||
signerRelays: loginData.value!.signerRelays,
|
||||
wallet: cfg,
|
||||
);
|
||||
_setWallet(cfg);
|
||||
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
@ -73,13 +177,7 @@ class _Inner extends State<SettingsWalletPage> {
|
||||
return BasicButton.text(
|
||||
t.settings.wallet.disconnect_wallet,
|
||||
onTap: (context) {
|
||||
loginData.value = LoginAccount(
|
||||
type: loginData.value!.type,
|
||||
pubkey: loginData.value!.pubkey,
|
||||
privateKey: loginData.value!.privateKey,
|
||||
signerRelays: loginData.value!.signerRelays,
|
||||
wallet: null,
|
||||
);
|
||||
_setWallet(null);
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
|
Reference in New Issue
Block a user