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));
-      }
-    });
-  }
 }