diff --git a/assets/translations/en.json b/assets/translations/en.json index b5a7356136c9fff23919b30fd21c60a65dbbc8c7..2684a50619c9201d332e5252195ce6e9acb31079 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -13,6 +13,7 @@ "g1_amount_hint": "Amount to send in {currency}", "g1_form_pay_send": "Send", "search_user_title": "User to pay", + "search_user_title_in_contacts": "Search for users", "search_user": "Search (user or public key)", "search_user_btn": "Search user", "g1_form_pay_desc": "Comment", diff --git a/assets/translations/es.json b/assets/translations/es.json index 1e2e1f3fc38ad30c9bfce7f2e1873f189af2527b..ab797010fb5f5e2865eeaff88bea512dbb103e93 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -12,7 +12,8 @@ "g1_amount": "Monto a enviar", "g1_amount_hint": "Monto a enviar en {currency}", "g1_form_pay_send": "Enviar", - "search_user_title": "Usuario a pagar", + "search_user_title": "Usuari@ a pagar", + "search_user_title_in_contacts": "Busca usuari@", "search_user": "Buscar (usuari@ o clave pública)", "search_user_btn": "Buscar usuari@", "g1_form_pay_desc": "Comentario", diff --git a/lib/main.dart b/lib/main.dart index 40026409fee7954cf875f2ecacb880a0ee8517c9..10077db6486b12dedfe82f5cfef2d556492ba9a3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -46,7 +46,7 @@ import 'ui/notification_controller.dart'; import 'ui/screens/skeleton_screen.dart'; import 'ui/ui_helpers.dart'; import 'ui/widgets/connectivity_widget_wrapper_wrapper.dart'; -import 'ui/widgets/first_screen/pay_contact_search_page.dart'; +import 'ui/widgets/first_screen/contact_search_page.dart'; void main() async { await NotificationController.initializeLocalNotifications(); @@ -82,7 +82,7 @@ void main() async { final Box<dynamic> box = await Hive.openBox('hydrated_box', path: HydratedStorage.webStorageDirectory.path); final List<dynamic> keysToDelete = - box.keys.where((dynamic key) => '$key'.startsWith('minified')).toList(); + box.keys.where((dynamic key) => '$key'.startsWith('minified')).toList(); box.deleteAll(keysToDelete); // This should we done after init // await HydratedBloc.storage.clear(); @@ -96,7 +96,7 @@ void main() async { final Directory tmpDir = await getTemporaryDirectory(); Hive.init(tmpDir.toString()); HydratedBloc.storage = - await HydratedStorage.build(storageDirectory: tmpDir); + await HydratedStorage.build(storageDirectory: tmpDir); } PWAInstall().setup(installCallback: () { @@ -105,8 +105,7 @@ void main() async { Bloc.observer = AppBlocObserver(); - void appRunner() => - runApp( + void appRunner() => runApp( EasyLocalization( path: 'assets/translations', supportedLocales: const <Locale>[ @@ -150,7 +149,9 @@ void main() async { if (kReleaseMode) { // Only use sentry in production - await SentryFlutter.init((SentryFlutterOptions options,) { + await SentryFlutter.init(( + SentryFlutterOptions options, + ) { options.tracesSampleRate = 1.0; options.reportPackages = false; // options.addInAppInclude('sentry_flutter_example'); @@ -197,7 +198,7 @@ class AppIntro extends StatefulWidget { class _AppIntro extends State<AppIntro> { final GlobalKey<IntroductionScreenState> introKey = - GlobalKey<IntroductionScreenState>(); + GlobalKey<IntroductionScreenState>(); void _onIntroEnd(BuildContext context, AppCubit cubit) { cubit.introViewed(); @@ -211,43 +212,40 @@ class _AppIntro extends State<AppIntro> { Widget build(BuildContext context) { return BlocBuilder<AppCubit, AppState>( builder: (BuildContext buildContext, AppState state) { - final AppCubit cubit = context.read<AppCubit>(); - return IntroductionScreen( - key: introKey, - pages: <PageViewModel>[ - for (int i = 1; i <= 5; i++) - createPageViewModel( - 'intro_${i}_title', 'intro_${i}_description', - 'assets/img/undraw_intro_$i.png', context), - ], - onDone: () => _onIntroEnd(buildContext, cubit), - showSkipButton: true, - skipOrBackFlex: 0, - onSkip: () => _onIntroEnd(buildContext, cubit), - nextFlex: 0, - skip: Text(tr('skip')), - next: const Icon(Icons.arrow_forward), - done: Text(tr('start'), - style: const TextStyle(fontWeight: FontWeight.w600)), - dotsDecorator: const DotsDecorator( - size: Size(10.0, 10.0), - color: Color(0xFFBDBDBD), - activeColor: Colors.blueAccent, - activeSize: Size(22.0, 10.0), - activeShape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(25.0)), - ), - ), - ); - }); + final AppCubit cubit = context.read<AppCubit>(); + return IntroductionScreen( + key: introKey, + pages: <PageViewModel>[ + for (int i = 1; i <= 5; i++) + createPageViewModel('intro_${i}_title', 'intro_${i}_description', + 'assets/img/undraw_intro_$i.png', context), + ], + onDone: () => _onIntroEnd(buildContext, cubit), + showSkipButton: true, + skipOrBackFlex: 0, + onSkip: () => _onIntroEnd(buildContext, cubit), + nextFlex: 0, + skip: Text(tr('skip')), + next: const Icon(Icons.arrow_forward), + done: Text(tr('start'), + style: const TextStyle(fontWeight: FontWeight.w600)), + dotsDecorator: const DotsDecorator( + size: Size(10.0, 10.0), + color: Color(0xFFBDBDBD), + activeColor: Colors.blueAccent, + activeSize: Size(22.0, 10.0), + activeShape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(25.0)), + ), + ), + ); + }); } } -PageViewModel createPageViewModel(String title, String body, String imageAsset, - BuildContext context) { - final ColorScheme colorScheme = Theme - .of(context) - .colorScheme; +PageViewModel createPageViewModel( + String title, String body, String imageAsset, BuildContext context) { + final ColorScheme colorScheme = Theme.of(context).colorScheme; final TextStyle titleStyle = TextStyle( color: colorScheme.primary, fontWeight: FontWeight.bold, @@ -275,7 +273,7 @@ class GinkgoApp extends StatefulWidget { // The navigator key is necessary to navigate using static methods static final GlobalKey<NavigatorState> navigatorKey = - GlobalKey<NavigatorState>(); + GlobalKey<NavigatorState>(); @override State<GinkgoApp> createState() => _GinkgoAppState(); @@ -291,16 +289,10 @@ class _GinkgoAppState extends State<GinkgoApp> { } void _printNodeStatus({String prefix = 'Starting'}) { - final int nDuniterNodes = NodeManager() - .nodeList(NodeType.duniter) - .length; + final int nDuniterNodes = NodeManager().nodeList(NodeType.duniter).length; final int nCesiumPlusNodes = - NodeManager() - .nodeList(NodeType.cesiumPlus) - .length; - final int nGvaNodes = NodeManager() - .nodeList(NodeType.gva) - .length; + NodeManager().nodeList(NodeType.cesiumPlus).length; + final int nGvaNodes = NodeManager().nodeList(NodeType.gva).length; logger( '$prefix with $nDuniterNodes duniter nodes, $nCesiumPlusNodes c+ nodes, and $nGvaNodes gva nodes'); if (!kReleaseMode) { @@ -320,7 +312,7 @@ class _GinkgoAppState extends State<GinkgoApp> { NotificationController.startListeningNotificationEvents(); Once.runHourly('load_nodes', callback: () async { final bool isConnected = - await ConnectivityWidgetWrapperWrapper.isConnected; + await ConnectivityWidgetWrapperWrapper.isConnected; if (isConnected) { logger('Load nodes via once'); _loadNodes(); @@ -335,10 +327,7 @@ class _GinkgoAppState extends State<GinkgoApp> { Once.runDaily('clear_cache', callback: () { logger('clear cache via once'); ContactsCache().clear(); - ContactsCache().addContacts(context - .read<ContactsCubit>() - .state - .contacts); + ContactsCache().addContacts(context.read<ContactsCubit>().state.contacts); }); Once.runOnce('resize_avatars', callback: () { logger('resize avatar via once'); @@ -366,8 +355,7 @@ class _GinkgoAppState extends State<GinkgoApp> { showDialog( context: context, builder: (BuildContext context) { - return - PayContactSearchPage(uri: link); + return ContactSearchPage(uri: link); }, ); } @@ -391,104 +379,94 @@ class _GinkgoAppState extends State<GinkgoApp> { Widget build(BuildContext context) { return BlocBuilder<NodeListCubit, NodeListState>( builder: (BuildContext nodeContext, NodeListState state) { - return ConnectivityAppWrapper( - app: FilesystemPickerDefaultOptions( - fileTileSelectMode: FileTileSelectMode.wholeTile, - theme: FilesystemPickerTheme( - topBar: FilesystemPickerTopBarThemeData( - backgroundColor: Theme - .of(context) - .colorScheme - .primary, - ), - ), - child: MaterialApp( - - /// Localization is not available for the title. - title: 'Ğ1nkgo', - theme: ThemeData( - useMaterial3: true, colorScheme: lightColorScheme), - darkTheme: + return ConnectivityAppWrapper( + app: FilesystemPickerDefaultOptions( + fileTileSelectMode: FileTileSelectMode.wholeTile, + theme: FilesystemPickerTheme( + topBar: FilesystemPickerTopBarThemeData( + backgroundColor: Theme.of(context).colorScheme.primary, + ), + ), + child: MaterialApp( + /// Localization is not available for the title. + title: 'Ğ1nkgo', + theme: ThemeData( + useMaterial3: true, colorScheme: lightColorScheme), + darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme), - navigatorKey: GinkgoApp.navigatorKey, - scaffoldMessengerKey: globalMessengerKey, - - /// Theme stuff - themeMode: context - .watch<ThemeCubit>() - .state - .themeMode, - - /// Localization stuff - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - debugShowCheckedModeBanner: false, - home: context - .read<AppCubit>() - .isIntroViewed - ? BetterFeedback( + navigatorKey: GinkgoApp.navigatorKey, + scaffoldMessengerKey: globalMessengerKey, + + /// Theme stuff + themeMode: context.watch<ThemeCubit>().state.themeMode, + + /// Localization stuff + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + debugShowCheckedModeBanner: false, + home: context.read<AppCubit>().isIntroViewed + ? BetterFeedback( localizationsDelegates: context.localizationDelegates ..add(CustomFeedbackLocalizationsDelegate()), child: const SkeletonScreen()) - : const AppIntro(), - builder: (BuildContext buildContext, Widget? widget) { - NotificationController.locale = context.locale; - return ResponsiveWrapper.builder( - BouncingScrollWrapper.builder( - context, - ConnectivityWidgetWrapperWrapper( - //message: tr('offline'), - //height: 18, - - offlineWidget: /* Container( + : const AppIntro(), + builder: (BuildContext buildContext, Widget? widget) { + NotificationController.locale = context.locale; + return ResponsiveWrapper.builder( + BouncingScrollWrapper.builder( + context, + ConnectivityWidgetWrapperWrapper( + //message: tr('offline'), + //height: 18, + + offlineWidget: /* Container( color: Colors.transparent, child: Center( child: */ Column( - mainAxisSize: MainAxisSize.min, - children: <Widget>[ - const Icon( - Icons.cloud_off, - size: 48, + mainAxisSize: MainAxisSize.min, + children: <Widget>[ + const Icon( + Icons.cloud_off, + size: 48, + color: Colors.grey, + ), + const SizedBox(height: 6), + Container( + padding: const EdgeInsets.all(5.0), + decoration: const BoxDecoration( color: Colors.grey, - ), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.all(5.0), - decoration: const BoxDecoration( - color: Colors.grey, - borderRadius: + borderRadius: BorderRadius.all(Radius.circular(10.0)), - ), - child: Text( - tr('offline'), - style: const TextStyle( - color: Colors.white, - decoration: TextDecoration.none, - fontSize: 14, - ), - )), - const SizedBox(height: 110), - ], - ), - - child: widget!, - )), - maxWidth: 480, - minWidth: 480, - // defaultScale: true, - breakpoints: <ResponsiveBreakpoint>[ - const ResponsiveBreakpoint.resize(200, name: MOBILE), - const ResponsiveBreakpoint.resize(480, name: TABLET), - const ResponsiveBreakpoint.resize( - 1000, name: DESKTOP), - ], - background: Container(color: const Color(0xFFF5F5F5)), - ); - }, - ))); - }); + ), + child: Text( + tr('offline'), + style: const TextStyle( + color: Colors.white, + decoration: TextDecoration.none, + fontSize: 14, + ), + )), + const SizedBox(height: 110), + ], + ), + + child: widget!, + )), + maxWidth: 480, + minWidth: 480, + // defaultScale: true, + breakpoints: <ResponsiveBreakpoint>[ + const ResponsiveBreakpoint.resize(200, name: MOBILE), + const ResponsiveBreakpoint.resize(480, name: TABLET), + const ResponsiveBreakpoint.resize(1000, name: DESKTOP), + ], + background: Container(color: const Color(0xFFF5F5F5)), + ); + }, + ))); + }); } } diff --git a/lib/ui/screens/third_screen.dart b/lib/ui/screens/third_screen.dart index eba4e21a2ded496b9ce79944aabc606607f0cef9..bfb5f0ea35fce7f05305035178055917091735a2 100644 --- a/lib/ui/screens/third_screen.dart +++ b/lib/ui/screens/third_screen.dart @@ -11,6 +11,7 @@ import '../qr_manager.dart'; import '../tutorial.dart'; import '../tutorial_keys.dart'; import '../widgets/card_drawer.dart'; +import '../widgets/first_screen/contact_search_page.dart'; import '../widgets/third_screen/contact_form_dialog.dart'; import '../widgets/third_screen/contacts_page.dart'; import '../widgets/third_screen/third_tutorial.dart'; @@ -38,6 +39,16 @@ class _ThirdScreenState extends State<ThirdScreen> { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(tr('bottom_nav_trd')), actions: <Widget>[ + IconButton( + icon: const Icon(Icons.travel_explore), + onPressed: () async { + showDialog( + context: context, + builder: (BuildContext context) { + return const ContactSearchPage(forPayment: false); + }, + ); + }), IconButton( key: contactsQrKey, icon: const Icon(Icons.qr_code), diff --git a/lib/ui/widgets/first_screen/pay_contact_search_page.dart b/lib/ui/widgets/first_screen/contact_search_page.dart similarity index 95% rename from lib/ui/widgets/first_screen/pay_contact_search_page.dart rename to lib/ui/widgets/first_screen/contact_search_page.dart index f13954a8f8bb6093346406662f6a21d405fe1a0e..f935d880f716f2ff1093fa16cfdeee1311311f9a 100644 --- a/lib/ui/widgets/first_screen/pay_contact_search_page.dart +++ b/lib/ui/widgets/first_screen/contact_search_page.dart @@ -24,16 +24,17 @@ import '../loading_box.dart'; import '../third_screen/contacts_page.dart'; import 'contact_fav_icon.dart'; -class PayContactSearchPage extends StatefulWidget { - const PayContactSearchPage({super.key, this.uri}); +class ContactSearchPage extends StatefulWidget { + const ContactSearchPage({super.key, this.uri, this.forPayment = true}); final String? uri; + final bool forPayment; @override - State<PayContactSearchPage> createState() => _PayContactSearchPageState(); + State<ContactSearchPage> createState() => _ContactSearchPageState(); } -class _PayContactSearchPageState extends State<PayContactSearchPage> { +class _ContactSearchPageState extends State<ContactSearchPage> { final TextEditingController _searchController = TextEditingController(); String _searchTerm = ''; @@ -135,7 +136,9 @@ class _PayContactSearchPageState extends State<PayContactSearchPage> { final PaymentCubit paymentCubit = context.read<PaymentCubit>(); return Scaffold( appBar: AppBar( - title: Text(tr('search_user_title')), + title: Text(widget.forPayment + ? tr('search_user_title') + : tr('search_user_title_in_contacts')), backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Theme.of(context).colorScheme.inversePrimary, actions: <Widget>[ @@ -241,6 +244,9 @@ class _PayContactSearchPageState extends State<PayContactSearchPage> { Future<void> _onKeyScanned( String scannedKey, PaymentCubit paymentCubit) async { + if (!widget.forPayment) { + return; + } final PaymentState? pay = parseScannedUri(scannedKey); if (pay != null) { logger('Scanned $pay'); diff --git a/lib/ui/widgets/first_screen/pay_contact_search_button.dart b/lib/ui/widgets/first_screen/pay_contact_search_button.dart index d961ada79a267493fec63a519573c1fee7bee76d..3e95cb13a73c503351affc5819a29db04ea0f6fd 100644 --- a/lib/ui/widgets/first_screen/pay_contact_search_button.dart +++ b/lib/ui/widgets/first_screen/pay_contact_search_button.dart @@ -4,7 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../data/models/payment_cubit.dart'; import '../../../data/models/payment_state.dart'; -import 'pay_contact_search_page.dart'; +import 'contact_search_page.dart'; import 'pay_recipient_widget.dart'; class PayContactSearchButton extends StatefulWidget { @@ -25,7 +25,7 @@ class _PayContactSearchButtonState extends State<PayContactSearchButton> { showDialog( context: context, builder: (BuildContext context) { - return const PayContactSearchPage(); + return const ContactSearchPage(); }, ); },