mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-15 00:25:17 +00:00
feat: show loading indicator on play track
This commit is contained in:
parent
1540999f50
commit
d12ea48b97
@ -131,7 +131,7 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
|
|||||||
class UserLocalTracks extends HookConsumerWidget {
|
class UserLocalTracks extends HookConsumerWidget {
|
||||||
const UserLocalTracks({Key? key}) : super(key: key);
|
const UserLocalTracks({Key? key}) : super(key: key);
|
||||||
|
|
||||||
void playLocalTracks(
|
Future<void> playLocalTracks(
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
List<LocalTrack> tracks, {
|
List<LocalTrack> tracks, {
|
||||||
LocalTrack? currentTrack,
|
LocalTrack? currentTrack,
|
||||||
@ -203,10 +203,10 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: trackSnapshot.value != null
|
onPressed: trackSnapshot.value != null
|
||||||
? () {
|
? () async {
|
||||||
if (trackSnapshot.value?.isNotEmpty == true) {
|
if (trackSnapshot.value?.isNotEmpty == true) {
|
||||||
if (!isPlaylistPlaying) {
|
if (!isPlaylistPlaying) {
|
||||||
playLocalTracks(
|
await playLocalTracks(
|
||||||
ref,
|
ref,
|
||||||
trackSnapshot.value!,
|
trackSnapshot.value!,
|
||||||
);
|
);
|
||||||
@ -295,8 +295,8 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
index: index,
|
index: index,
|
||||||
track: track,
|
track: track,
|
||||||
userPlaylist: false,
|
userPlaylist: false,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
playLocalTracks(
|
await playLocalTracks(
|
||||||
ref,
|
ref,
|
||||||
sortedTracks,
|
sortedTracks,
|
||||||
currentTrack: track,
|
currentTrack: track,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -27,7 +29,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
final Query<List<TrackSimple>, T> tracksSnapshot;
|
final Query<List<TrackSimple>, T> tracksSnapshot;
|
||||||
final String titleImage;
|
final String titleImage;
|
||||||
final PlayButtonState playingState;
|
final PlayButtonState playingState;
|
||||||
final void Function([Track? currentTrack]) onPlay;
|
final Future<void> Function([Track? currentTrack]) onPlay;
|
||||||
final void Function([Track? currentTrack]) onShuffledPlay;
|
final void Function([Track? currentTrack]) onShuffledPlay;
|
||||||
final void Function() onAddToQueue;
|
final void Function() onAddToQueue;
|
||||||
final void Function() onShare;
|
final void Function() onShare;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
@ -21,7 +23,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
final Track track;
|
final Track track;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final ValueChanged<bool?>? onChanged;
|
final ValueChanged<bool?>? onChanged;
|
||||||
final VoidCallback? onTap;
|
final Future<void> Function()? onTap;
|
||||||
final VoidCallback? onLongPress;
|
final VoidCallback? onLongPress;
|
||||||
final bool userPlaylist;
|
final bool userPlaylist;
|
||||||
final String? playlistId;
|
final String? playlistId;
|
||||||
@ -62,6 +64,10 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
|
|
||||||
final isPlaying = track.id == playlist.activeTrack?.id;
|
final isPlaying = track.id == playlist.activeTrack?.id;
|
||||||
|
|
||||||
|
final isLoading = useState(false);
|
||||||
|
|
||||||
|
final isSelected = isPlaying || isLoading.value;
|
||||||
|
|
||||||
return LayoutBuilder(builder: (context, constrains) {
|
return LayoutBuilder(builder: (context, constrains) {
|
||||||
return Listener(
|
return Listener(
|
||||||
onPointerDown: (event) {
|
onPointerDown: (event) {
|
||||||
@ -76,11 +82,18 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: HoverBuilder(
|
child: HoverBuilder(
|
||||||
permanentState: isPlaying || constrains.smAndDown ? true : null,
|
permanentState: isSelected || constrains.smAndDown ? true : null,
|
||||||
builder: (context, isHovering) {
|
builder: (context, isHovering) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
selected: isPlaying,
|
selected: isSelected,
|
||||||
onTap: onTap,
|
onTap: () async {
|
||||||
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
await onTap?.call();
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress,
|
||||||
enabled: !isBlackListed,
|
enabled: !isBlackListed,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
@ -145,22 +158,23 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
.copyWith(size: 26, color: Colors.white),
|
.copyWith(size: 26, color: Colors.white),
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: !isHovering
|
child: (isPlaying && playlist.isFetching) ||
|
||||||
? const SizedBox.shrink()
|
isLoading.value
|
||||||
: isPlaying && playlist.isFetching
|
? const SizedBox(
|
||||||
? const SizedBox(
|
width: 26,
|
||||||
width: 26,
|
height: 26,
|
||||||
height: 26,
|
child: CircularProgressIndicator(
|
||||||
child: CircularProgressIndicator(
|
strokeWidth: 1.5,
|
||||||
strokeWidth: 1.5,
|
color: Colors.white,
|
||||||
color: Colors.white,
|
),
|
||||||
),
|
)
|
||||||
|
: isPlaying
|
||||||
|
? Icon(
|
||||||
|
SpotubeIcons.pause,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
: isPlaying
|
: !isHovering
|
||||||
? Icon(
|
? const SizedBox.shrink()
|
||||||
SpotubeIcons.pause,
|
|
||||||
color: theme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
: const Icon(SpotubeIcons.play),
|
: const Icon(SpotubeIcons.play),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
@ -27,7 +29,7 @@ final trackCollectionSortState =
|
|||||||
StateProvider.family<SortBy, String>((ref, _) => SortBy.none);
|
StateProvider.family<SortBy, String>((ref, _) => SortBy.none);
|
||||||
|
|
||||||
class TracksTableView extends HookConsumerWidget {
|
class TracksTableView extends HookConsumerWidget {
|
||||||
final void Function(Track currentTrack)? onTrackPlayButtonPressed;
|
final Future<void> Function(Track currentTrack)? onTrackPlayButtonPressed;
|
||||||
final List<Track> tracks;
|
final List<Track> tracks;
|
||||||
final bool userPlaylist;
|
final bool userPlaylist;
|
||||||
final String? playlistId;
|
final String? playlistId;
|
||||||
@ -58,8 +60,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
final downloader = ref.watch(downloadManagerProvider.notifier);
|
final downloader = ref.watch(downloadManagerProvider.notifier);
|
||||||
final apiType =
|
final apiType =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.youtubeApiType));
|
ref.watch(userPreferencesProvider.select((s) => s.youtubeApiType));
|
||||||
final tableHeadStyle =
|
const tableHeadStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
|
||||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
|
|
||||||
|
|
||||||
final selected = useState<List<String>>([]);
|
final selected = useState<List<String>>([]);
|
||||||
final showCheck = useState<bool>(false);
|
final showCheck = useState<bool>(false);
|
||||||
@ -297,7 +298,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
selected: selected.value.contains(track.id),
|
selected: selected.value.contains(track.id),
|
||||||
userPlaylist: userPlaylist,
|
userPlaylist: userPlaylist,
|
||||||
playlistId: playlistId,
|
playlistId: playlistId,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
if (showCheck.value) {
|
if (showCheck.value) {
|
||||||
final alreadyChecked = selected.value.contains(track.id);
|
final alreadyChecked = selected.value.contains(track.id);
|
||||||
if (alreadyChecked) {
|
if (alreadyChecked) {
|
||||||
@ -314,9 +315,8 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!isBlackListed) {
|
if (isBlackListed) return;
|
||||||
onTrackPlayButtonPressed?.call(track);
|
await onTrackPlayButtonPressed?.call(track);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
|
@ -85,10 +85,10 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
album: album,
|
album: album,
|
||||||
routePath: "/album/${album.id}",
|
routePath: "/album/${album.id}",
|
||||||
bottomSpace: mediaQuery.mdAndDown,
|
bottomSpace: mediaQuery.mdAndDown,
|
||||||
onPlay: ([track]) {
|
onPlay: ([track]) async {
|
||||||
if (tracksSnapshot.hasData) {
|
if (tracksSnapshot.hasData) {
|
||||||
if (!isAlbumPlaying) {
|
if (!isAlbumPlaying) {
|
||||||
playPlaylist(
|
await playPlaylist(
|
||||||
tracksSnapshot.data!
|
tracksSnapshot.data!
|
||||||
.map((track) =>
|
.map((track) =>
|
||||||
TypeConversionUtils.simpleTrack_X_Track(track, album))
|
TypeConversionUtils.simpleTrack_X_Track(track, album))
|
||||||
@ -96,7 +96,7 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
ref,
|
ref,
|
||||||
);
|
);
|
||||||
} else if (isAlbumPlaying && track != null) {
|
} else if (isAlbumPlaying && track != null) {
|
||||||
playPlaylist(
|
await playPlaylist(
|
||||||
tracksSnapshot.data!
|
tracksSnapshot.data!
|
||||||
.map((track) =>
|
.map((track) =>
|
||||||
TypeConversionUtils.simpleTrack_X_Track(track, album))
|
TypeConversionUtils.simpleTrack_X_Track(track, album))
|
||||||
@ -105,7 +105,7 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
ref,
|
ref,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
playback
|
await playback
|
||||||
.removeTracks(tracksSnapshot.data!.map((track) => track.id!));
|
.removeTracks(tracksSnapshot.data!.map((track) => track.id!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
return TrackTile(
|
return TrackTile(
|
||||||
index: i,
|
index: i,
|
||||||
track: track,
|
track: track,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
playPlaylist(
|
playPlaylist(
|
||||||
topTracks.toList(),
|
topTracks.toList(),
|
||||||
currentTrack: track,
|
currentTrack: track,
|
||||||
|
@ -104,22 +104,16 @@ class PlaylistView extends HookConsumerWidget {
|
|||||||
tracksSnapshot: tracksSnapshot,
|
tracksSnapshot: tracksSnapshot,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
isOwned: ownPlaylist,
|
isOwned: ownPlaylist,
|
||||||
onPlay: ([track]) {
|
onPlay: ([track]) async {
|
||||||
if (tracksSnapshot.hasData) {
|
if (tracksSnapshot.hasData) {
|
||||||
if (!isPlaylistPlaying) {
|
if (!isPlaylistPlaying || (isPlaylistPlaying && track != null)) {
|
||||||
playPlaylist(
|
await playPlaylist(
|
||||||
tracksSnapshot.data!,
|
|
||||||
ref,
|
|
||||||
currentTrack: track,
|
|
||||||
);
|
|
||||||
} else if (isPlaylistPlaying && track != null) {
|
|
||||||
playPlaylist(
|
|
||||||
tracksSnapshot.data!,
|
tracksSnapshot.data!,
|
||||||
ref,
|
ref,
|
||||||
currentTrack: track,
|
currentTrack: track,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
playlistNotifier
|
await playlistNotifier
|
||||||
.removeTracks(tracksSnapshot.data!.map((e) => e.id!));
|
.removeTracks(tracksSnapshot.data!.map((e) => e.id!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user