mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: use expanded sidebar tiles for library
This commit is contained in:
parent
2411f46877
commit
6e357230ac
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
@ -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": []
|
||||
|
@ -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<NavigationIntent> {
|
||||
enum HomeTabs {
|
||||
browse,
|
||||
search,
|
||||
library,
|
||||
|
||||
lyrics,
|
||||
userPlaylists,
|
||||
userArtists,
|
||||
userAlbums,
|
||||
userLocalLibrary,
|
||||
userDownloads,
|
||||
}
|
||||
|
||||
class HomeTabIntent extends Intent {
|
||||
@ -73,12 +82,24 @@ class HomeTabAction extends Action<HomeTabIntent> {
|
||||
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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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<SideBarTiles> 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<SideBarTiles> getSidebarTileList(AppLocalizations l10n) => [
|
||||
),
|
||||
];
|
||||
|
||||
List<SideBarTiles> 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<SideBarTiles> getNavbarTileList(AppLocalizations l10n) => [
|
||||
SideBarTiles(
|
||||
id: "browse",
|
||||
@ -69,7 +93,7 @@ List<SideBarTiles> getNavbarTileList(AppLocalizations l10n) => [
|
||||
),
|
||||
SideBarTiles(
|
||||
id: "library",
|
||||
name: LibraryPage.name,
|
||||
name: UserPlaylistsPage.name,
|
||||
icon: SpotubeIcons.library,
|
||||
title: l10n.library,
|
||||
),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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) {
|
@ -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) {
|
@ -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) {
|
@ -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) {
|
@ -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) {
|
@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -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';
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user