Skip to content

Commit

Permalink
feat: add friend activity in home screen
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Jan 23, 2024
1 parent 74c47b3 commit 8c6c6a9
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 21 deletions.
32 changes: 32 additions & 0 deletions lib/collections/fake.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:spotify/spotify.dart';
import 'package:spotube/extensions/track.dart';
import 'package:spotube/models/spotify_friends.dart';

abstract class FakeData {
static final Image image = Image()
Expand Down Expand Up @@ -164,4 +165,35 @@ abstract class FakeData {
..icons = [image]
..id = "1"
..name = "category";

static final friends = SpotifyFriends(
friends: [
for (var i = 0; i < 3; i++)
SpotifyFriendActivity(
user: const SpotifyFriend(
name: "name",
imageUrl: "imageUrl",
uri: "uri",
),
track: SpotifyActivityTrack(
name: "name",
artist: const SpotifyActivityArtist(
name: "name",
uri: "uri",
),
album: const SpotifyActivityAlbum(
name: "name",
uri: "uri",
),
context: SpotifyActivityContext(
name: "name",
index: i,
uri: "uri",
),
imageUrl: "imageUrl",
uri: "uri",
),
),
],
);
}
96 changes: 96 additions & 0 deletions lib/components/home/sections/friends.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/home/sections/friends/friend_item.dart';
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
import 'package:spotube/models/spotify_friends.dart';
import 'package:spotube/services/queries/queries.dart';

class HomePageFriendsSection extends HookConsumerWidget {
const HomePageFriendsSection({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, ref) {
final friendsQuery = useQueries.user.friendActivity(ref);
final friends = friendsQuery.data?.friends ?? FakeData.friends.friends;

final groupCount = useBreakpointValue(
sm: 3,
xs: 2,
md: 4,
lg: 5,
xl: 6,
xxl: 7,
);

final friendGroup = friends.fold<List<List<SpotifyFriendActivity>>>(
[],
(previousValue, element) {
if (previousValue.isEmpty) {
return [
[element]
];
}

final lastGroup = previousValue.last;
if (lastGroup.length < groupCount) {
return [
...previousValue.sublist(0, previousValue.length - 1),
[...lastGroup, element]
];
}

return [
...previousValue,
[element]
];
},
);

if (friendsQuery.hasData &&
friendsQuery.data?.friends.isEmpty == true &&
!friendsQuery.isLoading) {
return const SliverToBoxAdapter(
child: SizedBox.shrink(),
);
}

return Skeletonizer.sliver(
enabled: friendsQuery.isLoading,
child: SliverMainAxisGroup(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Friends',
style: Theme.of(context).textTheme.titleMedium,
),
),
),
SliverToBoxAdapter(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final group in friendGroup)
Row(
children: [
for (final friend in group)
Padding(
padding: const EdgeInsets.all(8.0),
child: FriendItem(friend: friend),
),
],
),
],
),
),
),
],
),
);
}
}
136 changes: 136 additions & 0 deletions lib/components/home/sections/friends/friend_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/models/spotify_friends.dart';
import 'package:spotube/provider/spotify_provider.dart';

class FriendItem extends HookConsumerWidget {
final SpotifyFriendActivity friend;
const FriendItem({
Key? key,
required this.friend,
}) : super(key: key);

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

final queryClient = useQueryClient();
final spotify = ref.watch(spotifyProvider);

return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: colorScheme.surfaceVariant.withOpacity(0.3),
borderRadius: BorderRadius.circular(15),
),
constraints: const BoxConstraints(
minWidth: 300,
),
height: 80,
child: Row(
children: [
CircleAvatar(
backgroundImage: UniversalImage.imageProvider(
friend.user.imageUrl,
),
),
const Gap(8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
friend.user.name,
style: textTheme.bodyLarge,
),
RichText(
text: TextSpan(
style: textTheme.bodySmall,
children: [
TextSpan(
text: friend.track.name,
recognizer: TapGestureRecognizer()
..onTap = () {
context.push("/track/${friend.track.id}");
},
),
const TextSpan(text: " • "),
const WidgetSpan(
child: Icon(
SpotubeIcons.artist,
size: 12,
),
),
TextSpan(
text: " ${friend.track.artist.name}",
recognizer: TapGestureRecognizer()
..onTap = () {
context.push(
"/artist/${friend.track.artist.id}",
);
},
),
const TextSpan(text: "\n"),
TextSpan(
text: friend.track.context.name,
recognizer: TapGestureRecognizer()
..onTap = () async {
context.push(
"/${friend.track.context.path}",
extra: !friend.track.context.path
.startsWith("album")
? null
: await queryClient.fetchQuery<Album, dynamic>(
"album/${friend.track.album.id}",
() => spotify.albums.get(
friend.track.album.id,
),
),
);
},
),
const TextSpan(text: " • "),
const WidgetSpan(
child: Icon(
SpotubeIcons.album,
size: 12,
),
),
TextSpan(
text: " ${friend.track.album.name}",
recognizer: TapGestureRecognizer()
..onTap = () async {
final album =
await queryClient.fetchQuery<Album, dynamic>(
"album/${friend.track.album.id}",
() => spotify.albums.get(
friend.track.album.id,
),
);
if (context.mounted) {
context.push(
"/album/${friend.track.album.id}",
extra: album,
);
}
},
),
],
),
),
],
),
],
),
);
}
}
4 changes: 1 addition & 3 deletions lib/components/shared/page_window_title_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
import 'package:spotube/utils/platform.dart';
import 'package:titlebar_buttons/titlebar_buttons.dart';
import 'dart:math';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show Platform, exit;
import 'dart:io' show Platform;
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:local_notifier/local_notifier.dart';

class PageWindowTitleBar extends StatefulHookConsumerWidget
implements PreferredSizeWidget {
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,6 @@
"discord_rich_presence": "Discord Rich Presence",
"browse_all": "Browse All",
"genres": "Genres",
"explore_genres": "Explore Genres"
"explore_genres": "Explore Genres",
"friends": "Friends"
}
11 changes: 11 additions & 0 deletions lib/models/spotify_friends.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class SpotifyFriend {

factory SpotifyFriend.fromJson(Map<String, dynamic> json) =>
_$SpotifyFriendFromJson(json);

String get id => uri.split(":").last;
}

@JsonSerializable(createToJson: false)
Expand All @@ -27,6 +29,8 @@ class SpotifyActivityArtist {

factory SpotifyActivityArtist.fromJson(Map<String, dynamic> json) =>
_$SpotifyActivityArtistFromJson(json);

String get id => uri.split(":").last;
}

@JsonSerializable(createToJson: false)
Expand All @@ -38,6 +42,8 @@ class SpotifyActivityAlbum {

factory SpotifyActivityAlbum.fromJson(Map<String, dynamic> json) =>
_$SpotifyActivityAlbumFromJson(json);

String get id => uri.split(":").last;
}

@JsonSerializable(createToJson: false)
Expand All @@ -54,6 +60,9 @@ class SpotifyActivityContext {

factory SpotifyActivityContext.fromJson(Map<String, dynamic> json) =>
_$SpotifyActivityContextFromJson(json);

String get id => uri.split(":").last;
String get path => uri.split(":").skip(1).join("/");
}

@JsonSerializable(createToJson: false)
Expand All @@ -76,6 +85,8 @@ class SpotifyActivityTrack {

factory SpotifyActivityTrack.fromJson(Map<String, dynamic> json) =>
_$SpotifyActivityTrackFromJson(json);

String get id => uri.split(":").last;
}

@JsonSerializable(createToJson: false)
Expand Down
2 changes: 2 additions & 0 deletions lib/pages/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/components/home/sections/featured.dart';
import 'package:spotube/components/home/sections/friends.dart';
import 'package:spotube/components/home/sections/genres.dart';
import 'package:spotube/components/home/sections/made_for_user.dart';
import 'package:spotube/components/home/sections/new_releases.dart';
Expand Down Expand Up @@ -31,6 +32,7 @@ class HomePage extends HookConsumerWidget {
HomeNewReleasesSection(),
],
),
const HomePageFriendsSection(),
const SliverSafeArea(sliver: HomeMadeForUserSection()),
],
),
Expand Down
13 changes: 13 additions & 0 deletions lib/services/queries/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/hooks/spotify/use_spotify_query.dart';
import 'package:spotube/models/spotify_friends.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/custom_spotify_endpoint_provider.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

class UserQueries {
Expand Down Expand Up @@ -37,4 +39,15 @@ class UserQueries {
ref: ref,
);
}

Query<SpotifyFriends, dynamic> friendActivity(WidgetRef ref) {
final customSpotify = ref.read(customSpotifyEndpointProvider);
return useSpotifyQuery<SpotifyFriends, dynamic>(
"friend-activity",
(spotify) {
return customSpotify.getFriendActivity();
},
ref: ref,
);
}
}
Loading

0 comments on commit 8c6c6a9

Please sign in to comment.