import 'package:dart_nostr/dart_nostr.dart'; import 'package:drifter/models/keys.dart'; import 'package:drifter/pages/profile_screen/widgets/delete_keys_dialog.dart'; import 'package:drifter/pages/profile_screen/widgets/key_exist_dialog.dart'; import 'package:drifter/pages/profile_screen/widgets/keys_option_modal_bottom_sheet.dart'; import 'package:drifter/pages/profile_screen/widgets/message_snack_bar.dart'; import 'package:drifter/pages/profile_screen/widgets/user_info_widget.dart'; import 'package:drifter/theme/app_colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @override State createState() => ProfileScreenState(); } class ProfileScreenState extends State { final _secureStorage = const FlutterSecureStorage(); TextEditingController privateKeyInput = TextEditingController(); TextEditingController publicKeyInput = TextEditingController(); // final keyGenerator = KeyApi(); // final nip19 = Nip19(); @override void initState() { _getKeysFromStorage(); super.initState(); } Future generateNewKeys() async { final newPrivateKey = await Nostr.instance.keysService.generatePrivateKey(); // final newPrivateKey = keyGenerator.generatePrivateKey(); final nsec = Nostr.instance.keysService.encodePrivateKeyToNsec(newPrivateKey); // final nsecDecoded = // Nostr.instance.keysService.decodeNsecKeyToPrivateKey(nsec); // assert(nsecDecoded['type'] == 'nsec'); // assert(nsecDecoded['data'] == newPrivateKey); final newPublicKey = await Nostr.instance.keysService .derivePublicKey(privateKey: newPrivateKey); // final newPublicKey = keyGenerator.getPublicKey(newPrivateKey); final npub = Nostr.instance.keysService.encodePublicKeyToNpub(newPublicKey); // final npubDecoded = // Nostr.instance.keysService.decodeNpubKeyToPublicKey(npub); // assert(npubDecoded['type'] == 'npub'); // assert(npubDecoded['data'] == newPublicKey); return await addKeyToStorage(newPrivateKey, newPublicKey, nsec, npub); } Future _getKeysFromStorage() async { // Reading values associated with the " privateKey " and " publicKey " keys from a secure repository final storedPrivateKey = await _secureStorage.read(key: 'privateKey'); final storedPublicKey = await _secureStorage.read(key: 'publicKey'); final storedNsecKey = await _secureStorage.read(key: 'nsec'); final storedNpubKey = await _secureStorage.read(key: 'npub'); // Indicates that both private and public keys are stored in a secure repository, after which, the state variables are updated if (storedPrivateKey != null && storedPublicKey != null && storedNsecKey != null && storedNpubKey != null) { setState(() { Keys.privateKey = storedPrivateKey; Keys.publicKey = storedPublicKey; Keys.nsecKey = storedNsecKey; Keys.npubKey = storedNpubKey; Keys.keysExist = true; }); } } // Adding a new key // Writing a private and public key to a secure vault Future addKeyToStorage( String privateKeyHex, String publicKeyHex, String nsecKey, String npubKey, ) async { // Waiting for both write operations to complete Future.wait([ _secureStorage.write(key: 'privateKey', value: privateKeyHex), _secureStorage.write(key: 'publicKey', value: publicKeyHex), _secureStorage.write(key: 'nsec', value: nsecKey), _secureStorage.write(key: 'npub', value: npubKey), ]); // Updating status variables and starting widget rebuilding setState(() { Keys.privateKey = privateKeyHex; Keys.publicKey = publicKeyHex; Keys.nsecKey = nsecKey; Keys.npubKey = npubKey; Keys.keysExist = true; }); // Returns a boolean value indicating whether the keys were successfully added to the repository or not. return Keys.keysExist; } Future _deleteKeysStorage() async { // Calling secure storage to remove keys from storage Future.wait([ _secureStorage.delete(key: 'privateKey'), _secureStorage.delete(key: 'publicKey'), _secureStorage.delete(key: 'nsec'), _secureStorage.delete(key: 'npub'), ]); // Updating status variables, resetting values after deleting keys from the repository setState(() { Keys.privateKey = ''; Keys.publicKey = ''; Keys.nsecKey = ''; Keys.npubKey = ''; Keys.keysExist = false; }); } @override Widget build(BuildContext context) { privateKeyInput.text = Keys.nsecKey; publicKeyInput.text = Keys.npubKey; return ListView( children: [ SizedBox( height: 60, ), UserInfo(), SizedBox( height: 40, ), FormKeys(), SizedBox(height: 20), Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Keys.keysExist ? ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(AppColors.background)), onPressed: () { keysExistDialog( Nostr.instance.keysService .encodePublicKeyToNpub(Keys.publicKey), Nostr.instance.keysService .encodePrivateKeyToNsec(Keys.privateKey), ); }, child: Text( 'Keys', ), ) : ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(AppColors.background)), onPressed: () { modalBottomSheet(); }, child: Text( 'Generate Keys', ), ), Keys.keysExist ? Row( children: [ IconButton( onPressed: () { deleteKeysDialog(); }, icon: const Icon(Icons.delete)), ], ) : Container(), ], ), ) ], ); } Form FormKeys() { return Form( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextFormField( controller: privateKeyInput, // _toHex ? widget.hexPriv : widget.nsecEncoded, decoration: const InputDecoration( labelText: 'Private Key', border: OutlineInputBorder(), ), maxLength: 64, ), const SizedBox( height: 20, ), TextFormField( controller: publicKeyInput, // _toHex ? widget.hexPub : widget.npubEncoded, decoration: const InputDecoration( labelText: 'Public Key', border: OutlineInputBorder(), ), ), const SizedBox( height: 40, ), TextFormField( decoration: const InputDecoration( labelText: 'Relay', border: OutlineInputBorder(), ), ), ], ), ), ); } void modalBottomSheet() { showModalBottomSheet( context: context, builder: (BuildContext context) { return KeysOptionModalBottomSheet( generateNewKeyPressed: () { final currentContext = context; generateNewKeys().then( (keysGenerated) { if (keysGenerated) { ScaffoldMessenger.of(currentContext).showSnackBar( MessageSnackBar(label: 'Keys Generated!')); } }, ); Navigator.pop(context); }, ); }); } void keysExistDialog(String npubEncode, String nsecEncode) async { await showDialog( context: context, builder: ((context) { return KeysExistDialog( npubEncoded: npubEncode, nsecEncoded: nsecEncode, hexPriv: Keys.privateKey, hexPub: Keys.publicKey, ); }), ); } void deleteKeysDialog() async { await showDialog( context: context, builder: ((context) { return DeleteKeysDialog( onNoPressed: () { Navigator.pop(context); }, onYesPressed: () { final currentContext = context; _deleteKeysStorage().then((_) { if (!Keys.keysExist) { ScaffoldMessenger.of(currentContext).showSnackBar( MessageSnackBar( label: 'Keys successfully deleted!', isWarning: true, ), ); } }); Navigator.pop(context); }, ); }), ); } }