diff --git a/lib/main.dart b/lib/main.dart index fa567129..3ed4314b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:desktop_webview_window/desktop_webview_window.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_discord_rpc/flutter_discord_rpc.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -34,7 +33,6 @@ 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/palette_provider.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'; @@ -42,8 +40,6 @@ 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/themes/theme.dart'; -import 'package:spotube/utils/migrations/hive.dart'; import 'package:spotube/utils/migrations/sandbox.dart'; import 'package:spotube/utils/platform.dart'; import 'package:system_theme/system_theme.dart'; @@ -53,6 +49,7 @@ 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 main(List rawArgs) async { if (rawArgs.contains("web_view_title_bar")) { @@ -110,8 +107,6 @@ Future main(List rawArgs) async { final database = AppDatabase(); - await migrateFromHiveToDrift(database); - if (kIsDesktop) { await localNotifier.setup(appName: "Spotube"); await WindowManagerTools.initialize(); @@ -142,13 +137,13 @@ class Spotube extends HookConsumerWidget { Widget build(BuildContext context, ref) { final themeMode = ref.watch(userPreferencesProvider.select((s) => s.themeMode)); - final accentMaterialColor = - ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme)); - final isAmoledTheme = - ref.watch(userPreferencesProvider.select((s) => s.amoledDarkTheme)); final locale = ref.watch(userPreferencesProvider.select((s) => s.locale)); - final paletteColor = - ref.watch(paletteProvider.select((s) => s?.dominantColor?.color)); + // 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(); @@ -178,20 +173,20 @@ class Spotube extends HookConsumerWidget { }; }, []); - final lightTheme = useMemoized( - () => theme(paletteColor ?? accentMaterialColor, Brightness.light, false), - [paletteColor, accentMaterialColor], - ); - final darkTheme = useMemoized( - () => theme( - paletteColor ?? accentMaterialColor, - Brightness.dark, - isAmoledTheme, - ), - [paletteColor, accentMaterialColor, isAmoledTheme], - ); + // final lightTheme = useMemoized( + // () => theme(paletteColor ?? accentMaterialColor, Brightness.light, false), + // [paletteColor, accentMaterialColor], + // ); + // final darkTheme = useMemoized( + // () => theme( + // paletteColor ?? accentMaterialColor, + // Brightness.dark, + // isAmoledTheme, + // ), + // [paletteColor, accentMaterialColor, isAmoledTheme], + // ); - return MaterialApp.router( + return ShadcnApp.router( supportedLocales: L10n.all, locale: locale.languageCode == "system" ? null : locale, localizationsDelegates: const [ @@ -221,9 +216,17 @@ class Spotube extends HookConsumerWidget { return child; }, + theme: ThemeData( + radius: .5, + iconTheme: const IconThemeProperties(), + colorScheme: ColorSchemes.lightNeutral(), + ), + darkTheme: ThemeData( + radius: .5, + iconTheme: const IconThemeProperties(), + colorScheme: ColorSchemes.darkNeutral(), + ), themeMode: themeMode, - theme: lightTheme, - darkTheme: darkTheme, shortcuts: { ...WidgetsApp.defaultShortcuts.map((key, value) { return MapEntry( diff --git a/lib/models/database/database.dart b/lib/models/database/database.dart index 0f30df19..f76d25bc 100644 --- a/lib/models/database/database.dart +++ b/lib/models/database/database.dart @@ -8,13 +8,14 @@ import 'package:encrypt/encrypt.dart'; import 'package:media_kit/media_kit.dart' hide Track; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart' show ThemeMode, Colors; import 'package:spotify/spotify.dart' hide Playlist; import 'package:spotube/models/database/database.steps.dart'; import 'package:spotube/models/lyrics.dart'; import 'package:spotube/services/kv_store/encrypted_kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/sourced_track/enums.dart'; -import 'package:flutter/material.dart' hide Table, Key, View; +import 'package:flutter/widgets.dart' hide Table, Key, View; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; import 'package:drift/native.dart'; import 'package:sqlite3/sqlite3.dart'; diff --git a/lib/modules/root/sidebar.dart b/lib/modules/root/sidebar.dart index f29644fb..4f3c4442 100644 --- a/lib/modules/root/sidebar.dart +++ b/lib/modules/root/sidebar.dart @@ -1,33 +1,28 @@ -import 'package:collection/collection.dart'; +import 'package:flutter/material.dart' show Badge; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:flutter/material.dart'; -import 'package:sidebarx/sidebarx.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/collections/spotube_icons.dart'; -import 'package:spotube/models/database/database.dart'; -import 'package:spotube/modules/connect/connect_device.dart'; import 'package:spotube/components/image/universal_image.dart'; +import 'package:spotube/extensions/image.dart'; +import 'package:spotube/models/database/database.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/extensions/image.dart'; -import 'package:spotube/hooks/utils/use_brightness_value.dart'; -import 'package:spotube/hooks/controllers/use_sidebarx_controller.dart'; +import 'package:spotube/modules/connect/connect_device.dart'; import 'package:spotube/pages/profile/profile.dart'; import 'package:spotube/pages/settings/settings.dart'; -import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/authentication/authentication.dart'; +import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; -import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/service_utils.dart'; -import 'package:window_manager/window_manager.dart'; class Sidebar extends HookConsumerWidget { final Widget child; @@ -66,173 +61,65 @@ class Sidebar extends HookConsumerWidget { (e) => routerState.namedLocation(e.name) == routerState.matchedLocation, ); - final controller = useSidebarXController( - selectedIndex: selectedIndex, - extended: mediaQuery.lgAndUp, - ); - - final theme = Theme.of(context); - final bg = theme.colorScheme.surfaceContainerHighest; - - final bgColor = useBrightnessValue( - Color.lerp(bg, Colors.white, 0.6), - Color.lerp(bg, Colors.black, 0.45)!, - ); - - useEffect(() { - if (!context.mounted) return; - if (mediaQuery.lgAndUp && !controller.extended) { - controller.setExtended(true); - } else if (mediaQuery.mdAndDown && controller.extended) { - controller.setExtended(false); - } - return null; - }, [mediaQuery, controller]); - - useEffect(() { - if (controller.selectedIndex != selectedIndex) { - controller.selectIndex(selectedIndex); - } - return null; - }, [selectedIndex]); - if (layoutMode == LayoutMode.compact || (mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) { - return Scaffold(body: child); + return Scaffold(child: child); } - return Row( - children: [ - SafeArea( - child: SidebarX( - controller: controller, - items: sidebarTileList.mapIndexed( - (index, e) { - return SidebarXItem( - onTap: () { - context.goNamed(e.name); - }, - iconBuilder: (selected, hovered) { - return Badge( - backgroundColor: theme.colorScheme.primary, - isLabelVisible: e.title == "Library" && downloadCount > 0, - label: Text( - downloadCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - ), - ), - child: Icon( - e.icon, - color: selected || hovered - ? theme.colorScheme.primary - : null, - ), - ); - }, - label: e.title, - ); - }, - ).toList(), - headerBuilder: (_, __) => const SidebarHeader(), - footerBuilder: (_, __) => const Padding( - padding: EdgeInsets.only(bottom: 5), - child: SidebarFooter(), - ), - showToggleButton: false, - theme: SidebarXTheme( - width: 50, - margin: EdgeInsets.only(bottom: 10, top: kIsMacOS ? 35 : 5), - selectedItemDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: theme.colorScheme.primary.withOpacity(0.1), - ), - selectedIconTheme: IconThemeData( - color: theme.colorScheme.primary, - ), - ), - extendedTheme: SidebarXTheme( - width: 250, - margin: EdgeInsets.only( - bottom: 10, - left: 0, - top: kIsMacOS ? 0 : 5, - ), - padding: const EdgeInsets.symmetric(horizontal: 6), - decoration: BoxDecoration( - color: bgColor?.withOpacity(0.8), - borderRadius: const BorderRadius.only( - topRight: Radius.circular(10), - bottomRight: Radius.circular(10), - ), - ), - selectedItemDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: theme.colorScheme.primary.withOpacity(0.1), - ), - selectedIconTheme: IconThemeData( - color: theme.colorScheme.primary, - ), - selectedTextStyle: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.primary, - fontWeight: FontWeight.w600, - ), - itemTextPadding: const EdgeInsets.only(left: 10), - selectedItemTextPadding: const EdgeInsets.only(left: 10), - hoverTextStyle: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.primary, - ), - ), - ), - ), - Expanded(child: child) - ], - ); - } -} - -class SidebarHeader extends HookWidget { - const SidebarHeader({super.key}); - - @override - Widget build(BuildContext context) { - final mediaQuery = MediaQuery.of(context); - final theme = Theme.of(context); - - if (mediaQuery.mdAndDown) { - return Container( - height: 40, - width: 40, - margin: const EdgeInsets.only(bottom: 5), - child: Sidebar.brandLogo(), - ); - } - - return DragToMoveArea( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - if (kIsMacOS) const SizedBox(height: 25), - Row( + return LayoutBuilder(builder: (context, constrains) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SafeArea( + child: Column( children: [ - Sidebar.brandLogo(), - const SizedBox(width: 10), - Text( - "Spotube", - style: theme.textTheme.titleLarge, + Expanded( + child: NavigationSidebar( + index: selectedIndex, + onSelected: (index) { + final tile = sidebarTileList[index]; + ServiceUtils.pushNamed(context, tile.name); + }, + children: [ + const NavigationLabel(child: Text("Spotube")), + for (final tile in sidebarTileList) + NavigationButton( + label: Text(tile.title), + child: Badge( + backgroundColor: context.theme.colorScheme.primary, + isLabelVisible: + tile.title == "Library" && downloadCount > 0, + label: Text( + downloadCount.toString(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + child: Icon(tile.icon), + ), + onChanged: (value) { + if (value) { + context.goNamed(tile.name); + } + }, + ), + ], + ), ), + const SidebarFooter(), ], ), - ], - ), - ), - ); + ), + const VerticalDivider(), + Expanded(child: child), + ], + ); + }); } } -class SidebarFooter extends HookConsumerWidget { +class SidebarFooter extends HookConsumerWidget implements NavigationBarItem { const SidebarFooter({ super.key, }); @@ -253,6 +140,7 @@ class SidebarFooter extends HookConsumerWidget { if (mediaQuery.mdAndDown) { return IconButton( + variance: ButtonVariance.ghost, icon: const Icon(SpotubeIcons.settings), onPressed: () => ServiceUtils.navigateNamed(context, SettingsPage.name), ); @@ -260,8 +148,9 @@ class SidebarFooter extends HookConsumerWidget { return Container( padding: const EdgeInsets.only(left: 12), - width: 250, + width: 180, child: Column( + mainAxisSize: MainAxisSize.min, children: [ const ConnectDeviceButton.sidebar(), const Gap(10), @@ -273,21 +162,16 @@ class SidebarFooter extends HookConsumerWidget { const CircularProgressIndicator() else if (data != null) Flexible( - child: InkWell( + child: GestureDetector( onTap: () { ServiceUtils.pushNamed(context, ProfilePage.name); }, - borderRadius: BorderRadius.circular(30), child: Row( children: [ - CircleAvatar( - backgroundImage: - UniversalImage.imageProvider(avatarImg), - onBackgroundImageError: (exception, stackTrace) => - Assets.userPlaceholder.image( - height: 16, - width: 16, - ), + Avatar( + initials: + Avatar.getInitials(data.displayName ?? "User"), + provider: UniversalImage.imageProvider(avatarImg), ), const SizedBox(width: 10), Flexible( @@ -296,8 +180,8 @@ class SidebarFooter extends HookConsumerWidget { maxLines: 1, softWrap: false, overflow: TextOverflow.fade, - style: theme.textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), + style: theme.typography.normal + .copyWith(fontWeight: FontWeight.bold), ), ), ], @@ -305,6 +189,7 @@ class SidebarFooter extends HookConsumerWidget { ), ), IconButton( + variance: ButtonVariance.ghost, icon: const Icon(SpotubeIcons.settings), onPressed: () { ServiceUtils.pushNamed(context, SettingsPage.name); @@ -316,4 +201,7 @@ class SidebarFooter extends HookConsumerWidget { ), ); } + + @override + bool get selectable => false; } diff --git a/lib/pages/settings/sections/appearance.dart b/lib/pages/settings/sections/appearance.dart index f97add42..9a95e60b 100644 --- a/lib/pages/settings/sections/appearance.dart +++ b/lib/pages/settings/sections/appearance.dart @@ -1,4 +1,5 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide ThemeMode; +import 'package:shadcn_flutter/shadcn_flutter.dart' show ThemeMode; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; diff --git a/lib/provider/user_preferences/user_preferences_provider.dart b/lib/provider/user_preferences/user_preferences_provider.dart index 053f0994..0f9efce1 100644 --- a/lib/provider/user_preferences/user_preferences_provider.dart +++ b/lib/provider/user_preferences/user_preferences_provider.dart @@ -1,8 +1,9 @@ import 'package:drift/drift.dart'; -import 'package:flutter/material.dart'; + import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart' as paths; +import 'package:shadcn_flutter/shadcn_flutter.dart' hide join; import 'package:spotify/spotify.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; diff --git a/lib/utils/migrations/hive.dart b/lib/utils/migrations/hive.dart deleted file mode 100644 index e5781931..00000000 --- a/lib/utils/migrations/hive.dart +++ /dev/null @@ -1,319 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:flutter/foundation.dart'; -import 'package:hive/hive.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:spotube/models/database/database.dart' - hide - SourceType, - AudioSource, - CloseBehavior, - MusicCodec, - LayoutMode, - SearchMode, - BlacklistedType; -import 'package:spotube/models/database/database.dart' as db; -import 'package:spotube/services/kv_store/kv_store.dart'; -import 'package:spotube/services/logger/logger.dart'; -import 'package:spotube/utils/migrations/adapters.dart'; -import 'package:spotube/utils/migrations/cache_box.dart'; - -late AppDatabase _database; - -Future getHiveCacheDir() async => - kIsWeb ? null : (await getApplicationSupportDirectory()).path; - -Future migrateAuthenticationInfo() async { - AppLogger.log.i("🔵 Migrating authentication info.."); - - final box = PersistenceCacheBox( - "authentication", - encrypted: true, - fromJson: (json) => AuthenticationCredentials.fromJson(json), - ); - - final credentials = await box.getData(); - - if (credentials == null) return; - - await _database.into(_database.authenticationTable).insert( - AuthenticationTableCompanion.insert( - accessToken: DecryptedText(credentials.accessToken), - cookie: DecryptedText(credentials.cookie), - expiration: credentials.expiration, - id: const Value(0), - ), - mode: InsertMode.insertOrReplace, - ); - - AppLogger.log.i("✅ Migrated authentication info"); -} - -Future migratePreferences() async { - AppLogger.log.i("🔵 Migrating preferences.."); - final box = PersistenceCacheBox( - "preferences", - fromJson: (json) => UserPreferences.fromJson(json), - ); - - final preferences = await box.getData(); - - if (preferences == null) return; - - await _database.into(_database.preferencesTable).insert( - PreferencesTableCompanion.insert( - id: const Value(0), - accentColorScheme: Value(preferences.accentColorScheme), - albumColorSync: Value(preferences.albumColorSync), - amoledDarkTheme: Value(preferences.amoledDarkTheme), - audioQuality: Value(preferences.audioQuality), - audioSource: Value( - switch (preferences.audioSource) { - AudioSource.youtube => db.AudioSource.youtube, - AudioSource.piped => db.AudioSource.piped, - AudioSource.jiosaavn => db.AudioSource.jiosaavn, - }, - ), - checkUpdate: Value(preferences.checkUpdate), - closeBehavior: Value( - switch (preferences.closeBehavior) { - CloseBehavior.minimizeToTray => db.CloseBehavior.minimizeToTray, - CloseBehavior.close => db.CloseBehavior.close, - }, - ), - discordPresence: Value(preferences.discordPresence), - downloadLocation: Value(preferences.downloadLocation), - downloadMusicCodec: Value(preferences.downloadMusicCodec), - enableConnect: Value(preferences.enableConnect), - endlessPlayback: Value(preferences.endlessPlayback), - layoutMode: Value( - switch (preferences.layoutMode) { - LayoutMode.adaptive => db.LayoutMode.adaptive, - LayoutMode.compact => db.LayoutMode.compact, - LayoutMode.extended => db.LayoutMode.extended, - }, - ), - localLibraryLocation: Value(preferences.localLibraryLocation), - locale: Value(preferences.locale), - market: Value(preferences.recommendationMarket), - normalizeAudio: Value(preferences.normalizeAudio), - pipedInstance: Value(preferences.pipedInstance), - searchMode: Value( - switch (preferences.searchMode) { - SearchMode.youtube => db.SearchMode.youtube, - SearchMode.youtubeMusic => db.SearchMode.youtubeMusic, - }, - ), - showSystemTrayIcon: Value(preferences.showSystemTrayIcon), - skipNonMusic: Value(preferences.skipNonMusic), - streamMusicCodec: Value(preferences.streamMusicCodec), - systemTitleBar: Value(preferences.systemTitleBar), - themeMode: Value(preferences.themeMode), - ), - mode: InsertMode.replace, - ); - - AppLogger.log.i("✅ Migrated preferences"); -} - -Future migrateSkipSegment() async { - AppLogger.log.i("🔵 Migrating skip segments.."); - Hive.registerAdapter(SkipSegmentAdapter()); - - final box = await Hive.openLazyBox( - SkipSegment.boxName, - path: await getHiveCacheDir(), - ); - - final skipSegments = await Future.wait( - box.keys.map( - (key) async => ( - id: key as String, - data: await box.get(key), - ), - ), - ); - - await _database.batch((batch) { - batch.insertAll( - _database.skipSegmentTable, - skipSegments - .where((element) => element.data != null) - .expand((element) => (element.data as List).map( - (segment) => SkipSegmentTableCompanion.insert( - trackId: element.id, - start: segment["start"], - end: segment["end"], - ), - )) - .toList(), - ); - }); - - AppLogger.log.i("✅ Migrated skip segments"); -} - -Future migrateSourceMatches() async { - AppLogger.log.i("🔵 Migrating source matches.."); - - Hive.registerAdapter(SourceMatchAdapter()); - Hive.registerAdapter(SourceTypeAdapter()); - - final box = await Hive.openBox( - SourceMatch.boxName, - path: await getHiveCacheDir(), - ); - - final sourceMatches = - box.keys.map((key) => (data: box.get(key), trackId: key)); - - await _database.batch((batch) { - batch.insertAll( - _database.sourceMatchTable, - sourceMatches - .where((element) => element.data != null) - .map( - (sourceMatch) => SourceMatchTableCompanion.insert( - sourceId: sourceMatch.data!.sourceId, - trackId: sourceMatch.trackId, - sourceType: Value( - switch (sourceMatch.data!.sourceType) { - SourceType.jiosaavn => db.SourceType.jiosaavn, - SourceType.youtube => db.SourceType.youtube, - SourceType.youtubeMusic => db.SourceType.youtubeMusic, - }, - ), - ), - ) - .toList(), - ); - }); - - AppLogger.log.i("✅ Migrated source matches"); -} - -Future migrateBlacklist() async { - AppLogger.log.i("🔵 Migrating blacklist.."); - - final box = PersistenceCacheBox>( - "blacklist", - fromJson: (json) => (json["blacklist"] as List) - .map((e) => BlacklistedElement.fromJson(e)) - .toSet(), - ); - - final data = await box.getData(); - - if (data == null) return; - - await _database.batch((batch) { - batch.insertAll( - _database.blacklistTable, - data.map( - (element) => BlacklistTableCompanion.insert( - name: element.name, - elementId: element.id, - elementType: switch (element.type) { - BlacklistedType.artist => db.BlacklistedType.artist, - BlacklistedType.track => db.BlacklistedType.track, - }, - ), - ), - ); - }); - - AppLogger.log.i("✅ Migrated blacklist"); -} - -Future migrateLastFmCredentials() async { - AppLogger.log.i("🔵 Migrating Last.fm credentials.."); - - final box = PersistenceCacheBox( - "scrobbler", - fromJson: (json) => ScrobblerState.fromJson(json), - encrypted: true, - ); - - final data = await box.getData(); - - if (data == null) return; - - await _database.into(_database.scrobblerTable).insert( - ScrobblerTableCompanion.insert( - id: const Value(0), - passwordHash: DecryptedText(data.passwordHash), - username: data.username, - ), - mode: InsertMode.replace, - ); - - AppLogger.log.i("✅ Migrated Last.fm credentials"); -} - -Future migratePlaybackHistory() async { - AppLogger.log.i("🔵 Migrating playback history.."); - - final box = PersistenceCacheBox( - "playback_history", - fromJson: (json) => PlaybackHistoryState.fromJson(json), - ); - - final data = await box.getData(); - - if (data == null) return; - - await _database.batch((batch) { - batch.insertAll( - _database.historyTable, - data.items.map( - (item) => switch (item) { - PlaybackHistoryAlbum() => HistoryTableCompanion.insert( - createdAt: Value(item.date), - itemId: item.album.id!, - data: item.album.toJson(), - type: db.HistoryEntryType.album, - ), - PlaybackHistoryPlaylist() => HistoryTableCompanion.insert( - createdAt: Value(item.date), - itemId: item.playlist.id!, - data: item.playlist.toJson(), - type: db.HistoryEntryType.playlist, - ), - PlaybackHistoryTrack() => HistoryTableCompanion.insert( - createdAt: Value(item.date), - itemId: item.track.id!, - data: item.track.toJson(), - type: db.HistoryEntryType.track, - ), - _ => throw Exception("Unknown history item type"), - }, - ), - ); - }); - - AppLogger.log.i("✅ Migrated playback history"); -} - -Future migrateFromHiveToDrift(AppDatabase database) async { - if (KVStoreService.hasMigratedToDrift) return; - - await PersistenceCacheBox.initializeBoxes( - path: await getHiveCacheDir(), - ); - - _database = database; - - await migrateAuthenticationInfo(); - await migratePreferences(); - - await migrateSkipSegment(); - await migrateSourceMatches(); - - await migrateBlacklist(); - await migratePlaybackHistory(); - - await migrateLastFmCredentials(); - - await KVStoreService.setHasMigratedToDrift(true); - - AppLogger.log.i("🚀 Migrated all data to Drift"); -} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 06c33e05..476ff787 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -21,6 +22,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); + audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin"); desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e98b18eb..a875e8f5 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_linux desktop_webview_window file_selector_linux flutter_secure_storage_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 608a854e..12972107 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import app_links import audio_service import audio_session +import audioplayers_darwin import bonsoir_darwin import desktop_webview_window import device_info_plus @@ -32,6 +33,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) + AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin")) DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index fee42515..db8ed79c 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -5,6 +5,8 @@ PODS: - FlutterMacOS - audio_session (0.0.1): - FlutterMacOS + - audioplayers_darwin (0.0.1): + - FlutterMacOS - bonsoir_darwin (0.0.1): - Flutter - FlutterMacOS @@ -46,20 +48,21 @@ PODS: - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - sqlite3 (3.47.0): - - sqlite3/common (= 3.47.0) - - sqlite3/common (3.47.0) - - sqlite3/dbstatvtab (3.47.0): + - sqlite3 (3.47.2): + - sqlite3/common (= 3.47.2) + - sqlite3/common (3.47.2) + - sqlite3/dbstatvtab (3.47.2): - sqlite3/common - - sqlite3/fts5 (3.47.0): + - sqlite3/fts5 (3.47.2): - sqlite3/common - - sqlite3/perf-threadsafe (3.47.0): + - sqlite3/perf-threadsafe (3.47.2): - sqlite3/common - - sqlite3/rtree (3.47.0): + - sqlite3/rtree (3.47.2): - sqlite3/common - sqlite3_flutter_libs (0.0.1): + - Flutter - FlutterMacOS - - sqlite3 (~> 3.47.0) + - sqlite3 (~> 3.47.1) - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/perf-threadsafe @@ -77,6 +80,7 @@ DEPENDENCIES: - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`) - audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) + - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) - bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`) - desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) @@ -95,7 +99,7 @@ DEPENDENCIES: - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) - - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) + - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin`) - system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`) - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) @@ -113,6 +117,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/audio_service/macos audio_session: :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos + audioplayers_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos bonsoir_darwin: :path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin desktop_webview_window: @@ -150,7 +156,7 @@ EXTERNAL SOURCES: sqflite_darwin: :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin sqlite3_flutter_libs: - :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos + :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin system_theme: :path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos tray_manager: @@ -164,6 +170,7 @@ SPEC CHECKSUMS: app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9 audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 + audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842 desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 @@ -183,8 +190,8 @@ SPEC CHECKSUMS: screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d - sqlite3: 0aa20658a9b238a3b1ff7175eb7bdd863b0ab4fd - sqlite3_flutter_libs: f0b7a85544d8bac7b8bac12eac7d05bcfdd786d0 + sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71 + sqlite3_flutter_libs: 1b4e98da20ebd4e9b1240269b78cdcf492dbe9f3 system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 diff --git a/pubspec.lock b/pubspec.lock index 5aa4f9b4..b58c16b8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -142,6 +142,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.21" + audioplayers: + dependency: transitive + description: + name: audioplayers + sha256: c346ba5a39dc208f1bab55fc239855f573d69b0e832402114bf0b793622adc4d + url: "https://pub.dev" + source: hosted + version: "6.1.0" + audioplayers_android: + dependency: transitive + description: + name: audioplayers_android + sha256: de576b890befe27175c2f511ba8b742bec83765fa97c3ce4282bba46212f58e4 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + audioplayers_darwin: + dependency: transitive + description: + name: audioplayers_darwin + sha256: e507887f3ff18d8e5a10a668d7bedc28206b12e10b98347797257c6ae1019c3b + url: "https://pub.dev" + source: hosted + version: "6.0.0" + audioplayers_linux: + dependency: transitive + description: + name: audioplayers_linux + sha256: "3d3d244c90436115417f170426ce768856d8fe4dfc5ed66a049d2890acfa82f9" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + audioplayers_platform_interface: + dependency: transitive + description: + name: audioplayers_platform_interface + sha256: "6834dd48dfb7bc6c2404998ebdd161f79cd3774a7e6779e1348d54a3bfdcfaa5" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + audioplayers_web: + dependency: transitive + description: + name: audioplayers_web + sha256: "3609bdf0e05e66a3d9750ee40b1e37f2a622c4edb796cc600b53a90a30a2ace4" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + audioplayers_windows: + dependency: transitive + description: + name: audioplayers_windows + sha256: "8605762dddba992138d476f6a0c3afd9df30ac5b96039929063eceed416795c2" + url: "https://pub.dev" + source: hosted + version: "4.0.0" auto_size_text: dependency: "direct main" description: @@ -390,6 +446,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + country_flags: + dependency: transitive + description: + name: country_flags + sha256: dad797491167a5b8dee465b969cb756795d842fdfc3fc1ff93f22e9c1884b73d + url: "https://pub.dev" + source: hosted + version: "3.1.0" coverage: dependency: transitive description: @@ -462,6 +526,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + data_widget: + dependency: transitive + description: + name: data_widget + sha256: "95388df890189014f702b7e93f9de6bcf7d45143a99f6288f31899f10be441ba" + url: "https://pub.dev" + source: hosted + version: "0.0.2" dbus: dependency: transitive description: @@ -552,6 +624,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.15" + email_validator: + dependency: transitive + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" encrypt: dependency: "direct main" description: @@ -1276,6 +1356,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + jovial_misc: + dependency: transitive + description: + name: jovial_misc + sha256: "4b10a4cac4f492d9692e97699bff775efa84abdba29909124cbccf3126e31cea" + url: "https://pub.dev" + source: hosted + version: "0.9.0" + jovial_svg: + dependency: transitive + description: + name: jovial_svg + sha256: ca14d42956b9949c36333065c9141f100e930c918f57f4bd8dd59d35581bd3fc + url: "https://pub.dev" + source: hosted + version: "1.1.24" js: dependency: transitive description: @@ -1740,6 +1836,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + phonecodes: + dependency: transitive + description: + name: phonecodes + sha256: "094a76b0ba3d8f9c1c83044ae8783d46e6906703c86eb08facd876844c264bf5" + url: "https://pub.dev" + source: hosted + version: "0.0.3" piped_client: dependency: "direct main" description: @@ -1748,6 +1852,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.1" + pixel_snap: + dependency: transitive + description: + name: pixel_snap + sha256: "677410ea37b07cd37ecb6d5e6c0d8d7615a7cf3bd92ba406fd1ac57e937d1fb0" + url: "https://pub.dev" + source: hosted + version: "0.1.5" platform: dependency: transitive description: @@ -1925,6 +2037,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + shadcn_flutter: + dependency: "direct main" + description: + name: shadcn_flutter + sha256: eaf10ec804beddf2059dd55b802188b64277a5e4fc577defbc7c012253caef1a + url: "https://pub.dev" + source: hosted + version: "0.0.23" shared_preferences: dependency: "direct main" description: @@ -2258,6 +2378,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.0+3" + syntax_highlight: + dependency: transitive + description: + name: syntax_highlight + sha256: ee33b6aa82cc722bb9b40152a792181dee222353b486c0255fde666a3e3a4997 + url: "https://pub.dev" + source: hosted + version: "0.4.0" system_theme: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3866e410..c9a6f341 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ dependencies: ref: dart-3-support url: https://github.com/KRTirtho/scrobblenaut.git scroll_to_index: ^3.0.1 + shadcn_flutter: ^0.0.23 shared_preferences: ^2.2.3 shelf: ^1.4.1 shelf_router: ^1.1.4 @@ -165,6 +166,53 @@ flutter: - assets/logos/ - assets/backgrounds/ - LICENSE + fonts: + - family: GeistSans + fonts: + - asset: packages/shadcn_flutter/fonts/Geist-Black.otf + weight: 800 + - asset: packages/shadcn_flutter/fonts/Geist-Bold.otf + weight: 700 + - asset: packages/shadcn_flutter/fonts/Geist-Light.otf + weight: 300 + - asset: packages/shadcn_flutter/fonts/Geist-Medium.otf + weight: 500 + - asset: packages/shadcn_flutter/fonts/Geist-SemiBold.otf + weight: 600 + - asset: packages/shadcn_flutter/fonts/Geist-Thin.otf + weight: 100 + - asset: packages/shadcn_flutter/fonts/Geist-UltraBlack.otf + weight: 900 + - asset: packages/shadcn_flutter/fonts/Geist-UltraLight.otf + weight: 200 + - asset: packages/shadcn_flutter/fonts/Geist-Regular.otf + weight: 400 + - family: GeistMono + fonts: + - asset: packages/shadcn_flutter/fonts/GeistMono-Black.otf + weight: 800 + - asset: packages/shadcn_flutter/fonts/GeistMono-Bold.otf + weight: 700 + - asset: packages/shadcn_flutter/fonts/GeistMono-Light.otf + weight: 300 + - asset: packages/shadcn_flutter/fonts/GeistMono-Medium.otf + weight: 500 + - asset: packages/shadcn_flutter/fonts/GeistMono-Regular.otf + weight: 400 + - asset: packages/shadcn_flutter/fonts/GeistMono-SemiBold.otf + weight: 600 + - asset: packages/shadcn_flutter/fonts/GeistMono-Thin.otf + weight: 100 + - asset: packages/shadcn_flutter/fonts/GeistMono-UltraBlack.otf + weight: 900 + - asset: packages/shadcn_flutter/fonts/GeistMono-UltraLight.otf + weight: 200 + - family: RadixIcons + fonts: + - asset: packages/shadcn_flutter/icons/RadixIcons.otf + - family: BootstrapIcons + fonts: + - asset: packages/shadcn_flutter/icons/BootstrapIcons.otf flutter_gen: output: lib/collections diff --git a/web/flutter_bootstrap.js b/web/flutter_bootstrap.js new file mode 100644 index 00000000..a9f703d6 --- /dev/null +++ b/web/flutter_bootstrap.js @@ -0,0 +1,238 @@ +const words = [ + 'Something is happening. Please wait.', + 'Please be patient. This may take a while.', + 'While you wait, please consider that this is a good time to take a break.', + 'Please wait. This is a good time to go grab a cup of coffee.', + 'Sometimes the things that are worth waiting for take time.', + 'Please wait. This is a good time to stretch your legs.', + 'Posture check! Please wait while we load the application.', +]; + +const loaderWidget = ` +
+ Loading Application... +
+ ${words[Math.floor(Math.random() * words.length)]} +
+
` + +const shadcn_flutter_config = { + loaderWidget: loaderWidget, + backgroundColor: null, + foregroundColor: null, + loaderColor: null, + fontFamily: 'Geist Sans', + fontSize: '24px', + fontWeight: '400', + mainAxisAlignment: 'end', + crossAxisAlignment: 'end', + externalScripts: [ + { + src: 'https://cdn.jsdelivr.net/npm/@fontsource/geist-sans@5.0.3/400.min.css', + type: 'stylesheet', + }, + { + src: 'https://cdn.jsdelivr.net/npm/@fontsource/geist-sans@5.0.3/300.min.css', + type: 'stylesheet', + }, + ] +}; + +{{flutter_js}} +{{flutter_build_config}} + +class ShadcnAppConfig { + background; + foreground; + fontFamily; + fontSize; + fontWeight; + mainAxisAlignment; + crossAxisAlignment; + loaderWidget; + loaderColor; + externalScripts; + + constructor({ background, foreground, fontFamily, fontSize, fontWeight, mainAxisAlignment, crossAxisAlignment, loaderWidget, loaderColor, externalScripts }) { + this.background = background; + this.foreground = foreground; + this.fontFamily = fontFamily; + this.fontSize = fontSize; + this.fontWeight = fontWeight; + this.mainAxisAlignment = mainAxisAlignment; + this.crossAxisAlignment = crossAxisAlignment; + this.loaderWidget = loaderWidget; + this.loaderColor = loaderColor; + this.externalScripts = externalScripts; + + if (this.background == null) { + this.background = localStorage.getItem('shadcn_flutter.background') || '#09090b'; + } + if (this.foreground == null) { + this.foreground = localStorage.getItem('shadcn_flutter.foreground') || '#ffffff'; + } + if (this.loaderColor == null) { + this.loaderColor = localStorage.getItem('shadcn_flutter.primary') || '#3c83f6'; + } + } +} + +class ShadcnAppThemeChangedEvent extends CustomEvent { + constructor(theme) { + super('shadcn_flutter_theme_changed', { detail: theme }); + } +} + +class ShadcnAppTheme { + background; + foreground; + primary; + + constructor(background, foreground, primary) { + this.background = background; + this.foreground = foreground; + this.primary = primary; + } +} + +class ShadcnApp { + config; + + constructor(config) { + this.config = config; + } + + loadApp() { + window.addEventListener('shadcn_flutter_app_ready', this.onAppReady); + window.addEventListener('shadcn_flutter_theme_changed', this.onThemeChanged); + this.#initializeDocument(); + let externalScriptIndex = 0; + this.#loadExternalScripts(externalScriptIndex, () => { + _flutter.loader.load({ + onEntrypointLoaded: async function(engineInitializer) { + const appRunner = await engineInitializer.initializeEngine(); + await appRunner.runApp(); + } + }); + }); + } + + #loadExternalScripts(index, onDone) { + if (index >= this.config.externalScripts.length) { + onDone(); + return; + } + this.#loadScriptDynamically(this.config.externalScripts[index], () => { + this.#loadExternalScripts(index + 1, onDone); + }); + } + + #createStyleSheet(css) { + const style = document.createElement('style'); + style.type = 'text/css'; + style.appendChild(document.createTextNode(css)); + document.head.appendChild(style); + } + + #loadScriptDynamically(src, callback) { + if (typeof src === 'string') { + src = { src: src, type: 'script' }; + } + if (src.type === 'script') { + const script = document.createElement('script'); + script.src = src.src; + script.onload = callback; + document.body.appendChild(script); + } else if (src.type === 'module') { + const script = document.createElement('script'); + script.type = 'module'; + script.src = src.src; + script.onload = callback; + document.body.appendChild(script); + } else if (src.type === 'stylesheet') { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = src.src; + link.onload = callback; + document.head.appendChild(link); + } else { + throw new Error('Unknown type of file to load: ' + src); + } + } + + #initializeDocument() { + const loaderStyle = ` + display: flex; + justify-content: ${this.config.mainAxisAlignment}; + align-items: ${this.config.crossAxisAlignment}; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: ${this.config.background}; + color: ${this.config.foreground}; + z-index: 9998; + font-family: ${this.config.fontFamily}; + font-size: ${this.config.fontSize}; + font-weight: ${this.config.fontWeight}; + text-align: center; + transition: opacity 0.5s; + opacity: 1; + pointer-events: initial; + `; + + const loaderBarCss = ` + .loader { + height: 7px; + background: repeating-linear-gradient(-45deg,${this.config.loaderColor} 0 15px,#000 0 20px) left/200% 100%; + animation: l3 20s infinite linear; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 9999; + } + @keyframes l3 { + 100% {background-position:right} + }`; + + const loaderDiv = document.createElement('div'); + loaderDiv.style.cssText = loaderStyle; + loaderDiv.innerHTML = this.config.loaderWidget; + + document.body.appendChild(loaderDiv); + + document.body.style.backgroundColor = this.config.background; + + const loaderBarDiv = document.createElement('div'); + loaderBarDiv.className = 'loader'; + loaderDiv.appendChild(loaderBarDiv); + + this.#createStyleSheet(loaderBarCss); + } + + onAppReady() { + const loaderDiv = document.querySelector('div'); + loaderDiv.style.opacity = 0; + loaderDiv.style.pointerEvents = 'none'; + } + + onThemeChanged(event) { + let theme = event.detail; + let background = theme['background']; + let foreground = theme['foreground']; + let primary = theme['primary']; + localStorage.setItem('shadcn_flutter.background', background); + localStorage.setItem('shadcn_flutter.foreground', foreground); + localStorage.setItem('shadcn_flutter.primary', primary); + } +} + +globalThis.ShadcnApp = ShadcnApp; +globalThis.ShadcnAppConfig = ShadcnAppConfig; +globalThis.ShadcnAppThemeChangedEvent = ShadcnAppThemeChangedEvent; +globalThis.ShadcnAppTheme = ShadcnAppTheme; + +const shadcn_flutter = new ShadcnApp(new ShadcnAppConfig(shadcn_flutter_config)); +shadcn_flutter.loadApp(); \ No newline at end of file diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 42fa2129..7c490ef1 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -25,6 +26,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { AppLinksPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + AudioplayersWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); BonsoirWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi")); DesktopWebviewWindowPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index cf14ec52..dfcc70cf 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links + audioplayers_windows bonsoir_windows desktop_webview_window file_selector_windows