mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
283 lines
11 KiB
Dart
283 lines
11 KiB
Dart
import 'package:auto_route/auto_route.dart';
|
|
import 'package:auto_size_text/auto_size_text.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Consumer;
|
|
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
|
|
|
import 'package:spotube/collections/assets.gen.dart';
|
|
import 'package:spotube/collections/routes.gr.dart';
|
|
import 'package:spotube/collections/spotube_icons.dart';
|
|
import 'package:spotube/components/framework/app_pop_scope.dart';
|
|
import 'package:spotube/models/metadata/metadata.dart';
|
|
import 'package:spotube/modules/player/player_actions.dart';
|
|
import 'package:spotube/modules/player/player_controls.dart';
|
|
import 'package:spotube/modules/player/volume_slider.dart';
|
|
import 'package:spotube/components/dialogs/track_details_dialog.dart';
|
|
import 'package:spotube/components/links/artist_link.dart';
|
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
|
import 'package:spotube/components/image/universal_image.dart';
|
|
import 'package:spotube/extensions/constrains.dart';
|
|
import 'package:spotube/extensions/context.dart';
|
|
import 'package:spotube/modules/root/spotube_navigation_bar.dart';
|
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
|
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
|
|
import 'package:spotube/provider/server/active_track_sources.dart';
|
|
import 'package:spotube/provider/volume_provider.dart';
|
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
|
import 'package:spotube/utils/platform.dart';
|
|
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
|
|
|
class PlayerView extends HookConsumerWidget {
|
|
final PanelController panelController;
|
|
final ScrollController scrollController;
|
|
const PlayerView({
|
|
super.key,
|
|
required this.panelController,
|
|
required this.scrollController,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, ref) {
|
|
final theme = Theme.of(context);
|
|
final authenticated = ref.watch(metadataPluginAuthenticatedProvider);
|
|
final sourcedCurrentTrack = ref.watch(activeTrackSourcesProvider);
|
|
final currentActiveTrack =
|
|
ref.watch(audioPlayerProvider.select((s) => s.activeTrack));
|
|
final currentActiveTrackSource = sourcedCurrentTrack.asData?.value?.source;
|
|
final isLocalTrack = currentActiveTrack is SpotubeLocalTrackObject;
|
|
final mediaQuery = MediaQuery.sizeOf(context);
|
|
|
|
final shouldHide = useState(true);
|
|
|
|
ref.listen(navigationPanelHeight, (_, height) {
|
|
shouldHide.value = height.ceil() == 50;
|
|
});
|
|
|
|
if (shouldHide.value) {
|
|
return const SizedBox();
|
|
}
|
|
|
|
useEffect(() {
|
|
if (mediaQuery.lgAndUp) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
panelController.close();
|
|
});
|
|
}
|
|
return null;
|
|
}, [mediaQuery.lgAndUp]);
|
|
|
|
String albumArt = useMemoized(
|
|
() => (currentActiveTrack?.album.images).asUrlString(
|
|
placeholder: ImagePlaceholder.albumArt,
|
|
),
|
|
[currentActiveTrack?.album.images],
|
|
);
|
|
|
|
useEffect(() {
|
|
for (final renderView in WidgetsBinding.instance.renderViews) {
|
|
renderView.automaticSystemUiAdjustment = false;
|
|
}
|
|
|
|
return () {
|
|
for (final renderView in WidgetsBinding.instance.renderViews) {
|
|
renderView.automaticSystemUiAdjustment = true;
|
|
}
|
|
};
|
|
}, [panelController.isAttached && panelController.isPanelOpen]);
|
|
|
|
return AppPopScope(
|
|
canPop: false,
|
|
onPopInvoked: (didPop) async {
|
|
await panelController.close();
|
|
},
|
|
child: SurfaceCard(
|
|
borderWidth: 0,
|
|
surfaceOpacity: 0.9,
|
|
padding: EdgeInsets.zero,
|
|
child: Scaffold(
|
|
backgroundColor: Colors.transparent,
|
|
headers: [
|
|
SafeArea(
|
|
minimum:
|
|
kIsMobile ? const EdgeInsets.only(top: 80) : EdgeInsets.zero,
|
|
bottom: false,
|
|
child: TitleBar(
|
|
surfaceOpacity: 0,
|
|
surfaceBlur: 0,
|
|
leading: [
|
|
IconButton.ghost(
|
|
icon: const Icon(SpotubeIcons.angleDown, size: 18),
|
|
onPressed: panelController.close,
|
|
)
|
|
],
|
|
trailing: [
|
|
if (currentActiveTrackSource is YoutubeSourcedTrack)
|
|
TextButton(
|
|
leading: Assets.logos.songlinkTransparent.image(
|
|
width: 20,
|
|
height: 20,
|
|
color: theme.colorScheme.foreground,
|
|
),
|
|
onPressed: () {
|
|
final url =
|
|
"https://song.link/s/${currentActiveTrack?.id}";
|
|
|
|
launchUrlString(url);
|
|
},
|
|
child: Text(context.l10n.song_link),
|
|
),
|
|
if (!isLocalTrack)
|
|
Tooltip(
|
|
tooltip: TooltipContainer(
|
|
child: Text(context.l10n.details),
|
|
).call,
|
|
child: IconButton.ghost(
|
|
icon: const Icon(SpotubeIcons.info, size: 18),
|
|
onPressed: currentActiveTrackSource == null
|
|
? null
|
|
: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return TrackDetailsDialog(
|
|
track: currentActiveTrack
|
|
as SpotubeFullTrackObject,
|
|
);
|
|
});
|
|
},
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
],
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
margin: const EdgeInsets.all(8),
|
|
constraints:
|
|
const BoxConstraints(maxHeight: 300, maxWidth: 300),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withAlpha(100),
|
|
spreadRadius: 2,
|
|
blurRadius: 10,
|
|
offset: Offset.zero,
|
|
),
|
|
],
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: UniversalImage(
|
|
path: albumArt,
|
|
placeholder: Assets.albumPlaceholder.path,
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 60),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
alignment: Alignment.centerLeft,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
AutoSizeText(
|
|
currentActiveTrack?.name ?? context.l10n.not_playing,
|
|
style: const TextStyle(fontSize: 22),
|
|
maxFontSize: 22,
|
|
maxLines: 1,
|
|
textAlign: TextAlign.start,
|
|
),
|
|
if (isLocalTrack)
|
|
Text(
|
|
currentActiveTrack.artists.asString(),
|
|
style: theme.typography.normal
|
|
.copyWith(fontWeight: FontWeight.bold),
|
|
)
|
|
else
|
|
ArtistLink(
|
|
artists: currentActiveTrack?.artists ?? [],
|
|
textStyle: theme.typography.normal
|
|
.copyWith(fontWeight: FontWeight.bold),
|
|
onRouteChange: (route) {
|
|
panelController.close();
|
|
context.router.navigateNamed(route);
|
|
},
|
|
onOverflowArtistClick: () => context.navigateTo(
|
|
TrackRoute(
|
|
trackId: currentActiveTrack!.id,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
const PlayerControls(),
|
|
const SizedBox(height: 25),
|
|
const PlayerActions(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
showQueue: false,
|
|
),
|
|
const SizedBox(height: 10),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: OutlineButton(
|
|
leading: const Icon(SpotubeIcons.queue),
|
|
child: Text(context.l10n.queue),
|
|
onPressed: () {
|
|
context.pushRoute(const PlayerQueueRoute());
|
|
},
|
|
),
|
|
),
|
|
if (authenticated.asData?.value == true)
|
|
const SizedBox(width: 10),
|
|
if (authenticated.asData?.value == true)
|
|
Expanded(
|
|
child: OutlineButton(
|
|
leading: const Icon(SpotubeIcons.music),
|
|
child: Text(context.l10n.lyrics),
|
|
onPressed: () {
|
|
context.pushRoute(const PlayerLyricsRoute());
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
],
|
|
),
|
|
const SizedBox(height: 25),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
child: Consumer(builder: (context, ref, _) {
|
|
final volume = ref.watch(volumeProvider);
|
|
return VolumeSlider(
|
|
fullWidth: true,
|
|
value: volume,
|
|
onChanged: (value) {
|
|
ref.read(volumeProvider.notifier).setVolume(value);
|
|
},
|
|
);
|
|
}),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|