mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-09 08:47:31 +00:00
Merge branch 'dev' into feat-jiosaavn
This commit is contained in:
commit
956a57b3f9
@ -163,6 +163,8 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
final searchFocus = useFocusNode();
|
final searchFocus = useFocusNode();
|
||||||
final isFiltering = useState(false);
|
final isFiltering = useState(false);
|
||||||
|
|
||||||
|
final controller = useScrollController();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@ -256,7 +258,9 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
ref.refresh(localTracksProvider);
|
ref.refresh(localTracksProvider);
|
||||||
},
|
},
|
||||||
child: InterScrollbar(
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: controller,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
itemCount: filteredTracks.length,
|
itemCount: filteredTracks.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
||||||
|
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
@ -81,6 +82,8 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: playlistsQuery.refresh,
|
onRefresh: playlistsQuery.refresh,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
slivers: [
|
slivers: [
|
||||||
@ -145,6 +148,7 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
topRight: Radius.circular(10),
|
topRight: Radius.circular(10),
|
||||||
);
|
);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final headlineColor = theme.textTheme.headlineSmall?.color;
|
final headlineColor = theme.textTheme.headlineSmall?.color;
|
||||||
|
|
||||||
final filteredTracks = useMemoized(
|
final filteredTracks = useMemoized(
|
||||||
@ -108,8 +109,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
searchText.value = '';
|
searchText.value = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: LayoutBuilder(builder: (context, constraints) {
|
child: Column(
|
||||||
return Column(
|
|
||||||
children: [
|
children: [
|
||||||
if (!floating)
|
if (!floating)
|
||||||
Container(
|
Container(
|
||||||
@ -125,7 +125,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (constraints.mdAndUp || !isSearching.value) ...[
|
if (mediaQuery.mdAndUp || !isSearching.value) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(
|
Text(
|
||||||
context.l10n.tracks_in_queue(tracks.length),
|
context.l10n.tracks_in_queue(tracks.length),
|
||||||
@ -137,7 +137,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
],
|
],
|
||||||
if (constraints.mdAndUp || isSearching.value)
|
if (mediaQuery.mdAndUp || isSearching.value)
|
||||||
TextField(
|
TextField(
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
searchText.value = value;
|
searchText.value = value;
|
||||||
@ -145,7 +145,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: context.l10n.search,
|
hintText: context.l10n.search,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
prefixIcon: constraints.smAndDown
|
prefixIcon: mediaQuery.smAndDown
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.arrow_back_ios_new_outlined,
|
Icons.arrow_back_ios_new_outlined,
|
||||||
@ -162,8 +162,8 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
: const Icon(SpotubeIcons.filter),
|
: const Icon(SpotubeIcons.filter),
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxHeight: 40,
|
maxHeight: 40,
|
||||||
maxWidth: constraints.smAndDown
|
maxWidth: mediaQuery.smAndDown
|
||||||
? constraints.maxWidth - 20
|
? mediaQuery.size.width - 40
|
||||||
: 300,
|
: 300,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -175,14 +175,13 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
isSearching.value = !isSearching.value;
|
isSearching.value = !isSearching.value;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (constraints.mdAndUp || !isSearching.value) ...[
|
if (mediaQuery.mdAndUp || !isSearching.value) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
style: FilledButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
theme.scaffoldBackgroundColor.withOpacity(0.5),
|
theme.scaffoldBackgroundColor.withOpacity(0.5),
|
||||||
foregroundColor:
|
foregroundColor: theme.textTheme.headlineSmall?.color,
|
||||||
theme.textTheme.headlineSmall?.color,
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -203,8 +202,6 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
if (!isSearching.value && searchText.value.isEmpty)
|
if (!isSearching.value && searchText.value.isEmpty)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: InterScrollbar(
|
|
||||||
controller: controller,
|
|
||||||
child: ReorderableListView.builder(
|
child: ReorderableListView.builder(
|
||||||
onReorder: (oldIndex, newIndex) {
|
onReorder: (oldIndex, newIndex) {
|
||||||
playlistNotifier.moveTrack(oldIndex, newIndex);
|
playlistNotifier.moveTrack(oldIndex, newIndex);
|
||||||
@ -234,8 +231,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
leadingActions: [
|
leadingActions: [
|
||||||
ReorderableDragStartListener(
|
ReorderableDragStartListener(
|
||||||
index: i,
|
index: i,
|
||||||
child:
|
child: const Icon(SpotubeIcons.dragHandle),
|
||||||
const Icon(SpotubeIcons.dragHandle),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -243,12 +239,13 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Flexible(
|
Flexible(
|
||||||
child: InterScrollbar(
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: controller,
|
||||||
itemCount: filteredTracks.length,
|
itemCount: filteredTracks.length,
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
final track = filteredTracks.elementAt(i);
|
final track = filteredTracks.elementAt(i);
|
||||||
@ -271,8 +268,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -56,7 +56,9 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
useValueListenable(searchController).text,
|
useValueListenable(searchController).text,
|
||||||
);
|
);
|
||||||
|
|
||||||
final searchRequest = useMemoized<Future<List<SourceInfo>>>(() async {
|
final controller = useScrollController();
|
||||||
|
|
||||||
|
final searchRequest = useMemoized(() async {
|
||||||
if (searchTerm.trim().isEmpty) {
|
if (searchTerm.trim().isEmpty) {
|
||||||
return <SourceInfo>[];
|
return <SourceInfo>[];
|
||||||
}
|
}
|
||||||
@ -219,8 +221,10 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
transitionBuilder: (child, animation) =>
|
transitionBuilder: (child, animation) =>
|
||||||
FadeTransition(opacity: animation, child: child),
|
FadeTransition(opacity: animation, child: child),
|
||||||
child: InterScrollbar(
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: switch (isSearching.value) {
|
child: switch (isSearching.value) {
|
||||||
false => ListView.builder(
|
false => ListView.builder(
|
||||||
|
controller: controller,
|
||||||
itemCount: siblings.length,
|
itemCount: siblings.length,
|
||||||
itemBuilder: (context, index) =>
|
itemBuilder: (context, index) =>
|
||||||
itemBuilder(siblings[index]),
|
itemBuilder(siblings[index]),
|
||||||
@ -238,7 +242,9 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return InterScrollbar(
|
return InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: controller,
|
||||||
itemCount: snapshot.data!.length,
|
itemCount: snapshot.data!.length,
|
||||||
itemBuilder: (context, index) =>
|
itemBuilder: (context, index) =>
|
||||||
itemBuilder(snapshot.data![index]),
|
itemBuilder(snapshot.data![index]),
|
||||||
|
|||||||
@ -49,6 +49,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
final scheme = preferences.accentColorScheme;
|
final scheme = preferences.accentColorScheme;
|
||||||
final active = useState<String>(colorsMap.firstWhere(
|
final active = useState<String>(colorsMap.firstWhere(
|
||||||
(element) {
|
(element) {
|
||||||
@ -57,7 +58,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
|||||||
).name);
|
).name);
|
||||||
|
|
||||||
onOk() {
|
onOk() {
|
||||||
preferences.setAccentColorScheme(
|
preferencesNotifier.setAccentColorScheme(
|
||||||
colorsMap.firstWhere(
|
colorsMap.firstWhere(
|
||||||
(element) {
|
(element) {
|
||||||
return element.name == active.value;
|
return element.name == active.value;
|
||||||
|
|||||||
@ -1,29 +1,16 @@
|
|||||||
|
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
class InterScrollbar extends HookWidget {
|
class InterScrollbar extends HookWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final ScrollController? controller;
|
final ScrollController controller;
|
||||||
final bool? thumbVisibility;
|
|
||||||
final bool? trackVisibility;
|
|
||||||
final double? thickness;
|
|
||||||
final Radius? radius;
|
|
||||||
final bool Function(ScrollNotification)? notificationPredicate;
|
|
||||||
final bool? interactive;
|
|
||||||
final ScrollbarOrientation? scrollbarOrientation;
|
|
||||||
|
|
||||||
const InterScrollbar({
|
const InterScrollbar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.controller,
|
required this.controller,
|
||||||
this.thumbVisibility,
|
|
||||||
this.trackVisibility,
|
|
||||||
this.thickness,
|
|
||||||
this.radius,
|
|
||||||
this.notificationPredicate,
|
|
||||||
this.interactive,
|
|
||||||
this.scrollbarOrientation,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -32,38 +19,9 @@ class InterScrollbar extends HookWidget {
|
|||||||
|
|
||||||
if (DesktopTools.platform.isDesktop) return child;
|
if (DesktopTools.platform.isDesktop) return child;
|
||||||
|
|
||||||
return ScrollbarTheme(
|
return DraggableScrollbar.semicircle(
|
||||||
data: theme.scrollbarTheme.copyWith(
|
|
||||||
crossAxisMargin: 10,
|
|
||||||
minThumbLength: 80,
|
|
||||||
thickness: MaterialStateProperty.resolveWith((states) {
|
|
||||||
if (states.contains(MaterialState.hovered) ||
|
|
||||||
states.contains(MaterialState.dragged) ||
|
|
||||||
states.contains(MaterialState.pressed)) {
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
return 20;
|
|
||||||
}),
|
|
||||||
radius: const Radius.circular(20),
|
|
||||||
thumbColor: MaterialStateProperty.resolveWith((states) {
|
|
||||||
if (states.contains(MaterialState.hovered) ||
|
|
||||||
states.contains(MaterialState.dragged)) {
|
|
||||||
return theme.colorScheme.onSurface.withOpacity(0.5);
|
|
||||||
}
|
|
||||||
return theme.colorScheme.onSurface.withOpacity(0.3);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
child: Scrollbar(
|
|
||||||
controller: controller,
|
controller: controller,
|
||||||
thumbVisibility: thumbVisibility,
|
|
||||||
trackVisibility: trackVisibility,
|
|
||||||
thickness: thickness,
|
|
||||||
radius: radius,
|
|
||||||
notificationPredicate: notificationPredicate,
|
|
||||||
interactive: interactive ?? true,
|
|
||||||
scrollbarOrientation: scrollbarOrientation,
|
|
||||||
child: child,
|
child: child,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,8 +79,6 @@ class GenrePage extends HookConsumerWidget {
|
|||||||
const ShimmerCategories()
|
const ShimmerCategories()
|
||||||
else
|
else
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InterScrollbar(
|
|
||||||
controller: scrollController,
|
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: categories.length,
|
itemCount: categories.length,
|
||||||
@ -102,7 +100,6 @@ class GenrePage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_categories.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_categories.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
@ -47,9 +46,7 @@ class PersonalizedPage extends HookConsumerWidget {
|
|||||||
[newReleases.pages],
|
[newReleases.pages],
|
||||||
);
|
);
|
||||||
|
|
||||||
return InterScrollbar(
|
return ListView(
|
||||||
controller: controller,
|
|
||||||
child: ListView(
|
|
||||||
controller: controller,
|
controller: controller,
|
||||||
children: [
|
children: [
|
||||||
if (!featuredPlaylistsQuery.hasPageData &&
|
if (!featuredPlaylistsQuery.hasPageData &&
|
||||||
@ -64,7 +61,8 @@ class PersonalizedPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (auth != null &&
|
if (auth != null &&
|
||||||
newReleases.hasPageData &&
|
newReleases.hasPageData &&
|
||||||
userArtistsQuery.hasData)
|
userArtistsQuery.hasData &&
|
||||||
|
!newReleases.isLoadingNextPage)
|
||||||
HorizontalPlaybuttonCardView<Album>(
|
HorizontalPlaybuttonCardView<Album>(
|
||||||
items: albums,
|
items: albums,
|
||||||
title: Text(context.l10n.new_releases),
|
title: Text(context.l10n.new_releases),
|
||||||
@ -87,7 +85,6 @@ class PersonalizedPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,8 +71,13 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
searchTerm.isNotEmpty;
|
searchTerm.isNotEmpty;
|
||||||
|
|
||||||
final resultWidget = HookBuilder(
|
final resultWidget = HookBuilder(
|
||||||
builder: (context) => InterScrollbar(
|
builder: (context) {
|
||||||
|
final controller = useScrollController();
|
||||||
|
|
||||||
|
return InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
controller: controller,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@ -90,7 +95,8 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class BlackListPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
|
final controller = useScrollController();
|
||||||
final blacklist = ref.watch(BlackListNotifier.provider);
|
final blacklist = ref.watch(BlackListNotifier.provider);
|
||||||
final searchText = useState("");
|
final searchText = useState("");
|
||||||
|
|
||||||
@ -58,7 +59,9 @@ class BlackListPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
InterScrollbar(
|
InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: controller,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: filteredBlacklist.length,
|
itemCount: filteredBlacklist.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|||||||
@ -52,6 +52,7 @@ class LogsPage extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final controller = useScrollController();
|
||||||
final logs = useState<List<({DateTime? date, String body})>>([]);
|
final logs = useState<List<({DateTime? date, String body})>>([]);
|
||||||
final rawLogs = useRef<String>("");
|
final rawLogs = useRef<String>("");
|
||||||
final path = useRef<File?>(null);
|
final path = useRef<File?>(null);
|
||||||
@ -93,7 +94,9 @@ class LogsPage extends HookWidget {
|
|||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: InterScrollbar(
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: controller,
|
||||||
itemCount: logs.value.length,
|
itemCount: logs.value.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final log = logs.value[index];
|
final log = logs.value[index];
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class SettingsAboutSection extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
|
|
||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
heading: context.l10n.about,
|
heading: context.l10n.about,
|
||||||
@ -68,7 +69,7 @@ class SettingsAboutSection extends HookConsumerWidget {
|
|||||||
secondary: const Icon(SpotubeIcons.update),
|
secondary: const Icon(SpotubeIcons.update),
|
||||||
title: Text(context.l10n.check_for_updates),
|
title: Text(context.l10n.check_for_updates),
|
||||||
value: preferences.checkUpdate,
|
value: preferences.checkUpdate,
|
||||||
onChanged: (checked) => preferences.setCheckUpdate(checked),
|
onChanged: (checked) => preferencesNotifier.setCheckUpdate(checked),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.info),
|
leading: const Icon(SpotubeIcons.info),
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
final pickColorScheme = useCallback(() {
|
final pickColorScheme = useCallback(() {
|
||||||
return () => showDialog(
|
return () => showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -33,7 +34,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
|||||||
value: preferences.layoutMode,
|
value: preferences.layoutMode,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setLayoutMode(value);
|
preferencesNotifier.setLayoutMode(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
@ -71,7 +72,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setThemeMode(value);
|
preferencesNotifier.setThemeMode(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -80,7 +81,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
|||||||
title: Text(context.l10n.use_amoled_mode),
|
title: Text(context.l10n.use_amoled_mode),
|
||||||
subtitle: Text(context.l10n.pitch_dark_theme),
|
subtitle: Text(context.l10n.pitch_dark_theme),
|
||||||
value: preferences.amoledDarkTheme,
|
value: preferences.amoledDarkTheme,
|
||||||
onChanged: preferences.setAmoledDarkTheme,
|
onChanged: preferencesNotifier.setAmoledDarkTheme,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.palette),
|
leading: const Icon(SpotubeIcons.palette),
|
||||||
@ -101,7 +102,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
|||||||
title: Text(context.l10n.sync_album_color),
|
title: Text(context.l10n.sync_album_color),
|
||||||
subtitle: Text(context.l10n.sync_album_color_description),
|
subtitle: Text(context.l10n.sync_album_color_description),
|
||||||
value: preferences.albumColorSync,
|
value: preferences.albumColorSync,
|
||||||
onChanged: preferences.setAlbumColorSync,
|
onChanged: preferencesNotifier.setAlbumColorSync,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ class SettingsDesktopSection extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
|
|
||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
heading: context.l10n.desktop,
|
heading: context.l10n.desktop,
|
||||||
@ -32,7 +33,7 @@ class SettingsDesktopSection extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setCloseBehavior(value);
|
preferencesNotifier.setCloseBehavior(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -40,13 +41,13 @@ class SettingsDesktopSection extends HookConsumerWidget {
|
|||||||
secondary: const Icon(SpotubeIcons.tray),
|
secondary: const Icon(SpotubeIcons.tray),
|
||||||
title: Text(context.l10n.show_tray_icon),
|
title: Text(context.l10n.show_tray_icon),
|
||||||
value: preferences.showSystemTrayIcon,
|
value: preferences.showSystemTrayIcon,
|
||||||
onChanged: preferences.setShowSystemTrayIcon,
|
onChanged: preferencesNotifier.setShowSystemTrayIcon,
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
secondary: const Icon(SpotubeIcons.window),
|
secondary: const Icon(SpotubeIcons.window),
|
||||||
title: Text(context.l10n.use_system_title_bar),
|
title: Text(context.l10n.use_system_title_bar),
|
||||||
value: preferences.systemTitleBar,
|
value: preferences.systemTitleBar,
|
||||||
onChanged: preferences.setSystemTitleBar,
|
onChanged: preferencesNotifier.setSystemTitleBar,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class SettingsDownloadsSection extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
|
||||||
final pickDownloadLocation = useCallback(() async {
|
final pickDownloadLocation = useCallback(() async {
|
||||||
@ -22,13 +23,13 @@ class SettingsDownloadsSection extends HookConsumerWidget {
|
|||||||
initialDirectory: preferences.downloadLocation,
|
initialDirectory: preferences.downloadLocation,
|
||||||
);
|
);
|
||||||
if (dirStr == null) return;
|
if (dirStr == null) return;
|
||||||
preferences.setDownloadLocation(dirStr);
|
preferencesNotifier.setDownloadLocation(dirStr);
|
||||||
} else {
|
} else {
|
||||||
String? dirStr = await getDirectoryPath(
|
String? dirStr = await getDirectoryPath(
|
||||||
initialDirectory: preferences.downloadLocation,
|
initialDirectory: preferences.downloadLocation,
|
||||||
);
|
);
|
||||||
if (dirStr == null) return;
|
if (dirStr == null) return;
|
||||||
preferences.setDownloadLocation(dirStr);
|
preferencesNotifier.setDownloadLocation(dirStr);
|
||||||
}
|
}
|
||||||
}, [preferences.downloadLocation]);
|
}, [preferences.downloadLocation]);
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
|
||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
@ -26,7 +27,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
|
|||||||
value: preferences.locale,
|
value: preferences.locale,
|
||||||
onChanged: (locale) {
|
onChanged: (locale) {
|
||||||
if (locale == null) return;
|
if (locale == null) return;
|
||||||
preferences.setLocale(locale);
|
preferencesNotifier.setLocale(locale);
|
||||||
},
|
},
|
||||||
title: Text(context.l10n.language),
|
title: Text(context.l10n.language),
|
||||||
secondary: const Icon(SpotubeIcons.language),
|
secondary: const Icon(SpotubeIcons.language),
|
||||||
@ -57,7 +58,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
|
|||||||
value: preferences.recommendationMarket,
|
value: preferences.recommendationMarket,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setRecommendationMarket(value);
|
preferencesNotifier.setRecommendationMarket(value);
|
||||||
},
|
},
|
||||||
options: spotifyMarkets
|
options: spotifyMarkets
|
||||||
.map(
|
.map(
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
@ -43,7 +44,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setAudioQuality(value);
|
preferencesNotifier.setAudioQuality(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -59,7 +60,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setAudioSource(value);
|
preferencesNotifier.setAudioSource(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
@ -117,7 +118,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setPipedInstance(value);
|
preferencesNotifier.setPipedInstance(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -145,7 +146,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setSearchMode(value);
|
preferencesNotifier.setSearchMode(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -159,7 +160,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
title: Text(context.l10n.skip_non_music),
|
title: Text(context.l10n.skip_non_music),
|
||||||
value: preferences.skipNonMusic,
|
value: preferences.skipNonMusic,
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
preferences.setSkipNonMusic(state);
|
preferencesNotifier.setSkipNonMusic(state);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
@ -177,7 +178,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
secondary: const Icon(SpotubeIcons.normalize),
|
secondary: const Icon(SpotubeIcons.normalize),
|
||||||
title: Text(context.l10n.normalize_audio),
|
title: Text(context.l10n.normalize_audio),
|
||||||
value: preferences.normalizeAudio,
|
value: preferences.normalizeAudio,
|
||||||
onChanged: preferences.setNormalizeAudio,
|
onChanged: preferencesNotifier.setNormalizeAudio,
|
||||||
),
|
),
|
||||||
if (preferences.audioSource != AudioSource.jiosaavn)
|
if (preferences.audioSource != AudioSource.jiosaavn)
|
||||||
AdaptiveSelectTile<SourceCodecs>(
|
AdaptiveSelectTile<SourceCodecs>(
|
||||||
@ -196,7 +197,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setStreamMusicCodec(value);
|
preferencesNotifier.setStreamMusicCodec(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (preferences.audioSource != AudioSource.jiosaavn)
|
if (preferences.audioSource != AudioSource.jiosaavn)
|
||||||
@ -216,7 +217,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setDownloadMusicCodec(value);
|
preferencesNotifier.setDownloadMusicCodec(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
@ -20,7 +21,8 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final controller = useScrollController();
|
||||||
|
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
@ -36,7 +38,9 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 1366),
|
constraints: const BoxConstraints(maxWidth: 1366),
|
||||||
child: InterScrollbar(
|
child: InterScrollbar(
|
||||||
|
controller: controller,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
controller: controller,
|
||||||
children: [
|
children: [
|
||||||
const SettingsAccountSection(),
|
const SettingsAccountSection(),
|
||||||
const SettingsLanguageRegionSection(),
|
const SettingsLanguageRegionSection(),
|
||||||
@ -49,7 +53,7 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
const SettingsAboutSection(),
|
const SettingsAboutSection(),
|
||||||
Center(
|
Center(
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: preferences.reset,
|
onPressed: preferencesNotifier.reset,
|
||||||
child: Text(context.l10n.restore_defaults),
|
child: Text(context.l10n.restore_defaults),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -217,6 +217,26 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
|||||||
Catcher2.reportCheckedError(e, stackTrace);
|
Catcher2.reportCheckedError(e, stackTrace);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
String? lastScrobbled;
|
||||||
|
audioPlayer.positionStream.listen((position) {
|
||||||
|
try {
|
||||||
|
final uid = state.activeTrack is LocalTrack
|
||||||
|
? (state.activeTrack as LocalTrack).path
|
||||||
|
: state.activeTrack?.id;
|
||||||
|
|
||||||
|
if (state.activeTrack == null ||
|
||||||
|
lastScrobbled == uid ||
|
||||||
|
position.inSeconds < 30) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrobbler.scrobble(state.activeTrack!);
|
||||||
|
lastScrobbled = uid;
|
||||||
|
} catch (e, stack) {
|
||||||
|
Catcher2.reportCheckedError(e, stack);
|
||||||
|
}
|
||||||
|
});
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,30 +651,12 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
set state(state) {
|
set state(state) {
|
||||||
final hasActiveTrackChanged = super.state.activeTrack is SourcedTrack
|
|
||||||
? state.activeTrack?.id != super.state.activeTrack?.id
|
|
||||||
: super.state.activeTrack is LocalTrack &&
|
|
||||||
state.activeTrack is LocalTrack
|
|
||||||
? (super.state.activeTrack as LocalTrack).path !=
|
|
||||||
(state.activeTrack as LocalTrack).path
|
|
||||||
: super.state.activeTrack?.id != state.activeTrack?.id;
|
|
||||||
|
|
||||||
final oldTrack = super.state.activeTrack;
|
|
||||||
|
|
||||||
super.state = state;
|
super.state = state;
|
||||||
if (state.tracks.isEmpty && ref.read(paletteProvider) != null) {
|
if (state.tracks.isEmpty && ref.read(paletteProvider) != null) {
|
||||||
ref.read(paletteProvider.notifier).state = null;
|
ref.read(paletteProvider.notifier).state = null;
|
||||||
} else {
|
} else {
|
||||||
updatePalette();
|
updatePalette();
|
||||||
}
|
}
|
||||||
audioPlayer.position.then((position) {
|
|
||||||
final isMoreThan30secs = position != null &&
|
|
||||||
(position == Duration.zero || position.inSeconds > 30);
|
|
||||||
|
|
||||||
if (hasActiveTrackChanged && oldTrack != null && isMoreThan30secs) {
|
|
||||||
scrobbler.scrobble(oldTrack);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
@ -14,7 +13,7 @@ import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
|||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/sourced_track/enums.dart';
|
import 'package:spotube/services/sourced_track/enums.dart';
|
||||||
|
|
||||||
import 'package:spotube/utils/persisted_change_notifier.dart';
|
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
@ -64,220 +63,103 @@ enum SearchMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserPreferences extends PersistedChangeNotifier {
|
class UserPreferences {
|
||||||
SourceQualities audioQuality;
|
final SourceQualities audioQuality;
|
||||||
bool albumColorSync;
|
final bool albumColorSync;
|
||||||
bool amoledDarkTheme;
|
final bool amoledDarkTheme;
|
||||||
bool checkUpdate;
|
final bool checkUpdate;
|
||||||
bool normalizeAudio;
|
final bool normalizeAudio;
|
||||||
bool showSystemTrayIcon;
|
final bool showSystemTrayIcon;
|
||||||
bool skipNonMusic;
|
final bool skipNonMusic;
|
||||||
bool systemTitleBar;
|
final bool systemTitleBar;
|
||||||
CloseBehavior closeBehavior;
|
final CloseBehavior closeBehavior;
|
||||||
late SpotubeColor accentColorScheme;
|
final SpotubeColor accentColorScheme;
|
||||||
LayoutMode layoutMode;
|
final LayoutMode layoutMode;
|
||||||
Locale locale;
|
final Locale locale;
|
||||||
Market recommendationMarket;
|
final Market recommendationMarket;
|
||||||
SearchMode searchMode;
|
final SearchMode searchMode;
|
||||||
String downloadLocation;
|
String downloadLocation;
|
||||||
String pipedInstance;
|
final String pipedInstance;
|
||||||
ThemeMode themeMode;
|
final ThemeMode themeMode;
|
||||||
AudioSource audioSource;
|
final AudioSource audioSource;
|
||||||
SourceCodecs streamMusicCodec;
|
final SourceCodecs streamMusicCodec;
|
||||||
SourceCodecs downloadMusicCodec;
|
final SourceCodecs downloadMusicCodec;
|
||||||
|
|
||||||
final Ref ref;
|
UserPreferences({
|
||||||
|
required SourceQualities? audioQuality,
|
||||||
UserPreferences(
|
required bool? albumColorSync,
|
||||||
this.ref, {
|
required bool? amoledDarkTheme,
|
||||||
this.recommendationMarket = Market.US,
|
required bool? checkUpdate,
|
||||||
this.themeMode = ThemeMode.system,
|
required bool? normalizeAudio,
|
||||||
this.layoutMode = LayoutMode.adaptive,
|
required bool? showSystemTrayIcon,
|
||||||
this.albumColorSync = true,
|
required bool? skipNonMusic,
|
||||||
this.checkUpdate = true,
|
required bool? systemTitleBar,
|
||||||
this.audioQuality = SourceQualities.high,
|
required CloseBehavior? closeBehavior,
|
||||||
this.downloadLocation = "",
|
required SpotubeColor? accentColorScheme,
|
||||||
this.closeBehavior = CloseBehavior.close,
|
required LayoutMode? layoutMode,
|
||||||
this.showSystemTrayIcon = true,
|
required Locale? locale,
|
||||||
this.locale = const Locale("system", "system"),
|
required Market? recommendationMarket,
|
||||||
this.pipedInstance = "https://pipedapi.kavin.rocks",
|
required SearchMode? searchMode,
|
||||||
this.searchMode = SearchMode.youtube,
|
required String? downloadLocation,
|
||||||
this.skipNonMusic = true,
|
required String? pipedInstance,
|
||||||
this.audioSource = AudioSource.youtube,
|
required ThemeMode? themeMode,
|
||||||
this.systemTitleBar = false,
|
required AudioSource? audioSource,
|
||||||
this.amoledDarkTheme = false,
|
required SourceCodecs? streamMusicCodec,
|
||||||
this.normalizeAudio = true,
|
required SourceCodecs? downloadMusicCodec,
|
||||||
this.streamMusicCodec = SourceCodecs.weba,
|
}) : accentColorScheme =
|
||||||
this.downloadMusicCodec = SourceCodecs.m4a,
|
accentColorScheme ?? const SpotubeColor(0xFF2196F3, name: "Blue"),
|
||||||
SpotubeColor? accentColorScheme,
|
albumColorSync = albumColorSync ?? true,
|
||||||
}) : super() {
|
amoledDarkTheme = amoledDarkTheme ?? false,
|
||||||
this.accentColorScheme =
|
audioQuality = audioQuality ?? SourceQualities.high,
|
||||||
accentColorScheme ?? SpotubeColor(Colors.blue.value, name: "Blue");
|
checkUpdate = checkUpdate ?? true,
|
||||||
if (downloadLocation.isEmpty && !kIsWeb) {
|
closeBehavior = closeBehavior ?? CloseBehavior.close,
|
||||||
|
downloadLocation = downloadLocation ?? "",
|
||||||
|
downloadMusicCodec = downloadMusicCodec ?? SourceCodecs.m4a,
|
||||||
|
layoutMode = layoutMode ?? LayoutMode.adaptive,
|
||||||
|
locale = locale ?? const Locale("system", "system"),
|
||||||
|
normalizeAudio = normalizeAudio ?? true,
|
||||||
|
pipedInstance = pipedInstance ?? "https://pipedapi.kavin.rocks",
|
||||||
|
recommendationMarket = recommendationMarket ?? Market.US,
|
||||||
|
searchMode = searchMode ?? SearchMode.youtube,
|
||||||
|
showSystemTrayIcon = showSystemTrayIcon ?? true,
|
||||||
|
skipNonMusic = skipNonMusic ?? true,
|
||||||
|
streamMusicCodec = streamMusicCodec ?? SourceCodecs.weba,
|
||||||
|
systemTitleBar = systemTitleBar ?? false,
|
||||||
|
themeMode = themeMode ?? ThemeMode.system,
|
||||||
|
audioSource = audioSource ?? AudioSource.youtube {
|
||||||
|
if (downloadLocation == null) {
|
||||||
_getDefaultDownloadDirectory().then(
|
_getDefaultDownloadDirectory().then(
|
||||||
(value) {
|
(value) => this.downloadLocation = value,
|
||||||
downloadLocation = value;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
factory UserPreferences.withDefaults() {
|
||||||
setRecommendationMarket(Market.US);
|
return UserPreferences(
|
||||||
setThemeMode(ThemeMode.system);
|
audioQuality: null,
|
||||||
setLayoutMode(LayoutMode.adaptive);
|
albumColorSync: null,
|
||||||
setAlbumColorSync(true);
|
amoledDarkTheme: null,
|
||||||
setCheckUpdate(true);
|
checkUpdate: null,
|
||||||
setAudioQuality(SourceQualities.high);
|
normalizeAudio: null,
|
||||||
setDownloadLocation("");
|
showSystemTrayIcon: null,
|
||||||
setCloseBehavior(CloseBehavior.close);
|
skipNonMusic: null,
|
||||||
setShowSystemTrayIcon(true);
|
systemTitleBar: null,
|
||||||
setLocale(const Locale("system", "system"));
|
closeBehavior: null,
|
||||||
setPipedInstance("https://pipedapi.kavin.rocks");
|
accentColorScheme: null,
|
||||||
setSearchMode(SearchMode.youtube);
|
layoutMode: null,
|
||||||
setSkipNonMusic(true);
|
locale: null,
|
||||||
setAudioSource(AudioSource.youtube);
|
recommendationMarket: null,
|
||||||
setSystemTitleBar(false);
|
searchMode: null,
|
||||||
setAmoledDarkTheme(false);
|
downloadLocation: null,
|
||||||
setNormalizeAudio(true);
|
pipedInstance: null,
|
||||||
setAccentColorScheme(SpotubeColor(Colors.blue.value, name: "Blue"));
|
themeMode: null,
|
||||||
setStreamMusicCodec(SourceCodecs.weba);
|
audioSource: null,
|
||||||
setDownloadMusicCodec(SourceCodecs.m4a);
|
streamMusicCodec: null,
|
||||||
}
|
downloadMusicCodec: null,
|
||||||
|
|
||||||
void setStreamMusicCodec(SourceCodecs codec) {
|
|
||||||
streamMusicCodec = codec;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDownloadMusicCodec(SourceCodecs codec) {
|
|
||||||
downloadMusicCodec = codec;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setThemeMode(ThemeMode mode) {
|
|
||||||
themeMode = mode;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRecommendationMarket(Market country) {
|
|
||||||
recommendationMarket = country;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAccentColorScheme(SpotubeColor color) {
|
|
||||||
accentColorScheme = color;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAlbumColorSync(bool sync) {
|
|
||||||
albumColorSync = sync;
|
|
||||||
if (!sync) {
|
|
||||||
ref.read(paletteProvider.notifier).state = null;
|
|
||||||
} else {
|
|
||||||
ref.read(ProxyPlaylistNotifier.notifier).updatePalette();
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCheckUpdate(bool check) {
|
|
||||||
checkUpdate = check;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAudioQuality(SourceQualities quality) {
|
|
||||||
audioQuality = quality;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDownloadLocation(String downloadDir) {
|
|
||||||
if (downloadDir.isEmpty) return;
|
|
||||||
downloadLocation = downloadDir;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLayoutMode(LayoutMode mode) {
|
|
||||||
layoutMode = mode;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCloseBehavior(CloseBehavior behavior) {
|
|
||||||
closeBehavior = behavior;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setShowSystemTrayIcon(bool show) {
|
|
||||||
showSystemTrayIcon = show;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLocale(Locale locale) {
|
|
||||||
this.locale = locale;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPipedInstance(String instance) {
|
|
||||||
pipedInstance = instance;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSearchMode(SearchMode mode) {
|
|
||||||
searchMode = mode;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSkipNonMusic(bool skip) {
|
|
||||||
skipNonMusic = skip;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAudioSource(AudioSource type) {
|
|
||||||
audioSource = type;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSystemTitleBar(bool isSystemTitleBar) {
|
|
||||||
systemTitleBar = isSystemTitleBar;
|
|
||||||
if (DesktopTools.platform.isDesktop) {
|
|
||||||
DesktopTools.window.setTitleBarStyle(
|
|
||||||
systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAmoledDarkTheme(bool isAmoled) {
|
static Future<String> _getDefaultDownloadDirectory() async {
|
||||||
amoledDarkTheme = isAmoled;
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNormalizeAudio(bool normalize) {
|
|
||||||
normalizeAudio = normalize;
|
|
||||||
audioPlayer.setAudioNormalization(normalize);
|
|
||||||
notifyListeners();
|
|
||||||
updatePersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> _getDefaultDownloadDirectory() async {
|
|
||||||
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
|
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
|
||||||
|
|
||||||
if (kIsMacOS) {
|
if (kIsMacOS) {
|
||||||
@ -289,102 +171,71 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static Future<UserPreferences> fromJson(Map<String, dynamic> json) async {
|
||||||
FutureOr<void> loadFromLocal(Map<String, dynamic> map) async {
|
final systemTitleBar = json["systemTitleBar"] ?? false;
|
||||||
recommendationMarket = Market.values.firstWhere(
|
if (DesktopTools.platform.isDesktop) {
|
||||||
(market) =>
|
await DesktopTools.window.setTitleBarStyle(
|
||||||
market.name == (map["recommendationMarket"] ?? recommendationMarket),
|
systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
|
||||||
orElse: () => Market.US,
|
|
||||||
);
|
);
|
||||||
checkUpdate = map["checkUpdate"] ?? checkUpdate;
|
|
||||||
|
|
||||||
themeMode = ThemeMode.values[map["themeMode"] ?? 0];
|
|
||||||
accentColorScheme = map["accentColorScheme"] != null
|
|
||||||
? SpotubeColor.fromString(map["accentColorScheme"])
|
|
||||||
: accentColorScheme;
|
|
||||||
albumColorSync = map["albumColorSync"] ?? albumColorSync;
|
|
||||||
audioQuality = map["audioQuality"] != null
|
|
||||||
? SourceQualities.values[map["audioQuality"]]
|
|
||||||
: audioQuality;
|
|
||||||
|
|
||||||
if (!kIsWeb) {
|
|
||||||
downloadLocation =
|
|
||||||
map["downloadLocation"] ?? await _getDefaultDownloadDirectory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutMode = LayoutMode.values.firstWhere(
|
final normalizeAudio = json["normalizeAudio"] ?? true;
|
||||||
(mode) => mode.name == map["layoutMode"],
|
|
||||||
orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact,
|
|
||||||
);
|
|
||||||
|
|
||||||
closeBehavior = map["closeBehavior"] != null
|
|
||||||
? CloseBehavior.values[map["closeBehavior"]]
|
|
||||||
: closeBehavior;
|
|
||||||
|
|
||||||
showSystemTrayIcon = map["showSystemTrayIcon"] ?? showSystemTrayIcon;
|
|
||||||
|
|
||||||
final localeMap = map["locale"] != null ? jsonDecode(map["locale"]) : null;
|
|
||||||
locale =
|
|
||||||
localeMap != null ? Locale(localeMap?["lc"], localeMap?["cc"]) : locale;
|
|
||||||
|
|
||||||
pipedInstance = map["pipedInstance"] ?? pipedInstance;
|
|
||||||
|
|
||||||
searchMode = SearchMode.values.firstWhere(
|
|
||||||
(mode) => mode.name == map["searchMode"],
|
|
||||||
orElse: () => SearchMode.youtube,
|
|
||||||
);
|
|
||||||
|
|
||||||
skipNonMusic = map["skipNonMusic"] ?? skipNonMusic;
|
|
||||||
|
|
||||||
audioSource = AudioSource.values.firstWhere(
|
|
||||||
(type) => type.name == map["youtubeApiType"],
|
|
||||||
orElse: () => AudioSource.youtube,
|
|
||||||
);
|
|
||||||
|
|
||||||
systemTitleBar = map["systemTitleBar"] ?? systemTitleBar;
|
|
||||||
// updates the title bar
|
|
||||||
setSystemTitleBar(systemTitleBar);
|
|
||||||
|
|
||||||
amoledDarkTheme = map["amoledDarkTheme"] ?? amoledDarkTheme;
|
|
||||||
|
|
||||||
normalizeAudio = map["normalizeAudio"] ?? normalizeAudio;
|
|
||||||
audioPlayer.setAudioNormalization(normalizeAudio);
|
audioPlayer.setAudioNormalization(normalizeAudio);
|
||||||
|
|
||||||
streamMusicCodec = SourceCodecs.values.firstWhere(
|
final Map<String, dynamic>? localeMap =
|
||||||
(codec) => codec.name == map["streamMusicCodec"],
|
json["locale"] == null ? null : jsonDecode(json["locale"]);
|
||||||
orElse: () => SourceCodecs.weba,
|
|
||||||
);
|
|
||||||
|
|
||||||
downloadMusicCodec = SourceCodecs.values.firstWhere(
|
return UserPreferences(
|
||||||
(codec) => codec.name == map["downloadMusicCodec"],
|
accentColorScheme: json["accentColorScheme"] == null
|
||||||
orElse: () => SourceCodecs.m4a,
|
? null
|
||||||
|
: SpotubeColor.fromString(json["accentColorScheme"]),
|
||||||
|
albumColorSync: json["albumColorSync"],
|
||||||
|
amoledDarkTheme: json["amoledDarkTheme"],
|
||||||
|
audioQuality: SourceQualities.values[json["audioQuality"]],
|
||||||
|
checkUpdate: json["checkUpdate"],
|
||||||
|
closeBehavior: CloseBehavior.values[json["closeBehavior"]],
|
||||||
|
downloadLocation:
|
||||||
|
json["downloadLocation"] ?? await _getDefaultDownloadDirectory(),
|
||||||
|
downloadMusicCodec: SourceCodecs.values[json["downloadMusicCodec"]],
|
||||||
|
layoutMode: LayoutMode.values[json["layoutMode"]],
|
||||||
|
locale:
|
||||||
|
localeMap == null ? null : Locale(localeMap["lc"], localeMap["cc"]),
|
||||||
|
normalizeAudio: json["normalizeAudio"],
|
||||||
|
pipedInstance: json["pipedInstance"],
|
||||||
|
recommendationMarket: Market.values[json["recommendationMarket"]],
|
||||||
|
searchMode: SearchMode.values[json["searchMode"]],
|
||||||
|
showSystemTrayIcon: json["showSystemTrayIcon"],
|
||||||
|
skipNonMusic: json["skipNonMusic"],
|
||||||
|
streamMusicCodec: SourceCodecs.values[json["streamMusicCodec"]],
|
||||||
|
systemTitleBar: json["systemTitleBar"],
|
||||||
|
themeMode: ThemeMode.values[json["themeMode"]],
|
||||||
|
audioSource: AudioSource.values[json["audioSource"]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Map<String, dynamic> toJson() {
|
||||||
FutureOr<Map<String, dynamic>> toMap() {
|
|
||||||
return {
|
return {
|
||||||
"recommendationMarket": recommendationMarket.name,
|
"recommendationMarket": recommendationMarket.index,
|
||||||
"themeMode": themeMode.index,
|
"themeMode": themeMode.index,
|
||||||
"accentColorScheme": accentColorScheme.toString(),
|
"accentColorScheme": accentColorScheme.toString(),
|
||||||
"albumColorSync": albumColorSync,
|
"albumColorSync": albumColorSync,
|
||||||
"checkUpdate": checkUpdate,
|
"checkUpdate": checkUpdate,
|
||||||
"audioQuality": audioQuality.index,
|
"audioQuality": audioQuality.index,
|
||||||
"downloadLocation": downloadLocation,
|
"downloadLocation": downloadLocation,
|
||||||
"layoutMode": layoutMode.name,
|
"layoutMode": layoutMode.index,
|
||||||
"closeBehavior": closeBehavior.index,
|
"closeBehavior": closeBehavior.index,
|
||||||
"showSystemTrayIcon": showSystemTrayIcon,
|
"showSystemTrayIcon": showSystemTrayIcon,
|
||||||
"locale":
|
"locale":
|
||||||
jsonEncode({"lc": locale.languageCode, "cc": locale.countryCode}),
|
jsonEncode({"lc": locale.languageCode, "cc": locale.countryCode}),
|
||||||
"pipedInstance": pipedInstance,
|
"pipedInstance": pipedInstance,
|
||||||
"searchMode": searchMode.name,
|
"searchMode": searchMode.index,
|
||||||
"skipNonMusic": skipNonMusic,
|
"skipNonMusic": skipNonMusic,
|
||||||
"youtubeApiType": audioSource.name,
|
"audioSource": audioSource.index,
|
||||||
'systemTitleBar': systemTitleBar,
|
'systemTitleBar': systemTitleBar,
|
||||||
"amoledDarkTheme": amoledDarkTheme,
|
"amoledDarkTheme": amoledDarkTheme,
|
||||||
"normalizeAudio": normalizeAudio,
|
"normalizeAudio": normalizeAudio,
|
||||||
"streamMusicCodec": streamMusicCodec.name,
|
"streamMusicCodec": streamMusicCodec.index,
|
||||||
"downloadMusicCodec": downloadMusicCodec.name,
|
"downloadMusicCodec": downloadMusicCodec.index,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,12 +253,16 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
String? pipedInstance,
|
String? pipedInstance,
|
||||||
SearchMode? searchMode,
|
SearchMode? searchMode,
|
||||||
bool? skipNonMusic,
|
bool? skipNonMusic,
|
||||||
AudioSource? youtubeApiType,
|
AudioSource? audioSource,
|
||||||
Market? recommendationMarket,
|
Market? recommendationMarket,
|
||||||
bool? saveTrackLyrics,
|
bool? saveTrackLyrics,
|
||||||
|
bool? amoledDarkTheme,
|
||||||
|
bool? normalizeAudio,
|
||||||
|
SourceCodecs? downloadMusicCodec,
|
||||||
|
SourceCodecs? streamMusicCodec,
|
||||||
|
bool? systemTitleBar,
|
||||||
}) {
|
}) {
|
||||||
return UserPreferences(
|
return UserPreferences(
|
||||||
ref,
|
|
||||||
themeMode: themeMode ?? this.themeMode,
|
themeMode: themeMode ?? this.themeMode,
|
||||||
accentColorScheme: accentColorScheme ?? this.accentColorScheme,
|
accentColorScheme: accentColorScheme ?? this.accentColorScheme,
|
||||||
albumColorSync: albumColorSync ?? this.albumColorSync,
|
albumColorSync: albumColorSync ?? this.albumColorSync,
|
||||||
@ -421,12 +276,132 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
pipedInstance: pipedInstance ?? this.pipedInstance,
|
pipedInstance: pipedInstance ?? this.pipedInstance,
|
||||||
searchMode: searchMode ?? this.searchMode,
|
searchMode: searchMode ?? this.searchMode,
|
||||||
skipNonMusic: skipNonMusic ?? this.skipNonMusic,
|
skipNonMusic: skipNonMusic ?? this.skipNonMusic,
|
||||||
audioSource: youtubeApiType ?? this.audioSource,
|
audioSource: audioSource ?? this.audioSource,
|
||||||
recommendationMarket: recommendationMarket ?? this.recommendationMarket,
|
recommendationMarket: recommendationMarket ?? this.recommendationMarket,
|
||||||
|
amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme,
|
||||||
|
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
||||||
|
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
||||||
|
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
||||||
|
systemTitleBar: systemTitleBar ?? this.systemTitleBar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final userPreferencesProvider = ChangeNotifierProvider(
|
class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
|
||||||
(ref) => UserPreferences(ref),
|
final Ref ref;
|
||||||
|
|
||||||
|
UserPreferencesNotifier(this.ref)
|
||||||
|
: super(UserPreferences.withDefaults(), "preferences");
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
state = UserPreferences.withDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStreamMusicCodec(SourceCodecs codec) {
|
||||||
|
state = state.copyWith(streamMusicCodec: codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDownloadMusicCodec(SourceCodecs codec) {
|
||||||
|
state = state.copyWith(downloadMusicCodec: codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setThemeMode(ThemeMode mode) {
|
||||||
|
state = state.copyWith(themeMode: mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRecommendationMarket(Market country) {
|
||||||
|
state = state.copyWith(recommendationMarket: country);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccentColorScheme(SpotubeColor color) {
|
||||||
|
state = state.copyWith(accentColorScheme: color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAlbumColorSync(bool sync) {
|
||||||
|
state = state.copyWith(albumColorSync: sync);
|
||||||
|
|
||||||
|
if (!sync) {
|
||||||
|
ref.read(paletteProvider.notifier).state = null;
|
||||||
|
} else {
|
||||||
|
ref.read(ProxyPlaylistNotifier.notifier).updatePalette();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCheckUpdate(bool check) {
|
||||||
|
state = state.copyWith(checkUpdate: check);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAudioQuality(SourceQualities quality) {
|
||||||
|
state = state.copyWith(audioQuality: quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDownloadLocation(String downloadDir) {
|
||||||
|
if (downloadDir.isEmpty) return;
|
||||||
|
state = state.copyWith(downloadLocation: downloadDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLayoutMode(LayoutMode mode) {
|
||||||
|
state = state.copyWith(layoutMode: mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCloseBehavior(CloseBehavior behavior) {
|
||||||
|
state = state.copyWith(closeBehavior: behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setShowSystemTrayIcon(bool show) {
|
||||||
|
state = state.copyWith(showSystemTrayIcon: show);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLocale(Locale locale) {
|
||||||
|
state = state.copyWith(locale: locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPipedInstance(String instance) {
|
||||||
|
state = state.copyWith(pipedInstance: instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSearchMode(SearchMode mode) {
|
||||||
|
state = state.copyWith(searchMode: mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSkipNonMusic(bool skip) {
|
||||||
|
state = state.copyWith(skipNonMusic: skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAudioSource(AudioSource type) {
|
||||||
|
state = state.copyWith(audioSource: type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSystemTitleBar(bool isSystemTitleBar) {
|
||||||
|
state = state.copyWith(systemTitleBar: isSystemTitleBar);
|
||||||
|
if (DesktopTools.platform.isDesktop) {
|
||||||
|
DesktopTools.window.setTitleBarStyle(
|
||||||
|
isSystemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAmoledDarkTheme(bool isAmoled) {
|
||||||
|
state = state.copyWith(amoledDarkTheme: isAmoled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNormalizeAudio(bool normalize) {
|
||||||
|
state = state.copyWith(normalizeAudio: normalize);
|
||||||
|
audioPlayer.setAudioNormalization(normalize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<UserPreferences> fromJson(Map<String, dynamic> json) {
|
||||||
|
return UserPreferences.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return state.toJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final userPreferencesProvider =
|
||||||
|
StateNotifierProvider<UserPreferencesNotifier, UserPreferences>(
|
||||||
|
(ref) => UserPreferencesNotifier(ref),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -473,6 +473,15 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
draggable_scrollbar:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cfd570035bf393de541d32e9b28808b5d7e602df
|
||||||
|
resolved-ref: cfd570035bf393de541d32e9b28808b5d7e602df
|
||||||
|
url: "https://github.com/thielepaul/flutter-draggable-scrollbar.git"
|
||||||
|
source: git
|
||||||
|
version: "0.1.0"
|
||||||
duration:
|
duration:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -109,6 +109,10 @@ dependencies:
|
|||||||
jiosaavn:
|
jiosaavn:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/KRTirtho/jiosaavn.git
|
url: https://github.com/KRTirtho/jiosaavn.git
|
||||||
|
draggable_scrollbar:
|
||||||
|
git:
|
||||||
|
url: https://github.com/thielepaul/flutter-draggable-scrollbar.git
|
||||||
|
ref: cfd570035bf393de541d32e9b28808b5d7e602df
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.3.2
|
build_runner: ^2.3.2
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user