From 8376a69d0bb56bcf0bddbcaa8b5a6e8ffe2ce9e2 Mon Sep 17 00:00:00 2001 From: Feichtmeier <frederik.feichtmeier@gmail.com> Date: Wed, 7 Feb 2024 14:41:26 +0100 Subject: [PATCH] Let users find the podcast on player artist tap --- lib/podcasts.dart | 1 + lib/src/app/master_items.dart | 2 - lib/src/common/audio_page_control_panel.dart | 29 ++--- lib/src/common/confirmation_dialog.dart | 10 +- .../player/bottom_player_title_artist.dart | 25 ++-- .../player/full_height_title_and_artist.dart | 43 ++++--- lib/src/player/player_utils.dart | 21 +++- lib/src/podcasts/podcast_audio_tile.dart | 1 - lib/src/podcasts/podcast_page.dart | 14 +-- lib/src/podcasts/podcast_utils.dart | 104 ++++++++++++++++ .../podcasts/podcasts_collection_body.dart | 9 +- lib/src/podcasts/podcasts_discover_grid.dart | 113 +----------------- 12 files changed, 195 insertions(+), 177 deletions(-) create mode 100644 lib/src/podcasts/podcast_utils.dart diff --git a/lib/podcasts.dart b/lib/podcasts.dart index fe69a33ea..20ced1389 100644 --- a/lib/podcasts.dart +++ b/lib/podcasts.dart @@ -2,4 +2,5 @@ export 'src/podcasts/podcast_audio_tile.dart'; export 'src/podcasts/podcast_model.dart'; export 'src/podcasts/podcast_page.dart'; export 'src/podcasts/podcast_service.dart'; +export 'src/podcasts/podcast_utils.dart'; export 'src/podcasts/podcasts_page.dart'; diff --git a/lib/src/app/master_items.dart b/lib/src/app/master_items.dart index 6b0fcf775..a8db8df2e 100644 --- a/lib/src/app/master_items.dart +++ b/lib/src/app/master_items.dart @@ -99,8 +99,6 @@ List<MasterItem> createMasterItems({ podcast.value.firstOrNull?.title ?? podcast.value.firstOrNull.toString(), audios: podcast.value, - addPodcast: libraryModel.addPodcast, - removePodcast: libraryModel.removePodcast, imageUrl: podcast.value.firstOrNull?.albumArtUrl ?? podcast.value.firstOrNull?.imageUrl, ), diff --git a/lib/src/common/audio_page_control_panel.dart b/lib/src/common/audio_page_control_panel.dart index 3e5f8f746..fddbec00e 100644 --- a/lib/src/common/audio_page_control_panel.dart +++ b/lib/src/common/audio_page_control_panel.dart @@ -43,26 +43,15 @@ class AudioPageControlPanel extends StatelessWidget { backgroundColor: theme.colorScheme.inverseSurface, child: IconButton( onPressed: () { - if (audios.length > kAudioQueueThreshHold) { - showDialog<bool>( - context: context, - builder: (context) { - return ConfirmationDialog( - message: Text( - context.l10n.queueConfirmMessage( - audios.length.toString(), - ), - ), - ); - }, - ).then((value) { - if (value == true) { - onTap!(); - } - }); - } else { - onTap!(); - } + runOrConfirm( + context: context, + noConfirm: audios.length < kAudioQueueThreshHold, + message: context.l10n.queueConfirmMessage( + audios.length.toString(), + ), + run: () => onTap!(), + onCancel: () {}, + ); }, icon: Padding( padding: appleStyled diff --git a/lib/src/common/confirmation_dialog.dart b/lib/src/common/confirmation_dialog.dart index 33771db45..0309ab612 100644 --- a/lib/src/common/confirmation_dialog.dart +++ b/lib/src/common/confirmation_dialog.dart @@ -4,9 +4,8 @@ import 'package:yaru_widgets/constants.dart'; import '../../build_context_x.dart'; import '../l10n/l10n.dart'; -class ConfirmationDialog extends StatelessWidget { - const ConfirmationDialog({ - super.key, +class _ConfirmationDialog extends StatelessWidget { + const _ConfirmationDialog({ required this.message, }); @@ -64,6 +63,7 @@ void runOrConfirm({ required bool noConfirm, required String message, required Function run, + required Function onCancel, }) { if (noConfirm) { run(); @@ -71,7 +71,7 @@ void runOrConfirm({ showDialog<bool>( context: context, builder: (context) { - return ConfirmationDialog( + return _ConfirmationDialog( message: Text( context.l10n.queueConfirmMessage( message, @@ -82,6 +82,8 @@ void runOrConfirm({ ).then((value) { if (value == true) { run(); + } else { + onCancel(); } }); } diff --git a/lib/src/player/bottom_player_title_artist.dart b/lib/src/player/bottom_player_title_artist.dart index b9292d2ce..4427b83d5 100644 --- a/lib/src/player/bottom_player_title_artist.dart +++ b/lib/src/player/bottom_player_title_artist.dart @@ -18,6 +18,17 @@ class BottomPlayerTitleArtist extends StatelessWidget { final icyName = mpvMetaData?.icyName; final icyTitle = mpvMetaData?.icyTitle; + final subTitle = icyName?.isNotEmpty == true + ? icyName! + : (audio?.audioType == AudioType.podcast + ? audio?.album + : audio?.artist ?? ' ') ?? + ''; + + final title = icyTitle?.isNotEmpty == true + ? icyTitle! + : (audio?.title?.isNotEmpty == true ? audio!.title! : ' '); + return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, @@ -30,13 +41,9 @@ class BottomPlayerTitleArtist extends StatelessWidget { context: context, ), child: Tooltip( - message: icyTitle?.isNotEmpty == true - ? icyTitle! - : (audio?.title?.isNotEmpty == true ? audio!.title! : ' '), + message: title, child: Text( - icyTitle?.isNotEmpty == true - ? icyTitle! - : (audio?.title?.isNotEmpty == true ? audio!.title! : ' '), + title, style: const TextStyle( fontWeight: FontWeight.w400, fontSize: 14, @@ -56,11 +63,9 @@ class BottomPlayerTitleArtist extends StatelessWidget { context: context, ), child: Tooltip( - message: icyName?.isNotEmpty == true - ? icyName! - : (audio?.artist ?? ' '), + message: subTitle, child: Text( - icyName?.isNotEmpty == true ? icyName! : (audio?.artist ?? ' '), + subTitle, style: TextStyle( fontWeight: smallTextFontWeight, fontSize: 12, diff --git a/lib/src/player/full_height_title_and_artist.dart b/lib/src/player/full_height_title_and_artist.dart index aeb3aefba..abd2c6688 100644 --- a/lib/src/player/full_height_title_and_artist.dart +++ b/lib/src/player/full_height_title_and_artist.dart @@ -18,10 +18,20 @@ class FullHeightTitleAndArtist extends StatelessWidget { Widget build(BuildContext context) { final theme = context.t; final mpvMetaData = context.select((PlayerModel m) => m.mpvMetaData); + final icyName = mpvMetaData?.icyName; + final icyTitle = mpvMetaData?.icyTitle; + + final subTitle = icyName?.isNotEmpty == true + ? icyName! + : (audio?.audioType == AudioType.podcast + ? audio?.album + : audio?.artist ?? ' ') ?? + ''; + + final title = icyTitle?.isNotEmpty == true + ? icyTitle! + : (audio?.title?.isNotEmpty == true ? audio!.title! : ' '); - final label = mpvMetaData?.icyTitle.isNotEmpty == true - ? mpvMetaData!.icyTitle - : (audio?.title?.isNotEmpty == true ? audio!.title! : ''); return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -33,9 +43,9 @@ class FullHeightTitleAndArtist extends StatelessWidget { context: context, ), child: Tooltip( - message: label, + message: title, child: Text( - label, + title, style: TextStyle( fontWeight: largeTextWeight, fontSize: 30, @@ -54,18 +64,19 @@ class FullHeightTitleAndArtist extends StatelessWidget { artist: mpvMetaData?.icyName, context: context, ), - child: Text( - mpvMetaData?.icyName.isNotEmpty == true - ? mpvMetaData!.icyName - : (audio?.artist ?? ''), - style: TextStyle( - fontWeight: smallTextFontWeight, - fontSize: 20, - color: theme.colorScheme.onSurface, + child: Tooltip( + message: subTitle, + child: Text( + subTitle, + style: TextStyle( + fontWeight: smallTextFontWeight, + fontSize: 20, + color: theme.colorScheme.onSurface, + ), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + maxLines: 1, ), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - maxLines: 1, ), ), ], diff --git a/lib/src/player/player_utils.dart b/lib/src/player/player_utils.dart index 8842ab76a..3ca019d25 100644 --- a/lib/src/player/player_utils.dart +++ b/lib/src/player/player_utils.dart @@ -9,7 +9,9 @@ import '../../data.dart'; import '../../globals.dart'; import '../../library.dart'; import '../../local_audio.dart'; +import '../../podcasts.dart'; import '../../utils.dart'; +import '../app/app_model.dart'; void onLocalAudioTitleTap({ required Audio audio, @@ -68,6 +70,9 @@ void onTitleTap({ if (audio?.audioType == null || audio?.title == null) { return; } + if (audio?.audioType == AudioType.local) { + context.read<AppModel>().setFullScreen(false); + } if (text?.isNotEmpty == true) { Clipboard.setData(ClipboardData(text: text!)); @@ -106,14 +111,17 @@ void onTitleTap({ } } -void onArtistTap({ +Future<void> onArtistTap({ required Audio? audio, required String? artist, required BuildContext context, -}) { +}) async { if (audio?.audioType == null || audio?.artist == null) { return; } + if (audio?.audioType != AudioType.radio) { + context.read<AppModel>().setFullScreen(false); + } if (audio!.audioType == AudioType.radio && audio.url?.isNotEmpty == true) { Clipboard.setData(ClipboardData(text: audio.url!)); ScaffoldMessenger.of(context).clearSnackBars(); @@ -131,6 +139,15 @@ void onArtistTap({ ), ), ); + } else if (audio.audioType == AudioType.podcast && + audio.website?.isNotEmpty == true) { + await searchAndPushPodcastPage( + context: context, + feedUrl: audio.website, + itemImageUrl: audio.albumArtUrl, + genre: audio.genre, + play: false, + ); } else { onLocalAudioArtistTap(audio: audio, context: context); } diff --git a/lib/src/podcasts/podcast_audio_tile.dart b/lib/src/podcasts/podcast_audio_tile.dart index ecfdb2165..3bfe0123e 100644 --- a/lib/src/podcasts/podcast_audio_tile.dart +++ b/lib/src/podcasts/podcast_audio_tile.dart @@ -313,7 +313,6 @@ class _Description extends StatelessWidget { ), children: [ SizedBox( - height: 400, width: 400, child: _createHtml( color: theme.colorScheme.onSurface, diff --git a/lib/src/podcasts/podcast_page.dart b/lib/src/podcasts/podcast_page.dart index 147342b71..c55e8a623 100644 --- a/lib/src/podcasts/podcast_page.dart +++ b/lib/src/podcasts/podcast_page.dart @@ -15,9 +15,6 @@ class PodcastPage extends StatelessWidget { this.imageUrl, required this.pageId, this.audios, - this.subscribed = true, - required this.removePodcast, - required this.addPodcast, required this.title, }); @@ -47,19 +44,18 @@ class PodcastPage extends StatelessWidget { ); } - final void Function(String feedUrl) removePodcast; - final void Function(String feedUrl, Set<Audio> audios) addPodcast; - final String? imageUrl; final String pageId; final String title; final Set<Audio>? audios; - final bool subscribed; @override Widget build(BuildContext context) { final theme = context.t; final genre = audios?.firstWhereOrNull((e) => e.genre != null)?.genre; + final libraryModel = context.read<LibraryModel>(); + + final subscribed = libraryModel.podcastSubscribed(pageId); context.select((LibraryModel m) => m.lastPositions?.length); context.select((LibraryModel m) => m.downloadsLength); @@ -115,9 +111,9 @@ class PodcastPage extends StatelessWidget { ? null : () { if (subscribed) { - removePodcast(pageId); + libraryModel.removePodcast(pageId); } else if (audios?.isNotEmpty == true) { - addPodcast(pageId, audios!); + libraryModel.addPodcast(pageId, audios!); } }, ), diff --git a/lib/src/podcasts/podcast_utils.dart b/lib/src/podcasts/podcast_utils.dart new file mode 100644 index 000000000..8107e580a --- /dev/null +++ b/lib/src/podcasts/podcast_utils.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../common.dart'; +import '../../constants.dart'; +import '../../globals.dart'; +import '../../l10n.dart'; +import '../../player.dart'; +import '../../podcasts.dart'; + +Future<void> searchAndPushPodcastPage({ + required BuildContext context, + required String? feedUrl, + required String? itemImageUrl, + required String? genre, + required bool play, +}) async { + ScaffoldMessenger.of(context).clearSnackBars(); + + if (feedUrl == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.podcastFeedIsEmpty), + ), + ); + return; + } + final model = context.read<PodcastModel>(); + final startPlaylist = context.read<PlayerModel>().startPlaylist; + final selectedFeedUrl = model.selectedFeedUrl; + final setSelectedFeedUrl = model.setSelectedFeedUrl; + + setSelectedFeedUrl(feedUrl); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: const Duration(seconds: 20), + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(context.l10n.loadingPodcastFeed), + SizedBox( + height: iconSize, + width: iconSize, + child: const Progress(), + ), + ], + ), + ), + ); + + await findEpisodes( + feedUrl: feedUrl, + itemImageUrl: itemImageUrl, + genre: genre, + ).then((podcast) async { + if (selectedFeedUrl == feedUrl) { + return; + } + if (podcast.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.podcastFeedIsEmpty), + ), + ); + return; + } + + if (play) { + runOrConfirm( + context: context, + noConfirm: podcast.length < kAudioQueueThreshHold, + message: context.l10n.queueConfirmMessage(podcast.length.toString()), + run: () { + startPlaylist.call(listName: feedUrl, audios: podcast).then( + (_) => setSelectedFeedUrl(null), + ); + }, + onCancel: () { + model.setSelectedFeedUrl(null); + ScaffoldMessenger.of(context).clearSnackBars(); + }, + ); + } else { + await navigatorKey.currentState?.push( + MaterialPageRoute( + builder: (context) { + final id = feedUrl; + + ScaffoldMessenger.of(context).clearSnackBars(); + + return PodcastPage( + imageUrl: itemImageUrl, + audios: podcast, + pageId: id, + title: podcast.firstOrNull?.album ?? + podcast.firstOrNull?.title ?? + feedUrl, + ); + }, + ), + ).then((_) => setSelectedFeedUrl(null)); + } + }); +} diff --git a/lib/src/podcasts/podcasts_collection_body.dart b/lib/src/podcasts/podcasts_collection_body.dart index b73452337..ad89eb7d1 100644 --- a/lib/src/podcasts/podcasts_collection_body.dart +++ b/lib/src/podcasts/podcasts_collection_body.dart @@ -42,7 +42,6 @@ class PodcastsCollectionBody extends StatelessWidget { context.select((LibraryModel m) => m.feedsWithDownloadsLength); final setUpdatesOnly = model.setUpdatesOnly; final setDownloadsOnly = model.setDownloadsOnly; - final subscribed = libraryModel.podcastSubscribed; final removeUpdate = libraryModel.removePodcastUpdate; final itemCount = updatesOnly @@ -166,6 +165,11 @@ class PodcastsCollectionBody extends StatelessWidget { listName: podcast.key, ) .then((_) => removeUpdate(podcast.key)), + onCancel: () { + model.setSelectedFeedUrl(null); + ScaffoldMessenger.of(context) + .clearSnackBars(); + }, ); }, onTap: () => navigatorKey.currentState?.push( @@ -174,14 +178,11 @@ class PodcastsCollectionBody extends StatelessWidget { if (!isOnline) return const OfflinePage(); return PodcastPage( - subscribed: subscribed(podcast.key), pageId: podcast.key, title: podcast.value.firstOrNull?.album ?? podcast.value.firstOrNull?.title ?? podcast.value.firstOrNull.toString(), audios: podcast.value, - addPodcast: libraryModel.addPodcast, - removePodcast: libraryModel.removePodcast, imageUrl: podcast .value.firstOrNull?.albumArtUrl ?? podcast.value.firstOrNull?.imageUrl, diff --git a/lib/src/podcasts/podcasts_discover_grid.dart b/lib/src/podcasts/podcasts_discover_grid.dart index 239b9105f..06c28fa00 100644 --- a/lib/src/podcasts/podcasts_discover_grid.dart +++ b/lib/src/podcasts/podcasts_discover_grid.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:podcast_search/podcast_search.dart'; -import 'package:provider/provider.dart'; import '../../common.dart'; import '../../constants.dart'; import '../../l10n.dart'; -import '../../library.dart'; -import '../../player.dart'; -import '../../podcasts.dart'; +import 'podcast_utils.dart'; class PodcastsDiscoverGrid extends StatefulWidget { const PodcastsDiscoverGrid({ @@ -50,9 +47,6 @@ class _PodcastsDiscoverGridState extends State<PodcastsDiscoverGrid> { return NoSearchResultPage(message: Text(context.l10n.noPodcastFound)); } - final selectedFeedUrl = - context.select((PodcastModel m) => m.selectedFeedUrl); - return Column( children: [ Expanded( @@ -79,18 +73,17 @@ class _PodcastsDiscoverGridState extends State<PodcastsDiscoverGrid> { image: image, onPlay: () async => await searchAndPushPodcastPage( context: context, - podcastItem: podcastItem, + feedUrl: podcastItem.feedUrl, itemImageUrl: art, genre: podcastItem.primaryGenreName, - selectedFeedUrl: selectedFeedUrl, play: true, ), onTap: () async => await searchAndPushPodcastPage( context: context, - podcastItem: podcastItem, + feedUrl: podcastItem.feedUrl, itemImageUrl: art, genre: podcastItem.primaryGenreName, - selectedFeedUrl: selectedFeedUrl, + play: false, ), ); }, @@ -115,102 +108,4 @@ class _PodcastsDiscoverGridState extends State<PodcastsDiscoverGrid> { ], ); } - - Future<void> searchAndPushPodcastPage({ - required BuildContext context, - required Item podcastItem, - String? itemImageUrl, - String? genre, - String? selectedFeedUrl, - bool play = false, - }) async { - if (podcastItem.feedUrl == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.podcastFeedIsEmpty), - ), - ); - return; - } - final model = context.read<PodcastModel>(); - final libraryModel = context.read<LibraryModel>(); - final startPlaylist = context.read<PlayerModel>().startPlaylist; - - final setSelectedFeedUrl = model.setSelectedFeedUrl; - - setSelectedFeedUrl(podcastItem.feedUrl); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - duration: const Duration(seconds: 20), - content: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(context.l10n.loadingPodcastFeed), - SizedBox( - height: iconSize, - width: iconSize, - child: const Progress(), - ), - ], - ), - ), - ); - - await findEpisodes( - feedUrl: podcastItem.feedUrl!, - itemImageUrl: itemImageUrl, - genre: genre, - ).then((podcast) async { - if (selectedFeedUrl == podcastItem.feedUrl) { - return; - } - if (podcast.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.podcastFeedIsEmpty), - ), - ); - return; - } - - if (play) { - ScaffoldMessenger.of(context).clearSnackBars(); - - runOrConfirm( - context: context, - noConfirm: podcast.length < kAudioQueueThreshHold, - message: context.l10n.queueConfirmMessage(podcast.length.toString()), - run: () => startPlaylist - .call(listName: podcastItem.feedUrl!, audios: podcast) - .then( - (_) => setSelectedFeedUrl(null), - ), - ); - } else { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - final subscribed = - libraryModel.podcastSubscribed(podcastItem.feedUrl); - final id = podcastItem.feedUrl; - - ScaffoldMessenger.of(context).clearSnackBars(); - - return PodcastPage( - subscribed: subscribed, - imageUrl: itemImageUrl, - addPodcast: libraryModel.addPodcast, - removePodcast: libraryModel.removePodcast, - audios: podcast, - pageId: id!, - title: podcast.firstOrNull?.album ?? - podcast.firstOrNull?.title ?? - podcastItem.feedUrl!, - ); - }, - ), - ).then((_) => setSelectedFeedUrl(null)); - } - }); - } }