1
1
import 'dart:io' ;
2
2
3
- import 'package:flutter/material.dart' ;
3
+ import 'package:fl_query/fl_query.dart' ;
4
+ import 'package:flutter/material.dart' hide Page;
4
5
import 'package:flutter/services.dart' ;
5
6
import 'package:flutter_hooks/flutter_hooks.dart' ;
6
7
import 'package:go_router/go_router.dart' ;
@@ -10,6 +11,7 @@ import 'package:spotube/collections/spotube_icons.dart';
10
11
import 'package:spotube/components/library/user_local_tracks.dart' ;
11
12
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart' ;
12
13
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart' ;
14
+ import 'package:spotube/components/shared/dialogs/prompt_dialog.dart' ;
13
15
import 'package:spotube/components/shared/dialogs/track_details_dialog.dart' ;
14
16
import 'package:spotube/components/shared/heart_button.dart' ;
15
17
import 'package:spotube/components/shared/image/universal_image.dart' ;
@@ -20,7 +22,9 @@ import 'package:spotube/provider/authentication_provider.dart';
20
22
import 'package:spotube/provider/blacklist_provider.dart' ;
21
23
import 'package:spotube/provider/download_manager_provider.dart' ;
22
24
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart' ;
25
+ import 'package:spotube/provider/spotify_provider.dart' ;
23
26
import 'package:spotube/services/mutations/mutations.dart' ;
27
+ import 'package:spotube/services/queries/search.dart' ;
24
28
import 'package:spotube/utils/type_conversion_utils.dart' ;
25
29
26
30
enum TrackOptionValue {
@@ -36,6 +40,7 @@ enum TrackOptionValue {
36
40
favorite,
37
41
details,
38
42
download,
43
+ startRadio,
39
44
}
40
45
41
46
class TrackOptions extends HookConsumerWidget {
@@ -82,6 +87,67 @@ class TrackOptions extends HookConsumerWidget {
82
87
);
83
88
}
84
89
90
+ void actionStartRadio (
91
+ BuildContext context,
92
+ WidgetRef ref,
93
+ Track track,
94
+ ) async {
95
+ final playback = ref.read (ProxyPlaylistNotifier .notifier);
96
+ final playlist = ref.read (ProxyPlaylistNotifier .provider);
97
+ final spotify = ref.read (spotifyProvider);
98
+ final pages = await QueryClient .of (context)
99
+ .fetchInfiniteQueryJob <List <Page >, dynamic , int , SearchParams >(
100
+ job: SearchQueries .queryJob (SearchType .playlist.name),
101
+ args: (
102
+ spotify: spotify,
103
+ searchType: SearchType .playlist,
104
+ query: "${track .name } Radio"
105
+ ),
106
+ ) ??
107
+ [];
108
+
109
+ final radios = pages.expand ((e) => e.items ?? < PlaylistSimple > []).toList ();
110
+
111
+ final artists = track.artists! .map ((e) => e.name);
112
+
113
+ final radio = radios.firstWhere (
114
+ (e) =>
115
+ e.name == "${track .name } Radio" &&
116
+ artists.where ((a) => e.name! .contains (a! )).length >= 2 ,
117
+ orElse: () => radios.first,
118
+ );
119
+
120
+ bool replaceQueue = false ;
121
+
122
+ if (context.mounted && playlist.tracks.isNotEmpty) {
123
+ replaceQueue = await showPromptDialog (
124
+ context: context,
125
+ title: context.l10n.how_to_start_radio,
126
+ message: context.l10n.replace_queue_question,
127
+ okText: context.l10n.replace,
128
+ cancelText: context.l10n.add_to_queue,
129
+ );
130
+ }
131
+
132
+ if (replaceQueue) {
133
+ await playback.stop ();
134
+ await playback.load ([track], autoPlay: true );
135
+ } else {
136
+ await playback.addTrack (track);
137
+ }
138
+
139
+ final tracks =
140
+ await spotify.playlists.getTracksByPlaylistId (radio.id! ).all ();
141
+
142
+ await playback.addTracks (
143
+ tracks.toList ()
144
+ ..removeWhere ((e) {
145
+ final isDuplicate = playlist.tracks.any ((t) => t.id == e.id);
146
+ return e.id == track.id || isDuplicate;
147
+ }),
148
+ );
149
+ }
150
+
85
151
@override
86
152
Widget build (BuildContext context, ref) {
87
153
final scaffoldMessenger = ScaffoldMessenger .of (context);
@@ -207,6 +273,9 @@ class TrackOptions extends HookConsumerWidget {
207
273
case TrackOptionValue .download:
208
274
await downloadManager.addToQueue (track);
209
275
break ;
276
+ case TrackOptionValue .startRadio:
277
+ actionStartRadio (context, ref, track);
278
+ break ;
210
279
}
211
280
},
212
281
icon: icon ?? const Icon (SpotubeIcons .moreHorizontal),
@@ -287,12 +356,18 @@ class TrackOptions extends HookConsumerWidget {
287
356
: context.l10n.save_as_favorite,
288
357
),
289
358
),
290
- if (auth != null )
359
+ if (auth != null ) ...[
360
+ PopSheetEntry (
361
+ value: TrackOptionValue .startRadio,
362
+ leading: const Icon (SpotubeIcons .radio),
363
+ title: Text (context.l10n.start_a_radio),
364
+ ),
291
365
PopSheetEntry (
292
366
value: TrackOptionValue .addToPlaylist,
293
367
leading: const Icon (SpotubeIcons .playlistAdd),
294
368
title: Text (context.l10n.add_to_playlist),
295
369
),
370
+ ],
296
371
if (userPlaylist && auth != null )
297
372
PopSheetEntry (
298
373
value: TrackOptionValue .removeFromPlaylist,
0 commit comments