From e8cf907e2818d1b9871b0694db14f5c46b34b787 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 23 Sep 2023 12:37:32 +0100 Subject: [PATCH] Fix download/delete button, split out into separate widgets --- .../AlbumScreen/add_download_button.dart | 168 +++++++++++++++++ .../AlbumScreen/delete_download_button.dart | 45 +++++ .../AlbumScreen/download_button.dart | 170 ++---------------- 3 files changed, 226 insertions(+), 157 deletions(-) create mode 100644 lib/components/AlbumScreen/add_download_button.dart create mode 100644 lib/components/AlbumScreen/delete_download_button.dart diff --git a/lib/components/AlbumScreen/add_download_button.dart b/lib/components/AlbumScreen/add_download_button.dart new file mode 100644 index 000000000..a9026fcf9 --- /dev/null +++ b/lib/components/AlbumScreen/add_download_button.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:file_sizes/file_sizes.dart'; +import 'package:get_it/get_it.dart'; + +import '../../models/jellyfin_models.dart'; +import '../../services/finamp_settings_helper.dart'; +import '../../services/finamp_user_helper.dart'; +import 'download_button.dart'; +import 'download_dialog.dart'; + +class AddDownloadButton extends StatelessWidget { + const AddDownloadButton({ + super.key, + required this.parent, + required this.items, + this.onDownloadsAdded, + }); + + final BaseItemDto parent; + final List items; + final VoidCallback? onDownloadsAdded; + + @override + Widget build(BuildContext context) { + final finampUserHelper = GetIt.instance(); + + return PopupMenuButton( + icon: const Icon(Icons.file_download), + enabled: !FinampSettingsHelper.finampSettings.isOffline, + itemBuilder: (context) { + // To get the original file sizes, we just count up the given sizes + final originalFileSize = items + .map((e) => e.mediaSources?.first.size ?? 0) + .fold(0, (a, b) => a + b); + + final transcodedFileSize = items + .map((e) => e.mediaSources?.first.transcodedSize( + FinampSettingsHelper + .finampSettings.transcodingProfile.bitrateChannels)) + .fold(0, (a, b) => a + (b ?? 0)); + + final originalFileSizeFormatted = FileSize.getSize( + originalFileSize, + precision: PrecisionValue.None, + ); + + final transcodedFileSizeFormatted = FileSize.getSize( + transcodedFileSize, + precision: PrecisionValue.None, + ); + + final formats = items + .map((e) => e.mediaSources?.first.mediaStreams.first.codec) + .toSet(); + + return [ + PopupMenuItem( + value: DownloadChoice.original, + child: ListTile( + title: Text(AppLocalizations.of(context)!.original), + // We don't want to show the format on items with multiple, + // since it's messy and there isn't a clean way to handle it + // in multiple languages. + subtitle: Text(formats.length == 1 + ? AppLocalizations.of(context)!.fileSizeFormat( + originalFileSizeFormatted, + formats.first!.toUpperCase(), + ) + : originalFileSizeFormatted), + ), + ), + PopupMenuItem( + value: DownloadChoice.transcoded, + child: ListTile( + title: Text(AppLocalizations.of(context)!.transcoded), + subtitle: Text(AppLocalizations.of(context)! + .approxFileSizeFormat( + transcodedFileSizeFormatted, + FinampSettingsHelper + .finampSettings.transcodingProfile.codec.name + .toUpperCase())), + )), + ]; + }, + onSelected: (value) async { + final transcodingProfile = value == DownloadChoice.original + ? null + : FinampSettingsHelper.finampSettings.transcodingProfile; + + if (FinampSettingsHelper.finampSettings.downloadLocationsMap.length == + 1) { + await checkedAddDownloads( + context, + downloadLocation: FinampSettingsHelper + .finampSettings.downloadLocationsMap.values.first, + parents: [parent], + items: [items], + viewId: finampUserHelper.currentUser!.currentViewId!, + transcodingProfile: transcodingProfile, + ); + } else { + await showDialog( + context: context, + builder: (context) => DownloadDialog( + parents: [parent], + items: [items], + viewId: finampUserHelper.currentUser!.currentViewId!, + transcodingProfile: transcodingProfile, + ), + ); + } + + if (onDownloadsAdded != null) { + onDownloadsAdded!(); + } + }, + // If offline, we don't allow the user to delete items. + // If we did, we'd have to implement listeners for MusicScreenTabView so that the user can't delete a parent, go back, and select the same parent. + // If they did, AlbumScreen would show an error since the item no longer exists. + // Also, the user could delete the parent and immediately redownload it, which will either cause unwanted network usage or cause more errors becuase the user is offline. + // onPressed: isOffline ?? false + // ? null + // : () async { + // final transcodingChoice = + // if (isDownloaded) { + // _downloadsHelper + // .deleteDownloads( + // jellyfinItemIds: widget.items.map((e) => e.id).toList(), + // deletedFor: widget.parent.id, + // ) + // .then((_) { + // checkIfDownloaded(); + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Text( + // AppLocalizations.of(context)!.downloadsDeleted), + // )); + // }, + // onError: (error, stackTrace) => + // errorSnackbar(error, context)); + // } else { + // if (FinampSettingsHelper + // .finampSettings.downloadLocationsMap.length == + // 1) { + // await checkedAddDownloads( + // context, + // downloadLocation: FinampSettingsHelper + // .finampSettings.downloadLocationsMap.values.first, + // parents: [widget.parent], + // items: [widget.items], + // viewId: _finampUserHelper.currentUser!.currentViewId!, + // ); + // } else { + // await showDialog( + // context: context, + // builder: (context) => DownloadDialog( + // parents: [widget.parent], + // items: [widget.items], + // viewId: _finampUserHelper.currentUser!.currentViewId!, + // ), + // ); + // } + // checkIfDownloaded(); + // } + // }, + ); + } +} diff --git a/lib/components/AlbumScreen/delete_download_button.dart b/lib/components/AlbumScreen/delete_download_button.dart new file mode 100644 index 000000000..4e0981f98 --- /dev/null +++ b/lib/components/AlbumScreen/delete_download_button.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:get_it/get_it.dart'; + +import '../../models/jellyfin_models.dart'; +import '../../services/downloads_helper.dart'; +import '../error_snackbar.dart'; + +class DeleteDownloadButton extends StatelessWidget { + const DeleteDownloadButton({ + super.key, + required this.parent, + required this.items, + this.onDownloadsDeleted, + }); + + final BaseItemDto parent; + final List items; + final VoidCallback? onDownloadsDeleted; + + @override + Widget build(BuildContext context) { + final downloadsHelper = GetIt.instance(); + + return IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + downloadsHelper + .deleteDownloads( + jellyfinItemIds: items.map((e) => e.id).toList(), + deletedFor: parent.id, + ) + .then((_) { + if (onDownloadsDeleted != null) { + onDownloadsDeleted!(); + } + + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(AppLocalizations.of(context)!.downloadsDeleted), + )); + }, onError: (error, stackTrace) => errorSnackbar(error, context)); + }, + ); + } +} diff --git a/lib/components/AlbumScreen/download_button.dart b/lib/components/AlbumScreen/download_button.dart index e61a2b9bc..6e865bd78 100644 --- a/lib/components/AlbumScreen/download_button.dart +++ b/lib/components/AlbumScreen/download_button.dart @@ -1,16 +1,13 @@ -import 'package:file_sizes/file_sizes.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; import '../../services/downloads_helper.dart'; import '../../services/finamp_settings_helper.dart'; -import '../../services/finamp_user_helper.dart'; import '../../models/jellyfin_models.dart'; import '../../models/finamp_models.dart'; -import '../error_snackbar.dart'; -import 'download_dialog.dart'; +import 'add_download_button.dart'; +import 'delete_download_button.dart'; enum DownloadChoice { original, @@ -33,7 +30,6 @@ class DownloadButton extends StatefulWidget { class _DownloadButtonState extends State { final _downloadsHelper = GetIt.instance(); - final _finampUserHelper = GetIt.instance(); late bool isDownloaded; @override @@ -54,158 +50,18 @@ class _DownloadButtonState extends State { return ValueListenableBuilder>( valueListenable: FinampSettingsHelper.finampSettingsListener, builder: (context, box, child) { - return PopupMenuButton( - icon: isDownloaded - ? const Icon(Icons.delete) - : const Icon(Icons.file_download), - enabled: !FinampSettingsHelper.finampSettings.isOffline, - itemBuilder: (context) { - // To get the original file sizes, we just count up the given sizes - final originalFileSize = widget.items - .map((e) => e.mediaSources?.first.size ?? 0) - .fold(0, (a, b) => a + b); + if (isDownloaded) { + return DeleteDownloadButton( + parent: widget.parent, + items: widget.items, + onDownloadsDeleted: checkIfDownloaded, + ); + } - final transcodedFileSize = widget.items - .map((e) => e.mediaSources?.first.transcodedSize( - FinampSettingsHelper - .finampSettings.transcodingProfile.bitrateChannels)) - .fold(0, (a, b) => a + (b ?? 0)); - - final originalFileSizeFormatted = FileSize.getSize( - originalFileSize, - precision: PrecisionValue.None, - ); - - final transcodedFileSizeFormatted = FileSize.getSize( - transcodedFileSize, - precision: PrecisionValue.None, - ); - - final formats = widget.items - .map((e) => e.mediaSources?.first.mediaStreams.first.codec) - .toSet(); - - return [ - PopupMenuItem( - value: DownloadChoice.original, - child: ListTile( - title: Text(AppLocalizations.of(context)!.original), - // We don't want to show the format on items with multiple, - // since it's messy and there isn't a clean way to handle it - // in multiple languages. - subtitle: Text(formats.length == 1 - ? AppLocalizations.of(context)!.fileSizeFormat( - originalFileSizeFormatted, - formats.first!.toUpperCase(), - ) - : originalFileSizeFormatted), - ), - ), - PopupMenuItem( - value: DownloadChoice.transcoded, - child: ListTile( - title: Text(AppLocalizations.of(context)!.transcoded), - subtitle: Text(AppLocalizations.of(context)! - .approxFileSizeFormat( - transcodedFileSizeFormatted, - FinampSettingsHelper - .finampSettings.transcodingProfile.codec.name - .toUpperCase())), - )), - ]; - }, - onSelected: (value) async { - if (isDownloaded) { - _downloadsHelper - .deleteDownloads( - jellyfinItemIds: widget.items.map((e) => e.id).toList(), - deletedFor: widget.parent.id, - ) - .then((_) { - checkIfDownloaded(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context)!.downloadsDeleted), - )); - }, onError: (error, stackTrace) => errorSnackbar(error, context)); - } else { - final transcodingProfile = value == DownloadChoice.original - ? null - : FinampSettingsHelper.finampSettings.transcodingProfile; - - if (FinampSettingsHelper - .finampSettings.downloadLocationsMap.length == - 1) { - await checkedAddDownloads( - context, - downloadLocation: FinampSettingsHelper - .finampSettings.downloadLocationsMap.values.first, - parents: [widget.parent], - items: [widget.items], - viewId: _finampUserHelper.currentUser!.currentViewId!, - transcodingProfile: transcodingProfile, - ); - } else { - await showDialog( - context: context, - builder: (context) => DownloadDialog( - parents: [widget.parent], - items: [widget.items], - viewId: _finampUserHelper.currentUser!.currentViewId!, - transcodingProfile: transcodingProfile, - ), - ); - } - checkIfDownloaded(); - } - }, - // If offline, we don't allow the user to delete items. - // If we did, we'd have to implement listeners for MusicScreenTabView so that the user can't delete a parent, go back, and select the same parent. - // If they did, AlbumScreen would show an error since the item no longer exists. - // Also, the user could delete the parent and immediately redownload it, which will either cause unwanted network usage or cause more errors becuase the user is offline. - // onPressed: isOffline ?? false - // ? null - // : () async { - // final transcodingChoice = - // if (isDownloaded) { - // _downloadsHelper - // .deleteDownloads( - // jellyfinItemIds: widget.items.map((e) => e.id).toList(), - // deletedFor: widget.parent.id, - // ) - // .then((_) { - // checkIfDownloaded(); - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Text( - // AppLocalizations.of(context)!.downloadsDeleted), - // )); - // }, - // onError: (error, stackTrace) => - // errorSnackbar(error, context)); - // } else { - // if (FinampSettingsHelper - // .finampSettings.downloadLocationsMap.length == - // 1) { - // await checkedAddDownloads( - // context, - // downloadLocation: FinampSettingsHelper - // .finampSettings.downloadLocationsMap.values.first, - // parents: [widget.parent], - // items: [widget.items], - // viewId: _finampUserHelper.currentUser!.currentViewId!, - // ); - // } else { - // await showDialog( - // context: context, - // builder: (context) => DownloadDialog( - // parents: [widget.parent], - // items: [widget.items], - // viewId: _finampUserHelper.currentUser!.currentViewId!, - // ), - // ); - // } - // checkIfDownloaded(); - // } - // }, + return AddDownloadButton( + parent: widget.parent, + items: widget.items, + onDownloadsAdded: checkIfDownloaded, ); }, );