From e6b195c6a9c730590bcbd2cffcf2671f314280e1 Mon Sep 17 00:00:00 2001 From: Fmar Date: Tue, 17 Dec 2024 15:53:33 +0100 Subject: [PATCH] inital code WiP --- lib/main.data.dart | 11 +++ lib/screens/settings_screen.dart | 91 ++++++++++++++++++- lib/utils/nwc_notifier.dart | 24 +++++ linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 50 +++++++++- pubspec.yaml | 2 + 8 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 lib/utils/nwc_notifier.dart diff --git a/lib/main.data.dart b/lib/main.data.dart index 403cff0..7fac59b 100644 --- a/lib/main.data.dart +++ b/lib/main.data.dart @@ -3,6 +3,7 @@ import 'package:flutter_data/flutter_data.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ndk/ndk.dart'; import 'package:zapstore/models/app_curation_set.dart'; import 'package:zapstore/models/app.dart'; @@ -11,6 +12,7 @@ import 'package:zapstore/models/local_app.dart'; import 'package:zapstore/models/release.dart'; import 'package:zapstore/models/settings.dart'; import 'package:zapstore/models/user.dart'; +import 'package:zapstore/utils/nwc_notifier.dart'; final adapterProvidersMap = >>{ 'appCurationSets': appCurationSetsAdapterProvider, @@ -50,4 +52,13 @@ extension AdapterRefX on Ref { Adapter get settings => watch(settingsAdapterProvider)..internalWatch = watch; Adapter get users => watch(usersAdapterProvider)..internalWatch = watch; + } + +final nwcSecretProvider = + StateNotifierProvider((ref) { + return NwcSecretNotifier(); +}); + +final ndk = Ndk.emptyBootstrapRelaysConfig(); + diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 952cdaa..63415fa 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -10,6 +10,8 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; +import 'package:ndk/domain_layer/usecases/nwc/consts/nwc_method.dart'; +import 'package:ndk/domain_layer/usecases/nwc/nwc_connection.dart'; import 'package:path_provider/path_provider.dart'; import 'package:zapstore/main.data.dart'; import 'package:zapstore/models/feedback.dart'; @@ -26,12 +28,16 @@ class SettingsScreen extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final systemInfoState = ref.watch(systemInfoNotifierProvider); - final controller = useTextEditingController(); + final feedbackController = useTextEditingController(); + final nwcController = useTextEditingController(); final user = ref.settings .watchOne('_', alsoWatch: (_) => {_.user}) .model! .user .value; + final isTextFieldEmpty = useState(true); + + final nwcSecret = ref.watch(nwcSecretProvider); return SingleChildScrollView( child: Column( @@ -51,7 +57,7 @@ class SettingsScreen extends HookConsumerWidget { Text('Comments, suggestions and error reports welcome here.'), Gap(20), TextField( - controller: controller, + controller: feedbackController, maxLines: 10, ), Gap(20), @@ -61,9 +67,9 @@ class SettingsScreen extends HookConsumerWidget { loadingWidget: SizedBox( width: 14, height: 14, child: CircularProgressIndicator()), onPressed: () async { - if (controller.text.trim().isNotEmpty) { + if (feedbackController.text.trim().isNotEmpty) { final text = - '${controller.text.trim()} [from ${user.npub} on ${DateFormat('MMMM d, y').format(DateTime.now())}]'; + '${feedbackController.text.trim()} [from ${user.npub} on ${DateFormat('MMMM d, y').format(DateTime.now())}]'; final event = AppFeedback(content: text).sign(kI); try { await http.post(Uri.parse('https://relay.zapstore.dev/'), @@ -73,7 +79,7 @@ class SettingsScreen extends HookConsumerWidget { context.showInfo('Thank you', description: 'Message sent successfully'); } - controller.clear(); + feedbackController.clear(); } catch (e, stack) { if (context.mounted) { context.showError( @@ -96,6 +102,81 @@ class SettingsScreen extends HookConsumerWidget { Gap(40), Divider(), Gap(40), + if (user != null) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Gap(20), + Text( + "Nostr Wallet Connect ${nwcSecret != null ? 'connected' : 'URI'}"), + if (nwcSecret != null && nwcSecret != '') + ElevatedButton( + onPressed: () async { + await ref + .read(nwcSecretProvider.notifier) + .updateNwcSecret(null); + }, + style: ElevatedButton.styleFrom( + disabledBackgroundColor: Colors.transparent, + backgroundColor: Colors.transparent), + child: Text('Disconnect'), + ), + if (nwcSecret == null || nwcSecret == '') + TextField( + autocorrect: false, + controller: nwcController, + onChanged: (value) { + isTextFieldEmpty.value = value.isEmpty; + }, + decoration: InputDecoration( + hintText: 'nostr+walletconnect://...', + suffixIcon: AsyncButtonBuilder( + disabled: isTextFieldEmpty.value, + loadingWidget: SizedBox( + width: 14, + height: 14, + child: CircularProgressIndicator(), + ), + onPressed: () async { + final nwcUri = nwcController.text.trim(); + NwcConnection connection = await ndk.nwc.connect(nwcUri, doGetInfoMethod: true, onError: (error) { + context.showError( + title: 'Unable to connect', + description: error); + }); + if (connection.info!=null && connection.permissions.contains(NwcMethod.PAY_INVOICE.name)) { + await ref + .read(nwcSecretProvider.notifier) + .updateNwcSecret(nwcUri); + } else { + context.showError( + title: 'Wallet missing pay_invoice permission', + description: ''); + } + }, + builder: (context, child, callback, buttonState) { + return Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SizedBox( + child: ElevatedButton( + onPressed: callback, + style: ElevatedButton.styleFrom( + disabledBackgroundColor: Colors.transparent, + backgroundColor: Colors.transparent), + child: child, + ), + ), + ); + }, + child: Text('Connect'), + ), + ), + ), + ], + ), + Gap(40), + Divider(), + Gap(40), Text( 'Tools', style: context.theme.textTheme.headlineLarge! diff --git a/lib/utils/nwc_notifier.dart b/lib/utils/nwc_notifier.dart new file mode 100644 index 0000000..ecdfe84 --- /dev/null +++ b/lib/utils/nwc_notifier.dart @@ -0,0 +1,24 @@ +import 'package:flutter_data/flutter_data.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class NwcSecretNotifier extends StateNotifier { + final _storage = const FlutterSecureStorage(); + + NwcSecretNotifier() : super(null) { + _loadNwcSecret(); + } + + Future _loadNwcSecret() async { + final secret = await _storage.read(key: 'nwc_secret'); + state = secret; + } + + Future updateNwcSecret(String? newSecret) async { + if (newSecret != null) { + await _storage.write(key: 'nwc_secret', value: newSecret); + } else { + await _storage.delete(key: 'nwc_secret'); + } + await _loadNwcSecret(); + } +} \ No newline at end of file diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index fc949e0..57efdf4 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,11 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); g_autoptr(FlPluginRegistrar) gtk_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); gtk_plugin_register_with_registrar(gtk_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index c34d078..9137173 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux gtk sqlite3_flutter_libs url_launcher_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 2060261..dc32300 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,6 +7,7 @@ import Foundation import app_links import device_info_plus +import flutter_secure_storage_macos import path_provider_foundation import shared_preferences_foundation import sqflite_darwin @@ -16,6 +17,7 @@ import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 88c81d5..f87169f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -452,6 +452,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter @@ -767,7 +815,7 @@ packages: source: hosted version: "3.1.0" ndk: - dependency: "direct overridden" + dependency: "direct main" description: path: "packages/ndk" ref: c279076973ff7b017697f4fbd172738535756c50 diff --git a/pubspec.yaml b/pubspec.yaml index 03d640a..62a371e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,8 @@ dependencies: background_downloader: ^8.8.0 mutex: ^3.1.0 easy_image_viewer: ^1.5.1 + flutter_secure_storage: ^9.2.2 + ndk: ^0.2.0-dev005 dependency_overrides: ndk: