spotube/lib/pages/search/tabs/tracks.dart
2025-07-13 15:11:56 +06:00

120 lines
4.5 KiB
Dart

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/dialogs/prompt_dialog.dart';
import 'package:spotube/components/dialogs/select_device_dialog.dart';
import 'package:spotube/components/track_tile/track_tile.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/connect/connect.dart';
import 'package:spotube/modules/search/loading.dart';
import 'package:spotube/pages/search/search.dart';
import 'package:spotube/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/connect/connect.dart';
import 'package:spotube/provider/metadata_plugin/search/tracks.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
class SearchPageTracksTab extends HookConsumerWidget {
const SearchPageTracksTab({super.key});
@override
Widget build(BuildContext context, ref) {
final searchTerm = ref.watch(searchTermStateProvider);
final searchTracksSnapshot =
ref.watch(metadataPluginSearchTracksProvider(searchTerm));
final searchTracksNotifier =
ref.read(metadataPluginSearchTracksProvider(searchTerm).notifier);
final searchTracks =
searchTracksSnapshot.asData?.value.items ?? [FakeData.track];
final playlist = ref.watch(audioPlayerProvider);
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
return SearchPlaceholder(
snapshot: searchTracksSnapshot,
child: InfiniteList(
itemCount: searchTracksSnapshot.asData?.value.items.length ?? 0,
hasReachedMax: searchTracksSnapshot.asData?.value.hasMore != true,
isLoading: searchTracksSnapshot.isLoading &&
!searchTracksSnapshot.isLoadingNextPage,
loadingBuilder: (context) {
return Skeletonizer(
enabled: true,
child: TrackTile(track: FakeData.track, playlist: playlist),
);
},
onFetchData: () {
searchTracksNotifier.fetchMore();
},
itemBuilder: (context, index) {
final track = searchTracks[index];
return TrackTile(
track: track,
playlist: playlist,
index: index,
onTap: () async {
final isRemoteDevice = await showSelectDeviceDialog(context, ref);
if (isRemoteDevice == null) return;
if (isRemoteDevice) {
final remotePlayback = ref.read(connectProvider.notifier);
final remotePlaylist = ref.read(queueProvider);
final isTrackPlaying =
remotePlaylist.activeTrack?.id == track.id;
if (!isTrackPlaying && context.mounted) {
final shouldPlay = (playlist.tracks.length) > 20
? await showPromptDialog(
context: context,
title: context.l10n.playing_track(
track.name,
),
message: context.l10n.queue_clear_alert(
playlist.tracks.length,
),
)
: true;
if (shouldPlay) {
await remotePlayback.load(
WebSocketLoadEventData.playlist(
tracks: [track],
),
);
}
}
} else {
final isTrackPlaying = playlist.activeTrack?.id == track.id;
if (!isTrackPlaying && context.mounted) {
final shouldPlay = (playlist.tracks.length) > 20
? await showPromptDialog(
context: context,
title: context.l10n.playing_track(
track.name,
),
message: context.l10n.queue_clear_alert(
playlist.tracks.length,
),
)
: true;
if (shouldPlay) {
await playlistNotifier.load(
[track],
autoPlay: true,
);
}
}
}
},
);
},
),
);
}
}