Skip to content

Commit

Permalink
feat(deep-link): add track opening page
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Jan 5, 2024
1 parent d1ed569 commit 988a975
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 26 deletions.
10 changes: 10 additions & 0 deletions lib/collections/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:spotube/pages/search/search.dart';
import 'package:spotube/pages/settings/blacklist.dart';
import 'package:spotube/pages/settings/about.dart';
import 'package:spotube/pages/settings/logs.dart';
import 'package:spotube/pages/track/track.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/components/shared/spotube_page_route.dart';
import 'package:spotube/pages/artist/artist.dart';
Expand Down Expand Up @@ -144,6 +145,15 @@ final router = GoRouter(
);
},
),
GoRoute(
path: "/track/:id",
pageBuilder: (context, state) {
final id = state.pathParameters["id"]!;
return SpotubePage(
child: TrackPage(trackId: id),
);
},
),
],
),
GoRoute(
Expand Down
11 changes: 8 additions & 3 deletions lib/components/player/player_track_details.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:spotify/spotify.dart';

import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/links/link_text.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/utils/service_utils.dart';
Expand Down Expand Up @@ -44,10 +45,12 @@ class PlayerTrackDetails extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
Text(
LinkText(
playback.activeTrack?.name ?? "",
"/track/${playback.activeTrack?.id}",
push: true,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyMedium?.copyWith(
style: theme.textTheme.bodyMedium!.copyWith(
color: color,
),
),
Expand All @@ -66,8 +69,10 @@ class PlayerTrackDetails extends HookConsumerWidget {
flex: 1,
child: Column(
children: [
Text(
LinkText(
playback.activeTrack?.name ?? "",
"/track/${playback.activeTrack?.id}",
push: true,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold, color: color),
),
Expand Down
3 changes: 3 additions & 0 deletions lib/components/shared/links/link_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class LinkText<T> extends StatelessWidget {
final TextAlign? textAlign;
final TextOverflow? overflow;
final String route;
final int? maxLines;
final T? extra;

final bool push;
Expand All @@ -19,6 +20,7 @@ class LinkText<T> extends StatelessWidget {
this.extra,
this.overflow,
this.style = const TextStyle(),
this.maxLines,
this.push = false,
}) : super(key: key);

Expand All @@ -37,6 +39,7 @@ class LinkText<T> extends StatelessWidget {
overflow: overflow,
style: style,
textAlign: textAlign,
maxLines: maxLines,
);
}
}
4 changes: 3 additions & 1 deletion lib/components/shared/track_tile/track_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ class TrackOptions extends HookConsumerWidget {
final bool userPlaylist;
final String? playlistId;
final ObjectRef<ValueChanged<RelativeRect>?>? showMenuCbRef;
final Widget? icon;
const TrackOptions({
Key? key,
required this.track,
this.showMenuCbRef,
this.userPlaylist = false,
this.playlistId,
this.icon,
}) : super(key: key);

void actionShare(BuildContext context, Track track) {
Expand Down Expand Up @@ -207,7 +209,7 @@ class TrackOptions extends HookConsumerWidget {
break;
}
},
icon: const Icon(SpotubeIcons.moreHorizontal),
icon: icon ?? const Icon(SpotubeIcons.moreHorizontal),
headings: [
ListTile(
dense: true,
Expand Down
4 changes: 3 additions & 1 deletion lib/components/shared/track_tile/track_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ class TrackTile extends HookConsumerWidget {
children: [
Expanded(
flex: 6,
child: Text(
child: LinkText(
track.name!,
"/track/${track.id}",
push: true,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Expand Down
8 changes: 8 additions & 0 deletions lib/hooks/configurators/use_deep_linking.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ void useDeepLinking(WidgetRef ref) {
),
);
break;
case "track":
router.push(
"/track/${url.pathSegments.last}",
);
break;
default:
break;
}
Expand Down Expand Up @@ -80,6 +85,9 @@ void useDeepLinking(WidgetRef ref) {
case "spotify:artist":
await router.push("/artist/$endSegment");
break;
case "spotify:track":
await router.push("/track/$endSegment");
break;
case "spotify:playlist":
await router.push(
"/playlist/$endSegment",
Expand Down
2 changes: 1 addition & 1 deletion lib/pages/artist/artist.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ArtistPage extends HookConsumerWidget {
),
extendBodyBehindAppBar: true,
body: Builder(builder: (context) {
if (artistQuery.hasError) {
if (artistQuery.hasError && artistQuery.data == null) {
return Center(child: Text(artistQuery.error.toString()));
}
return Skeletonizer(
Expand Down
227 changes: 227 additions & 0 deletions lib/pages/track/track.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/links/link_text.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/track_tile/track_options.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:spotube/extensions/constrains.dart';

class TrackPage extends HookConsumerWidget {
final String trackId;
const TrackPage({
Key? key,
required this.trackId,
}) : super(key: key);

@override
Widget build(BuildContext context, ref) {
final ThemeData(:textTheme, :colorScheme) = Theme.of(context);
final mediaQuery = MediaQuery.of(context);

final playlist = ref.watch(ProxyPlaylistNotifier.provider);
final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier);

final isActive = playlist.activeTrack?.id == trackId;

final trackQuery = useQueries.tracks.track(ref, trackId);

final track = trackQuery.data ?? FakeData.track;

void onPlay() async {
if (isActive) {
audioPlayer.pause();
} else {
await playlistNotifier.load([track], autoPlay: true);
}
}

return Scaffold(
appBar: const PageWindowTitleBar(
automaticallyImplyLeading: true,
backgroundColor: Colors.transparent,
),
extendBodyBehindAppBar: true,
body: Stack(
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(
TypeConversionUtils.image_X_UrlString(
track.album!.images,
placeholder: ImagePlaceholder.albumArt,
),
),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
colorScheme.surface.withOpacity(0.5),
BlendMode.srcOver,
),
alignment: Alignment.topCenter,
),
),
),
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Skeletonizer(
enabled: trackQuery.isLoading,
child: Container(
alignment: Alignment.topCenter,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.surface,
Colors.transparent,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.2, 1],
),
),
child: SafeArea(
child: Wrap(
spacing: 20,
runSpacing: 20,
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
runAlignment: WrapAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: UniversalImage(
path: TypeConversionUtils.image_X_UrlString(
track.album!.images,
placeholder: ImagePlaceholder.albumArt,
),
height: 200,
width: 200,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: mediaQuery.smAndDown
? CrossAxisAlignment.center
: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
track.name!,
style: textTheme.titleLarge,
),
const Gap(10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(SpotubeIcons.album),
const Gap(5),
Flexible(
child: LinkText(
track.album!.name!,
'/album/${track.album!.id}',
push: true,
extra: track.album,
),
),
],
),
const Gap(10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(SpotubeIcons.artist),
const Gap(5),
TypeConversionUtils
.artists_X_ClickableArtists(
track.artists!,
),
],
),
const Gap(10),
ConstrainedBox(
constraints:
const BoxConstraints(maxWidth: 350),
child: Row(
mainAxisSize: mediaQuery.smAndDown
? MainAxisSize.max
: MainAxisSize.min,
children: [
const Gap(5),
if (!isActive &&
!playlist.tracks.contains(track))
OutlinedButton.icon(
icon: const Icon(SpotubeIcons.queueAdd),
label: Text(context.l10n.queue),
onPressed: () {
playlistNotifier.addTrack(track);
},
),
const Gap(5),
if (!isActive &&
!playlist.tracks.contains(track))
IconButton.outlined(
icon:
const Icon(SpotubeIcons.lightning),
tooltip: context.l10n.play_next,
onPressed: () {
playlistNotifier
.addTracksAtFirst([track]);
},
),
const Gap(5),
IconButton.filled(
tooltip: isActive
? context.l10n.pause_playback
: context.l10n.play,
icon: Icon(
isActive
? SpotubeIcons.pause
: SpotubeIcons.play,
color: colorScheme.onPrimary,
),
onPressed: onPlay,
),
const Gap(5),
if (mediaQuery.smAndDown)
const Spacer()
else
const Gap(20),
TrackHeartButton(track: track),
TrackOptions(
track: track,
userPlaylist: false,
),
const Gap(5),
],
),
),
],
),
),
],
),
),
),
),
),
),
],
),
);
}
}
2 changes: 2 additions & 0 deletions lib/services/queries/queries.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:spotube/services/queries/category.dart';
import 'package:spotube/services/queries/lyrics.dart';
import 'package:spotube/services/queries/playlist.dart';
import 'package:spotube/services/queries/search.dart';
import 'package:spotube/services/queries/tracks.dart';
import 'package:spotube/services/queries/user.dart';
import 'package:spotube/services/queries/views.dart';

Expand All @@ -17,6 +18,7 @@ class Queries {
final search = const SearchQueries();
final user = const UserQueries();
final views = const ViewsQueries();
final tracks = const TracksQueries();
}

const useQueries = Queries._();
Loading

0 comments on commit 988a975

Please sign in to comment.