You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
10 KiB
279 lines
10 KiB
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
import 'package:firebase_core/firebase_core.dart';
|
|
import '../config/config_loader.dart';
|
|
import '../data/local/local_storage_service.dart';
|
|
import '../data/nostr/nostr_service.dart';
|
|
import '../data/sync/sync_engine.dart';
|
|
import '../data/firebase/firebase_service.dart';
|
|
import '../data/session/session_service.dart';
|
|
import '../data/immich/immich_service.dart';
|
|
import '../data/blossom/blossom_service.dart';
|
|
import '../data/media/media_service_interface.dart';
|
|
import '../data/media/multi_media_service.dart';
|
|
import '../data/media/models/media_server_config.dart';
|
|
import '../data/nostr/models/nostr_keypair.dart';
|
|
import '../data/recipes/recipe_service.dart';
|
|
import 'app_services.dart';
|
|
import 'service_locator.dart';
|
|
import 'logger.dart';
|
|
import 'theme_notifier.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// Initializes all application services.
|
|
///
|
|
/// Handles the complete initialization sequence:
|
|
/// 1. Load environment configuration
|
|
/// 2. Initialize Firebase (if enabled)
|
|
/// 3. Initialize local storage
|
|
/// 4. Initialize Nostr service and sync engine
|
|
/// 5. Initialize Immich service
|
|
/// 6. Initialize Firebase service (if enabled)
|
|
/// 7. Initialize session service
|
|
/// 8. Register all services with ServiceLocator
|
|
class AppInitializer {
|
|
/// Initializes all application services.
|
|
///
|
|
/// [environment] - The environment to use ('dev' or 'prod').
|
|
///
|
|
/// Returns an [AppServices] instance with all initialized services.
|
|
///
|
|
/// Throws if initialization fails.
|
|
static Future<AppServices> initialize({
|
|
String environment = 'dev',
|
|
}) async {
|
|
Logger.info('Starting application initialization...');
|
|
|
|
// Load .env file (optional - falls back to defaults if not found)
|
|
try {
|
|
await dotenv.load(fileName: '.env');
|
|
Logger.debug('.env file loaded successfully');
|
|
} catch (e) {
|
|
Logger.warning('.env file not found, using default values: $e');
|
|
}
|
|
|
|
// Load configuration based on environment
|
|
final config = ConfigLoader.load(environment);
|
|
Logger.setEnabled(config.enableLogging);
|
|
Logger.info('Configuration loaded for environment: $environment');
|
|
|
|
// Initialize Firebase if enabled
|
|
if (config.firebaseConfig.enabled) {
|
|
try {
|
|
await Firebase.initializeApp();
|
|
Logger.info('Firebase initialized successfully');
|
|
} catch (e) {
|
|
Logger.error(
|
|
'Firebase initialization failed: $e',
|
|
e,
|
|
);
|
|
Logger.warning(
|
|
'Note: Firebase requires google-services.json (Android) and GoogleService-Info.plist (iOS)',
|
|
);
|
|
}
|
|
}
|
|
|
|
// Initialize local storage service
|
|
Logger.debug('Initializing local storage service...');
|
|
final storageService = LocalStorageService();
|
|
try {
|
|
await storageService.initialize();
|
|
Logger.info('Local storage service initialized');
|
|
} catch (e) {
|
|
Logger.error('Failed to initialize storage: $e', e);
|
|
rethrow;
|
|
}
|
|
|
|
// Initialize Nostr service and sync engine
|
|
Logger.debug('Initializing Nostr service...');
|
|
final nostrService = NostrService();
|
|
final nostrKeyPair = nostrService.generateKeyPair();
|
|
|
|
final syncEngine = SyncEngine(
|
|
localStorage: storageService,
|
|
nostrService: nostrService,
|
|
nostrKeyPair: nostrKeyPair,
|
|
);
|
|
Logger.info('Nostr service and sync engine initialized');
|
|
|
|
// Load relays from config
|
|
for (final relayUrl in config.nostrRelays) {
|
|
nostrService.addRelay(relayUrl);
|
|
}
|
|
Logger.debug('Loaded ${config.nostrRelays.length} relay(s) from config');
|
|
|
|
// Initialize MultiMediaService for managing multiple media servers with fallback
|
|
Logger.debug('Initializing MultiMediaService...');
|
|
final multiMediaService = MultiMediaService(localStorage: storageService);
|
|
await multiMediaService.loadServers();
|
|
|
|
// Migrate old single server config if no servers exist
|
|
if (multiMediaService.getServers().isEmpty) {
|
|
final settingsItem = await storageService.getItem('app_settings');
|
|
final immichEnabled = config.immichEnabled;
|
|
final defaultBlossomServer = config.blossomServer;
|
|
|
|
final mediaServerType = settingsItem?.data['media_server_type'] as String? ??
|
|
(immichEnabled ? 'immich' : 'blossom');
|
|
final immichBaseUrl = settingsItem?.data['immich_base_url'] as String? ?? config.immichBaseUrl;
|
|
final immichApiKey = settingsItem?.data['immich_api_key'] as String? ?? config.immichApiKey;
|
|
final blossomBaseUrl = settingsItem?.data['blossom_base_url'] as String? ?? defaultBlossomServer;
|
|
|
|
// Migrate Immich config
|
|
if (immichEnabled && mediaServerType == 'immich' && immichBaseUrl.isNotEmpty && immichApiKey.isNotEmpty) {
|
|
final config = MediaServerConfig(
|
|
id: 'immich-${DateTime.now().millisecondsSinceEpoch}',
|
|
type: 'immich',
|
|
baseUrl: immichBaseUrl,
|
|
apiKey: immichApiKey,
|
|
isDefault: true,
|
|
name: 'Immich Server',
|
|
);
|
|
await multiMediaService.addServer(config);
|
|
Logger.info('Migrated Immich server config to MultiMediaService');
|
|
}
|
|
|
|
// Migrate Blossom config
|
|
if ((mediaServerType == 'blossom' || !immichEnabled) && blossomBaseUrl.isNotEmpty) {
|
|
final config = MediaServerConfig(
|
|
id: 'blossom-${DateTime.now().millisecondsSinceEpoch}',
|
|
type: 'blossom',
|
|
baseUrl: blossomBaseUrl,
|
|
isDefault: true,
|
|
name: 'Blossom Server',
|
|
);
|
|
await multiMediaService.addServer(config);
|
|
Logger.info('Migrated Blossom server config to MultiMediaService');
|
|
}
|
|
|
|
await multiMediaService.saveServers();
|
|
}
|
|
|
|
MediaServiceInterface? mediaService = multiMediaService;
|
|
if (multiMediaService.getServers().isEmpty) {
|
|
Logger.warning('No media servers configured');
|
|
mediaService = null;
|
|
} else {
|
|
Logger.info('MultiMediaService initialized with ${multiMediaService.getServers().length} server(s)');
|
|
}
|
|
|
|
// Initialize Firebase service if enabled
|
|
FirebaseService? firebaseService;
|
|
if (config.firebaseConfig.enabled) {
|
|
try {
|
|
Logger.debug('Initializing Firebase service...');
|
|
firebaseService = FirebaseService(
|
|
config: config.firebaseConfig,
|
|
localStorage: storageService,
|
|
);
|
|
await firebaseService.initialize();
|
|
Logger.info(
|
|
'Firebase service initialized: ${firebaseService.isEnabled}');
|
|
} catch (e) {
|
|
Logger.error('Firebase service initialization failed: $e', e);
|
|
firebaseService = null;
|
|
}
|
|
}
|
|
|
|
// Initialize SessionService with Firebase and Nostr integration
|
|
Logger.debug('Initializing session service...');
|
|
final sessionService = SessionService(
|
|
localStorage: storageService,
|
|
syncEngine: syncEngine,
|
|
firebaseService: firebaseService,
|
|
nostrService: nostrService,
|
|
);
|
|
Logger.info('Session service initialized');
|
|
|
|
// Initialize RecipeService
|
|
Logger.debug('Initializing recipe service...');
|
|
final recipeService = RecipeService(
|
|
localStorage: storageService,
|
|
nostrService: nostrService,
|
|
);
|
|
try {
|
|
await recipeService.initialize();
|
|
Logger.info('Recipe service initialized');
|
|
} catch (e) {
|
|
Logger.error('Failed to initialize recipe service: $e', e);
|
|
// Continue without recipe service - it will be initialized later when user logs in
|
|
}
|
|
|
|
// Initialize ThemeNotifier and load theme preference
|
|
Logger.debug('Initializing theme notifier...');
|
|
final themeNotifier = ThemeNotifier();
|
|
try {
|
|
final settingsItem = await storageService.getItem('app_settings');
|
|
if (settingsItem != null && settingsItem.data.containsKey('dark_mode')) {
|
|
final isDark = settingsItem.data['dark_mode'] == true;
|
|
themeNotifier.setThemeMode(isDark ? ThemeMode.dark : ThemeMode.light);
|
|
Logger.info('Theme preference loaded: ${isDark ? "dark" : "light"}');
|
|
} else {
|
|
Logger.info('No theme preference found, using light mode');
|
|
}
|
|
} catch (e) {
|
|
Logger.warning('Failed to load theme preference: $e');
|
|
}
|
|
|
|
// Create AppServices container
|
|
final appServices = AppServices(
|
|
localStorageService: storageService,
|
|
nostrService: nostrService,
|
|
syncEngine: syncEngine,
|
|
firebaseService: firebaseService,
|
|
sessionService: sessionService,
|
|
mediaService: mediaService,
|
|
recipeService: recipeService,
|
|
);
|
|
|
|
// Register all services with ServiceLocator
|
|
ServiceLocator.instance.registerServices(
|
|
localStorageService: storageService,
|
|
nostrService: nostrService,
|
|
syncEngine: syncEngine,
|
|
firebaseService: firebaseService,
|
|
sessionService: sessionService,
|
|
mediaService: mediaService,
|
|
recipeService: recipeService,
|
|
themeNotifier: themeNotifier,
|
|
);
|
|
|
|
Logger.info('Application initialization completed successfully');
|
|
return appServices;
|
|
}
|
|
|
|
/// Reinitializes the media service based on current settings.
|
|
///
|
|
/// This should be called when media server settings are changed.
|
|
/// Preserves the Nostr keypair if Blossom is selected.
|
|
static Future<void> reinitializeMediaService() async {
|
|
Logger.info('Reinitializing media service...');
|
|
|
|
final localStorage = ServiceLocator.instance.localStorageService;
|
|
|
|
if (localStorage == null) {
|
|
Logger.warning(
|
|
'Cannot reinitialize media service: LocalStorageService not available');
|
|
return;
|
|
}
|
|
|
|
// Create new MultiMediaService instance and load servers
|
|
final multiMediaService = MultiMediaService(localStorage: localStorage);
|
|
await multiMediaService.loadServers();
|
|
|
|
MediaServiceInterface? newMediaService = multiMediaService;
|
|
if (multiMediaService.getServers().isEmpty) {
|
|
Logger.warning('No media servers configured');
|
|
newMediaService = null;
|
|
} else {
|
|
Logger.info('MultiMediaService reinitialized with ${multiMediaService.getServers().length} server(s)');
|
|
}
|
|
|
|
// Register the new media service with ServiceLocator
|
|
ServiceLocator.instance.registerServices(
|
|
mediaService: newMediaService,
|
|
);
|
|
|
|
Logger.info('Media service reinitialized and registered successfully');
|
|
}
|
|
}
|