mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45: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: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<void> main(List<String> rawArgs) async {
|
||||
if (rawArgs.contains("web_view_title_bar")) {
|
||||
@ -110,8 +107,6 @@ Future<void> main(List<String> 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(
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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 <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
@ -21,6 +22,9 @@
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
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);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
desktop_webview_window
|
||||
file_selector_linux
|
||||
flutter_secure_storage_linux
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
128
pubspec.lock
128
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:
|
||||
|
48
pubspec.yaml
48
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
|
||||
|
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 <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 <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
@ -25,6 +26,8 @@
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AppLinksPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
BonsoirWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
||||
DesktopWebviewWindowPluginRegisterWithRegistrar(
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
app_links
|
||||
audioplayers_windows
|
||||
bonsoir_windows
|
||||
desktop_webview_window
|
||||
file_selector_windows
|
||||
|
Loading…
Reference in New Issue
Block a user