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) { publicKeyInput.text = Keys.npubKey; return Container( color: AppColors.profileWrapperBg, child: Column( children: [ Stack( children: [ Container( width: double.infinity, height: 70, child: FittedBox( child: Image.asset('assets/images/banner.png'), fit: BoxFit.fill, ), ), Row( children: [ Padding( padding: const EdgeInsets.only(top: 30, left: 16, right: 58), child: ClipRRect( borderRadius: BorderRadius.circular(200), child: Container( width: 80, height: 80, decoration: BoxDecoration( border: Border.all( width: 2, color: Color(0xFFF2EFFF))), child: Image.asset('assets/images/avatar.png')), ), ), Expanded( child: Container( margin: EdgeInsets.only(top: 74, right: 16), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ SizedBox( width: 32, height: 32, child: FloatingActionButton( backgroundColor: AppColors.buttonSecondaryDefaultBg, onPressed: () {}, elevation: 0, child: Icon( Icons.edit, size: 18, color: AppColors.buttonSecondaryIcon, ), ), ), SizedBox( width: 8, ), SizedBox( width: 32, height: 32, child: FloatingActionButton( backgroundColor: AppColors.buttonSecondaryDefaultBg, onPressed: () { Navigator.pushNamed(context, '/QrCodeScreen'); }, elevation: 0, child: Icon( Icons.qr_code, size: 18, color: AppColors.buttonSecondaryIcon, ), ), ), SizedBox( width: 8, ), SizedBox( width: 32, height: 32, child: FloatingActionButton( backgroundColor: AppColors.buttonSecondaryDefaultBg, onPressed: () {}, elevation: 0, child: Icon( Icons.mail_outlined, size: 18, color: AppColors.buttonSecondaryIcon, ), ), ), SizedBox( width: 8, ), SizedBox( width: 100, height: 32, child: ElevatedButton( onPressed: () {}, child: Text( 'Follow', style: TextStyle( color: AppColors.buttonPrimaryDefaultText), ), style: ElevatedButton.styleFrom( backgroundColor: AppColors.buttonPrimaryDefaultBg, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100)))), ) ], ), ), ) ], ) ], ), Container( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Cameron Williamson', style: TextStyle( color: AppColors.profileFullName, fontWeight: FontWeight.w600), ), SizedBox( height: 5, ), Text('@cameron', style: TextStyle( color: AppColors.profileUserName, )), SizedBox( height: 5, ), Text( 'This is the description of my profile. I am a nostrich and I love cats. Follow me for fun pictures and bad jokes. I also want to be a politician.', style: TextStyle( color: AppColors.profileSummary, )), SizedBox( height: 12, ), Container( child: TextField( style: TextStyle(color: AppColors.profileKeyText), readOnly: true, decoration: InputDecoration( suffixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon(Icons.copy), color: AppColors.buttonSecondaryIcon, onPressed: () {}, ), IconButton( icon: Icon(Icons.qr_code), color: AppColors.buttonSecondaryIcon, onPressed: () {}, ), ], ), filled: true, fillColor: AppColors.profileKeyField, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(width: 0, style: BorderStyle.none), )), minLines: 2, maxLines: 2, controller: publicKeyInput, ), ), Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.check_circle, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( 'nip-05-name', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), SizedBox( height: 6, ), TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.check_circle, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( 'www.cameronforpresident.com', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), SizedBox( height: 6, ), TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.score, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( '@cameron_official', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), SizedBox( height: 6, ), TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.check_circle, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( 'cameron.williamson.com/@cameron_official', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), SizedBox( height: 6, ), TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.flash_on, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( 'whoknowswhatgoeshere', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), SizedBox( height: 6, ), TextButton.icon( // <-- TextButton onPressed: () {}, icon: Icon( Icons.flash_on, size: 15.0, color: AppColors.profileSocialIcons, ), label: Text( 'cameron@satoshiwallet.com', style: TextStyle( color: AppColors.profileSocialLinks, fontWeight: FontWeight.w500), ), ), ]), ) ]), ), ), ], ), ); // 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); }, ); }), ); } }