refactor: genres section

This commit is contained in:
Kingkor Roy Tirtho 2025-01-06 20:45:37 +06:00
parent 6dd9b753b0
commit bf94a490bb
4 changed files with 236 additions and 76 deletions

View File

@ -408,5 +408,6 @@
"add_all_to_playlist": "Add all to playlist", "add_all_to_playlist": "Add all to playlist",
"add_all_to_queue": "Add all to queue", "add_all_to_queue": "Add all to queue",
"play_all_next": "Play all next", "play_all_next": "Play all next",
"pause": "Pause" "pause": "Pause",
"view_all": "View all"
} }

View File

@ -4,14 +4,16 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/extensions/string.dart';
import 'package:spotube/pages/home/genres/genre_playlists.dart'; import 'package:spotube/pages/home/genres/genre_playlists.dart';
import 'package:spotube/pages/home/genres/genres.dart'; import 'package:spotube/pages/home/genres/genres.dart';
import 'package:spotube/pages/playlist/playlist.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
class HomeGenresSection extends HookConsumerWidget { class HomeGenresSection extends HookConsumerWidget {
@ -19,7 +21,8 @@ class HomeGenresSection extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final mediaQuery = MediaQuery.of(context); final theme = context.theme;
final mediaQuery = MediaQuery.sizeOf(context);
final categoriesQuery = ref.watch(categoriesProvider); final categoriesQuery = ref.watch(categoriesProvider);
final categories = useMemoized( final categories = useMemoized(
@ -28,7 +31,9 @@ class HomeGenresSection extends HookConsumerWidget {
.where((c) => (c.icons?.length ?? 0) > 0) .where((c) => (c.icons?.length ?? 0) > 0)
.take(mediaQuery.mdAndDown ? 6 : 10) .take(mediaQuery.mdAndDown ? 6 : 10)
.toList() ?? .toList() ??
<Category>[], [
FakeData.category,
],
[mediaQuery.mdAndDown, categoriesQuery.asData?.value], [mediaQuery.mdAndDown, categoriesQuery.asData?.value],
); );
@ -61,51 +66,175 @@ class HomeGenresSection extends HookConsumerWidget {
), ),
), ),
const SliverGap(8), const SliverGap(8),
SliverPadding( SliverToBoxAdapter(
padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox(
sliver: Skeletonizer.sliver( height: 280 * theme.scaling,
enabled: categoriesQuery.isLoading, child: Carousel(
child: SliverGrid.builder( transition: const CarouselTransition.sliding(gap: 24),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( sizeConstraint: CarouselSizeConstraint.fixed(
maxCrossAxisExtent: mediaQuery.mdAndDown ? 200 : 250, mediaQuery.mdAndUp
mainAxisExtent: 50, ? mediaQuery.width * .6
crossAxisSpacing: 16, : mediaQuery.width * .95,
mainAxisSpacing: 16,
), ),
itemCount: categoriesQuery.isLoading itemCount: categories.length,
? mediaQuery.mdAndDown autoplaySpeed: const Duration(seconds: 2),
? 6 duration: const Duration(seconds: 5),
: 10 pauseOnHover: true,
: categories.length, direction: Axis.horizontal,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final category = final category = categories[index];
categories.elementAtOrNull(index) ?? FakeData.category; final playlists =
ref.watch(categoryPlaylistsProvider(category.id!));
final playlistsData = playlists.asData?.value.items.take(8) ??
List.generate(5, (index) => FakeData.playlistSimple);
return Button( return Container(
style: ButtonVariance.secondary.copyWith( margin: const EdgeInsets.symmetric(horizontal: 8),
padding: (context, states, value) { padding: const EdgeInsets.all(16),
return EdgeInsets.zero; decoration: BoxDecoration(
}, borderRadius: theme.borderRadiusXxl,
border: Border.all(
color: theme.colorScheme.border,
width: 1,
), ),
onPressed: () {}, image: DecorationImage(
child: CardImage( image: UniversalImage.imageProvider(
category.icons!.first.url!,
),
colorFilter: ColorFilter.mode(
theme.colorScheme.background.withAlpha(125),
BlendMode.darken,
),
fit: BoxFit.cover,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
category.name!,
style: const TextStyle(color: Colors.white),
).h3(),
Button.link(
onPressed: () { onPressed: () {
context.pushNamed( context.pushNamed(
GenrePlaylistsPage.name, GenrePlaylistsPage.name,
pathParameters: { pathParameters: {'categoryId': category.id!},
"categoryId": category.id!,
},
extra: category, extra: category,
); );
}, },
direction: Axis.horizontal, child: Text(
image: UniversalImage( context.l10n.view_all,
path: category.icons!.first.url!, style: const TextStyle(color: Colors.white),
fit: BoxFit.cover, ).muted(),
height: 50,
width: 50,
), ),
title: Text(category.name!), ],
),
Expanded(
child: Skeleton.ignore(
child: Skeletonizer(
enabled: playlists.isLoading,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
spacing: 12,
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (final playlist in playlistsData)
Container(
width: 115 * theme.scaling,
decoration: BoxDecoration(
color: theme.colorScheme.background
.withAlpha(75),
borderRadius: theme.borderRadiusMd,
),
child: SurfaceBlur(
borderRadius: theme.borderRadiusMd,
surfaceBlur: theme.surfaceBlur,
child: Button(
style:
ButtonVariance.secondary.copyWith(
padding: (context, states, value) =>
const EdgeInsets.all(8),
decoration:
(context, states, value) {
final decoration = ButtonVariance
.secondary
.decoration(
context, states)
as BoxDecoration;
if (states.isNotEmpty) {
return decoration;
}
return decoration.copyWith(
color: decoration.color
?.withAlpha(180),
);
},
),
onPressed: () {
context.pushNamed(
PlaylistPage.name,
pathParameters: {
"id": playlist.id!,
},
extra: playlist,
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
spacing: 5,
children: [
ClipRRect(
borderRadius:
theme.borderRadiusSm,
child: UniversalImage(
path: (playlist.images)!
.asUrlString(
placeholder:
ImagePlaceholder
.collection,
index: 1,
),
fit: BoxFit.cover,
height: 100 * theme.scaling,
width: 100 * theme.scaling,
),
),
Text(
playlist.name!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
).semiBold().small(),
if (playlist.description != null)
Text(
playlist.description
?.unescapeHtml()
.cleanHtml() ??
"",
maxLines: 2,
overflow:
TextOverflow.ellipsis,
).xSmall().muted(),
],
),
),
),
),
],
),
),
),
),
)
],
), ),
); );
}, },

View File

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:html/dom.dart' hide Text; import 'package:html/dom.dart' hide Text;
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Element;
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/modules/library/user_local_tracks.dart'; import 'package:spotube/modules/library/user_local_tracks.dart';
import 'package:spotube/modules/root/update_dialog.dart'; import 'package:spotube/modules/root/update_dialog.dart';
@ -20,7 +21,6 @@ import 'package:html/parser.dart' as parser;
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart' hide Element;
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/env.dart';
@ -304,7 +304,9 @@ abstract class ServiceUtils {
.map((e) => e.matchedLocation); .map((e) => e.matchedLocation);
if (routerState.matchedLocation == location || if (routerState.matchedLocation == location ||
routerStack.contains(location)) return; routerStack.contains(location)) {
return;
}
router.push(location, extra: extra); router.push(location, extra: extra);
} }
@ -418,7 +420,7 @@ abstract class ServiceUtils {
await showDialog( await showDialog(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
barrierColor: Colors.black26, barrierColor: Colors.black.withAlpha(66),
builder: (context) { builder: (context) {
return RootAppUpdateDialog.nightly(nightlyBuildNum: buildNum); return RootAppUpdateDialog.nightly(nightlyBuildNum: buildNum);
}, },
@ -439,14 +441,16 @@ abstract class ServiceUtils {
if (currentVersion == null || if (currentVersion == null ||
latestVersion == null || latestVersion == null ||
(latestVersion.isPreRelease && !currentVersion.isPreRelease) || (latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return; (!latestVersion.isPreRelease && currentVersion.isPreRelease)) {
return;
}
if (latestVersion <= currentVersion || !context.mounted) return; if (latestVersion <= currentVersion || !context.mounted) return;
showDialog( showDialog(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
barrierColor: Colors.black26, barrierColor: Colors.black.withAlpha(66),
builder: (context) { builder: (context) {
return RootAppUpdateDialog(version: latestVersion); return RootAppUpdateDialog(version: latestVersion);
}, },

View File

@ -8,7 +8,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"bn": [ "bn": [
@ -20,7 +21,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ca": [ "ca": [
@ -32,7 +34,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"cs": [ "cs": [
@ -44,7 +47,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"de": [ "de": [
@ -56,7 +60,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"es": [ "es": [
@ -68,7 +73,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"eu": [ "eu": [
@ -80,7 +86,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"fa": [ "fa": [
@ -92,7 +99,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"fi": [ "fi": [
@ -104,7 +112,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"fr": [ "fr": [
@ -116,7 +125,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"hi": [ "hi": [
@ -128,7 +138,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"id": [ "id": [
@ -140,7 +151,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"it": [ "it": [
@ -152,7 +164,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ja": [ "ja": [
@ -164,7 +177,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ka": [ "ka": [
@ -176,7 +190,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ko": [ "ko": [
@ -188,7 +203,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ne": [ "ne": [
@ -200,7 +216,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"nl": [ "nl": [
@ -212,7 +229,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"pl": [ "pl": [
@ -224,7 +242,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"pt": [ "pt": [
@ -236,7 +255,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"ru": [ "ru": [
@ -248,7 +268,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"th": [ "th": [
@ -260,7 +281,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"tr": [ "tr": [
@ -272,7 +294,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"uk": [ "uk": [
@ -284,7 +307,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"vi": [ "vi": [
@ -296,7 +320,8 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
], ],
"zh": [ "zh": [
@ -308,6 +333,7 @@
"add_all_to_playlist", "add_all_to_playlist",
"add_all_to_queue", "add_all_to_queue",
"play_all_next", "play_all_next",
"pause" "pause",
"view_all"
] ]
} }