artist + album search

This commit is contained in:
Moritz Weber 2021-07-15 19:27:48 +02:00
parent d90e323d82
commit c6b0bc4581
4 changed files with 72 additions and 12 deletions

View file

@ -2,12 +2,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../../domain/entities/song.dart'; import '../../domain/entities/song.dart';
import '../state/audio_store.dart'; import '../state/audio_store.dart';
import '../state/navigation_store.dart';
import '../state/search_page_store.dart'; import '../state/search_page_store.dart';
import '../theming.dart'; import '../theming.dart';
import '../widgets/album_art_list_tile.dart';
import '../widgets/song_bottom_sheet.dart'; import '../widgets/song_bottom_sheet.dart';
import '../widgets/song_list_tile.dart'; import '../widgets/song_list_tile.dart';
import 'album_details_page.dart';
import 'artist_details_page.dart';
class SearchPage extends StatefulWidget { class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key); const SearchPage({Key? key}) : super(key: key);
@ -23,6 +29,7 @@ class _SearchPageState extends State<SearchPage> {
final searchStore = GetIt.I<SearchPageStore>(); final searchStore = GetIt.I<SearchPageStore>();
final audioStore = GetIt.I<AudioStore>(); final audioStore = GetIt.I<AudioStore>();
final navStore = GetIt.I<NavigationStore>();
return SafeArea( return SafeArea(
child: Column( child: Column(
@ -40,11 +47,12 @@ class _SearchPageState extends State<SearchPage> {
hintStyle: TEXT_HEADER.copyWith(color: Colors.white), hintStyle: TEXT_HEADER.copyWith(color: Colors.white),
fillColor: Colors.white10, fillColor: Colors.white10,
filled: true, filled: true,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0), enabledBorder:
focusedBorder: const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0), const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
focusedBorder:
const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
), ),
onChanged: (text) { onChanged: (text) {
searchStore.search(text); searchStore.search(text);
}, },
@ -64,6 +72,36 @@ class _SearchPageState extends State<SearchPage> {
onTap: () => audioStore.playSong(0, [song]), onTap: () => audioStore.playSong(0, [song]),
onTapMore: () => SongBottomSheet()(song, context), onTapMore: () => SongBottomSheet()(song, context),
); );
} else if (results[index] is Album) {
final album = results[index] as Album;
return AlbumArtListTile(
title: album.title,
subtitle: album.artist,
albumArtPath: album.albumArtPath,
onTap: () {
navStore.pushOnLibrary(
MaterialPageRoute<Widget>(
builder: (BuildContext context) => AlbumDetailsPage(
album: album,
),
),
);
},
);
} else if (results[index] is Artist) {
final artist = results[index] as Artist;
return ListTile(
title: Text(artist.name),
onTap: () {
navStore.pushOnLibrary(
MaterialPageRoute<Widget>(
builder: (BuildContext context) => ArtistDetailsPage(
artist: artist,
),
),
);
},
);
} }
return Container(); return Container();
}, },

View file

@ -303,10 +303,22 @@ class MusicDataDao extends DatabaseAccessor<MoorDatabase>
} }
@override @override
Future<List> search(String searchText) { Future<List> search(String searchText, {int limit = 0}) async {
print('Search: $searchText'); List<dynamic> result = [];
return (select(songs)..where((tbl) => tbl.title.like(searchText))).get().then( result += await (select(artists)..where((tbl) => tbl.name.like(searchText))).get().then(
(moorSongList) => moorSongList.map((moorSong) => SongModel.fromMoor(moorSong)).toList()); (moorList) => moorList.map((moorArtist) => ArtistModel.fromMoor(moorArtist)).toList(),
);
result += await (select(albums)..where((tbl) => tbl.title.like(searchText))).get().then(
(moorList) => moorList.map((moorAlbum) => AlbumModel.fromMoor(moorAlbum)).toList(),
);
result += await (select(songs)..where((tbl) => tbl.title.like(searchText))).get().then(
(moorList) => moorList.map((moorSong) => SongModel.fromMoor(moorSong)).toList(),
);
if (limit <= 0) return result;
return result.take(limit).toList();
} }
} }

View file

@ -44,5 +44,5 @@ abstract class MusicDataSource {
Future<void> setAlbumOfDay(AlbumOfDay albumOfDay); Future<void> setAlbumOfDay(AlbumOfDay albumOfDay);
Future<AlbumOfDay?> getAlbumOfDay(); Future<AlbumOfDay?> getAlbumOfDay();
Future<List> search(String searchText); Future<List> search(String searchText, {int limit});
} }

View file

@ -230,15 +230,19 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
Future<List> search(String searchText) async { Future<List> search(String searchText) async {
if (searchText == '') return []; if (searchText == '') return [];
final searchTextLower = searchText.toLowerCase();
// TODO: need to clean the string? sql injection? // TODO: need to clean the string? sql injection?
final dbResult = await _musicDataSource.search(_fuzzy(searchText)); final dbResult = await _musicDataSource.search(_fuzzy(searchTextLower), limit: 200);
print(dbResult.length);
final List<List> ratedResults = []; final List<List> ratedResults = [];
for (final x in dbResult) { for (final x in dbResult) {
if (x is SongModel) { if (x is SongModel) {
// print('${x.title}: ${x.title.similarityTo(searchText)}'); ratedResults.add([_similarity(x.title.toLowerCase(), searchTextLower), x]);
ratedResults.add([x.title.similarityTo(searchText), x]); } else if (x is AlbumModel) {
ratedResults.add([_similarity(x.title.toLowerCase(), searchTextLower), x]);
} else if (x is ArtistModel) {
ratedResults.add([_similarity(x.name.toLowerCase(), searchTextLower), x]);
} }
} }
ratedResults.sort((List a, List b) => -(a[0] as double).compareTo(b[0] as double)); ratedResults.sort((List a, List b) => -(a[0] as double).compareTo(b[0] as double));
@ -247,6 +251,12 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
return results.toList(); return results.toList();
} }
double _similarity(String value, String searchText) {
return value.startsWith(searchText)
? value.similarityTo(searchText) + 1
: value.similarityTo(searchText);
}
String _fuzzy(String text) { String _fuzzy(String text) {
String fuzzyText = '%$text%'; String fuzzyText = '%$text%';
// for (final c in text.toLowerCase().replaceAll(' ', '').split('')) { // for (final c in text.toLowerCase().replaceAll(' ', '').split('')) {