Skip to content
Snippets Groups Projects
main.dart 9.43 KiB
Newer Older
tylersavery's avatar
tylersavery committed
import 'dart:io';
vjrj's avatar
vjrj committed
import 'package:connectivity_wrapper/connectivity_wrapper.dart';
anfeichtinger's avatar
anfeichtinger committed
import 'package:easy_localization/easy_localization.dart';
vjrj's avatar
vjrj committed
import 'package:easy_logger/easy_logger.dart';
vjrj's avatar
vjrj committed
import 'package:flutter/foundation.dart';
anfeichtinger's avatar
anfeichtinger committed
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
anfeichtinger's avatar
anfeichtinger committed
import 'package:flutter_displaymode/flutter_displaymode.dart';
vjrj's avatar
vjrj committed
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:hive_flutter/hive_flutter.dart';
anfeichtinger's avatar
anfeichtinger committed
import 'package:hydrated_bloc/hydrated_bloc.dart';
vjrj's avatar
vjrj committed
import 'package:introduction_screen/introduction_screen.dart';
import 'package:once/once.dart';
anfeichtinger's avatar
anfeichtinger committed
import 'package:path_provider/path_provider.dart';
vjrj's avatar
vjrj committed
import 'package:responsive_framework/responsive_wrapper.dart';
vjrj's avatar
vjrj committed
import 'package:responsive_framework/utils/scroll_behavior.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
anfeichtinger's avatar
anfeichtinger committed

vjrj's avatar
vjrj committed
import 'app_bloc_observer.dart';
vjrj's avatar
vjrj committed
import 'cubit/bottom_nav_cubit.dart';
vjrj's avatar
vjrj committed
import 'data/models/app_cubit.dart';
import 'data/models/app_state.dart';
vjrj's avatar
vjrj committed
import 'data/models/contact_cubit.dart';
vjrj's avatar
vjrj committed
import 'data/models/node_list_cubit.dart';
vjrj's avatar
vjrj committed
import 'data/models/node_list_state.dart';
import 'data/models/node_manager.dart';
vjrj's avatar
vjrj committed
import 'data/models/node_type.dart';
import 'data/models/payment_cubit.dart';
vjrj's avatar
vjrj committed
import 'data/models/transaction_cubit.dart';
vjrj's avatar
vjrj committed
import 'g1/api.dart';
vjrj's avatar
vjrj committed
import 'shared_prefs.dart';
import 'ui/screens/skeleton_screen.dart';
vjrj's avatar
vjrj committed

vjrj's avatar
vjrj committed
// logs
final EasyLogger logger = EasyLogger(
vjrj's avatar
vjrj committed
  name: 'ginkgo',
vjrj's avatar
vjrj committed
  defaultLevel: LevelMessages.debug,
  enableBuildModes: <BuildMode>[
    BuildMode.debug,
    BuildMode.profile,
    BuildMode.release
  ],
  enableLevels: <LevelMessages>[
    LevelMessages.debug,
    LevelMessages.info,
    LevelMessages.error,
    LevelMessages.warning
  ],
);
anfeichtinger's avatar
anfeichtinger committed

void main() async {
  /// Initialize packages
  WidgetsFlutterBinding.ensureInitialized();
  await EasyLocalization.ensureInitialized();
vjrj's avatar
vjrj committed
  Bloc.observer = AppBlocObserver();
vjrj's avatar
vjrj committed

  if (!kIsWeb && Platform.isAndroid) {
tylersavery's avatar
tylersavery committed
    await FlutterDisplayMode.setHighRefreshRate();
vjrj's avatar
vjrj committed
  }

vjrj's avatar
vjrj committed
  final SharedPreferencesHelper shared = SharedPreferencesHelper();
  await shared.init();
  await shared.getWallet();
  assert(shared.getPubKey() != null);

vjrj's avatar
vjrj committed
  // .env
vjrj's avatar
vjrj committed
  await dotenv.load(
      fileName: kReleaseMode
          ? 'assets/env.production.txt'
          : 'assets/.env.development');
vjrj's avatar
vjrj committed

  if (kIsWeb) {
    await Hive.initFlutter();
    HydratedBloc.storage = await HydratedStorage.build(
        storageDirectory: HydratedStorage.webStorageDirectory);
  } else {
    final Directory tmpDir = await getTemporaryDirectory();
    Hive.init(tmpDir.toString());
vjrj's avatar
vjrj committed
    HydratedBloc.storage =
        await HydratedStorage.build(storageDirectory: tmpDir);
tylersavery's avatar
tylersavery committed
  }
vjrj's avatar
vjrj committed

vjrj's avatar
vjrj committed
  // Reset hive during developing
  if (!kReleaseMode) {
    // Once.clearAll();
vjrj's avatar
vjrj committed
    // await HydratedBloc.storage.clear();
vjrj's avatar
vjrj committed
  }
vjrj's avatar
vjrj committed

  void appRunner() => runApp(
vjrj's avatar
vjrj committed
        EasyLocalization(
          path: 'assets/translations',
          supportedLocales: const <Locale>[
            Locale('en'),
            Locale('es'),
            Locale('fr'),
          ],
          fallbackLocale: const Locale('en'),
          useFallbackTranslations: true,
          child: MultiBlocProvider(providers: <BlocProvider<dynamic>>[
            BlocProvider<BottomNavCubit>(
                create: (BuildContext context) => BottomNavCubit()),
            BlocProvider<AppCubit>(
                create: (BuildContext context) => AppCubit()),
            BlocProvider<PaymentCubit>(
                create: (BuildContext context) => PaymentCubit()),
            BlocProvider<NodeListCubit>(
                create: (BuildContext context) => NodeListCubit()),
            BlocProvider<ContactsCubit>(
                create: (BuildContext context) => ContactsCubit()),
            BlocProvider<TransactionsCubit>(
                create: (BuildContext context) => TransactionsCubit())
            // Add other BlocProviders here if needed
          ], child: const GinkgoApp()),
        ),
      );

  if (!kReleaseMode) {
    await SentryFlutter.init((
      SentryFlutterOptions options,
    ) {
vjrj's avatar
vjrj committed
      options.dsn = "${dotenv.env['SENTRY_DSN']}";
      // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
      // We recommend adjusting this value in production.
      // options.tracesSampleRate = 1.0;
    }, appRunner: appRunner);
  } else {
    appRunner();
  }
anfeichtinger's avatar
anfeichtinger committed
}

vjrj's avatar
vjrj committed
class AppIntro extends StatefulWidget {
  const AppIntro({super.key});

  @override
  State<AppIntro> createState() => _AppIntro();
}

class _AppIntro extends State<AppIntro> {
  final GlobalKey<IntroductionScreenState> introKey =
      GlobalKey<IntroductionScreenState>();
vjrj's avatar
vjrj committed

  void _onIntroEnd(BuildContext context) {
vjrj's avatar
vjrj committed
    context.read<AppCubit>().introViewed();
vjrj's avatar
vjrj committed
    Navigator.of(context).pushReplacement(
vjrj's avatar
vjrj committed
      MaterialPageRoute<void>(
          builder: (BuildContext _) => const SkeletonScreen()),
vjrj's avatar
vjrj committed
    );
  }

  @override
  Widget build(BuildContext context) {
vjrj's avatar
vjrj committed
    return BlocBuilder<AppCubit, AppState>(
        builder: (BuildContext buildContext, AppState state) =>
            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'),
              ],
              onDone: () => _onIntroEnd(buildContext),
              showSkipButton: true,
              skipOrBackFlex: 0,
              onSkip: () => _onIntroEnd(buildContext),
              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) {
vjrj's avatar
vjrj committed
  return PageViewModel(
    title: tr(title),
    body: tr(body),
    image: Image.asset(imageAsset),
    decoration: const PageDecoration(
      pageColor: Colors.white,
      bodyTextStyle: TextStyle(fontSize: 18),
      titleTextStyle: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
    ),
  );
}

vjrj's avatar
vjrj committed
class GinkgoApp extends StatefulWidget {
  const GinkgoApp({super.key});
vjrj's avatar
vjrj committed
  @override
vjrj's avatar
vjrj committed
  State<GinkgoApp> createState() => _GinkgoAppState();
vjrj's avatar
vjrj committed
class _GinkgoAppState extends State<GinkgoApp> {
vjrj's avatar
vjrj committed
  Future<void> _loadNodes() async {
    _printNodeStatus();
    await fetchDuniterNodes();
    await fetchCesiumPlusNodes();
vjrj's avatar
vjrj committed
    await fetchGvaNodes();
vjrj's avatar
vjrj committed
    _printNodeStatus(prefix: 'Continuing');
  }

  void _printNodeStatus({String prefix = 'Starting'}) {
    final int nDuniterNodes = NodeManager().nodeList(NodeType.duniter).length;
    final int nCesiumPlusNodes =
        NodeManager().nodeList(NodeType.cesiumPlus).length;
vjrj's avatar
vjrj committed
    final int nGvaNodes = NodeManager().nodeList(NodeType.gva).length;
    logger(
vjrj's avatar
vjrj committed
        '$prefix with $nDuniterNodes duniter nodes, $nCesiumPlusNodes c+ nodes, and $nGvaNodes gva nodes');
vjrj's avatar
vjrj committed
    if (!kReleaseMode) {
      logger('${NodeManager().nodeList(NodeType.cesiumPlus)}');
    }
vjrj's avatar
vjrj committed
  @override
  void initState() {
    super.initState();
    NodeManager().loadFromCubit(context.read<NodeListCubit>());
  }

anfeichtinger's avatar
anfeichtinger committed
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<NodeListCubit, NodeListState>(
        builder: (BuildContext nodeContext, NodeListState state) {
      Once.runHourly('load_nodes',
vjrj's avatar
vjrj committed
          callback: () => _loadNodes(),
          fallback: () {
vjrj's avatar
vjrj committed
            _printNodeStatus(prefix: 'After once hourly having');
          });
      Once.runCustom('clear_errors', callback: () {
        NodeManager().cleanErrorStats();
      }, duration: const Duration(minutes: 90));
      return ConnectivityAppWrapper(
          app: MaterialApp(
        /// Localization is not available for the title.
        title: 'Ğ1nkgo',
        theme: ThemeData(useMaterial3: true, colorScheme: lightColorScheme),
        darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
vjrj's avatar
vjrj committed

        /// Theme stuff
vjrj's avatar
vjrj committed

        /// Localization stuff
        localizationsDelegates: context.localizationDelegates,
        supportedLocales: context.supportedLocales,
        locale: context.locale,
        debugShowCheckedModeBanner: false,
        home: context.read<AppCubit>().isIntroViewed
            ? const SkeletonScreen()
            : const AppIntro(),
        builder: (BuildContext buildContext, Widget? widget) {
          return ResponsiveWrapper.builder(
            BouncingScrollWrapper.builder(
                context,
                ConnectivityWidgetWrapper(
                  message: tr('offline'),
                  height: 20,
                  child: widget!,
                )),
            maxWidth: 480,
            minWidth: 480,
            // defaultScale: true,
            breakpoints: <ResponsiveBreakpoint>[
vjrj's avatar
vjrj committed
              const ResponsiveBreakpoint.resize(200, name: MOBILE),
              const ResponsiveBreakpoint.resize(480, name: TABLET),
vjrj's avatar
vjrj committed
              const ResponsiveBreakpoint.resize(1000, name: DESKTOP),
            ],
            background: Container(color: const Color(0xFFF5F5F5)),
          );
        },
      ));
    });
anfeichtinger's avatar
anfeichtinger committed
  }
}