import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:relationship_saver/core/config/app_config.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; class SentryInit { SentryInit(); Future? _initializeFuture; bool _initialized = false; Future initialize({FutureOr Function()? appRunner}) async { final Future? inFlight = _initializeFuture; if (inFlight != null) { await inFlight; if (appRunner != null && !_initialized) { await Future.sync(appRunner); } return; } _initializeFuture = _initializeInternal(appRunner: appRunner); return _initializeFuture; } Future _initializeInternal({ FutureOr Function()? appRunner, }) async { if (AppConfig.sentryDsn.isEmpty) { if (appRunner != null) { await Future.sync(appRunner); } return; } await SentryFlutter.init((options) { options.dsn = AppConfig.sentryDsn; options.environment = AppConfig.sentryEnvironment; options.release = AppConfig.sentryRelease; options.sendDefaultPii = false; options.tracesSampleRate = 0.1; }, appRunner: appRunner); await Sentry.configureScope((scope) async { await scope.setTag('service', 'relationship_saver'); await scope.setTag('build_mode', kReleaseMode ? 'release' : 'debug'); }); _installGlobalHandlers(); _initialized = true; } void captureException( Object error, { StackTrace? stackTrace, String? area, Map? context, }) { if (_initialized) { unawaited( Sentry.captureException( error, stackTrace: stackTrace, withScope: (scope) async { if (area != null) { await scope.setTag('area', area); } if (context != null) { await scope.setContexts('diagnostics', context); } }, ), ); } } void captureMessage( String message, { SentryLevel level = SentryLevel.info, String? area, Map? context, }) { if (_initialized) { unawaited( Sentry.captureMessage( message, level: level, withScope: (scope) async { if (area != null) { await scope.setTag('area', area); } if (context != null) { await scope.setContexts('diagnostics', context); } }, ), ); } } void _installGlobalHandlers() { final FlutterExceptionHandler? previousFlutterHandler = FlutterError.onError; FlutterError.onError = (FlutterErrorDetails details) { previousFlutterHandler?.call(details); captureException( details.exception, stackTrace: details.stack, area: 'flutter_framework', context: { 'library': details.library, 'context': details.context?.toDescription(), }, ); }; final ErrorCallback? previousPlatformHandler = PlatformDispatcher.instance.onError; PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { captureException(error, stackTrace: stack, area: 'platform_dispatcher'); return previousPlatformHandler?.call(error, stack) ?? false; }; } } final sentryInitProvider = Provider((Ref ref) { return SentryInit(); });