nexus/lib/main.dart

203 lines
6.9 KiB
Dart

import "dart:io";
import "package:dynamic_color/dynamic_color.dart";
import "package:flutter/foundation.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:material_emoji_picker/material_emoji_picker.dart";
import "package:media_kit/media_kit.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/controllers/gomuks_listener.dart";
import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/member_list_opened.dart";
import "package:nexus/controllers/multi_provider.dart";
import "package:nexus/controllers/notification.dart";
import "package:nexus/controllers/settings.dart";
import "package:nexus/controllers/unified_push.dart";
import "package:nexus/helpers/extensions/scheme_to_theme.dart";
import "package:nexus/helpers/font_licenses.dart";
import "package:nexus/widgets/pages/chat.dart";
import "package:nexus/widgets/pages/select_server.dart";
import "package:nexus/widgets/pages/settings.dart";
import "package:nexus/widgets/pages/verify.dart";
import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/loading.dart";
import "package:window_manager/window_manager.dart";
import "package:material_ui/material_ui.dart";
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
late final bool isInBackground;
final class Logger extends ProviderObserver {
@override
void didUpdateProvider(
ProviderObserverContext context,
Object? previousValue,
Object? newValue,
) => debugPrint("""
Time: ${DateTime.now().toIso8601String()}
Provider: ${context.provider}
Previous Value: ${previousValue is AsyncData ? previousValue.value : previousValue}
New Value: ${newValue is AsyncData ? newValue.value : newValue}
""");
}
void showError(Object error, [StackTrace? stackTrace]) {
if (error.toString().contains("'_nextFrame != null': is not true.")) {
return;
}
debugPrintStack(stackTrace: stackTrace, label: error.toString());
if (navigatorKey.currentContext != null) {
Future.delayed(
Duration.zero,
() => showDialog(
context: navigatorKey.currentContext!,
builder: (_) => ErrorDialog(error, stackTrace),
barrierDismissible: false,
),
);
}
}
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
LicenseRegistry.addLicense(() => .fromIterable(fontLicenses));
FlutterError.onError = (FlutterErrorDetails details) =>
showError(details.exception.toString(), details.stack);
isInBackground =
Platform.environment["FLUTTER_HEADLESS"] != null ||
args.contains("--unifiedpush-bg");
if (isInBackground) {
await ProviderContainer()
.read(UnifiedPushController.provider.future)
.timeout(Duration(seconds: 5));
await Future.delayed(Duration(seconds: 10));
exit(0);
} else {
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
await windowManager.ensureInitialized();
await windowManager.waitUntilReadyToShow(
WindowOptions(
titleBarStyle: TitleBarStyle.hidden,
windowButtonVisibility: false,
),
);
await windowManager.setMinimumSize(Size.square(500));
}
runApp(
ProviderScope(
retry: (_, _) => null,
observers: [
// Change false to true if you want debug information on provider reloads
// ignore: dead_code
if (false && kDebugMode) Logger(),
],
child: App(),
),
);
}
}
class const App({super.key}) extends StatelessWidget {
@override
Widget build(BuildContext context) => DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) => Consumer(
builder: (context, ref, child) => MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
theme:
(ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => lightDynamic,
data: (settings) =>
settings.useDynamicTheming ? lightDynamic : null,
) ??
ThemeData.light().colorScheme)
.theme,
darkTheme:
(ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => darkDynamic,
data: (settings) =>
settings.useDynamicTheming ? darkDynamic : null,
) ??
ThemeData.dark().colorScheme)
.theme,
themeMode: ref
.watch(SettingsController.provider)
.maybeWhen(
data: (settings) => settings.theme,
orElse: () => ThemeMode.system,
),
home: child,
),
child: DefaultTextHeightBehavior(
textHeightBehavior: .new(leadingDistribution: .even),
child: Scaffold(
body: Consumer(
builder: (context, ref, _) => switch (ref.watch(
MultiProviderController.provider(
.new([
GomuksListenerController.provider,
NotificationController.provider,
UnifiedPushController.provider,
MemberListOpenedController.provider,
KeyController.provider(KeyController.roomKey),
KeyController.provider(KeyController.spaceKey),
EmojiController.provider,
]),
),
)) {
AsyncData(value: _) || AsyncLoading(value: _?) => Consumer(
builder: (_, ref, _) {
final clientState = ref.watch(ClientStateController.provider);
if (clientState == null || !clientState.isInitialized) {
return Loading();
}
if (!clientState.isLoggedIn) {
return SelectServerPage();
} else if (!clientState.isVerified) {
return VerifyPage();
} else {
return ChatPage();
}
},
),
AsyncLoading _ => Scaffold(
appBar: Appbar(
actions: .new([
IconButton(
onPressed: () => showDialog(
context: context,
builder: (_) => SettingsPage(),
),
icon: Icon(Icons.settings),
),
]),
),
body: Loading(),
),
AsyncError(:final error, :final stackTrace) => ErrorDialog(
error,
stackTrace,
),
},
),
),
),
),
);
}