- Changed window size to mobile phone format (400x800) - Removed width condition for ActiveProxyFooter - now always visible - Added run-umbrix.sh launch script with icon copying - Stats cards now display on all screen sizes
211 lines
6.3 KiB
Dart
211 lines
6.3 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||
import 'package:umbrix/core/analytics/analytics_controller.dart';
|
||
import 'package:umbrix/core/app_info/app_info_provider.dart';
|
||
import 'package:umbrix/core/directories/directories_provider.dart';
|
||
import 'package:umbrix/core/logger/logger.dart';
|
||
import 'package:umbrix/core/logger/logger_controller.dart';
|
||
import 'package:umbrix/core/model/environment.dart';
|
||
import 'package:umbrix/core/preferences/general_preferences.dart';
|
||
import 'package:umbrix/core/preferences/preferences_migration.dart';
|
||
import 'package:umbrix/core/preferences/preferences_provider.dart';
|
||
import 'package:umbrix/features/app/widget/app.dart';
|
||
import 'package:umbrix/features/auto_start/notifier/auto_start_notifier.dart';
|
||
import 'package:umbrix/features/common/custom_splash_screen.dart';
|
||
import 'package:umbrix/features/deep_link/notifier/deep_link_notifier.dart';
|
||
import 'package:umbrix/features/log/data/log_data_providers.dart';
|
||
import 'package:umbrix/features/profile/data/profile_data_providers.dart';
|
||
import 'package:umbrix/features/profile/notifier/active_profile_notifier.dart';
|
||
import 'package:umbrix/features/system_tray/notifier/system_tray_notifier.dart';
|
||
import 'package:umbrix/features/window/notifier/window_notifier.dart';
|
||
import 'package:umbrix/singbox/service/singbox_service_provider.dart';
|
||
import 'package:umbrix/utils/utils.dart';
|
||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||
|
||
Future<void> lazyBootstrap(
|
||
WidgetsBinding widgetsBinding,
|
||
Environment env,
|
||
) async {
|
||
LoggerController.preInit();
|
||
FlutterError.onError = Logger.logFlutterError;
|
||
WidgetsBinding.instance.platformDispatcher.onError = Logger.logPlatformDispatcherError;
|
||
|
||
final stopWatch = Stopwatch()..start();
|
||
|
||
final container = ProviderContainer(
|
||
overrides: [
|
||
environmentProvider.overrideWithValue(env),
|
||
],
|
||
);
|
||
|
||
// Показываем кастомный splash screen с круговым индикатором
|
||
runApp(
|
||
MaterialApp(
|
||
debugShowCheckedModeBanner: false,
|
||
home: CustomSplashScreen(
|
||
onInitializationComplete: () async {
|
||
await _performBootstrap(container, stopWatch, env);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<void> _performBootstrap(
|
||
ProviderContainer container,
|
||
Stopwatch stopWatch,
|
||
Environment env,
|
||
) async {
|
||
await _init(
|
||
"directories",
|
||
() => container.read(appDirectoriesProvider.future),
|
||
);
|
||
LoggerController.init(container.read(logPathResolverProvider).appFile().path);
|
||
|
||
final appInfo = await _init(
|
||
"app info",
|
||
() => container.read(appInfoProvider.future),
|
||
);
|
||
await _init(
|
||
"preferences",
|
||
() => container.read(sharedPreferencesProvider.future),
|
||
);
|
||
|
||
final enableAnalytics = await container.read(analyticsControllerProvider.future);
|
||
if (enableAnalytics) {
|
||
await _init(
|
||
"analytics",
|
||
() => container.read(analyticsControllerProvider.notifier).enableAnalytics(),
|
||
);
|
||
}
|
||
|
||
await _init(
|
||
"preferences migration",
|
||
() async {
|
||
try {
|
||
await PreferencesMigration(
|
||
sharedPreferences: container.read(sharedPreferencesProvider).requireValue,
|
||
).migrate();
|
||
} catch (e, stackTrace) {
|
||
Logger.bootstrap.error("preferences migration failed", e, stackTrace);
|
||
if (env == Environment.dev) rethrow;
|
||
Logger.bootstrap.info("clearing preferences");
|
||
await container.read(sharedPreferencesProvider).requireValue.clear();
|
||
}
|
||
},
|
||
);
|
||
|
||
final debug = container.read(debugModeNotifierProvider) || kDebugMode;
|
||
|
||
if (PlatformUtils.isDesktop) {
|
||
await _init(
|
||
"window controller",
|
||
() => container.read(windowNotifierProvider.future),
|
||
);
|
||
|
||
final silentStart = container.read(Preferences.silentStart);
|
||
Logger.bootstrap.debug("silent start [${silentStart ? "Enabled" : "Disabled"}]");
|
||
if (!silentStart) {
|
||
await container.read(windowNotifierProvider.notifier).open(focus: false);
|
||
} else {
|
||
Logger.bootstrap.debug("silent start, remain hidden accessible via tray");
|
||
}
|
||
await _init(
|
||
"auto start service",
|
||
() => container.read(autoStartNotifierProvider.future),
|
||
);
|
||
}
|
||
await _init(
|
||
"logs repository",
|
||
() => container.read(logRepositoryProvider.future),
|
||
);
|
||
await _init("logger controller", () => LoggerController.postInit(debug));
|
||
|
||
Logger.bootstrap.info(appInfo.format());
|
||
|
||
await _init(
|
||
"profile repository",
|
||
() => container.read(profileRepositoryProvider.future),
|
||
);
|
||
|
||
await _safeInit(
|
||
"active profile",
|
||
() => container.read(activeProfileProvider.future),
|
||
timeout: 1000,
|
||
);
|
||
await _safeInit(
|
||
"deep link service",
|
||
() => container.read(deepLinkNotifierProvider.future),
|
||
timeout: 1000,
|
||
);
|
||
await _init(
|
||
"sing-box",
|
||
() => container.read(singboxServiceProvider).init(),
|
||
);
|
||
if (PlatformUtils.isDesktop) {
|
||
await _safeInit(
|
||
"system tray",
|
||
() => container.read(systemTrayNotifierProvider.future),
|
||
timeout: 1000,
|
||
);
|
||
}
|
||
|
||
if (Platform.isAndroid) {
|
||
await _safeInit(
|
||
"android display mode",
|
||
() async {
|
||
await FlutterDisplayMode.setHighRefreshRate();
|
||
},
|
||
);
|
||
}
|
||
|
||
Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]");
|
||
stopWatch.stop();
|
||
|
||
runApp(
|
||
ProviderScope(
|
||
parent: container,
|
||
child: SentryUserInteractionWidget(
|
||
child: const App(),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<T> _init<T>(
|
||
String name,
|
||
Future<T> Function() initializer, {
|
||
int? timeout,
|
||
}) async {
|
||
final stopWatch = Stopwatch()..start();
|
||
Logger.bootstrap.info("initializing [$name]");
|
||
Future<T> func() => timeout != null ? initializer().timeout(Duration(milliseconds: timeout)) : initializer();
|
||
try {
|
||
final result = await func();
|
||
Logger.bootstrap.debug("[$name] initialized in ${stopWatch.elapsedMilliseconds}ms");
|
||
return result;
|
||
} catch (e, stackTrace) {
|
||
Logger.bootstrap.error("[$name] error initializing", e, stackTrace);
|
||
rethrow;
|
||
} finally {
|
||
stopWatch.stop();
|
||
}
|
||
}
|
||
|
||
Future<T?> _safeInit<T>(
|
||
String name,
|
||
Future<T> Function() initializer, {
|
||
int? timeout,
|
||
}) async {
|
||
try {
|
||
return await _init(name, initializer, timeout: timeout);
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|