spotube/lib/main.dart

275 lines
9.2 KiB
Dart

import 'dart:async';
import 'dart:ui';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_discord_rpc/flutter_discord_rpc.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:home_widget/home_widget.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:local_notifier/local_notifier.dart';
import 'package:media_kit/media_kit.dart';
import 'package:metadata_god/metadata_god.dart';
import 'package:smtc_windows/smtc_windows.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/initializers.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/intents.dart';
import 'package:spotube/hooks/configurators/use_close_behavior.dart';
import 'package:spotube/hooks/configurators/use_deep_linking.dart';
import 'package:spotube/hooks/configurators/use_disable_battery_optimizations.dart';
import 'package:spotube/hooks/configurators/use_fix_window_stretching.dart';
import 'package:spotube/hooks/configurators/use_get_storage_perms.dart';
import 'package:spotube/hooks/configurators/use_has_touch.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/glance/glance.dart';
import 'package:spotube/provider/server/bonsoir.dart';
import 'package:spotube/provider/server/server.dart';
import 'package:spotube/provider/tray_manager/tray_manager.dart';
import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/provider/connect/clients.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/cli/cli.dart';
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart';
import 'package:spotube/utils/migrations/sandbox.dart';
import 'package:spotube/utils/platform.dart';
import 'package:system_theme/system_theme.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:window_manager/window_manager.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
Future<void> main(List<String> rawArgs) async {
if (rawArgs.contains("web_view_title_bar")) {
WidgetsFlutterBinding.ensureInitialized();
if (runWebViewTitleBarWidget(rawArgs)) {
return;
}
}
final arguments = await startCLI(rawArgs);
AppLogger.initialize(arguments["verbose"]);
AppLogger.runZoned(() async {
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
await registerWindowsScheme("spotify");
tz.initializeTimeZones();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
MediaKit.ensureInitialized();
await migrateMacOsFromSandboxToNoSandbox();
// force High Refresh Rate on some Android devices (like One Plus)
if (kIsAndroid) {
await FlutterDisplayMode.setHighRefreshRate();
}
if (kIsDesktop) {
await windowManager.setPreventClose(true);
}
await SystemTheme.accentColor.load();
if (!kIsWeb) {
MetadataGod.initialize();
}
if (kIsDesktop) {
await FlutterDiscordRPC.initialize(Env.discordAppId);
}
if (kIsWindows) {
await SMTCWindows.initialize();
}
await KVStoreService.initialize();
await EncryptedKvStoreService.initialize();
final database = AppDatabase();
if (kIsDesktop) {
await localNotifier.setup(appName: "Spotube");
await WindowManagerTools.initialize();
}
if (kIsIOS) {
HomeWidget.setAppGroupId("group.spotube_home_player_widget");
}
runApp(
ProviderScope(
overrides: [
databaseProvider.overrideWith((ref) => database),
],
observers: const [
AppLoggerProviderObserver(),
],
child: const Spotube(),
),
);
});
}
class Spotube extends HookConsumerWidget {
const Spotube({super.key});
@override
Widget build(BuildContext context, ref) {
final themeMode =
ref.watch(userPreferencesProvider.select((s) => s.themeMode));
final locale = ref.watch(userPreferencesProvider.select((s) => s.locale));
// final accentMaterialColor =
// ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme));
// final isAmoledTheme =
// ref.watch(userPreferencesProvider.select((s) => s.amoledDarkTheme));
// final paletteColor =
// ref.watch(paletteProvider.select((s) => s?.dominantColor?.color));
final router = ref.watch(routerProvider);
final hasTouchSupport = useHasTouch();
ref.listen(audioPlayerStreamListenersProvider, (_, __) {});
ref.listen(bonsoirProvider, (_, __) {});
ref.listen(connectClientsProvider, (_, __) {});
ref.listen(serverProvider, (_, __) {});
ref.listen(trayManagerProvider, (_, __) {});
useFixWindowStretching();
useDisableBatteryOptimizations();
useDeepLinking(ref);
useCloseBehavior(ref);
useGetStoragePermissions(ref);
useEffect(() {
FlutterNativeSplash.remove();
if (kIsMobile) {
HomeWidget.registerInteractivityCallback(glanceBackgroundCallback);
}
return () {
/// For enabling hot reload for audio player
if (!kDebugMode) return;
audioPlayer.dispose();
};
}, []);
// final lightTheme = useMemoized(
// () => theme(paletteColor ?? accentMaterialColor, Brightness.light, false),
// [paletteColor, accentMaterialColor],
// );
// final darkTheme = useMemoized(
// () => theme(
// paletteColor ?? accentMaterialColor,
// Brightness.dark,
// isAmoledTheme,
// ),
// [paletteColor, accentMaterialColor, isAmoledTheme],
// );
return ShadcnApp.router(
supportedLocales: L10n.all,
locale: locale.languageCode == "system" ? null : locale,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
routerConfig: router,
debugShowCheckedModeBanner: false,
title: 'Spotube',
builder: (context, child) {
child = ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: hasTouchSupport
? {
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
}
: null,
),
child: child!,
);
if (kIsDesktop && !kIsMacOS) child = DragToResizeArea(child: child);
return child;
},
scaling: const AdaptiveScaling(1),
theme: ThemeData(
radius: .5,
iconTheme: const IconThemeProperties(),
colorScheme: ColorSchemes.lightBlue(),
surfaceOpacity: .8,
surfaceBlur: 10,
),
darkTheme: ThemeData(
radius: .5,
iconTheme: const IconThemeProperties(),
colorScheme: ColorSchemes.darkNeutral(),
surfaceOpacity: .8,
surfaceBlur: 10,
),
themeMode: themeMode,
shortcuts: {
...WidgetsApp.defaultShortcuts.map((key, value) {
return MapEntry(
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
value,
);
}),
LogicalKeySet(LogicalKeyboardKey.space): PlayPauseIntent(ref),
LogicalKeySet(LogicalKeyboardKey.comma, LogicalKeyboardKey.control):
NavigationIntent(router, "/settings"),
LogicalKeySet(
LogicalKeyboardKey.digit1,
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): HomeTabIntent(ref, tab: HomeTabs.browse),
LogicalKeySet(
LogicalKeyboardKey.digit2,
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): HomeTabIntent(ref, tab: HomeTabs.search),
LogicalKeySet(
LogicalKeyboardKey.digit3,
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): HomeTabIntent(ref, tab: HomeTabs.library),
LogicalKeySet(
LogicalKeyboardKey.digit4,
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): HomeTabIntent(ref, tab: HomeTabs.lyrics),
LogicalKeySet(
LogicalKeyboardKey.keyW,
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): CloseAppIntent(),
},
actions: {
...WidgetsApp.defaultActions,
PlayPauseIntent: PlayPauseAction(),
NavigationIntent: NavigationAction(),
HomeTabIntent: HomeTabAction(),
CloseAppIntent: CloseAppAction(),
},
);
}
}