diff --git a/lib/data/nostrconnect_remote_signing_info.dart b/lib/data/nostrconnect_remote_signing_info.dart new file mode 100644 index 0000000..7aaf5c1 --- /dev/null +++ b/lib/data/nostrconnect_remote_signing_info.dart @@ -0,0 +1,83 @@ +import 'package:nostr_sdk/utils/string_util.dart'; +import 'package:nowser/data/remote_signing_info.dart'; + +class NostrconnectRemoteSigningInfo extends RemoteSigningInfo { + String? perms; + + String? name; + + String? url; + + String? image; + + NostrconnectRemoteSigningInfo({ + super.id, + super.appId, + super.localPubkey, + super.remotePubkey, + super.remoteSignerKey, + super.relays, + super.secret, + super.createdAt, + super.updatedAt, + this.perms, + this.name, + this.url, + this.image, + }); + + NostrconnectRemoteSigningInfo.fromJson(Map json) { + id = json['id']; + appId = json['app_id']; + localPubkey = json['local_pubkey']; + remotePubkey = json['remote_pubkey']; + remoteSignerKey = json['remote_signer_key']; + relays = json['relays']; + secret = json['secret']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + perms = json['perms']; + name = json['name']; + url = json['url']; + image = json['image']; + } + + // don't implement toJson method and when saving data it only save the super.toJson data. + // @override + // Map toJson() { + // final data = super.toJson(); + // data['perms'] = perms; + // data['name'] = name; + // data['url'] = url; + // data['image'] = image; + // return data; + // } + + static NostrconnectRemoteSigningInfo? parseNostrConnectUrl( + String nostrconnectUrlText) { + var uri = Uri.parse(nostrconnectUrlText); + var parsList = uri.queryParametersAll; + var pars = uri.queryParameters; + var localPubkey = uri.host; + + var relays = parsList['relay']; + var secret = pars['secret']; + var perms = pars['perms']; + var name = pars['name']; + var url = pars['url']; + var image = pars['image']; + if (relays == null || relays.isEmpty || StringUtil.isBlank(secret)) { + return null; + } + + return NostrconnectRemoteSigningInfo( + localPubkey: localPubkey, + relays: relays.join(","), + secret: secret, + perms: perms, + name: name, + url: url, + image: image, + ); + } +} diff --git a/lib/data/remote_signing_info.dart b/lib/data/remote_signing_info.dart index 6ff48a8..1a7d4d2 100644 --- a/lib/data/remote_signing_info.dart +++ b/lib/data/remote_signing_info.dart @@ -45,20 +45,4 @@ class RemoteSigningInfo { data['updated_at'] = this.updatedAt; return data; } - - static RemoteSigningInfo? parseNostrConnectUrl(String nostrconnectUrlText) { - var uri = Uri.parse(nostrconnectUrlText); - var pars = uri.queryParametersAll; - var localPubkey = uri.host; - - var relays = pars["relay"]; - if (relays == null || relays.isEmpty) { - return null; - } - - return RemoteSigningInfo( - localPubkey: localPubkey, - relays: relays.join(","), - ); - } } diff --git a/lib/provider/remote_signing_provider.dart b/lib/provider/remote_signing_provider.dart index 912c592..1ab933f 100644 --- a/lib/provider/remote_signing_provider.dart +++ b/lib/provider/remote_signing_provider.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:developer'; +import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:nostr_sdk/client_utils/keys.dart'; @@ -9,6 +10,7 @@ import 'package:nostr_sdk/event_kind.dart'; import 'package:nostr_sdk/filter.dart'; import 'package:nostr_sdk/nip46/nostr_remote_request.dart'; import 'package:nostr_sdk/nip46/nostr_remote_response.dart'; +import 'package:nostr_sdk/relay/client_connected.dart'; import 'package:nostr_sdk/relay/relay.dart'; import 'package:nostr_sdk/relay/relay_isolate.dart'; import 'package:nostr_sdk/relay/relay_status.dart'; @@ -19,6 +21,7 @@ import 'package:nostr_sdk/utils/string_util.dart'; import 'package:nowser/const/app_type.dart'; import 'package:nowser/const/auth_type.dart'; import 'package:nowser/data/app.dart'; +import 'package:nowser/data/nostrconnect_remote_signing_info.dart'; import 'package:nowser/provider/permission_check_mixin.dart'; import 'package:relay_sdk/network/memory/mem_relay_client.dart'; @@ -77,7 +80,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin { return str!; } - Future addRemoteApp(App remoteApp, String encryptKey) async { + Future?> addRemoteApp(App remoteApp, String encryptKey) async { var remoteSigningInfo = await RemoteSigningInfoDB.getByAppId(remoteApp.id!, encryptKey); if (remoteSigningInfo != null && @@ -91,6 +94,8 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin { remoteSigningInfoMap[remoteSignerPubkey] = remoteSigningInfo; appMap[remoteSignerPubkey] = remoteApp; relayMap[remoteSignerPubkey] = relays; + + return relays; } } @@ -313,7 +318,12 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin { if (event != null) { for (var relay in relays) { - relay.send(["EVENT", event.toJson()]); + if (relay.relayStatus.connected == ClientConneccted.CONNECTED) { + relay.send(["EVENT", event.toJson()]); + } else { + relay.pendingAuthedMessages.add(["EVENT", event.toJson()]); + relay.pendingMessages.add(["EVENT", event.toJson()]); + } } } } @@ -400,11 +410,49 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin { } } + Future addNostrconnectRemoteSigningInfo( + NostrconnectRemoteSigningInfo remoteSigningInfo) async { + NostrSigner signerSigner = + LocalNostrSigner(remoteSigningInfo.remoteSignerKey!); + var remoteSignerPubkey = getPublicKey(remoteSigningInfo.remoteSignerKey!); + var appType = AppType.REMOTE; + var code = remoteSignerPubkey; + + App? app = await getApp(appType, code); + app.name = remoteSigningInfo.name; + if (StringUtil.isBlank(app.name)) { + app.name = remoteSigningInfo.url; + } + app.pubkey = remoteSigningInfo.remotePubkey; + app.image = remoteSigningInfo.image; + await AuthAppConnectDialog.show(context!, app); + app = appProvider.getApp(appType, code); + if (app == null) { + BotToast.showText(text: "Remote App connect fail."); + return false; + } + + remoteSigningInfo.appId = app.id; + remoteSigningInfo.updatedAt = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + await RemoteSigningInfoDB.insert(remoteSigningInfo, encryptKey); + + var relays = await addRemoteApp(app, encryptKey); + if (relays != null) { + var response = NostrRemoteResponse( + StringUtil.rndNameStr(12), remoteSigningInfo.secret!); + await sendResponse(relays, response, signerSigner, + remoteSigningInfo.localPubkey!, remoteSignerPubkey); + } + + return true; + } + List _penddingRemoteApps = []; List get penddingRemoteApps => _penddingRemoteApps; - Future saveRemoteSigningInfo( + Future saveBunkerRemoteSigningInfo( RemoteSigningInfo remoteSigningInfo) async { await RemoteSigningInfoDB.insert(remoteSigningInfo, encryptKey); await reloadPenddingRemoteApps(); diff --git a/lib/router/apps/add_remote_app_router.dart b/lib/router/apps/add_remote_app_router.dart index b156fe5..a2d179a 100644 --- a/lib/router/apps/add_remote_app_router.dart +++ b/lib/router/apps/add_remote_app_router.dart @@ -4,11 +4,13 @@ import 'package:flutter/services.dart'; import 'package:nostr_sdk/client_utils/keys.dart'; import 'package:nostr_sdk/nip19/nip19.dart'; import 'package:nostr_sdk/nip46/nostr_remote_signer_info.dart'; +import 'package:nostr_sdk/utils/platform_util.dart'; import 'package:nostr_sdk/utils/string_util.dart'; import 'package:nowser/component/cust_state.dart'; import 'package:nowser/component/qrscanner.dart'; import 'package:nowser/component/simple_qrcode_dialog.dart'; import 'package:nowser/component/user/user_name_component.dart'; +import 'package:nowser/data/nostrconnect_remote_signing_info.dart'; import 'package:nowser/data/remote_signing_info.dart'; import 'package:nowser/data/remote_signing_info_db.dart'; import 'package:nowser/main.dart'; @@ -205,14 +207,9 @@ class _AddRemoteAppRouter extends CustState { unselectedLabelColor: textColor, indicatorColor: mainColor, tabs: [ - GestureDetector( - child: Text( - "${s.Connect_by}\nnostrconnect:// url", - textAlign: TextAlign.center, - ), - onTap: () { - BotToast.showText(text: s.Comming_soon); - }, + Text( + "${s.Connect_by}\nnostrconnect:// url", + textAlign: TextAlign.center, ), Text( "${s.Connect_by}\nbunker:// url", @@ -246,12 +243,14 @@ class _AddRemoteAppRouter extends CustState { child: Row( children: [ Expanded(child: Container()), - IconButton( - onPressed: () { - scanNostrConnectQRCode(); - }, - icon: const Icon(Icons.qr_code_scanner), - ), + PlatformUtil.isAndroid() || PlatformUtil.isIOS() + ? IconButton( + onPressed: () { + scanNostrConnectQRCode(); + }, + icon: const Icon(Icons.qr_code_scanner), + ) + : Container(), ], ), ), @@ -259,9 +258,7 @@ class _AddRemoteAppRouter extends CustState { margin: const EdgeInsets.only(top: Base.BASE_PADDING), width: double.infinity, child: FilledButton( - onPressed: () { - BotToast.showText(text: s.Comming_soon); - }, + onPressed: confirmNostrConnect, child: Text(s.Confirm), ), ), @@ -347,19 +344,27 @@ class _AddRemoteAppRouter extends CustState { String remoteSignerKey = generatePrivateKey(); - void confirmNostrConnect() { - var remoteSigningInfo = - RemoteSigningInfo.parseNostrConnectUrl(nostrconnectConn.text); + Future confirmNostrConnect() async { + var remoteSigningInfo = NostrconnectRemoteSigningInfo.parseNostrConnectUrl( + nostrconnectConn.text.trim()); if (remoteSigningInfo == null) { - // TODO + BotToast.showText(text: 'Invalid nostrconnect url'); return; } + remoteSigningInfo.remotePubkey = pubkey; remoteSigningInfo.remoteSignerKey = remoteSignerKey; remoteSigningInfo.createdAt = DateTime.now().millisecondsSinceEpoch ~/ 1000; remoteSigningInfo.updatedAt = remoteSigningInfo.createdAt; - // TODO + var result = await remoteSigningProvider + .addNostrconnectRemoteSigningInfo(remoteSigningInfo); + if (result) { + BotToast.showText(text: 'Add remote app success'); + RouterUtil.back(context); + } else { + BotToast.showText(text: 'Add remote app fail'); + } } TextEditingController relayAddrController = TextEditingController(); @@ -412,7 +417,7 @@ class _AddRemoteAppRouter extends CustState { ); remoteSigningInfo.updatedAt = remoteSigningInfo.createdAt; - remoteSigningProvider.saveRemoteSigningInfo(remoteSigningInfo); + remoteSigningProvider.saveBunkerRemoteSigningInfo(remoteSigningInfo); RouterUtil.back(context); } diff --git a/packages/nostr_sdk b/packages/nostr_sdk index af5304d..a900fb9 160000 --- a/packages/nostr_sdk +++ b/packages/nostr_sdk @@ -1 +1 @@ -Subproject commit af5304d08a99c6ec88f00f85c3279e1e60b87dbe +Subproject commit a900fb91d487abe7f8b2df5eba28ee06952a1e5f