main function

Future<void> main()

Entry point of this application.

Implementation

Future<void> main() async {
  final Stopwatch watch = Stopwatch()..start();

  await Config.init();

  me.Log.options = me.LogOptions(
    level: Config.logLevel,

    // Browsers collect timestamps for log themselves.
    timeStamp: !PlatformUtils.isWeb,
    dateStamp: !PlatformUtils.isWeb,
  );

  // Initializes and runs the [App].
  Future<void> appRunner() async {
    MediaKit.ensureInitialized();
    WebUtils.setPathUrlStrategy();

    Get.putOrGet<CommonDriftProvider>(
      () => CommonDriftProvider.from(
        Get.putOrGet(() => CommonDatabase(), permanent: true),
      ),
      permanent: true,
    );

    final myUserProvider = Get.put(MyUserDriftProvider(Get.find()));
    Get.put(SettingsDriftProvider(Get.find()));
    Get.put(BackgroundDriftProvider(Get.find()));
    Get.put(GeoLocationDriftProvider(Get.find()));
    Get.put(LockDriftProvider(Get.find()));

    if (!PlatformUtils.isWeb) {
      Get.put(WindowRectDriftProvider(Get.find()));
      Get.put(CacheDriftProvider(Get.find()));
      Get.put(DownloadDriftProvider(Get.find()));
      Get.put(SkippedVersionDriftProvider(Get.find()));
    }

    final accountProvider = Get.put(AccountDriftProvider(Get.find()));
    await accountProvider.init();

    final credentialsProvider = Get.put(CredentialsDriftProvider(Get.find()));
    await credentialsProvider.init();

    if (PlatformUtils.isDesktop && !PlatformUtils.isWeb) {
      await windowManager.ensureInitialized();
      await windowManager.setMinimumSize(const Size(400, 400));

      final WindowRectDriftProvider? preferences =
          Get.findOrNull<WindowRectDriftProvider>();
      final WindowPreferences? prefs = await preferences?.read();

      if (prefs?.size != null) {
        await windowManager.setSize(prefs!.size!);
      }

      if (prefs?.position != null) {
        await windowManager.setPosition(prefs!.position!);
      }

      await windowManager.show();

      WebUtils.registerScheme().onError((_, __) => false);

      Get.put(WindowWorker(preferences));
    }

    final graphQlProvider = Get.put(GraphQlProvider());
    Get.put(GeoLocationProvider());

    final authRepository = Get.put<AbstractAuthRepository>(
      AuthRepository(graphQlProvider, myUserProvider, Get.find()),
    );
    final authService = Get.put(
      AuthService(authRepository, Get.find(), Get.find(), Get.find()),
    );

    Uri? initial;
    try {
      final AppLinks links = AppLinks();
      initial = await links.getInitialLink();
      Log.debug('initial -> $initial', 'AppLinks');

      _linkSubscription?.cancel();
      _linkSubscription = links.uriLinkStream.listen((uri) async {
        Log.debug('uriLinkStream -> $uri', 'AppLinks');
        router.delegate.setNewRoutePath(
          await router.parser.parseRouteInformation(RouteInformation(uri: uri)),
        );
      });
    } catch (e) {
      // No-op.
    }

    router = RouterState(
      authService,
      initial: initial == null ? null : RouteInformation(uri: initial),
    );

    try {
      PWAInstall().setup(
        installCallback: () {
          Log.debug('PWA is detected as installed', 'PWAInstall()');
          WebUtils.hasPwa = true;
        },
      );
    } catch (_) {
      // No-op.
    }

    await authService.init();
    await L10n.init();

    Get.put(CacheWorker(Get.findOrNull(), Get.findOrNull()));
    Get.put(UpgradeWorker(Get.findOrNull()));

    WebUtils.deleteLoader();

    runApp(App(key: UniqueKey()));
  }

  // No need to initialize the Sentry if no DSN is provided, otherwise useless
  // messages are printed to the console every time the application starts.
  if (Config.sentryDsn.isEmpty || kDebugMode) {
    return appRunner();
  }

  await SentryFlutter.init((options) {
    options.dsn = Config.sentryDsn;
    options.tracesSampleRate = 1.0;
    options.sampleRate = 1.0;
    options.release = '${Pubspec.name}@${Pubspec.ref}';
    options.debug = true;
    options.diagnosticLevel = SentryLevel.info;
    options.enablePrintBreadcrumbs = true;
    options.maxBreadcrumbs = 512;
    options.enableTimeToFullDisplayTracing = true;
    options.enableAppHangTracking = true;
    options.beforeSend = (SentryEvent event, Hint? hint) {
      final exception = event.exceptions?.firstOrNull?.throwable;

      // Connection related exceptions shouldn't be logged.
      if (exception is ConnectionException ||
          exception is SocketException ||
          exception is WebSocketException ||
          exception is WebSocketChannelException ||
          exception is HttpException ||
          exception is ClientException ||
          exception is DioException ||
          exception is TimeoutException ||
          exception is ResubscriptionRequiredException) {
        return null;
      }

      // [Backoff] related exceptions shouldn't be logged.
      if (exception is OperationCanceledException ||
          exception.toString() == 'Data is not loaded') {
        return null;
      }

      return event;
    };
    options.logger =
        (
          SentryLevel level,
          String message, {
          String? logger,
          Object? exception,
          StackTrace? stackTrace,
        }) {
          if (exception != null) {
            if (stackTrace == null) {
              stackTrace = StackTrace.current;
            } else {
              stackTrace = FlutterError.demangleStackTrace(stackTrace);
            }

            final Iterable<String> lines = stackTrace
                .toString()
                .trimRight()
                .split('\n')
                .take(100);

            Log.error(
              [
                exception.toString(),
                if (lines.where((e) => e.isNotEmpty).isNotEmpty)
                  FlutterError.defaultStackFilter(lines).join('\n'),
              ].join('\n'),
            );
          }
        };
  }, appRunner: appRunner);

  // Transaction indicating Flutter engine has rasterized the first frame.
  final ISentrySpan ready = Sentry.startTransaction(
    'ui.app.ready',
    'ui',
    autoFinishAfter: const Duration(minutes: 2),
    startTimestamp: DateTime.now().subtract(watch.elapsed),
  )..startChild('ready');

  WidgetsBinding.instance.waitUntilFirstFrameRasterized.then(
    (_) => ready.finish(),
  );
}