diff --git a/lib/models/keys.dart b/lib/models/keys.dart index 85335ea..fd23c7f 100644 --- a/lib/models/keys.dart +++ b/lib/models/keys.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:nostr_tools/nostr_tools.dart'; class Keys { @@ -11,3 +12,6 @@ class Keys { class Relay { static final relay = RelayApi(relayUrl: 'wss://relay.damus.io'); } + +final keyController = TextEditingController(); +final formKey = GlobalKey(); diff --git a/lib/pages/home_screen/home_screen_widget.dart b/lib/pages/home_screen/home_screen_widget.dart index 175f735..e9e7a31 100644 --- a/lib/pages/home_screen/home_screen_widget.dart +++ b/lib/pages/home_screen/home_screen_widget.dart @@ -372,7 +372,7 @@ class _CreatePostState extends State { padding: const EdgeInsets.symmetric(vertical: 24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - color: AppColors.mainDarkBlue, + color: AppColors.background, ), child: const Center( child: Text( diff --git a/lib/pages/login_screen/login_screen.dart b/lib/pages/login_screen/login_screen.dart new file mode 100644 index 0000000..0fc0794 --- /dev/null +++ b/lib/pages/login_screen/login_screen.dart @@ -0,0 +1,153 @@ +import 'package:dart_nostr/dart_nostr.dart'; +import 'package:drifter/models/keys.dart'; +import 'package:drifter/pages/home_screen/widgets/message_text_button_widget.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/ok_button_widget.dart'; + +import 'package:drifter/theme/app_colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:nostr_tools/nostr_tools.dart'; + +class LoginScreen extends StatelessWidget { + const LoginScreen({super.key}); + + final _secureStorage = const FlutterSecureStorage(); + + 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: privateKeyHex), + _secureStorage.write(key: 'nsec', value: nsecKey), + _secureStorage.write(key: 'npub', value: npubKey), + ]); + + // Updating status variables and starting widget rebuilding + +// Returns a boolean value indicating whether the keys were successfully added to the repository or not. + return Keys.keysExist; + } + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + SizedBox( + height: 200, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const Text( + 'Enter your private key', + style: TextStyle( + fontSize: 20, + ), + ), + const SizedBox(height: 30), + TextFormField( + decoration: const InputDecoration( + labelText: 'Enter nsec or hex', + border: OutlineInputBorder(), + ), + maxLength: 64, + controller: keyController, + key: formKey, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your private key'; + } + try { + bool isValidHexKey = + Nostr.instance.keysService.isValidPrivateKey(value); + bool isValidNSec = value.trim().startsWith('nsec') && + Nostr.instance.keysService.isValidPrivateKey( + NostrClientUtils.hexEncode(value)['data']); + if (!(isValidHexKey || isValidNSec)) { + return 'Your private key is not valid.'; + } + } on ChecksumVerificationException catch (e) { + return e.message; + } catch (e) { + return 'Error: $e'; + } + return null; + }), + ], + ), + ), + SizedBox(height: 20), + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(AppColors.background)), + onPressed: () { + if (formKey.currentState!.validate()) { + // Он получает значение закрытого ключа из _keyController текстового поля и присваивает его переменной privateKeyHex. + String privateKeyHex = keyController.text.trim(); + String publicKeyHex; + String nsecKey; + String npubKey; + + // Он проверяет, начинается ли строка privateKeyHex со строки « nsec », что указывает на то, что она может быть в формате NIP-19. Если это так, он декодирует метод privateKeyHex using _nip19.decode(privateKeyHex) для получения поля « данные », которое представляет фактический закрытый ключ в шестнадцатеричном формате. + if (privateKeyHex.startsWith('nsec')) { + nsecKey = privateKeyHex; + final decoded = Nostr.instance.keysService + .decodeNsecKeyToPrivateKey(privateKeyHex); + privateKeyHex = decoded; + publicKeyHex = Nostr.instance.keysService + .derivePublicKey(privateKey: 'privateKeyHex'); + npubKey = Nostr.instance.keysService + .encodePublicKeyToNpub(publicKeyHex); + } + // Если privateKeyHex не начинается с « nsec », это означает, что это обычный шестнадцатеричный закрытый ключ. + else { + publicKeyHex = Nostr.instance.keysService + .derivePublicKey(privateKey: 'privateKeyHex'); + nsecKey = Nostr.instance.keysService + .encodePrivateKeyToNsec(privateKeyHex); + npubKey = Nostr.instance.keysService + .encodePublicKeyToNpub(publicKeyHex); + } + + // Затем он вызывает _addKeysToStorage метод для добавления закрытого ключа и открытого ключа в хранилище. Он прикрепляет then() к этому методу обратный вызов для обработки случая, когда ключи успешно добавлены в хранилище. + addKeyToStorage(privateKeyHex, publicKeyHex, nsecKey, npubKey) + .then((keysAdded) { + if (keysAdded) { + keyController.clear(); + ScaffoldMessenger.of(context).showSnackBar( + MessageSnackBar(label: 'Congratulations! Keys Stored!'), + ); + setState() { + Keys.privateKey = privateKeyHex; + Keys.publicKey = publicKeyHex; + Keys.nsecKey = nsecKey; + Keys.npubKey = npubKey; + Keys.keysExist = true; + } + } + }); + } else { + formKey.currentState?.setState(() {}); + } + }, + child: Text( + 'Login', + ), + ), + ) + ], + ); + } +} diff --git a/lib/pages/main_screen/main_screen_widget.dart b/lib/pages/main_screen/main_screen_widget.dart index 06c478c..abc87dd 100644 --- a/lib/pages/main_screen/main_screen_widget.dart +++ b/lib/pages/main_screen/main_screen_widget.dart @@ -1,5 +1,6 @@ // import 'package:drifter/pages/home_screen/home_screen_widget.dart'; import 'package:drifter/pages/home_screen/home_screen_widget.dart'; +import 'package:drifter/pages/login_screen/login_screen.dart'; import 'package:drifter/pages/message_screen/message_screen_widget.dart'; import 'package:drifter/pages/profile_screen/profile_screen.dart'; import 'package:drifter/theme/app_colors.dart'; @@ -58,6 +59,7 @@ class _MainScreenWidgetState extends State { HomeScreen(), MessageScreen(), ProfileScreen(), + LoginScreen(), ], ), bottomNavigationBar: BottomNavigationBar( @@ -75,6 +77,10 @@ class _MainScreenWidgetState extends State { icon: Icon(Icons.person), label: 'Profile', ), + BottomNavigationBarItem( + icon: Icon(Icons.login), + label: 'Login', + ), ], onTap: onSelectedtap, ), diff --git a/lib/pages/profile_screen/profile_screen.dart b/lib/pages/profile_screen/profile_screen.dart index a1eaf26..3f40f70 100644 --- a/lib/pages/profile_screen/profile_screen.dart +++ b/lib/pages/profile_screen/profile_screen.dart @@ -37,8 +37,8 @@ class ProfileScreenState extends State { final nsec = Nostr.instance.keysService.encodePrivateKeyToNsec(newPrivateKey); - final nsecDecoded = - Nostr.instance.keysService.decodeNsecKeyToPrivateKey(nsec); + // final nsecDecoded = + // Nostr.instance.keysService.decodeNsecKeyToPrivateKey(nsec); // assert(nsecDecoded['type'] == 'nsec'); // assert(nsecDecoded['data'] == newPrivateKey); @@ -47,12 +47,12 @@ class ProfileScreenState extends State { // final newPublicKey = keyGenerator.getPublicKey(newPrivateKey); final npub = Nostr.instance.keysService.encodePublicKeyToNpub(newPublicKey); - final npubDecoded = - Nostr.instance.keysService.decodeNpubKeyToPublicKey(npub); + // final npubDecoded = + // Nostr.instance.keysService.decodeNpubKeyToPublicKey(npub); // assert(npubDecoded['type'] == 'npub'); // assert(npubDecoded['data'] == newPublicKey); - return await _addKeyToStorage(newPrivateKey, newPublicKey, nsec, npub); + return await addKeyToStorage(newPrivateKey, newPublicKey, nsec, npub); } Future _getKeysFromStorage() async { @@ -80,7 +80,7 @@ class ProfileScreenState extends State { // Adding a new key // Writing a private and public key to a secure vault - Future _addKeyToStorage( + Future addKeyToStorage( String privateKeyHex, String publicKeyHex, String nsecKey, @@ -89,7 +89,7 @@ class ProfileScreenState extends State { // Waiting for both write operations to complete Future.wait([ _secureStorage.write(key: 'privateKey', value: privateKeyHex), - _secureStorage.write(key: 'publicKey', value: privateKeyHex), + _secureStorage.write(key: 'publicKey', value: publicKeyHex), _secureStorage.write(key: 'nsec', value: nsecKey), _secureStorage.write(key: 'npub', value: npubKey), ]); @@ -150,8 +150,8 @@ class ProfileScreenState extends State { Keys.keysExist ? ElevatedButton( style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - AppColors.mainDarkBlue)), + backgroundColor: + MaterialStateProperty.all(AppColors.background)), onPressed: () { keysExistDialog( Nostr.instance.keysService @@ -166,8 +166,8 @@ class ProfileScreenState extends State { ) : ElevatedButton( style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - AppColors.mainDarkBlue)), + backgroundColor: + MaterialStateProperty.all(AppColors.background)), onPressed: () { modalBottomSheet(); }, diff --git a/lib/pages/profile_screen/widgets/key_exist_dialog.dart b/lib/pages/profile_screen/widgets/key_exist_dialog.dart index d74e34f..84230b1 100644 --- a/lib/pages/profile_screen/widgets/key_exist_dialog.dart +++ b/lib/pages/profile_screen/widgets/key_exist_dialog.dart @@ -43,7 +43,7 @@ class _KeysExistDialogState extends State { padding: const EdgeInsets.symmetric(vertical: 24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - color: AppColors.mainDarkBlue, + color: AppColors.background, ), child: const Center( child: Text( diff --git a/lib/pages/profile_screen/widgets/ok_button_widget.dart b/lib/pages/profile_screen/widgets/ok_button_widget.dart index 6dbaad7..ae7412b 100644 --- a/lib/pages/profile_screen/widgets/ok_button_widget.dart +++ b/lib/pages/profile_screen/widgets/ok_button_widget.dart @@ -16,7 +16,7 @@ class OkButton extends StatelessWidget { return ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.mainDarkBlue, + backgroundColor: AppColors.background, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/pubspec.yaml b/pubspec.yaml index 68dea71..6b26206 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -63,7 +63,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - assets/ + - assets/images/logo/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see