main function
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()));
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()),
);
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),
);
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(),
);
}