253 lines
10 KiB
Dart
253 lines
10 KiB
Dart
import 'package:dart_nostr/dart_nostr.dart';
|
||
import 'package:drifter/models/models.dart';
|
||
import 'package:drifter/pages/home_screen/widgets/message_text_button_widget.dart';
|
||
import 'package:drifter/pages/profile_screen/profile_screen.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/widgets/btn_continue.dart';
|
||
|
||
import 'package:drifter/theme/app_colors.dart';
|
||
import 'package:drifter/utilities/assets.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||
import 'package:flutter_svg/svg.dart';
|
||
import 'package:nostr_tools/nostr_tools.dart';
|
||
import 'package:sliding_switch/sliding_switch.dart';
|
||
import 'package:toggle_switch/toggle_switch.dart';
|
||
|
||
class LoginScreen extends StatefulWidget {
|
||
const LoginScreen({super.key});
|
||
|
||
@override
|
||
State<LoginScreen> createState() => LoginScreenState();
|
||
}
|
||
|
||
class LoginScreenState extends State<LoginScreen> {
|
||
final keyGenerator = KeyApi();
|
||
final nip19 = Nip19();
|
||
final indexToggle = 0;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
leading: IconButton(
|
||
icon: Icon(
|
||
Icons.arrow_back,
|
||
color: AppColors.topNavIconBack,
|
||
),
|
||
onPressed: () => Navigator.of(context).pop(),
|
||
),
|
||
actions: <Widget>[
|
||
TextButton(
|
||
onPressed: () {},
|
||
child: Text('Skip'),
|
||
)
|
||
],
|
||
elevation: 0,
|
||
backgroundColor: AppColors.mainBackground,
|
||
),
|
||
backgroundColor: AppColors.mainBackground,
|
||
body: Padding(
|
||
padding: const EdgeInsets.all(16.0),
|
||
child: Column(
|
||
children: [
|
||
const SizedBox(
|
||
height: 56,
|
||
),
|
||
ToggleSwitch(
|
||
minWidth: double.infinity,
|
||
minHeight: 56,
|
||
totalSwitches: 2,
|
||
labels: ['Login', 'View only'],
|
||
activeBgColor: [AppColors.toggleSwitchActiveBg],
|
||
activeFgColor: AppColors.toggleSwitchTextActive,
|
||
inactiveBgColor: AppColors.toggleSwitchBg,
|
||
inactiveFgColor: AppColors.toggleSwitchTextInactive,
|
||
activeBorders: [
|
||
Border.all(
|
||
color: AppColors.toggleSwitchBg,
|
||
width: 4,
|
||
),
|
||
],
|
||
radiusStyle: true,
|
||
cornerRadius: 8,
|
||
customTextStyles: [
|
||
TextStyle(fontSize: 14, fontWeight: FontWeight.w600)
|
||
],
|
||
onToggle: (indexToggle) {
|
||
print(indexToggle);
|
||
},
|
||
),
|
||
SizedBox(
|
||
height: 56,
|
||
),
|
||
const Text(
|
||
'Login',
|
||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
|
||
),
|
||
const SizedBox(height: 13),
|
||
const Text(
|
||
'Paste your password (AKA Private Key)',
|
||
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w400),
|
||
),
|
||
const SizedBox(height: 24),
|
||
TextFormField(
|
||
decoration: InputDecoration(
|
||
filled: true,
|
||
fillColor: AppColors.textFieldDefaultBg,
|
||
labelText: 'nsec... / hex...',
|
||
labelStyle:
|
||
TextStyle(color: AppColors.textFieldDefaultText),
|
||
suffixIcon: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: <Widget>[
|
||
IconButton(
|
||
icon: const Icon(Icons.paste),
|
||
color: AppColors.textFieldActiveIconTrail,
|
||
onPressed: () {},
|
||
),
|
||
IconButton(
|
||
icon: const Icon(Icons.qr_code_scanner),
|
||
color: AppColors.textFieldActiveIconTrail,
|
||
onPressed: () {},
|
||
),
|
||
],
|
||
),
|
||
border: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(8),
|
||
borderSide: BorderSide(
|
||
width: 0,
|
||
style: BorderStyle.none,
|
||
),
|
||
),
|
||
iconColor: AppColors.textFieldDefaultIconTrail),
|
||
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(Nostr
|
||
.instance.keysService
|
||
.decodeNsecKeyToPrivateKey(value));
|
||
if (!(isValidHexKey || isValidNSec)) {
|
||
return 'Your private key is not valid.';
|
||
}
|
||
} on ChecksumVerificationException catch (e) {
|
||
return e.message;
|
||
} catch (e) {
|
||
return 'Error: $e';
|
||
}
|
||
return null;
|
||
}),
|
||
const SizedBox(height: 10),
|
||
Container(
|
||
padding: EdgeInsets.all(12),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(8),
|
||
color: Colors.white,
|
||
),
|
||
height: 168,
|
||
child: Note.note,
|
||
),
|
||
Expanded(child: SizedBox()),
|
||
SizedBox(
|
||
width: double.infinity,
|
||
height: 56,
|
||
child: ElevatedButton(
|
||
onPressed: () {
|
||
if (formKey.currentState!.validate()) {
|
||
String privateKeyHex = keyController.text.trim();
|
||
String publicKeyHex;
|
||
String nsecKey;
|
||
String npubKey;
|
||
|
||
// 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);
|
||
// } else {
|
||
// publicKeyHex = Nostr.instance.keysService
|
||
// .derivePublicKey(privateKey: 'privateKeyHex');
|
||
// nsecKey = Nostr.instance.keysService
|
||
// .encodePrivateKeyToNsec(privateKeyHex);
|
||
// npubKey = Nostr.instance.keysService
|
||
// .encodePublicKeyToNpub(publicKeyHex);
|
||
// }
|
||
|
||
if (privateKeyHex.startsWith('nsec')) {
|
||
final decoded = nip19.decode(privateKeyHex);
|
||
privateKeyHex = decoded['data'];
|
||
publicKeyHex = keyGenerator.getPublicKey(privateKeyHex);
|
||
nsecKey = nip19.nsecEncode(privateKeyHex);
|
||
npubKey = nip19.npubEncode(publicKeyHex);
|
||
} else {
|
||
publicKeyHex = keyGenerator.getPublicKey(privateKeyHex);
|
||
nsecKey = nip19.nsecEncode(privateKeyHex);
|
||
npubKey = nip19.npubEncode(publicKeyHex);
|
||
}
|
||
try {
|
||
ProfileScreenState().addKeyToStorage(
|
||
privateKeyHex, publicKeyHex, nsecKey, npubKey);
|
||
|
||
keyController.clear();
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
MessageSnackBar(
|
||
label: 'Congratulations! Keys Stored!'),
|
||
);
|
||
|
||
Navigator.of(context).pop(true);
|
||
} catch (e) {}
|
||
} else {
|
||
formKey.currentState?.setState(() {});
|
||
}
|
||
},
|
||
child: Text(
|
||
'Continue',
|
||
style: TextStyle(fontSize: 16),
|
||
),
|
||
style: ButtonStyle(
|
||
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(100))),
|
||
backgroundColor: const MaterialStatePropertyAll(
|
||
AppColors.buttonPrimaryDefaultBg),
|
||
foregroundColor: const MaterialStatePropertyAll(
|
||
AppColors.buttonPrimaryDefaultText)),
|
||
)),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
abstract class Note {
|
||
static final note = Text.rich(
|
||
TextSpan(
|
||
style: TextStyle(
|
||
height: 1.6,
|
||
fontSize: 10,
|
||
fontWeight: FontWeight.w400,
|
||
),
|
||
children: <TextSpan>[
|
||
TextSpan(
|
||
style: TextStyle(color: AppColors.noteText),
|
||
text:
|
||
"Your private key starts with “nsec” or “hex” and gives your full access to your account. That means, if you log in using your private key, you will be able to make posts and send and receive private messages.\n\n"
|
||
"Your public key starts with “npub” and gives your view-only access to account. If you log in using your public key, you won’t be able to make posts or access private messages, but you will be able to view your feed. Go to “View only” tab to log in via your public key.")
|
||
]),
|
||
);
|
||
}
|