import 'package:dart_nostr/dart_nostr.dart'; import 'package:drifter/models/models.dart'; import 'package:drifter/pages/profile_screen/widgets/delete_keys_dialog.dart'; import 'package:drifter/main.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(); bool _toHex = false; 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 = _toHex ? Keys.nsecKey : Keys.privateKey; publicKeyInput.text = _toHex ? Keys.npubKey : Keys.publicKey; return ListView( children: [ const SizedBox( height: 60, ), const UserInfo(), const SizedBox( height: 40, ), FormKeys(), const SizedBox(height: 20), Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Keys.keysExist ? IconButton( onPressed: () { setState(() { _toHex = !_toHex; }); }, icon: const Icon(Icons.refresh)) // ElevatedButton( // style: ButtonStyle( // backgroundColor: // MaterialStateProperty.all(AppColors.background)), // onPressed: () { // keysExistDialog( // Nostr.instance.keysService // .encodePublicKeyToNpub(Keys.publicKey), // Nostr.instance.keysService // .encodePrivateKeyToNsec(Keys.privateKey), // ); // }, // child: const Text( // 'Keys', // ), // ) : Row( children: [ ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( AppColors.background)), onPressed: () { modalBottomSheet(); }, child: const Text( 'Generate Keys', ), ), SizedBox(width: 100), ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( AppColors.background)), onPressed: () { Navigator.pushNamed(context, '/login').then((_) { initState(); }); }, child: const Text( 'Login', ), ), ], ), 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); }, ); }), ); } }