Skip to content

Commit

Permalink
feat: add playlists to localaudio view and search (#980)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feichtmeier authored Oct 26, 2024
1 parent 13fefb6 commit 5293dca
Show file tree
Hide file tree
Showing 16 changed files with 170 additions and 46 deletions.
4 changes: 3 additions & 1 deletion lib/app/view/scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ class _DiscordConnectContent extends StatelessWidget {
),
Icon(
TablerIcons.brand_discord_filled,
color: context.theme.primaryColor,
color: context.theme.snackBarTheme.backgroundColor != null
? contrastColor(context.theme.snackBarTheme.backgroundColor!)
: null,
),
],
),
Expand Down
21 changes: 7 additions & 14 deletions lib/common/view/audio_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,13 @@ class _AudioCardState extends State<AudioCard> {
Positioned(
bottom: 10,
right: 10,
child: CircleAvatar(
radius: avatarIconRadius,
backgroundColor: theme.colorScheme.primary,
child: IconButton(
onPressed: widget.onPlay,
icon: Padding(
padding: appleStyled
? const EdgeInsets.only(left: 3)
: EdgeInsets.zero,
child: Icon(
Iconz.playFilled,
color: contrastColor(theme.colorScheme.primary),
),
),
child: FloatingActionButton.small(
onPressed: widget.onPlay,
elevation: 0.5,
backgroundColor: Colors.white,
child: Icon(
Iconz.playFilled,
color: Colors.black,
),
),
),
Expand Down
12 changes: 11 additions & 1 deletion lib/local_audio/local_audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef LocalSearchResult = ({
List<String>? artists,
List<String>? albums,
List<String>? genres,
List<String>? playlists,
});

class LocalAudioService {
Expand Down Expand Up @@ -178,7 +179,15 @@ class LocalAudioService {

LocalSearchResult? search(String? query) {
if (query == null) return null;
if (query.isEmpty) return (titles: [], artists: [], albums: [], genres: []);
if (query.isEmpty) {
return (
titles: [],
artists: [],
albums: [],
genres: [],
playlists: [],
);
}

final allAlbumsFindings =
allAlbums?.where((e) => e.toLowerCase().contains(query.toLowerCase()));
Expand Down Expand Up @@ -222,6 +231,7 @@ class LocalAudioService {
artists: allArtists
?.where((a) => a.toLowerCase().contains(query.toLowerCase()))
.toList(),
playlists: [],
);
}

Expand Down
6 changes: 6 additions & 0 deletions lib/local_audio/view/local_audio_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'album_view.dart';
import 'artists_view.dart';
import 'genres_view.dart';
import 'local_audio_view.dart';
import 'playlists_view.dart';
import 'titles_view.dart';

class LocalAudioBody extends StatelessWidget {
Expand All @@ -15,6 +16,7 @@ class LocalAudioBody extends StatelessWidget {
required this.artists,
required this.albums,
required this.genres,
required this.playlists,
this.noResultMessage,
this.noResultIcon,
});
Expand All @@ -24,6 +26,7 @@ class LocalAudioBody extends StatelessWidget {
final List<String>? artists;
final List<String>? albums;
final List<String>? genres;
final List<String>? playlists;
final Widget? noResultMessage, noResultIcon;

@override
Expand All @@ -49,6 +52,9 @@ class LocalAudioBody extends StatelessWidget {
noResultMessage: noResultMessage,
noResultIcon: noResultIcon,
),
LocalAudioView.playlists => PlaylistsView(
playlists: playlists ?? [],
)
};
}
}
45 changes: 26 additions & 19 deletions lib/local_audio/view/local_audio_control_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,32 @@ class LocalAudioControlPanel extends StatelessWidget with WatchItMixin {

return Align(
alignment: Alignment.center,
child: YaruChoiceChipBar(
chipBackgroundColor: chipColor(theme),
selectedChipBackgroundColor: chipSelectionColor(theme, false),
borderColor: chipBorder(theme, false),
yaruChoiceChipBarStyle: YaruChoiceChipBarStyle.wrap,
selectedFirst: false,
clearOnSelect: false,
labels: LocalAudioView.values
.map(
(e) => Text(
e.localize(context.l10n),
style: chipTextStyle(theme),
),
)
.toList(),
isSelected: LocalAudioView.values
.map((e) => e == LocalAudioView.values[index])
.toList(),
onSelected: (index) => di<LocalAudioModel>().localAudioindex = index,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: YaruChoiceChipBar(
chipBackgroundColor: chipColor(theme),
selectedChipBackgroundColor: chipSelectionColor(theme, false),
borderColor: chipBorder(theme, false),
yaruChoiceChipBarStyle: YaruChoiceChipBarStyle.wrap,
selectedFirst: false,
clearOnSelect: false,
labels: LocalAudioView.values
.map(
(e) => Text(
e.localize(context.l10n),
style: chipTextStyle(theme),
),
)
.toList(),
isSelected: LocalAudioView.values
.map((e) => e == LocalAudioView.values[index])
.toList(),
onSelected: (index) =>
di<LocalAudioModel>().localAudioindex = index,
),
),
),
);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/local_audio/view/local_audio_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class _LocalAudioPageState extends State<LocalAudioPage> {
final allArtists = watchPropertyValue((LocalAudioModel m) => m.allArtists);
final allAlbums = watchPropertyValue((LocalAudioModel m) => m.allAlbums);
final allGenres = watchPropertyValue((LocalAudioModel m) => m.allGenres);
final playlists =
watchPropertyValue((LibraryModel m) => m.playlists.keys.toList());
final index = watchPropertyValue((LocalAudioModel m) => m.localAudioindex);
final localAudioView = LocalAudioView.values[index];

Expand Down Expand Up @@ -101,6 +103,7 @@ class _LocalAudioPageState extends State<LocalAudioPage> {
albums: allAlbums,
artists: allArtists,
genres: allGenres,
playlists: playlists,
noResultIcon: const AnimatedEmoji(AnimatedEmojis.bird),
noResultMessage: Column(
mainAxisSize: MainAxisSize.min,
Expand Down
4 changes: 3 additions & 1 deletion lib/local_audio/view/local_audio_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ enum LocalAudioView {
titles,
artists,
albums,
genres;
genres,
playlists;

String localize(AppLocalizations l10n) {
return switch (this) {
titles => l10n.titles,
artists => l10n.artists,
albums => l10n.albums,
genres => l10n.genres,
playlists => l10n.playlists,
};
}
}
78 changes: 78 additions & 0 deletions lib/local_audio/view/playlists_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';
import 'package:yaru/yaru.dart';

import '../../common/view/icons.dart';
import '../../common/view/round_image_container.dart';
import '../../constants.dart';
import '../../extensions/build_context_x.dart';
import '../../library/library_model.dart';
import '../../playlists/view/manual_add_dialog.dart';
import '../../playlists/view/playlist_page.dart';

class PlaylistsView extends StatelessWidget {
const PlaylistsView({
super.key,
this.noResultMessage,
this.noResultIcon,
required this.playlists,
});

final List<String>? playlists;
final Widget? noResultMessage, noResultIcon;

@override
Widget build(BuildContext context) {
final lists = [
kNewPlaylistPageId,
...(playlists ?? []),
];

return SliverGrid.builder(
itemCount: lists.length,
gridDelegate: kDiskGridDelegate,
itemBuilder: (context, index) {
final id = lists.elementAt(index);
return YaruSelectableContainer(
selected: false,
onTap: () => id == kNewPlaylistPageId
? showDialog(
context: context,
builder: (context) => const ManualAddDialog(),
)
: di<LibraryModel>().push(
builder: (_) => PlaylistPage(
pageId: id,
),
pageId: id,
),
borderRadius: BorderRadius.circular(300),
child: Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: double.infinity,
height: double.infinity,
child: id == kNewPlaylistPageId
? Container(
decoration: BoxDecoration(
color: context.colorScheme.surface.scale(
lightness: context.colorScheme.isDark ? 0.1 : -0.1,
),
shape: BoxShape.circle,
),
child: Icon(Iconz.plus),
)
: RoundImageContainer(
images: const {},
fallBackText: id,
),
),
if (id != kNewPlaylistPageId) ArtistVignette(text: id),
],
),
);
},
);
}
}
3 changes: 1 addition & 2 deletions lib/player/view/volume_popup.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:phoenix_theme/phoenix_theme.dart'
hide CustomTrackShape, BuildContextX;
import 'package:phoenix_theme/phoenix_theme.dart' hide CustomTrackShape;
import 'package:watch_it/watch_it.dart';

import '../../common/view/icons.dart';
Expand Down
17 changes: 16 additions & 1 deletion lib/search/search_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,20 @@ class SearchModel extends SafeChangeNotifier {

Future<LocalSearchResult?> localSearch(String? query) async {
await Future.delayed(const Duration(microseconds: 1));
return _localAudioService.search(_searchQuery);
final search = _localAudioService.search(_searchQuery);
return (
albums: search?.albums,
titles: search?.titles,
genres: search?.genres,
artists: search?.artists,
playlists: (query != null && query.isNotEmpty)
? _libraryService.playlists.keys.toList()
: _libraryService.playlists.keys
.where(
(e) => e.toLowerCase().contains(query?.toLowerCase() ?? ''),
)
.toList()
);
}

static const _podcastDefaultLimit = 32;
Expand Down Expand Up @@ -299,6 +312,8 @@ class SearchModel extends SafeChangeNotifier {
setSearchType(SearchType.localArtist);
} else if (localSearchResult?.genres?.isNotEmpty == true) {
setSearchType(SearchType.localGenreName);
} else if (localSearchResult?.playlists?.isNotEmpty == true) {
setSearchType(SearchType.localPlaylists);
}
}
}).then(
Expand Down
2 changes: 2 additions & 0 deletions lib/search/search_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum SearchType {
localArtist,
localAlbum,
localGenreName,
localPlaylists,
radioName,
radioTag,
radioCountry,
Expand All @@ -17,6 +18,7 @@ enum SearchType {
localArtist => l10n.artists,
localAlbum => l10n.albums,
localGenreName => l10n.genres,
localPlaylists => l10n.playlists,
radioName => l10n.name,
radioTag => l10n.tag,
podcastTitle => l10n.title,
Expand Down
2 changes: 1 addition & 1 deletion lib/search/view/search_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class SearchPage extends StatelessWidget with WatchItMixin {
},
title: switch (audioType) {
AudioType.podcast => const SliverPodcastFilterBar(),
_ => const SliverSearchTypeFilterBar(),
_ => const SearchTypeFilterBar(),
},
),
SliverPadding(
Expand Down
6 changes: 5 additions & 1 deletion lib/search/view/sliver_local_search_results.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class _SliverLocalSearchResultState extends State<SliverLocalSearchResult> {
SearchType.localAlbum => LocalAudioView.albums,
SearchType.localArtist => LocalAudioView.artists,
SearchType.localTitle => LocalAudioView.titles,
_ => LocalAudioView.genres,
SearchType.localGenreName => LocalAudioView.genres,
_ => LocalAudioView.playlists,
},
);

Expand All @@ -55,6 +56,8 @@ class _SliverLocalSearchResultState extends State<SliverLocalSearchResult> {
watchPropertyValue((SearchModel m) => m.localSearchResult?.albums);
final genresResult =
watchPropertyValue((SearchModel m) => m.localSearchResult?.genres);
final playlistsResult =
watchPropertyValue((SearchModel m) => m.localSearchResult?.playlists);

final searchQuery = watchPropertyValue((SearchModel m) => m.searchQuery);

Expand All @@ -73,6 +76,7 @@ class _SliverLocalSearchResultState extends State<SliverLocalSearchResult> {
artists: artists,
albums: albums,
genres: genresResult,
playlists: playlistsResult,
);
}
}
7 changes: 5 additions & 2 deletions lib/search/view/sliver_search_type_filter_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import '../../radio/view/radio_reconnect_button.dart';
import '../search_model.dart';
import '../search_type.dart';

class SliverSearchTypeFilterBar extends StatelessWidget with WatchItMixin {
const SliverSearchTypeFilterBar({super.key});
class SearchTypeFilterBar extends StatelessWidget with WatchItMixin {
const SearchTypeFilterBar({super.key});

@override
Widget build(BuildContext context) {
Expand All @@ -27,6 +27,7 @@ class SliverSearchTypeFilterBar extends StatelessWidget with WatchItMixin {
final searchQuery = watchPropertyValue((SearchModel m) => m.searchQuery);

return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
Expand Down Expand Up @@ -89,6 +90,8 @@ class SliverSearchTypeFilterBar extends StatelessWidget with WatchItMixin {
'(${localSearchResult?.artists?.length ?? '0'})',
SearchType.localGenreName =>
'(${localSearchResult?.genres?.length ?? '0'})',
SearchType.localPlaylists =>
'(${localSearchResult?.playlists?.length ?? '0'})',
_ => ''
}}';
}
Loading

0 comments on commit 5293dca

Please sign in to comment.