WIP Search

This commit is contained in:
Henry Hiles 2025-01-03 20:59:59 -05:00
parent 94eb363245
commit a2ef7da707
5 changed files with 102 additions and 20 deletions

View file

@ -10,7 +10,7 @@ extension BetterWhen<T> on AsyncValue<T> {
when(
data: data,
error: (error, stackTrace) =>
Text("error"), // TODO: Better err reporting
Text("$error"), // TODO: Better err reporting
loading: loading,
skipLoadingOnRefresh: false,
);

View file

@ -1,8 +1,8 @@
enum SearchType {
any,
songs,
albums,
videos,
artists,
albums,
playlists,
}

View file

@ -0,0 +1,23 @@
import 'package:canal/models/search_type.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:canal/providers/ytmusic_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
part "search_provider.g.dart";
@riverpod
Future<IList<dynamic>> searchProvider(
Ref ref, {
required String search,
required SearchType searchType,
}) async {
final yt = await ytmusic(ref);
return IList(switch (searchType) {
SearchType.any => await yt.search(search),
SearchType.songs => await yt.searchSongs(search),
SearchType.albums => await yt.searchAlbums(search),
SearchType.videos => await yt.searchVideos(search),
SearchType.artists => await yt.searchAlbums(search),
SearchType.playlists => await yt.searchPlaylists(search),
});
}

View file

@ -1,10 +1,16 @@
import 'package:canal/models/search_type.dart';
import 'package:canal/widgets/select_button.dart';
import 'package:flutter/material.dart';
import 'package:canal/models/tab.dart';
import 'package:canal/helpers/extension_helper.dart';
import 'package:canal/providers/search_provider.dart';
import 'package:canal/widgets/thumbnail.dart';
import 'package:dart_ytmusic_api/types.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:canal/widgets/select_button.dart';
import 'package:canal/models/search_type.dart';
import 'package:canal/models/tab.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:yaru/yaru.dart';
class SearchTab extends HookWidget implements TabPage {
class SearchTab extends HookConsumerWidget implements TabPage {
const SearchTab({super.key});
@override
@ -14,13 +20,65 @@ class SearchTab extends HookWidget implements TabPage {
String get title => "Search";
@override
Widget build(BuildContext context) => ListView(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
children: [
SelectButton(
Widget build(BuildContext context, WidgetRef ref) {
final type = useState(SearchType.any);
final search = useState("");
final debouncedSearch = useDebounced(search, Duration(milliseconds: 250));
return ListView(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
children: [
YaruSearchField(
hintText: "Search YouTube music...",
fillColor: Theme.of(context).colorScheme.surface,
onChanged: (value) => search.value = value,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: SelectButton(
value: type.value,
values: SearchType.values,
defaultValue: SearchType.any,
)
],
);
onChanged: (value) => type.value = value,
),
),
Divider(),
SizedBox(height: 8),
ref
.watch(searchProviderProvider(
search: search.value, searchType: type.value))
.betterWhen(
data: (results) => Wrap(
children: results
.map((result) => SizedBox(
height: 64,
width: 64,
child: switch (result) {
SongDetailed _ => Thumbnail(
url: result.thumbnails.first.url,
onClick: () {},
),
AlbumDetailed _ => Thumbnail(
url: result.thumbnails.first.url,
onClick: () {},
),
VideoDetailed _ => Thumbnail(
url: result.thumbnails.first.url,
onClick: () {},
),
ArtistDetailed _ => Thumbnail(
url: result.thumbnails.first.url,
onClick: () {},
),
PlaylistDetailed _ => Thumbnail(
url: result.thumbnails.first.url,
onClick: () {},
),
_ => throw Exception(
"Unknown Detailed Result: ${result.runtimeType}",
),
}))
.toList())),
],
);
}
}

View file

@ -3,17 +3,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class SelectButton<T extends Enum> extends HookWidget {
final T defaultValue;
final T value;
final List<T> values;
final Function(T newValue) onChanged;
const SelectButton({
required this.defaultValue,
required this.value,
required this.values,
required this.onChanged,
super.key,
});
@override
Widget build(BuildContext context) {
final selected = useState(defaultValue);
final oldValue = useState(<T>{});
return SegmentedButton(
segments: values
@ -23,10 +24,10 @@ class SelectButton<T extends Enum> extends HookWidget {
))
.toList(),
onSelectionChanged: (newValue) {
selected.value = newValue.difference(oldValue.value).first;
onChanged(newValue.difference(oldValue.value).first);
oldValue.value = newValue;
},
selected: {selected.value},
selected: {value},
);
}
}