mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: initial integration of shadcn-ui
This commit is contained in:
parent
b52bf0f448
commit
5ad151932a
@ -3,7 +3,6 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_discord_rpc/flutter_discord_rpc.dart';
|
import 'package:flutter_discord_rpc/flutter_discord_rpc.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.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/provider/tray_manager/tray_manager.dart';
|
||||||
import 'package:spotube/l10n/l10n.dart';
|
import 'package:spotube/l10n/l10n.dart';
|
||||||
import 'package:spotube/provider/connect/clients.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/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/cli/cli.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/kv_store/kv_store.dart';
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
import 'package:spotube/services/wm_tools/wm_tools.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/migrations/sandbox.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:system_theme/system_theme.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:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:timezone/data/latest.dart' as tz;
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
if (rawArgs.contains("web_view_title_bar")) {
|
if (rawArgs.contains("web_view_title_bar")) {
|
||||||
@ -110,8 +107,6 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
|
|
||||||
final database = AppDatabase();
|
final database = AppDatabase();
|
||||||
|
|
||||||
await migrateFromHiveToDrift(database);
|
|
||||||
|
|
||||||
if (kIsDesktop) {
|
if (kIsDesktop) {
|
||||||
await localNotifier.setup(appName: "Spotube");
|
await localNotifier.setup(appName: "Spotube");
|
||||||
await WindowManagerTools.initialize();
|
await WindowManagerTools.initialize();
|
||||||
@ -142,13 +137,13 @@ class Spotube extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final themeMode =
|
final themeMode =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.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 locale = ref.watch(userPreferencesProvider.select((s) => s.locale));
|
||||||
final paletteColor =
|
// final accentMaterialColor =
|
||||||
ref.watch(paletteProvider.select((s) => s?.dominantColor?.color));
|
// 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 router = ref.watch(routerProvider);
|
||||||
final hasTouchSupport = useHasTouch();
|
final hasTouchSupport = useHasTouch();
|
||||||
|
|
||||||
@ -178,20 +173,20 @@ class Spotube extends HookConsumerWidget {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
final lightTheme = useMemoized(
|
// final lightTheme = useMemoized(
|
||||||
() => theme(paletteColor ?? accentMaterialColor, Brightness.light, false),
|
// () => theme(paletteColor ?? accentMaterialColor, Brightness.light, false),
|
||||||
[paletteColor, accentMaterialColor],
|
// [paletteColor, accentMaterialColor],
|
||||||
);
|
// );
|
||||||
final darkTheme = useMemoized(
|
// final darkTheme = useMemoized(
|
||||||
() => theme(
|
// () => theme(
|
||||||
paletteColor ?? accentMaterialColor,
|
// paletteColor ?? accentMaterialColor,
|
||||||
Brightness.dark,
|
// Brightness.dark,
|
||||||
isAmoledTheme,
|
// isAmoledTheme,
|
||||||
),
|
// ),
|
||||||
[paletteColor, accentMaterialColor, isAmoledTheme],
|
// [paletteColor, accentMaterialColor, isAmoledTheme],
|
||||||
);
|
// );
|
||||||
|
|
||||||
return MaterialApp.router(
|
return ShadcnApp.router(
|
||||||
supportedLocales: L10n.all,
|
supportedLocales: L10n.all,
|
||||||
locale: locale.languageCode == "system" ? null : locale,
|
locale: locale.languageCode == "system" ? null : locale,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
@ -221,9 +216,17 @@ class Spotube extends HookConsumerWidget {
|
|||||||
|
|
||||||
return child;
|
return child;
|
||||||
},
|
},
|
||||||
|
theme: ThemeData(
|
||||||
|
radius: .5,
|
||||||
|
iconTheme: const IconThemeProperties(),
|
||||||
|
colorScheme: ColorSchemes.lightNeutral(),
|
||||||
|
),
|
||||||
|
darkTheme: ThemeData(
|
||||||
|
radius: .5,
|
||||||
|
iconTheme: const IconThemeProperties(),
|
||||||
|
colorScheme: ColorSchemes.darkNeutral(),
|
||||||
|
),
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
theme: lightTheme,
|
|
||||||
darkTheme: darkTheme,
|
|
||||||
shortcuts: {
|
shortcuts: {
|
||||||
...WidgetsApp.defaultShortcuts.map((key, value) {
|
...WidgetsApp.defaultShortcuts.map((key, value) {
|
||||||
return MapEntry(
|
return MapEntry(
|
||||||
|
@ -8,13 +8,14 @@ import 'package:encrypt/encrypt.dart';
|
|||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.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:spotify/spotify.dart' hide Playlist;
|
||||||
import 'package:spotube/models/database/database.steps.dart';
|
import 'package:spotube/models/database/database.steps.dart';
|
||||||
import 'package:spotube/models/lyrics.dart';
|
import 'package:spotube/models/lyrics.dart';
|
||||||
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
|
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
|
||||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||||
import 'package:spotube/services/sourced_track/enums.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:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:sqlite3/sqlite3.dart';
|
import 'package:sqlite3/sqlite3.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:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:sidebarx/sidebarx.dart';
|
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
|
||||||
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/side_bar_tiles.dart';
|
import 'package:spotube/collections/side_bar_tiles.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.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/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/constrains.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/modules/connect/connect_device.dart';
|
||||||
import 'package:spotube/hooks/utils/use_brightness_value.dart';
|
|
||||||
import 'package:spotube/hooks/controllers/use_sidebarx_controller.dart';
|
|
||||||
import 'package:spotube/pages/profile/profile.dart';
|
import 'package:spotube/pages/profile/profile.dart';
|
||||||
import 'package:spotube/pages/settings/settings.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/authentication/authentication.dart';
|
||||||
|
import 'package:spotube/provider/download_manager_provider.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.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:spotube/utils/service_utils.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
class Sidebar extends HookConsumerWidget {
|
class Sidebar extends HookConsumerWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@ -66,56 +61,34 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
(e) => routerState.namedLocation(e.name) == routerState.matchedLocation,
|
(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 ||
|
if (layoutMode == LayoutMode.compact ||
|
||||||
(mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) {
|
(mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) {
|
||||||
return Scaffold(body: child);
|
return Scaffold(child: child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return LayoutBuilder(builder: (context, constrains) {
|
||||||
return Row(
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SafeArea(
|
SafeArea(
|
||||||
child: SidebarX(
|
child: Column(
|
||||||
controller: controller,
|
children: [
|
||||||
items: sidebarTileList.mapIndexed(
|
Expanded(
|
||||||
(index, e) {
|
child: NavigationSidebar(
|
||||||
return SidebarXItem(
|
index: selectedIndex,
|
||||||
onTap: () {
|
onSelected: (index) {
|
||||||
context.goNamed(e.name);
|
final tile = sidebarTileList[index];
|
||||||
|
ServiceUtils.pushNamed(context, tile.name);
|
||||||
},
|
},
|
||||||
iconBuilder: (selected, hovered) {
|
children: [
|
||||||
return Badge(
|
const NavigationLabel(child: Text("Spotube")),
|
||||||
backgroundColor: theme.colorScheme.primary,
|
for (final tile in sidebarTileList)
|
||||||
isLabelVisible: e.title == "Library" && downloadCount > 0,
|
NavigationButton(
|
||||||
|
label: Text(tile.title),
|
||||||
|
child: Badge(
|
||||||
|
backgroundColor: context.theme.colorScheme.primary,
|
||||||
|
isLabelVisible:
|
||||||
|
tile.title == "Library" && downloadCount > 0,
|
||||||
label: Text(
|
label: Text(
|
||||||
downloadCount.toString(),
|
downloadCount.toString(),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
@ -123,116 +96,30 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(tile.icon),
|
||||||
e.icon,
|
|
||||||
color: selected || hovered
|
|
||||||
? theme.colorScheme.primary
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
);
|
onChanged: (value) {
|
||||||
|
if (value) {
|
||||||
|
context.goNamed(tile.name);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
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(
|
|
||||||
children: [
|
|
||||||
Sidebar.brandLogo(),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
"Spotube",
|
|
||||||
style: theme.textTheme.titleLarge,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
const SidebarFooter(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const VerticalDivider(),
|
||||||
|
Expanded(child: child),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SidebarFooter extends HookConsumerWidget {
|
class SidebarFooter extends HookConsumerWidget implements NavigationBarItem {
|
||||||
const SidebarFooter({
|
const SidebarFooter({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
@ -253,6 +140,7 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
|
|
||||||
if (mediaQuery.mdAndDown) {
|
if (mediaQuery.mdAndDown) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
variance: ButtonVariance.ghost,
|
||||||
icon: const Icon(SpotubeIcons.settings),
|
icon: const Icon(SpotubeIcons.settings),
|
||||||
onPressed: () => ServiceUtils.navigateNamed(context, SettingsPage.name),
|
onPressed: () => ServiceUtils.navigateNamed(context, SettingsPage.name),
|
||||||
);
|
);
|
||||||
@ -260,8 +148,9 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.only(left: 12),
|
padding: const EdgeInsets.only(left: 12),
|
||||||
width: 250,
|
width: 180,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const ConnectDeviceButton.sidebar(),
|
const ConnectDeviceButton.sidebar(),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
@ -273,21 +162,16 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
const CircularProgressIndicator()
|
const CircularProgressIndicator()
|
||||||
else if (data != null)
|
else if (data != null)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: InkWell(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ServiceUtils.pushNamed(context, ProfilePage.name);
|
ServiceUtils.pushNamed(context, ProfilePage.name);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(30),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
Avatar(
|
||||||
backgroundImage:
|
initials:
|
||||||
UniversalImage.imageProvider(avatarImg),
|
Avatar.getInitials(data.displayName ?? "User"),
|
||||||
onBackgroundImageError: (exception, stackTrace) =>
|
provider: UniversalImage.imageProvider(avatarImg),
|
||||||
Assets.userPlaceholder.image(
|
|
||||||
height: 16,
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Flexible(
|
Flexible(
|
||||||
@ -296,8 +180,8 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
style: theme.textTheme.bodyMedium
|
style: theme.typography.normal
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -305,6 +189,7 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
variance: ButtonVariance.ghost,
|
||||||
icon: const Icon(SpotubeIcons.settings),
|
icon: const Icon(SpotubeIcons.settings),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ServiceUtils.pushNamed(context, SettingsPage.name);
|
ServiceUtils.pushNamed(context, SettingsPage.name);
|
||||||
@ -316,4 +201,7 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get selectable => false;
|
||||||
}
|
}
|
||||||
|
@ -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:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart' as paths;
|
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:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
||||||
|
@ -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<String?> getHiveCacheDir() async =>
|
|
||||||
kIsWeb ? null : (await getApplicationSupportDirectory()).path;
|
|
||||||
|
|
||||||
Future<void> migrateAuthenticationInfo() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating authentication info..");
|
|
||||||
|
|
||||||
final box = PersistenceCacheBox<AuthenticationCredentials>(
|
|
||||||
"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<void> migratePreferences() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating preferences..");
|
|
||||||
final box = PersistenceCacheBox<UserPreferences>(
|
|
||||||
"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<void> 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<void> migrateSourceMatches() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating source matches..");
|
|
||||||
|
|
||||||
Hive.registerAdapter(SourceMatchAdapter());
|
|
||||||
Hive.registerAdapter(SourceTypeAdapter());
|
|
||||||
|
|
||||||
final box = await Hive.openBox<SourceMatch>(
|
|
||||||
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<void> migrateBlacklist() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating blacklist..");
|
|
||||||
|
|
||||||
final box = PersistenceCacheBox<Set<BlacklistedElement>>(
|
|
||||||
"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<void> migrateLastFmCredentials() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating Last.fm credentials..");
|
|
||||||
|
|
||||||
final box = PersistenceCacheBox<ScrobblerState>(
|
|
||||||
"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<void> migratePlaybackHistory() async {
|
|
||||||
AppLogger.log.i("🔵 Migrating playback history..");
|
|
||||||
|
|
||||||
final box = PersistenceCacheBox<PlaybackHistoryState>(
|
|
||||||
"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<void> 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");
|
|
||||||
}
|
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
@ -21,6 +22,9 @@
|
|||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
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 =
|
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
||||||
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
audioplayers_linux
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
|
@ -8,6 +8,7 @@ import Foundation
|
|||||||
import app_links
|
import app_links
|
||||||
import audio_service
|
import audio_service
|
||||||
import audio_session
|
import audio_session
|
||||||
|
import audioplayers_darwin
|
||||||
import bonsoir_darwin
|
import bonsoir_darwin
|
||||||
import desktop_webview_window
|
import desktop_webview_window
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
@ -32,6 +33,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
||||||
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
|
@ -5,6 +5,8 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- audio_session (0.0.1):
|
- audio_session (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- audioplayers_darwin (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- bonsoir_darwin (0.0.1):
|
- bonsoir_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@ -46,20 +48,21 @@ PODS:
|
|||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (3.47.0):
|
- sqlite3 (3.47.2):
|
||||||
- sqlite3/common (= 3.47.0)
|
- sqlite3/common (= 3.47.2)
|
||||||
- sqlite3/common (3.47.0)
|
- sqlite3/common (3.47.2)
|
||||||
- sqlite3/dbstatvtab (3.47.0):
|
- sqlite3/dbstatvtab (3.47.2):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/fts5 (3.47.0):
|
- sqlite3/fts5 (3.47.2):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/perf-threadsafe (3.47.0):
|
- sqlite3/perf-threadsafe (3.47.2):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/rtree (3.47.0):
|
- sqlite3/rtree (3.47.2):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3_flutter_libs (0.0.1):
|
- sqlite3_flutter_libs (0.0.1):
|
||||||
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (~> 3.47.0)
|
- sqlite3 (~> 3.47.1)
|
||||||
- sqlite3/dbstatvtab
|
- sqlite3/dbstatvtab
|
||||||
- sqlite3/fts5
|
- sqlite3/fts5
|
||||||
- sqlite3/perf-threadsafe
|
- sqlite3/perf-threadsafe
|
||||||
@ -77,6 +80,7 @@ DEPENDENCIES:
|
|||||||
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
||||||
- audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`)
|
- audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`)
|
||||||
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/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`)
|
- bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`)
|
||||||
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
|
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
|
||||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/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`)
|
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
|
||||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/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`)
|
- system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`)
|
||||||
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/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
|
:path: Flutter/ephemeral/.symlinks/plugins/audio_service/macos
|
||||||
audio_session:
|
audio_session:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
|
||||||
|
audioplayers_darwin:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos
|
||||||
bonsoir_darwin:
|
bonsoir_darwin:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin
|
||||||
desktop_webview_window:
|
desktop_webview_window:
|
||||||
@ -150,7 +156,7 @@ EXTERNAL SOURCES:
|
|||||||
sqflite_darwin:
|
sqflite_darwin:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
|
||||||
sqlite3_flutter_libs:
|
sqlite3_flutter_libs:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin
|
||||||
system_theme:
|
system_theme:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos
|
||||||
tray_manager:
|
tray_manager:
|
||||||
@ -164,6 +170,7 @@ SPEC CHECKSUMS:
|
|||||||
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
|
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
|
||||||
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
|
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
|
||||||
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
|
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
|
||||||
|
audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c
|
||||||
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
||||||
desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a
|
desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a
|
||||||
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
|
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
|
||||||
@ -183,8 +190,8 @@ SPEC CHECKSUMS:
|
|||||||
screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161
|
screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||||
sqlite3: 0aa20658a9b238a3b1ff7175eb7bdd863b0ab4fd
|
sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71
|
||||||
sqlite3_flutter_libs: f0b7a85544d8bac7b8bac12eac7d05bcfdd786d0
|
sqlite3_flutter_libs: 1b4e98da20ebd4e9b1240269b78cdcf492dbe9f3
|
||||||
system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc
|
system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc
|
||||||
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
||||||
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
|
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
|
||||||
|
128
pubspec.lock
128
pubspec.lock
@ -142,6 +142,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.21"
|
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:
|
auto_size_text:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -390,6 +446,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
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:
|
coverage:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -462,6 +526,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
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:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -552,6 +624,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.15"
|
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:
|
encrypt:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1276,6 +1356,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
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:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1740,6 +1836,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
|
phonecodes:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: phonecodes
|
||||||
|
sha256: "094a76b0ba3d8f9c1c83044ae8783d46e6906703c86eb08facd876844c264bf5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.3"
|
||||||
piped_client:
|
piped_client:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1748,6 +1852,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
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:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1925,6 +2037,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
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:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -2258,6 +2378,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0+3"
|
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:
|
system_theme:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
48
pubspec.yaml
48
pubspec.yaml
@ -102,6 +102,7 @@ dependencies:
|
|||||||
ref: dart-3-support
|
ref: dart-3-support
|
||||||
url: https://github.com/KRTirtho/scrobblenaut.git
|
url: https://github.com/KRTirtho/scrobblenaut.git
|
||||||
scroll_to_index: ^3.0.1
|
scroll_to_index: ^3.0.1
|
||||||
|
shadcn_flutter: ^0.0.23
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
shelf: ^1.4.1
|
shelf: ^1.4.1
|
||||||
shelf_router: ^1.1.4
|
shelf_router: ^1.1.4
|
||||||
@ -165,6 +166,53 @@ flutter:
|
|||||||
- assets/logos/
|
- assets/logos/
|
||||||
- assets/backgrounds/
|
- assets/backgrounds/
|
||||||
- LICENSE
|
- 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:
|
flutter_gen:
|
||||||
output: lib/collections
|
output: lib/collections
|
||||||
|
238
web/flutter_bootstrap.js
Normal file
238
web/flutter_bootstrap.js
Normal file
@ -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 = `
|
||||||
|
<div style="padding-right: 32px; padding-bottom: 32px; font-smooth: always; display: flex; flex-direction: column; align-items: end">
|
||||||
|
Loading Application...
|
||||||
|
<div id="words" style="font-size: 16px; opacity: 0.6; font-weight: 300; text-align: right; margin-top: 4px">
|
||||||
|
${words[Math.floor(Math.random() * words.length)]}
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
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();
|
@ -7,6 +7,7 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <app_links/app_links_plugin_c_api.h>
|
#include <app_links/app_links_plugin_c_api.h>
|
||||||
|
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||||
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
@ -25,6 +26,8 @@
|
|||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
AppLinksPluginCApiRegisterWithRegistrar(
|
AppLinksPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||||
|
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||||
BonsoirWindowsPluginCApiRegisterWithRegistrar(
|
BonsoirWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
||||||
DesktopWebviewWindowPluginRegisterWithRegistrar(
|
DesktopWebviewWindowPluginRegisterWithRegistrar(
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
app_links
|
app_links
|
||||||
|
audioplayers_windows
|
||||||
bonsoir_windows
|
bonsoir_windows
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
Loading…
Reference in New Issue
Block a user