Skip to content

Commit 45dd75c

Browse files
committed
port new list tile to queue list
- some edge cases might still be missing, and refactoring to using constructors instead of separate classed would be good
1 parent 38f9c6c commit 45dd75c

File tree

3 files changed

+213
-7
lines changed

3 files changed

+213
-7
lines changed

lib/components/AlbumScreen/song_list_tile.dart

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,14 +387,172 @@ class _SongListTileState extends ConsumerState<SongListTile>
387387
}
388388
}
389389

390+
391+
class QueueListTile extends ConsumerStatefulWidget {
392+
final jellyfin_models.BaseItemDto item;
393+
final jellyfin_models.BaseItemDto? parentItem;
394+
final Future<int>? listIndex;
395+
final int actualIndex;
396+
final int indexOffset;
397+
final bool isCurrentTrack;
398+
final bool isInPlaylist;
399+
final bool allowReorder;
400+
401+
final void Function() onTap;
402+
final VoidCallback? onRemoveFromList;
403+
final void Function(FinampTheme)? themeCallback;
404+
405+
const QueueListTile({
406+
super.key,
407+
required this.item,
408+
required this.listIndex,
409+
required this.actualIndex,
410+
required this.indexOffset,
411+
required this.onTap,
412+
required this.isCurrentTrack,
413+
required this.isInPlaylist,
414+
required this.allowReorder,
415+
this.parentItem,
416+
this.onRemoveFromList,
417+
this.themeCallback,
418+
});
419+
420+
@override
421+
ConsumerState<QueueListTile> createState() => _QueueListTileState();
422+
}
423+
424+
class _QueueListTileState extends ConsumerState<QueueListTile>
425+
with SingleTickerProviderStateMixin {
426+
final _audioServiceHelper = GetIt.instance<AudioServiceHelper>();
427+
final _queueService = GetIt.instance<QueueService>();
428+
final _audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();
429+
430+
FinampTheme? _menuTheme;
431+
432+
@override
433+
void dispose() {
434+
_menuTheme?.dispose();
435+
super.dispose();
436+
}
437+
438+
@override
439+
Widget build(BuildContext context) {
440+
bool playable;
441+
if (FinampSettingsHelper.finampSettings.isOffline) {
442+
playable = ref.watch(GetIt.instance<DownloadsService>()
443+
.stateProvider(DownloadStub.fromItem(
444+
type: DownloadItemType.song, item: widget.item))
445+
.select((value) => value.value?.isComplete ?? false));
446+
} else {
447+
playable = true;
448+
}
449+
450+
final listTile = StreamBuilder<MediaItem?>(
451+
stream: _audioHandler.mediaItem,
452+
builder: (context, snapshot) {
453+
// I think past me did this check directly from the JSON for
454+
// performance. It works for now, apologies if you're debugging it
455+
// years in the future.
456+
final isCurrentlyPlaying =
457+
snapshot.data?.extras?["itemJson"]["Id"] == widget.item.id;
458+
459+
final trackListItem = TrackListItem(
460+
item: widget.item,
461+
parentItem: widget.parentItem,
462+
listIndex: widget.listIndex,
463+
actualIndex: widget.item.indexNumber ?? -1,
464+
isCurrentTrack: isCurrentlyPlaying,
465+
isPlayable: playable,
466+
isInPlaylist: widget.isInPlaylist,
467+
allowReorder: widget.allowReorder,
468+
onRemoveFromList: widget.onRemoveFromList,
469+
themeCallback: (x) => _menuTheme = x,
470+
// This must be in ListTile instead of parent GestureDetecter to
471+
// enable hover color changes
472+
onTap: widget.onTap,
473+
);
474+
475+
return isCurrentlyPlaying
476+
? ProviderScope(
477+
overrides: [
478+
themeDataProvider.overrideWith((ref) {
479+
return ref.watch(playerScreenThemeDataProvider) ??
480+
FinampTheme.defaultTheme();
481+
})
482+
],
483+
child: Consumer(
484+
builder:
485+
(BuildContext context, WidgetRef ref, Widget? child) {
486+
final imageTheme = ref.watch(playerScreenThemeProvider);
487+
return AnimatedTheme(
488+
duration: const Duration(milliseconds: 500),
489+
data: ThemeData(
490+
colorScheme: imageTheme,
491+
brightness: Theme.of(context).brightness,
492+
iconTheme: Theme.of(context).iconTheme.copyWith(
493+
color: imageTheme.primary,
494+
),
495+
),
496+
child: trackListItem,
497+
);
498+
},
499+
),
500+
)
501+
: trackListItem;
502+
});
503+
void menuCallback() async {
504+
if (playable) {
505+
FeedbackHelper.feedback(FeedbackType.selection);
506+
await showModalSongMenu(
507+
context: context,
508+
item: widget.item,
509+
isInPlaylist: widget.isInPlaylist,
510+
parentItem: widget.parentItem,
511+
onRemoveFromList: widget.onRemoveFromList,
512+
themeProvider: _menuTheme,
513+
confirmPlaylistRemoval: false,
514+
);
515+
}
516+
}
517+
518+
return GestureDetector(
519+
onTapDown: (_) {
520+
_menuTheme?.calculate(Theme.of(context).brightness);
521+
},
522+
onLongPressStart: (details) => menuCallback(),
523+
onSecondaryTapDown: (details) => menuCallback(),
524+
child: !playable
525+
? listTile
526+
: Dismissible(
527+
key: Key(widget.listIndex.toString()),
528+
direction: FinampSettingsHelper.finampSettings.disableGesture
529+
? DismissDirection.none
530+
: DismissDirection.horizontal,
531+
dismissThresholds: const {
532+
DismissDirection.startToEnd: 0.65,
533+
DismissDirection.endToStart: 0.65
534+
},
535+
// no background, dismissing really dismisses here
536+
onDismissed: (direction) async {
537+
FeedbackHelper.feedback(FeedbackType.impact);
538+
await _queueService.removeAtOffset(widget.indexOffset);
539+
setState(() {});
540+
},
541+
child: listTile,
542+
),
543+
);
544+
}
545+
}
546+
390547
class TrackListItem extends ConsumerWidget {
391548
final jellyfin_models.BaseItemDto item;
392549
final jellyfin_models.BaseItemDto? parentItem;
393550
final Future<int>? listIndex;
394551
final int actualIndex;
395552
final bool isCurrentTrack;
396553
final bool isInPlaylist;
397-
554+
final bool allowReorder;
555+
398556
final bool isPlayable;
399557
final void Function() onTap;
400558
final VoidCallback? onRemoveFromList;
@@ -410,6 +568,7 @@ class TrackListItem extends ConsumerWidget {
410568
this.isPlayable = true,
411569
this.isCurrentTrack = false,
412570
this.isInPlaylist = false,
571+
this.allowReorder = false,
413572
this.onRemoveFromList,
414573
this.themeCallback,
415574
});
@@ -446,7 +605,8 @@ class TrackListItem extends ConsumerWidget {
446605
Brightness.dark
447606
? 0.35
448607
: 0.3)
449-
: Theme.of(context).colorScheme.surfaceContainer,
608+
// : Theme.of(context).colorScheme.surfaceContainer,
609+
: Colors.transparent,
450610
),
451611
textTheme: Theme.of(context).textTheme.copyWith(
452612
bodyLarge: Theme.of(context)
@@ -463,8 +623,10 @@ class TrackListItem extends ConsumerWidget {
463623
)),
464624
child: TrackListItemTile(
465625
baseItem: baseItem,
626+
index: listIndex,
466627
themeCallback: themeCallback,
467628
isCurrentTrack: isCurrentTrack,
629+
allowReorder: allowReorder,
468630
onTap: onTap),
469631
)),
470632
);
@@ -479,12 +641,16 @@ class TrackListItemTile extends StatelessWidget {
479641
required this.baseItem,
480642
required this.themeCallback,
481643
required this.isCurrentTrack,
644+
required this.allowReorder,
482645
required this.onTap,
646+
this.index,
483647
});
484648

485649
final jellyfin_models.BaseItemDto baseItem;
486650
final void Function(FinampTheme theme)? themeCallback;
487651
final bool isCurrentTrack;
652+
final bool allowReorder;
653+
final Future<int>? index;
488654
final void Function() onTap;
489655

490656
static const double tileHeight = 60.0;
@@ -612,6 +778,26 @@ class TrackListItemTile extends StatelessWidget {
612778
horizontal: -4,
613779
),
614780
),
781+
if (allowReorder)
782+
FutureBuilder(
783+
future: index,
784+
builder: (context, snapshot) {
785+
return ReorderableDragStartListener(
786+
index: snapshot.data ??
787+
0, // will briefly use 0 as index, but should resolve quickly enough for user not to notice
788+
child: Padding(
789+
padding: const EdgeInsets.only(left: 6.0),
790+
child: Icon(
791+
TablerIcons.grip_horizontal,
792+
color:
793+
Theme.of(context).textTheme.bodyMedium?.color ??
794+
Colors.white,
795+
size: 28.0,
796+
weight: 1.5,
797+
),
798+
),
799+
);
800+
}),
615801
],
616802
),
617803
),

lib/components/PlayerScreen/queue_list.dart

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22

33
import 'package:audio_service/audio_service.dart';
4+
import 'package:finamp/components/AlbumScreen/song_list_tile.dart';
45
import 'package:finamp/components/AlbumScreen/song_menu.dart';
56
import 'package:finamp/components/Buttons/simple_button.dart';
67
import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart';
@@ -648,13 +649,32 @@ class _QueueTracksListState extends State<QueueTracksList> {
648649
final actualIndex = index;
649650
final indexOffset = index + _nextUp!.length + 1;
650651

651-
return QueueListItem(
652+
// return QueueListItem(
653+
// key: ValueKey(item.id),
654+
// item: item,
655+
// listIndex: index,
656+
// actualIndex: actualIndex,
657+
// indexOffset: indexOffset,
658+
// subqueue: _queue!,
659+
// allowReorder:
660+
// _queueService.playbackOrder == FinampPlaybackOrder.linear,
661+
// onTap: () async {
662+
// FeedbackHelper.feedback(FeedbackType.selection);
663+
// await _queueService.skipByOffset(indexOffset);
664+
// scrollToKey(
665+
// key: widget.previousTracksHeaderKey,
666+
// duration: const Duration(milliseconds: 500));
667+
// },
668+
// isCurrentTrack: false,
669+
// );
670+
return QueueListTile(
652671
key: ValueKey(item.id),
653-
item: item,
654-
listIndex: index,
672+
item: item.baseItem!,
673+
listIndex: Future.value(index),
655674
actualIndex: actualIndex,
656675
indexOffset: indexOffset,
657-
subqueue: _queue!,
676+
isInPlaylist: queueItemInPlaylist(item),
677+
parentItem: item.source.item,
658678
allowReorder:
659679
_queueService.playbackOrder == FinampPlaybackOrder.linear,
660680
onTap: () async {

lib/components/PlayerScreen/queue_list_item.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:get_it/get_it.dart';
1414

1515
import '../../services/theme_provider.dart';
1616

17+
@Deprecated("Use QueueListItem instead")
1718
class QueueListItem extends StatefulWidget {
1819
final FinampQueueItem item;
1920
final int listIndex;
@@ -123,7 +124,6 @@ class _QueueListItemState extends State<QueueListItem>
123124
: jellyfin_models.BaseItemDto.fromJson(
124125
widget.item.item.extras?["itemJson"]),
125126
borderRadius: BorderRadius.zero,
126-
themeCallback: (x) => _menuTheme = x,
127127
),
128128
title: Column(
129129
crossAxisAlignment: CrossAxisAlignment.start,

0 commit comments

Comments
 (0)