From 6e357230ac87c621c7823168b3db09ba171a5350 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 23 Jan 2025 20:34:04 +0600 Subject: [PATCH] refactor: use expanded sidebar tiles for library --- .vscode/launch.json | 11 ++ lib/collections/intents.dart | 31 +++++- lib/collections/routes.dart | 105 ++++++++++++------ lib/collections/side_bar_tiles.dart | 40 +++++-- lib/collections/spotube_icons.dart | 2 + .../presentation_state.dart | 2 +- .../sort_tracks_dropdown.dart | 2 +- lib/main.dart | 24 +++- lib/modules/connect/connect_device.dart | 12 ++ lib/modules/root/sidebar.dart | 99 +++++++++++++---- .../getting_started/getting_started.dart | 55 ++++----- lib/pages/library/library.dart | 92 +++++++-------- lib/pages/library/local_folder.dart | 3 +- .../library/user_albums.dart | 5 +- .../library/user_artists.dart | 5 +- .../library/user_downloads.dart | 5 +- .../library/user_local_tracks.dart | 5 +- .../library/user_playlists.dart | 5 +- lib/pages/lyrics/lyrics.dart | 86 ++++++-------- lib/utils/service_utils.dart | 2 +- macos/Podfile.lock | 52 ++++----- 21 files changed, 403 insertions(+), 240 deletions(-) rename lib/{modules => pages}/library/user_albums.dart (97%) rename lib/{modules => pages}/library/user_artists.dart (98%) rename lib/{modules => pages}/library/user_downloads.dart (92%) rename lib/{modules => pages}/library/user_local_tracks.dart (96%) rename lib/{modules => pages}/library/user_playlists.dart (97%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a1e8b9b..deabf1d3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -30,6 +30,17 @@ "request": "launch", "program": "lib/main.dart", "flutterMode": "release" + }, + { + "name": "spotube (mobile) (release)", + "type": "dart", + "request": "launch", + "program": "lib/main.dart", + "flutterMode": "release", + "args": [ + "--flavor", + "dev" + ] } ], "compounds": [] diff --git a/lib/collections/intents.dart b/lib/collections/intents.dart index 4f446831..1ec62812 100644 --- a/lib/collections/intents.dart +++ b/lib/collections/intents.dart @@ -7,7 +7,11 @@ import 'package:go_router/go_router.dart'; import 'package:spotube/collections/routes.dart'; import 'package:spotube/modules/player/player_controls.dart'; import 'package:spotube/pages/home/home.dart'; -import 'package:spotube/pages/library/library.dart'; +import 'package:spotube/pages/library/user_albums.dart'; +import 'package:spotube/pages/library/user_artists.dart'; +import 'package:spotube/pages/library/user_downloads.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_playlists.dart'; import 'package:spotube/pages/lyrics/lyrics.dart'; import 'package:spotube/pages/search/search.dart'; import 'package:spotube/provider/audio_player/querying_track_info.dart'; @@ -52,8 +56,13 @@ class NavigationAction extends Action { enum HomeTabs { browse, search, - library, + lyrics, + userPlaylists, + userArtists, + userAlbums, + userLocalLibrary, + userDownloads, } class HomeTabIntent extends Intent { @@ -73,12 +82,24 @@ class HomeTabAction extends Action { case HomeTabs.search: router.goNamed(SearchPage.name); break; - case HomeTabs.library: - router.goNamed(LibraryPage.name); - break; case HomeTabs.lyrics: router.goNamed(LyricsPage.name); break; + case HomeTabs.userPlaylists: + router.goNamed(UserPlaylistsPage.name); + break; + case HomeTabs.userArtists: + router.goNamed(UserArtistsPage.name); + break; + case HomeTabs.userAlbums: + router.goNamed(UserAlbumsPage.name); + break; + case HomeTabs.userLocalLibrary: + router.goNamed(UserLocalLibraryPage.name); + break; + case HomeTabs.userDownloads: + router.goNamed(UserDownloadsPage.name); + break; } return null; } diff --git a/lib/collections/routes.dart b/lib/collections/routes.dart index a0380e29..25cf995c 100644 --- a/lib/collections/routes.dart +++ b/lib/collections/routes.dart @@ -16,6 +16,11 @@ import 'package:spotube/pages/lastfm_login/lastfm_login.dart'; import 'package:spotube/pages/library/local_folder.dart'; import 'package:spotube/pages/library/playlist_generate/playlist_generate.dart'; import 'package:spotube/pages/library/playlist_generate/playlist_generate_result.dart'; +import 'package:spotube/pages/library/user_albums.dart'; +import 'package:spotube/pages/library/user_artists.dart'; +import 'package:spotube/pages/library/user_downloads.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_playlists.dart'; import 'package:spotube/pages/lyrics/mini_lyrics.dart'; import 'package:spotube/pages/playlist/liked_playlist.dart'; import 'package:spotube/pages/playlist/playlist.dart'; @@ -99,45 +104,73 @@ final routerProvider = Provider((ref) { pageBuilder: (context, state) => const SpotubePage(child: SearchPage()), ), + ShellRoute( + pageBuilder: (context, state, child) => + SpotubePage(child: LibraryPage(child: child)), + routes: [ + GoRoute( + path: "/library/playlists", + name: UserPlaylistsPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: UserPlaylistsPage()), + ), + GoRoute( + path: "/library/artists", + name: UserArtistsPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: UserArtistsPage()), + ), + GoRoute( + path: "/library/album", + name: UserAlbumsPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: UserAlbumsPage()), + ), + GoRoute( + path: "/library/local", + name: UserLocalLibraryPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: UserLocalLibraryPage()), + ), + GoRoute( + path: "/library/downloads", + name: UserDownloadsPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: UserDownloadsPage()), + ), + ], + ), GoRoute( - path: "/library", - name: LibraryPage.name, - pageBuilder: (context, state) => - const SpotubePage(child: LibraryPage()), - routes: [ - GoRoute( - path: "generate", - name: PlaylistGeneratorPage.name, - pageBuilder: (context, state) => - const SpotubePage(child: PlaylistGeneratorPage()), - routes: [ - GoRoute( - path: "result", - name: PlaylistGenerateResultPage.name, - pageBuilder: (context, state) => SpotubePage( - child: PlaylistGenerateResultPage( - state: state.extra as GeneratePlaylistProviderInput, - ), - ), - ) - ], + path: "/library/generate", + name: PlaylistGeneratorPage.name, + pageBuilder: (context, state) => + const SpotubePage(child: PlaylistGeneratorPage()), + routes: [ + GoRoute( + path: "result", + name: PlaylistGenerateResultPage.name, + pageBuilder: (context, state) => SpotubePage( + child: PlaylistGenerateResultPage( + state: state.extra as GeneratePlaylistProviderInput, + ), ), - GoRoute( - path: "local", - name: LocalLibraryPage.name, - pageBuilder: (context, state) { - assert(state.extra is String); - return SpotubePage( - child: LocalLibraryPage( - state.extra as String, - isDownloads: - state.uri.queryParameters["downloads"] != null, - isCache: state.uri.queryParameters["cache"] != null, - ), - ); - }, + ) + ], + ), + GoRoute( + path: "/library/local", + name: LocalLibraryPage.name, + pageBuilder: (context, state) { + assert(state.extra is String); + return SpotubePage( + child: LocalLibraryPage( + state.extra as String, + isDownloads: state.uri.queryParameters["downloads"] != null, + isCache: state.uri.queryParameters["cache"] != null, ), - ]), + ); + }, + ), GoRoute( path: "/lyrics", name: LyricsPage.name, diff --git a/lib/collections/side_bar_tiles.dart b/lib/collections/side_bar_tiles.dart index 4f23c049..26396e39 100644 --- a/lib/collections/side_bar_tiles.dart +++ b/lib/collections/side_bar_tiles.dart @@ -2,7 +2,10 @@ import 'package:flutter/material.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:spotube/pages/home/home.dart'; -import 'package:spotube/pages/library/library.dart'; +import 'package:spotube/pages/library/user_albums.dart'; +import 'package:spotube/pages/library/user_artists.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_playlists.dart'; import 'package:spotube/pages/lyrics/lyrics.dart'; import 'package:spotube/pages/search/search.dart'; import 'package:spotube/pages/stats/stats.dart'; @@ -34,12 +37,6 @@ List getSidebarTileList(AppLocalizations l10n) => [ icon: SpotubeIcons.search, title: l10n.search, ), - SideBarTiles( - id: "library", - name: LibraryPage.name, - icon: SpotubeIcons.library, - title: l10n.library, - ), SideBarTiles( id: "lyrics", name: LyricsPage.name, @@ -54,6 +51,33 @@ List getSidebarTileList(AppLocalizations l10n) => [ ), ]; +List getSidebarLibraryTileList(AppLocalizations l10n) => [ + SideBarTiles( + id: "playlists", + title: l10n.playlists, + name: UserPlaylistsPage.name, + icon: SpotubeIcons.playlist, + ), + SideBarTiles( + id: "artists", + title: l10n.artists, + name: UserArtistsPage.name, + icon: SpotubeIcons.artist, + ), + SideBarTiles( + id: "albums", + title: l10n.albums, + name: UserAlbumsPage.name, + icon: SpotubeIcons.album, + ), + SideBarTiles( + id: "local_library", + title: l10n.local_library, + name: UserLocalLibraryPage.name, + icon: SpotubeIcons.device, + ), + ]; + List getNavbarTileList(AppLocalizations l10n) => [ SideBarTiles( id: "browse", @@ -69,7 +93,7 @@ List getNavbarTileList(AppLocalizations l10n) => [ ), SideBarTiles( id: "library", - name: LibraryPage.name, + name: UserPlaylistsPage.name, icon: SpotubeIcons.library, title: l10n.library, ), diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index 9239875e..56192d18 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -37,6 +37,7 @@ abstract class SpotubeIcons { static const share = FeatherIcons.share2; static const playlistAdd = Icons.playlist_add_rounded; static const playlistRemove = Icons.playlist_remove_rounded; + static const playlist = Icons.playlist_play_rounded; static const trash = FeatherIcons.trash2; static const clock = FeatherIcons.clock; static const lyrics = Icons.lyrics_rounded; @@ -132,4 +133,5 @@ abstract class SpotubeIcons { static const radioUnchecked = Icons.radio_button_off_rounded; static const grid = FeatherIcons.grid; static const list = FeatherIcons.list; + static const device = FeatherIcons.smartphone; } diff --git a/lib/components/track_presentation/presentation_state.dart b/lib/components/track_presentation/presentation_state.dart index 11ca9809..91adbfcd 100644 --- a/lib/components/track_presentation/presentation_state.dart +++ b/lib/components/track_presentation/presentation_state.dart @@ -2,7 +2,7 @@ import 'package:collection/collection.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; -import 'package:spotube/modules/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/utils/service_utils.dart'; diff --git a/lib/components/track_presentation/sort_tracks_dropdown.dart b/lib/components/track_presentation/sort_tracks_dropdown.dart index 543bacb3..ecab615f 100644 --- a/lib/components/track_presentation/sort_tracks_dropdown.dart +++ b/lib/components/track_presentation/sort_tracks_dropdown.dart @@ -1,6 +1,6 @@ import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/collections/spotube_icons.dart'; -import 'package:spotube/modules/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; import 'package:spotube/components/adaptive/adaptive_pop_sheet_list.dart'; import 'package:spotube/extensions/context.dart'; diff --git a/lib/main.dart b/lib/main.dart index af7da1b7..bbf161c5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -264,12 +264,32 @@ class Spotube extends HookConsumerWidget { LogicalKeyboardKey.digit3, LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - ): HomeTabIntent(ref, tab: HomeTabs.library), + ): HomeTabIntent(ref, tab: HomeTabs.lyrics), LogicalKeySet( LogicalKeyboardKey.digit4, LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - ): HomeTabIntent(ref, tab: HomeTabs.lyrics), + ): HomeTabIntent(ref, tab: HomeTabs.userPlaylists), + LogicalKeySet( + LogicalKeyboardKey.digit5, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, + ): HomeTabIntent(ref, tab: HomeTabs.userArtists), + LogicalKeySet( + LogicalKeyboardKey.digit6, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, + ): HomeTabIntent(ref, tab: HomeTabs.userAlbums), + LogicalKeySet( + LogicalKeyboardKey.digit7, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, + ): HomeTabIntent(ref, tab: HomeTabs.userLocalLibrary), + LogicalKeySet( + LogicalKeyboardKey.digit8, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, + ): HomeTabIntent(ref, tab: HomeTabs.userDownloads), LogicalKeySet( LogicalKeyboardKey.keyW, LogicalKeyboardKey.control, diff --git a/lib/modules/connect/connect_device.dart b/lib/modules/connect/connect_device.dart index be9826a3..a285284c 100644 --- a/lib/modules/connect/connect_device.dart +++ b/lib/modules/connect/connect_device.dart @@ -1,6 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/pages/connect/connect.dart'; import 'package:spotube/provider/connect/clients.dart'; @@ -19,6 +20,17 @@ class ConnectDeviceButton extends HookConsumerWidget { connectClients.asData?.value.services.isNotEmpty == true; if (_sidebar) { + final mediaQuery = MediaQuery.sizeOf(context); + + if (mediaQuery.mdAndDown) { + return IconButton.ghost( + icon: const Icon(SpotubeIcons.speaker), + onPressed: () { + ServiceUtils.pushNamed(context, ConnectPage.name); + }, + ); + } + return SizedBox( width: double.infinity, child: Button.primary( diff --git a/lib/modules/root/sidebar.dart b/lib/modules/root/sidebar.dart index 9a92a1cb..1d5d9da0 100644 --- a/lib/modules/root/sidebar.dart +++ b/lib/modules/root/sidebar.dart @@ -3,7 +3,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/side_bar_tiles.dart'; @@ -14,6 +13,7 @@ import 'package:spotube/models/database/database.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/modules/connect/connect_device.dart'; +import 'package:spotube/pages/library/user_downloads.dart'; import 'package:spotube/pages/profile/profile.dart'; import 'package:spotube/pages/settings/settings.dart'; import 'package:spotube/provider/authentication/authentication.dart'; @@ -47,8 +47,6 @@ class Sidebar extends HookConsumerWidget { final routerState = GoRouterState.of(context); final mediaQuery = MediaQuery.of(context); - final downloadCount = ref.watch(downloadManagerProvider).$downloadCount; - final layoutMode = ref.watch(userPreferencesProvider.select((s) => s.layoutMode)); @@ -57,7 +55,14 @@ class Sidebar extends HookConsumerWidget { [context.l10n], ); - final selectedIndex = sidebarTileList.indexWhere( + final sidebarLibraryTileList = useMemoized( + () => getSidebarLibraryTileList(context.l10n), + [context.l10n], + ); + + final tileList = [...sidebarTileList, ...sidebarLibraryTileList]; + + final selectedIndex = tileList.indexWhere( (e) => routerState.namedLocation(e.name) == routerState.matchedLocation, ); @@ -73,16 +78,8 @@ class Sidebar extends HookConsumerWidget { for (final tile in sidebarTileList) NavigationButton( label: mediaQuery.lgAndUp ? Text(tile.title) : null, - child: Badge( - backgroundColor: context.theme.colorScheme.primary, - isLabelVisible: tile.title == "Library" && downloadCount > 0, - label: Text( - downloadCount.toString(), - style: TextStyle( - color: context.theme.colorScheme.primaryForeground, - fontSize: 10, - ), - ), + child: Tooltip( + tooltip: TooltipContainer(child: Text(tile.title)), child: Icon(tile.icon), ), onChanged: (value) { @@ -91,6 +88,22 @@ class Sidebar extends HookConsumerWidget { } }, ), + const NavigationDivider(), + if (mediaQuery.lgAndUp) + NavigationLabel(child: Text(context.l10n.library)), + for (final tile in sidebarLibraryTileList) + NavigationButton( + label: mediaQuery.lgAndUp ? Text(tile.title) : null, + onChanged: (value) { + if (value) { + context.goNamed(tile.name); + } + }, + child: Tooltip( + tooltip: TooltipContainer(child: Text(tile.title)), + child: Icon(tile.icon), + ), + ), ]; return Row( @@ -103,7 +116,7 @@ class Sidebar extends HookConsumerWidget { ? NavigationSidebar( index: selectedIndex, onSelected: (index) { - final tile = sidebarTileList[index]; + final tile = tileList[index]; context.goNamed(tile.name); }, children: navigationButtons, @@ -112,7 +125,7 @@ class Sidebar extends HookConsumerWidget { alignment: NavigationRailAlignment.start, index: selectedIndex, onSelected: (index) { - final tile = sidebarTileList[index]; + final tile = tileList[index]; context.goNamed(tile.name); }, children: navigationButtons, @@ -138,8 +151,10 @@ class SidebarFooter extends HookConsumerWidget implements NavigationBarItem { Widget build(BuildContext context, ref) { final theme = Theme.of(context); final mediaQuery = MediaQuery.of(context); - final me = ref.watch(meProvider); - final data = me.asData?.value; + final routerState = GoRouterState.of(context); + final downloadCount = ref.watch(downloadManagerProvider).$downloadCount; + final userSnapshot = ref.watch(meProvider); + final data = userSnapshot.asData?.value; final avatarImg = (data?.images).asUrlString( index: (data?.images?.length ?? 1) - 1, @@ -149,10 +164,30 @@ class SidebarFooter extends HookConsumerWidget implements NavigationBarItem { final auth = ref.watch(authenticationProvider); if (mediaQuery.mdAndDown) { - return IconButton( - variance: ButtonVariance.ghost, - icon: const Icon(SpotubeIcons.settings), - onPressed: () => ServiceUtils.navigateNamed(context, SettingsPage.name), + return Column( + mainAxisSize: MainAxisSize.min, + spacing: 10, + children: [ + Badge( + isLabelVisible: downloadCount > 0, + label: Text(downloadCount.toString()), + child: IconButton( + variance: routerState.topRoute?.name == UserDownloadsPage.name + ? ButtonVariance.secondary + : ButtonVariance.ghost, + icon: const Icon(SpotubeIcons.download), + onPressed: () => + ServiceUtils.navigateNamed(context, UserDownloadsPage.name), + ), + ), + const ConnectDeviceButton.sidebar(), + IconButton( + variance: ButtonVariance.ghost, + icon: const Icon(SpotubeIcons.settings), + onPressed: () => + ServiceUtils.navigateNamed(context, SettingsPage.name), + ), + ], ); } @@ -161,9 +196,27 @@ class SidebarFooter extends HookConsumerWidget implements NavigationBarItem { width: 180, child: Column( mainAxisSize: MainAxisSize.min, + spacing: 10, children: [ + SizedBox( + width: double.infinity, + child: Button( + style: routerState.topRoute?.name == UserDownloadsPage.name + ? ButtonVariance.secondary + : ButtonVariance.outline, + onPressed: () { + ServiceUtils.navigateNamed(context, UserDownloadsPage.name); + }, + leading: const Icon(SpotubeIcons.download), + trailing: downloadCount > 0 + ? PrimaryBadge( + child: Text(downloadCount.toString()), + ) + : null, + child: Text(context.l10n.downloads), + ), + ), const ConnectDeviceButton.sidebar(), - const Gap(10), Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/pages/getting_started/getting_started.dart b/lib/pages/getting_started/getting_started.dart index 2931a782..f710bd8f 100644 --- a/lib/pages/getting_started/getting_started.dart +++ b/lib/pages/getting_started/getting_started.dart @@ -34,32 +34,35 @@ class GettingStarting extends HookConsumerWidget { return Scaffold( headers: [ - TitleBar( - backgroundColor: Colors.transparent, - surfaceBlur: 0, - trailing: [ - ListenableBuilder( - listenable: pageController, - builder: (context, _) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: pageController.hasClients && - (pageController.page == 0 || pageController.page == 3) - ? const SizedBox() - : Button.secondary( - onPressed: () { - pageController.animateToPage( - 3, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - child: Text(context.l10n.skip_this_nonsense), - ), - ); - }, - ), - ], + SafeArea( + child: TitleBar( + backgroundColor: Colors.transparent, + surfaceBlur: 0, + trailing: [ + ListenableBuilder( + listenable: pageController, + builder: (context, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: pageController.hasClients && + (pageController.page == 0 || + pageController.page == 3) + ? const SizedBox() + : Button.secondary( + onPressed: () { + pageController.animateToPage( + 3, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + }, + child: Text(context.l10n.skip_this_nonsense), + ), + ); + }, + ), + ], + ), ), ], floatingHeader: true, diff --git a/lib/pages/library/library.dart b/lib/pages/library/library.dart index ec896228..7ae5a0e4 100644 --- a/lib/pages/library/library.dart +++ b/lib/pages/library/library.dart @@ -1,71 +1,63 @@ import 'package:flutter/material.dart' show Badge; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:spotube/modules/library/user_local_tracks.dart'; +import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; -import 'package:spotube/modules/library/user_albums.dart'; -import 'package:spotube/modules/library/user_artists.dart'; -import 'package:spotube/modules/library/user_downloads.dart'; -import 'package:spotube/modules/library/user_playlists.dart'; +import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/download_manager_provider.dart'; class LibraryPage extends HookConsumerWidget { - static const name = "library"; + final Widget child; + const LibraryPage({super.key, required this.child}); - const LibraryPage({super.key}); @override Widget build(BuildContext context, ref) { final downloadingCount = ref.watch(downloadManagerProvider).$downloadCount; - final index = useState(0); - - final children = [ - Text(context.l10n.playlists), - Text(context.l10n.local_tab), - Badge( - isLabelVisible: downloadingCount > 0, - label: Text(downloadingCount.toString()), - child: Text(context.l10n.downloads), - ), - Text(context.l10n.artists), - Text(context.l10n.albums), - ]; + final routerState = GoRouterState.of(context); + final sidebarLibraryTileList = useMemoized( + () => getSidebarLibraryTileList(context.l10n), + [context.l10n], + ); + final index = sidebarLibraryTileList.indexWhere( + (e) => routerState.namedLocation(e.name) == routerState.matchedLocation, + ); return SafeArea( bottom: false, - child: Scaffold( - headers: [ - TitleBar( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: TabList( - index: index.value, - children: [ - for (final child in children) - TabButton( - child: child, - onPressed: () { - index.value = children.indexOf(child); - }, - ), - ], + child: LayoutBuilder(builder: (context, constraints) { + return Scaffold( + headers: [ + if (constraints.smAndDown) + TitleBar( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: TabList( + index: index, + children: [ + for (final tile in sidebarLibraryTileList) + TabButton( + child: Badge( + isLabelVisible: + tile.id == 'downloads' && downloadingCount > 0, + label: Text(downloadingCount.toString()), + child: Text(tile.title), + ), + onPressed: () { + context.goNamed(tile.name); + }, + ), + ], + ), + ), ), - ), - ), - const Gap(10), - ], - child: IndexedStack( - index: index.value, - children: const [ - UserPlaylists(), - UserLocalTracks(), - UserDownloads(), - UserArtists(), - UserAlbums(), + const Gap(10), ], - ), - ), + child: child, + ); + }), ); } } diff --git a/lib/pages/library/local_folder.dart b/lib/pages/library/local_folder.dart index 84873b84..b8cdd096 100644 --- a/lib/pages/library/local_folder.dart +++ b/lib/pages/library/local_folder.dart @@ -16,9 +16,8 @@ import 'package:spotube/components/button/back_button.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/string.dart'; import 'package:spotube/modules/library/local_folder/cache_export_dialog.dart'; -import 'package:spotube/modules/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; import 'package:spotube/components/expandable_search/expandable_search.dart'; -import 'package:spotube/components/fallbacks/not_found.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/track_presentation/sort_tracks_dropdown.dart'; diff --git a/lib/modules/library/user_albums.dart b/lib/pages/library/user_albums.dart similarity index 97% rename from lib/modules/library/user_albums.dart rename to lib/pages/library/user_albums.dart index 8d55cf80..861d1705 100644 --- a/lib/modules/library/user_albums.dart +++ b/lib/pages/library/user_albums.dart @@ -16,8 +16,9 @@ import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/spotify/spotify.dart'; -class UserAlbums extends HookConsumerWidget { - const UserAlbums({super.key}); +class UserAlbumsPage extends HookConsumerWidget { + static const name = 'user_albums'; + const UserAlbumsPage({super.key}); @override Widget build(BuildContext context, ref) { diff --git a/lib/modules/library/user_artists.dart b/lib/pages/library/user_artists.dart similarity index 98% rename from lib/modules/library/user_artists.dart rename to lib/pages/library/user_artists.dart index fd23f426..aacf4102 100644 --- a/lib/modules/library/user_artists.dart +++ b/lib/pages/library/user_artists.dart @@ -19,8 +19,9 @@ import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/spotify/spotify.dart'; -class UserArtists extends HookConsumerWidget { - const UserArtists({super.key}); +class UserArtistsPage extends HookConsumerWidget { + static const name = 'user_artists'; + const UserArtistsPage({super.key}); @override Widget build(BuildContext context, ref) { diff --git a/lib/modules/library/user_downloads.dart b/lib/pages/library/user_downloads.dart similarity index 92% rename from lib/modules/library/user_downloads.dart rename to lib/pages/library/user_downloads.dart index 5db30342..871e21ab 100644 --- a/lib/modules/library/user_downloads.dart +++ b/lib/pages/library/user_downloads.dart @@ -6,8 +6,9 @@ import 'package:spotube/modules/library/user_downloads/download_item.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/download_manager_provider.dart'; -class UserDownloads extends HookConsumerWidget { - const UserDownloads({super.key}); +class UserDownloadsPage extends HookConsumerWidget { + static const name = 'user_downloads'; + const UserDownloadsPage({super.key}); @override Widget build(BuildContext context, ref) { diff --git a/lib/modules/library/user_local_tracks.dart b/lib/pages/library/user_local_tracks.dart similarity index 96% rename from lib/modules/library/user_local_tracks.dart rename to lib/pages/library/user_local_tracks.dart index 86b36dfb..66c011e5 100644 --- a/lib/modules/library/user_local_tracks.dart +++ b/lib/pages/library/user_local_tracks.dart @@ -24,8 +24,9 @@ enum SortBy { album, } -class UserLocalTracks extends HookConsumerWidget { - const UserLocalTracks({super.key}); +class UserLocalLibraryPage extends HookConsumerWidget { + static const name = 'user_local_library'; + const UserLocalLibraryPage({super.key}); @override Widget build(BuildContext context, ref) { diff --git a/lib/modules/library/user_playlists.dart b/lib/pages/library/user_playlists.dart similarity index 97% rename from lib/modules/library/user_playlists.dart rename to lib/pages/library/user_playlists.dart index 06f08ab6..a4711e1b 100644 --- a/lib/modules/library/user_playlists.dart +++ b/lib/pages/library/user_playlists.dart @@ -20,8 +20,9 @@ import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/service_utils.dart'; -class UserPlaylists extends HookConsumerWidget { - const UserPlaylists({super.key}); +class UserPlaylistsPage extends HookConsumerWidget { + static const name = 'user_playlists'; + const UserPlaylistsPage({super.key}); @override Widget build(BuildContext context, ref) { diff --git a/lib/pages/lyrics/lyrics.dart b/lib/pages/lyrics/lyrics.dart index f61dbbe3..85798eda 100644 --- a/lib/pages/lyrics/lyrics.dart +++ b/lib/pages/lyrics/lyrics.dart @@ -8,7 +8,6 @@ import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/image.dart'; -import 'package:spotube/hooks/utils/use_custom_status_bar_color.dart'; import 'package:spotube/hooks/utils/use_palette_color.dart'; import 'package:spotube/pages/lyrics/plain_lyrics.dart'; import 'package:spotube/pages/lyrics/synced_lyrics.dart'; @@ -33,15 +32,8 @@ class LyricsPage extends HookConsumerWidget { [playlist.activeTrack?.album?.images], ); final palette = usePaletteColor(albumArt, ref); - final route = ModalRoute.of(context); final selectedIndex = useState(0); - final resetStatusBar = useCustomStatusBarColor( - palette.color, - route?.isCurrent ?? false, - noSetBGColor: true, - ); - Widget tabbar = Padding( padding: const EdgeInsets.all(10), child: isModal @@ -93,51 +85,47 @@ class LyricsPage extends HookConsumerWidget { ); if (isModal) { - return PopScope( - canPop: true, - onPopInvokedWithResult: (_, __) => resetStatusBar(), - child: SafeArea( - bottom: false, - child: SurfaceCard( - surfaceBlur: context.theme.surfaceBlur, - surfaceOpacity: context.theme.surfaceOpacity, - padding: EdgeInsets.zero, - borderRadius: BorderRadius.zero, - borderWidth: 0, - child: Column( - children: [ - const SizedBox(height: 5), - Container( - height: 7, - width: 150, - decoration: BoxDecoration( - color: palette.titleTextColor, - borderRadius: BorderRadius.circular(10), - ), + return SafeArea( + bottom: false, + child: SurfaceCard( + surfaceBlur: context.theme.surfaceBlur, + surfaceOpacity: context.theme.surfaceOpacity, + padding: EdgeInsets.zero, + borderRadius: BorderRadius.zero, + borderWidth: 0, + child: Column( + children: [ + const SizedBox(height: 20), + Container( + height: 7, + width: 150, + decoration: BoxDecoration( + color: palette.titleTextColor, + borderRadius: BorderRadius.circular(10), ), - Row( + ), + Row( + children: [ + Expanded( + child: tabbar, + ), + IconButton.ghost( + icon: const Icon(SpotubeIcons.minimize), + onPressed: () => Navigator.of(context).pop(), + ), + const SizedBox(width: 5), + ], + ), + Expanded( + child: IndexedStack( + index: selectedIndex.value, children: [ - Expanded( - child: tabbar, - ), - IconButton.ghost( - icon: const Icon(SpotubeIcons.minimize), - onPressed: () => Navigator.of(context).pop(), - ), - const SizedBox(width: 5), + SyncedLyrics(palette: palette, isModal: isModal), + PlainLyrics(palette: palette, isModal: isModal), ], ), - Expanded( - child: IndexedStack( - index: selectedIndex.value, - children: [ - SyncedLyrics(palette: palette, isModal: isModal), - PlainLyrics(palette: palette, isModal: isModal), - ], - ), - ), - ], - ), + ), + ], ), ), ); diff --git a/lib/utils/service_utils.dart b/lib/utils/service_utils.dart index 2abb98ef..bc57dd4a 100644 --- a/lib/utils/service_utils.dart +++ b/lib/utils/service_utils.dart @@ -6,7 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:html/dom.dart' hide Text; import 'package:shadcn_flutter/shadcn_flutter.dart' hide Element; import 'package:spotify/spotify.dart'; -import 'package:spotube/modules/library/user_local_tracks.dart'; +import 'package:spotube/pages/library/user_local_tracks.dart'; import 'package:spotube/modules/root/update_dialog.dart'; import 'package:spotube/models/lyrics.dart'; diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 44da1f81..95feb26d 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -168,35 +168,35 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a - audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9 - audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 - bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842 - connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695 - desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a - device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 - file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d - flutter_discord_rpc: 67a7c10ea24d9d3bf35d01af643f48fbcfa7c24f - flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b - flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 + app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468 + audio_service: 0d9e4e25347bb3efb768f3b9f005911a81e587a7 + audio_session: 48ab6500f7a5e7c64363e206565a5dfe5a0c1441 + bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e + connectivity_plus: 2256d3e20624a7749ed21653aafe291a46446fee + desktop_webview_window: 2f0cdefecc06e21208a51589bd3d1580a87a703c + device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 + file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 + flutter_discord_rpc: 90614fcca26f3cebfd33263557ea7875936d184b + flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d + flutter_secure_storage_macos: b2d62a774c23b060f0b99d0173b0b36abb4a8632 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff - media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da - media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5 - metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435 - open_file_mac: 0e554648e2a87ce59e9438e3e5ca3e552e90d89a + local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e + media_kit_libs_macos_audio: 06f3cf88d6d89c7c3c87eae57689d1c6adb335b2 + media_kit_native_event_loop: a5833d1e4d5bedb6f691e9909fa57f15f436f2c8 + metadata_god: 8029e6ff4b1400ae4f13c38d2c478e8633f0e58b + open_file_mac: 01874b6d6a2c1485ac9b126d7105b99102dea2cf OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 - package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71 - sqlite3_flutter_libs: 1b4e98da20ebd4e9b1240269b78cdcf492dbe9f3 - system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc - tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 - url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + sqlite3_flutter_libs: f0b59f6bb2a18597d0796558725007e5a7428397 + system_theme: ed74293ad07d3a05e3e2d0059ff342360346f1a0 + tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166 + url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 + window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0