add sentry

This commit is contained in:
Phuoc Nguyen
2025-12-11 13:44:26 +07:00
parent f130820131
commit e3632d4445
9 changed files with 445 additions and 126 deletions

View File

@@ -1,17 +1,17 @@
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:worker/app.dart';
import 'package:worker/core/database/app_settings_box.dart';
import 'package:worker/core/database/hive_initializer.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:worker/core/services/onesignal_service.dart';
import 'package:worker/core/services/sentry_service.dart';
import 'package:worker/firebase_options.dart';
/// Main entry point of the Worker Mobile App
///
@@ -45,72 +45,35 @@ void main() async {
/// Initialize all app dependencies with comprehensive error handling
Future<void> _initializeApp() async {
// Set up error handlers before anything else
_setupErrorHandlers();
// Initialize Sentry first to capture any initialization errors
// Sentry wraps the app runner to capture errors during startup
await SentryService.init(
appRunner: () async {
// Set up error handlers
_setupErrorHandlers();
try {
// Initialize core dependencies in parallel for faster startup
await Future.wait([_initializeHive(), _initializeSharedPreferences()]);
try {
// Initialize core dependencies in parallel for faster startup
await Future.wait([_initializeHive(), _initializeSharedPreferences()]);
// Initialize OneSignal with verbose logging for debugging
OneSignal.Debug.setLogLevel(OSLogLevel.verbose);
// Initialize OneSignal push notifications
await OneSignalService.init();
// Initialize with your OneSignal App ID
OneSignal.initialize("778ca22d-c719-4ec8-86cb-a6b911166066");
// Run the app with Riverpod ProviderScope
runApp(const ProviderScope(child: WorkerApp()));
} catch (error, stackTrace) {
// Critical initialization error - capture and show error screen
debugPrint('Failed to initialize app: $error');
debugPrint('StackTrace: $stackTrace');
debugPrint('🔔 OneSignal initialized');
// Report to Sentry
await SentryService.captureException(error, stackTrace: stackTrace);
// Add observer BEFORE requesting permission to catch the subscription event
OneSignal.User.pushSubscription.addObserver((state) {
debugPrint('🔔 Push subscription state changed:');
debugPrint(' Previous - optedIn: ${state.previous.optedIn}, id: ${state.previous.id}');
debugPrint(' Current - optedIn: ${state.current.optedIn}, id: ${state.current.id}');
debugPrint(' Subscription ID: ${state.current.id}');
debugPrint(' Push Token: ${state.current.token}');
// Save subscription info when available
if (state.current.id != null) {
debugPrint('🔔 ✅ Device successfully subscribed!');
// Run minimal error app
runApp(_buildErrorApp(error, stackTrace));
}
});
// Add notification permission observer
OneSignal.Notifications.addPermissionObserver((isGranted) {
debugPrint('🔔 Notification permission changed: $isGranted');
});
// Request permission - TRUE to show the native permission prompt
final accepted = await OneSignal.Notifications.requestPermission(true);
debugPrint('🔔 Permission request result: $accepted');
// Give OneSignal SDK time to complete initialization and server registration
// This is necessary because the subscription happens asynchronously
await Future.delayed(const Duration(seconds: 2));
// Check current subscription status after initialization completes
final optedIn = OneSignal.User.pushSubscription.optedIn;
final id = OneSignal.User.pushSubscription.id;
final token = OneSignal.User.pushSubscription.token;
debugPrint('🔔 Current subscription status (after delay):');
debugPrint(' Opted In: $optedIn');
debugPrint(' Subscription ID: $id');
debugPrint(' Push Token: $token');
if (id == null) {
debugPrint('🔔 ⚠️ Subscription ID is still null - check device connectivity and OneSignal app ID');
}
// Run the app with Riverpod ProviderScope
runApp(const ProviderScope(child: WorkerApp()));
} catch (error, stackTrace) {
// Critical initialization error - show error screen
debugPrint('Failed to initialize app: $error');
debugPrint('StackTrace: $stackTrace');
// Run minimal error app
runApp(_buildErrorApp(error, stackTrace));
}
},
);
}
/// Initialize Hive database
@@ -164,7 +127,8 @@ Future<void> _initializeSharedPreferences() async {
/// Set up global error handlers
///
/// Captures and logs all Flutter framework errors and uncaught exceptions
/// Captures and logs all Flutter framework errors and uncaught exceptions.
/// Reports errors to Sentry for crash analytics.
void _setupErrorHandlers() {
// Handle Flutter framework errors
FlutterError.onError = (FlutterErrorDetails details) {
@@ -176,8 +140,11 @@ void _setupErrorHandlers() {
debugPrint('StackTrace: ${details.stack}');
}
// In production, you would send to crash analytics service
// Example: FirebaseCrashlytics.instance.recordFlutterError(details);
// Report to Sentry
SentryService.captureException(
details.exception,
stackTrace: details.stack,
);
};
// Handle errors outside of Flutter framework
@@ -187,27 +154,11 @@ void _setupErrorHandlers() {
debugPrint('StackTrace: $stackTrace');
}
// In production, you would send to crash analytics service
// Example: FirebaseCrashlytics.instance.recordError(error, stackTrace);
// Report to Sentry
SentryService.captureException(error, stackTrace: stackTrace);
return true; // Return true to indicate error was handled
};
// Handle zone errors (async errors not caught by Flutter)
runZonedGuarded(
() {
// App will run in this zone
},
(error, stackTrace) {
if (kDebugMode) {
debugPrint('Zone Error: $error');
debugPrint('StackTrace: $stackTrace');
}
// In production, you would send to crash analytics service
// Example: FirebaseCrashlytics.instance.recordError(error, stackTrace);
},
);
}
/// Build minimal error app when initialization fails