spotube/lib/components/root/sidebar.dart

261 lines
8.5 KiB
Dart

import 'package:badges/badges.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/provider/auth_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
class Sidebar extends HookConsumerWidget {
final int selectedIndex;
final void Function(int) onSelectedIndexChanged;
final Widget child;
const Sidebar({
required this.selectedIndex,
required this.onSelectedIndexChanged,
required this.child,
Key? key,
}) : super(key: key);
static Widget brandLogo() {
return Image.asset(
"assets/spotube-logo.png",
height: 50,
width: 50,
);
}
static void goToSettings(BuildContext context) {
GoRouter.of(context).go("/settings");
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final breakpoints = useBreakpoints();
final downloadCount = ref.watch(
downloaderProvider.select((s) => s.currentlyRunning),
);
final layoutMode =
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
if (breakpoints.isMd) {
return Row(
children: [
NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: onSelectedIndexChanged,
labelType: NavigationRailLabelType.all,
extended: false,
backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor,
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: brandLogo(),
),
trailing: PlatformIconButton(
icon: const Icon(fluent_ui.FluentIcons.settings),
onPressed: () => goToSettings(context),
),
destinations: [
for (final e in sidebarTileList)
NavigationRailDestination(
icon: Badge(
badgeColor: PlatformTheme.of(context).primaryColor!,
showBadge: e.title == "Library" && downloadCount > 0,
badgeContent: Text(
downloadCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
child: Icon(e.icon),
),
label: PlatformText.label(
e.title,
style: selectedIndex == sidebarTileList.indexOf(e)
? TextStyle(
color: PlatformTheme.of(context).primaryColor,
fontWeight: FontWeight.bold,
)
: null,
),
),
],
),
Container(
width: 1,
height: double.infinity,
color: PlatformTheme.of(context).borderColor,
),
Expanded(child: child)
],
);
}
if (layoutMode == LayoutMode.compact ||
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
return PlatformScaffold(body: child);
}
return SafeArea(
top: false,
child: PlatformSidebar(
currentIndex: selectedIndex,
onIndexChanged: onSelectedIndexChanged,
body: Map.fromEntries(
sidebarTileList.map(
(e) {
final icon = Icon(e.icon);
return MapEntry(
PlatformSidebarItem(
icon: Badge(
badgeColor: PlatformTheme.of(context).primaryColor!,
showBadge: e.title == "Library" && downloadCount > 0,
badgeContent: Text(
downloadCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
child: icon,
),
title: Text(
e.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
child,
);
},
),
),
expanded: true,
header: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
brandLogo(),
const SizedBox(
width: 10,
),
PlatformText.headline("Spotube"),
],
),
),
windowsFooterItems: [
fluent_ui.PaneItemAction(
icon: const fluent_ui.Icon(fluent_ui.FluentIcons.settings),
onTap: () => goToSettings(context),
),
],
footer: const SidebarFooter(),
),
);
}
}
class SidebarFooter extends HookConsumerWidget {
const SidebarFooter({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final auth = ref.watch(authProvider);
return SizedBox(
width: 256,
child: HookBuilder(
builder: (context) {
final me = useQuery(
job: Queries.user.me,
externalData: ref.watch(spotifyProvider),
);
final data = me.data;
final avatarImg = TypeConversionUtils.image_X_UrlString(
data?.images,
index: (data?.images?.length ?? 1) - 1,
placeholder: ImagePlaceholder.artist,
);
useEffect(() {
if (auth.isLoggedIn && !me.hasData) {
me.setExternalData(ref.read(spotifyProvider));
me.refetch();
}
return;
}, [auth.isLoggedIn, me.hasData]);
return Padding(
padding: const EdgeInsets.all(16).copyWith(left: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (auth.isLoggedIn && data == null)
const Center(
child: PlatformCircularProgressIndicator(),
)
else if (data != null)
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CircleAvatar(
backgroundImage:
UniversalImage.imageProvider(avatarImg),
onBackgroundImageError: (exception, stackTrace) =>
Image.asset(
"assets/user-placeholder.png",
height: 16,
width: 16,
),
),
const SizedBox(
width: 10,
),
Flexible(
child: Text(
data.displayName ?? "Guest",
maxLines: 1,
softWrap: false,
overflow: TextOverflow.fade,
style: PlatformTheme.of(context)
.textTheme
?.body
?.copyWith(fontWeight: FontWeight.bold),
),
),
],
),
),
PlatformIconButton(
icon: const Icon(Icons.settings_outlined),
onPressed: () => Sidebar.goToSettings(context)),
],
));
},
),
);
}
}