diff --git a/assets/images/icons/drifter_vector.png b/assets/images/icons/drifter_vector.png new file mode 100644 index 0000000..14ee6b2 Binary files /dev/null and b/assets/images/icons/drifter_vector.png differ diff --git a/lib/main.dart b/lib/main.dart index 4e83a28..f7c5dd0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:drifter/pages/create_account_screen/create_account_screen.dart'; import 'package:drifter/pages/home_screen/home_screen_widget.dart'; import 'package:drifter/pages/login_screen/login_screen.dart'; import 'package:drifter/pages/main_screen/main_screen_widget.dart'; +import 'package:drifter/pages/settings_screen/settings_screen.dart'; import 'package:drifter/pages/splash_screen/splash_screen.dart'; import 'package:drifter/pages/terms_of_service/terms_of_service.dart'; import 'package:drifter/pages/welcome_screen/welcome_screen.dart'; @@ -40,6 +41,7 @@ class MyApp extends StatelessWidget { '/terms': (context) => const TermsOfServiceScreen(), '/createAccount': (context) => const CreateAccountScreen(), '/MainScreen': (context) => const MainScreenWidget(), + '/Settings': (context) => const SettingsScreen(), }, ); } diff --git a/lib/pages/create_account_screen/create_account_screen.dart b/lib/pages/create_account_screen/create_account_screen.dart index c745baf..b4bf74e 100644 --- a/lib/pages/create_account_screen/create_account_screen.dart +++ b/lib/pages/create_account_screen/create_account_screen.dart @@ -2,6 +2,7 @@ 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/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'; @@ -24,6 +25,84 @@ class CreateAccountScreen extends StatefulWidget { } class CreateAccountScreenState extends State { + final secureStorage = const FlutterSecureStorage(); + + 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; + }); + } + } + + 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; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -123,6 +202,15 @@ class CreateAccountScreenState extends State { height: 56, child: ElevatedButton( onPressed: () { + final currentContext = context; + generateNewKeys().then( + (keysGenerated) { + if (keysGenerated) { + ScaffoldMessenger.of(currentContext).showSnackBar( + MessageSnackBar(label: 'Keys Generated!')); + } + }, + ); Navigator.pushNamedAndRemoveUntil( context, '/MainScreen', (_) => false); }, diff --git a/lib/pages/home_screen/home_screen_widget.dart b/lib/pages/home_screen/home_screen_widget.dart index 6524249..48391a0 100644 --- a/lib/pages/home_screen/home_screen_widget.dart +++ b/lib/pages/home_screen/home_screen_widget.dart @@ -90,6 +90,7 @@ class _HomeScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: AppColors.mainBackground, body: RefreshIndicator( onRefresh: () async { await _resubscribeStream(); @@ -192,16 +193,16 @@ class TimeAgo { String timeAgo = ''; if (difference.inDays > 0) { - timeAgo = - '${difference.inDays} ${difference.inDays == 1 ? 'day' : 'days'} ago'; + timeAgo = '${difference.inDays}d'; + // '${difference.inDays} ${difference.inDays == 1 ? 'day' : 'days'} ago'; } else if (difference.inHours > 0) { - timeAgo = - '${difference.inHours} ${difference.inHours == 1 ? 'hour' : 'hours'} ago'; + timeAgo = '${difference.inHours}h'; + // '${difference.inHours} ${difference.inHours == 1 ? 'hour' : 'hours'} ago'; } else if (difference.inMinutes > 0) { - timeAgo = - '${difference.inMinutes} ${difference.inMinutes == 1 ? 'minute' : 'minutes'} ago'; + timeAgo = '${difference.inMinutes}m'; + // '${difference.inMinutes} ${difference.inMinutes == 1 ? 'minute' : 'minutes'} ago'; } else { - timeAgo = 'just now'; + timeAgo = '${difference.inMinutes}m'; } return timeAgo; @@ -237,34 +238,79 @@ class DomainCard extends StatelessWidget { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( - color: Colors.grey, + color: AppColors.postBg, borderRadius: BorderRadius.circular(8), ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Row( + // children: [ + // CircleAvatar( + // radius: 25, + // backgroundImage: FadeInImage( + // placeholder: const NetworkImage( + // 'https://i.ibb.co/mJkxDkb/satoshi.png'), + // image: NetworkImage(domain.avatarUrl), + // ).image, + // ), + // Container( + // width: 300, + // child: Row( + // children: [ + // Text(domain.name), + // SizedBox( + // width: 4, + // ), + // Text('@${domain.username.toLowerCase()}', + // style: TextStyle(color: AppColors.postUserName)), + // Expanded(child: SizedBox()), + // Text('${domain.time}', + // style: TextStyle(color: AppColors.postUserName)), + // ], + // ), + // ) + // ], + // ), ListTile( + contentPadding: EdgeInsets.only(top: 16, right: 16, left: 16), leading: CircleAvatar( + radius: 25, backgroundImage: FadeInImage( placeholder: const NetworkImage('https://i.ibb.co/mJkxDkb/satoshi.png'), image: NetworkImage(domain.avatarUrl), ).image, ), - title: - Text(domain.name, style: const TextStyle(color: Colors.white)), - subtitle: Text('@${domain.username.toLowerCase()} • ${domain.time}', - style: TextStyle(color: Colors.grey.shade400)), - trailing: const Icon(Icons.more_vert, color: Colors.grey), + title: Row( + children: [ + Text(domain.name), + SizedBox( + width: 4, + ), + Expanded( + child: Container( + child: Text('@${domain.username.toLowerCase()}', + overflow: TextOverflow.ellipsis, + style: TextStyle(color: AppColors.postUserName)), + ), + ), + Text('${domain.time}', + style: TextStyle(color: AppColors.postUserName)), + ], + ), + trailing: + const Icon(Icons.more_horiz, color: AppColors.postMoreIcon), ), - Divider(height: 1, color: Colors.grey.shade400), Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + padding: + const EdgeInsets.only(top: 12, right: 16, bottom: 14, left: 78), child: Text(domain.content, - style: const TextStyle(color: Colors.white)), + style: const TextStyle(color: AppColors.postBodyText)), ), if (imageLinks != null && imageLinks.isNotEmpty) - Center( + Padding( + padding: const EdgeInsets.only( + top: 12, right: 16, bottom: 14, left: 78), child: Stack( children: [ const Placeholder( @@ -274,7 +320,7 @@ class DomainCard extends StatelessWidget { Center( child: FadeInImage( placeholder: const NetworkImage( - 'https://i.ibb.co/D9jqXgR/58038897-167f0280-7ae6-11e9-94eb-88e880a25f0f.gif', + 'https://media.tenor.com/On7kvXhzml4AAAAj/loading-gif.gif', ), image: NetworkImage(imageLinks.first), fit: BoxFit.cover, diff --git a/lib/pages/main_screen/main_screen_widget.dart b/lib/pages/main_screen/main_screen_widget.dart index 0d43224..de04474 100644 --- a/lib/pages/main_screen/main_screen_widget.dart +++ b/lib/pages/main_screen/main_screen_widget.dart @@ -2,7 +2,9 @@ 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/notifications_screen/notifications_screen.dart'; import 'package:drifter/pages/profile_screen/profile_screen.dart'; +import 'package:drifter/pages/search_screen/search_screen.dart'; import 'package:drifter/theme/app_colors.dart'; import 'package:drifter/main.dart'; import 'package:drifter/utilities/assets.dart'; @@ -17,15 +19,48 @@ class MainScreenWidget extends StatefulWidget { } class _MainScreenWidgetState extends State { - int _selectedTap = 0; + int _selectedTap = 2; + late String _title; void onSelectedtap(int index) { if (_selectedTap == index) return; setState(() { _selectedTap = index; + switch (index) { + case 0: + { + _title = 'Message'; + } + break; + case 1: + { + _title = 'Search'; + } + break; + case 2: + { + _title = 'Feed'; + } + break; + case 3: + { + _title = 'Notifications'; + } + break; + case 4: + { + _title = 'Profile'; + } + break; + } }); } + @override + initState() { + _title = 'Feed'; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -34,7 +69,9 @@ class _MainScreenWidgetState extends State { leading: IconButton( icon: Icon(Icons.settings), color: AppColors.topNavIconPtimary, - onPressed: () {}, + onPressed: () { + Navigator.pushNamed(context, '/Settings'); + }, ), actions: [ IconButton( @@ -43,8 +80,8 @@ class _MainScreenWidgetState extends State { onPressed: () {}, ), ], - title: const Text( - "Drifter", + title: Text( + _title, style: TextStyle( color: AppColors.topNavText, ), @@ -57,8 +94,10 @@ class _MainScreenWidgetState extends State { body: IndexedStack( index: _selectedTap, children: const [ - HomeScreen(), MessageScreen(), + SearchScreen(), + HomeScreen(), + NotificationsScreen(), ProfileScreen(), // LoginScreen(), ], @@ -67,19 +106,31 @@ class _MainScreenWidgetState extends State { backgroundColor: AppColors.bottomNavBackground, selectedItemColor: AppColors.bottomNavIconActive, unselectedItemColor: AppColors.bottomNavIconDefault, + selectedFontSize: 0, currentIndex: _selectedTap, + type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Home', + icon: Icon(Icons.mail_outline), + label: '', ), BottomNavigationBarItem( - icon: Icon(Icons.message), - label: 'Message', + icon: Icon(Icons.search), + label: '', ), BottomNavigationBarItem( - icon: Icon(Icons.person), - label: 'Profile', + icon: + ImageIcon(AssetImage('assets/images/icons/drifter_vector.png')), + label: '', + ), + BottomNavigationBarItem( + icon: Icon(Icons.notifications_outlined), + label: '', + ), + + BottomNavigationBarItem( + icon: Icon(Icons.person_2_outlined), + label: '', ), // BottomNavigationBarItem( // icon: Icon(Icons.login), diff --git a/lib/pages/message_screen/message_screen_widget.dart b/lib/pages/message_screen/message_screen_widget.dart index ced3ed2..d149c5f 100644 --- a/lib/pages/message_screen/message_screen_widget.dart +++ b/lib/pages/message_screen/message_screen_widget.dart @@ -18,7 +18,7 @@ class _MessageScreenState extends State { children: const [ Center( child: Text( - "List of posts", + "Message", ), ) ], diff --git a/lib/pages/notifications_screen/notifications_screen.dart b/lib/pages/notifications_screen/notifications_screen.dart new file mode 100644 index 0000000..26a33e7 --- /dev/null +++ b/lib/pages/notifications_screen/notifications_screen.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class NotificationsScreen extends StatefulWidget { + const NotificationsScreen({super.key}); + + @override + State createState() => _NotificationsScreenState(); +} + +class _NotificationsScreenState extends State { + @override + Widget build(BuildContext context) { + return Center( + child: Text('Notifications Page'), + ); + } +} diff --git a/lib/pages/search_screen/search_screen.dart b/lib/pages/search_screen/search_screen.dart new file mode 100644 index 0000000..30f920d --- /dev/null +++ b/lib/pages/search_screen/search_screen.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class SearchScreen extends StatefulWidget { + const SearchScreen({super.key}); + + @override + State createState() => _SearchScreenState(); +} + +class _SearchScreenState extends State { + @override + Widget build(BuildContext context) { + return Center( + child: Text('Search Page'), + ); + } +} diff --git a/lib/pages/settings_screen/settings_screen.dart b/lib/pages/settings_screen/settings_screen.dart new file mode 100644 index 0000000..062e4ab --- /dev/null +++ b/lib/pages/settings_screen/settings_screen.dart @@ -0,0 +1,41 @@ +import 'package:drifter/theme/app_colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class SettingsScreen extends StatefulWidget { + const SettingsScreen({super.key}); + + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.mainBackground, + appBar: AppBar( + leading: IconButton( + icon: Icon( + Icons.arrow_back, + color: AppColors.topNavIconPtimary, + ), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text( + ('Settings'), + style: TextStyle( + color: AppColors.topNavText, + ), + // textAlign: TextAlign.center, + ), + centerTitle: true, + backgroundColor: AppColors.mainBackground, + elevation: 0, + ), + body: Center( + child: Text('Settings'), + ), + ); + } +} diff --git a/lib/pages/splash_screen/splash_screen.dart b/lib/pages/splash_screen/splash_screen.dart index f05810e..4256876 100644 --- a/lib/pages/splash_screen/splash_screen.dart +++ b/lib/pages/splash_screen/splash_screen.dart @@ -35,7 +35,7 @@ class _SplashState extends State { height: 300, ), Image.asset( - 'assets/images/logo/drifter_vector.png', + 'assets/images/icons/drifter_vector.png', height: 111, width: 93, ), diff --git a/pubspec.yaml b/pubspec.yaml index c79fc99..d37d8e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,7 +64,8 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - assets/images/logo/ + - assets/images//icons/ + - assets/images//logo/ # - images/a_dot_ham.jpeg