From d4bd52832f60b7a16affc10d4e1dfe7402d8b8b1 Mon Sep 17 00:00:00 2001 From: Jagandeep Brar Date: Fri, 9 Apr 2021 11:13:16 -0500 Subject: [PATCH] (feat): [Radarr] Ability to view and manage the queue (#414) * (queue): Ability to view basic queue, delete queue record * (fix): [UI] Show mouse cursor on ListTile when onTap or onLongPress are not null * (fix): [UI] Fix height/width of sorting/filtering buttons, ensure consistent fixed-height for input bars * (fix): [Lists] Remove default drag handles from ReorderableListView * (fix): Further safe-guarded with platform checks * (fix): Hide settings options on incompatible platforms * (chore): Update CHANGELOG.md * (fix): Switch from package_info to package_info_plus to support MacOS and other platforms * (chore): Update packages * (chore): Update Podfile * (tweak): [Icons] Add font_awesome_flutter, change Icon() usages to FaIcon() to prepare * (tweak): [Sonarr/Queue] Utilize a cleaner messages dialog view * (feat): [Radarr/Queue] Add movie name and quality profile to collapsed tile * (fix): [UI/AppBar] Back button was top-left aligned instead of centered * (feat): [Icons] Add FontAwesome Pro icons * (tweak): [Icons] Rollback to FontAwesome 5 * (tweak): [Icons] Rollback to FontAwesome 5 * (fix): [Icons] Revert back to system icons for now * (fix): [Icons] Revert from FaIcon to Icon * (tweak): [UI/Expandable Tile] Show two buttons per line, wrapping to a new line when necessary * (feat): [Radarr] Queue implementation finished * (fix): [Queue] Safe-guard some data model usages * (chore): Update CHANGELOG.md --- .blackbox/blackbox-files.txt | 6 + .gitignore | 11 +- CHANGELOG.md | 7 + assets/localization/en.json | 1 + ios/Podfile.lock | 18 +- ios/Runner.xcodeproj/project.pbxproj | 4 +- lib/core/dialogs.dart | 19 + lib/core/system/firebase/analytics.dart | 4 +- lib/core/system/firebase/crashlytics.dart | 12 +- lib/core/system/quick_actions.dart | 7 +- lib/core/ui/appbar.dart | 4 +- lib/core/ui/bottom_bar/navigation_bar.dart | 6 +- lib/core/ui/card.dart | 8 +- lib/core/ui/icons/icon_custom.dart | 1 + lib/core/ui/input_bar.dart | 2 + lib/core/ui/list_tile/list_tile.dart | 1 + .../ui/list_view/reorderable_list_view.dart | 3 + .../reorderable_list_view_builder.dart | 3 + lib/core/ui/theme.dart | 4 +- lib/core/utilities/changelog.dart | 7 +- lib/core/utilities/logger.dart | 2 + lib/core/widgets/expandable_list_tile.dart | 22 +- .../routes/calendar/widgets/calendar.dart | 10 - .../lidarr/widgets/catalogue_hide_button.dart | 2 + .../widgets/catalogue_sorting_button.dart | 2 + .../lidarr/widgets/releases_hide_button.dart | 2 + .../widgets/releases_sorting_button.dart | 2 + .../nzbget/widgets/history_hide_button.dart | 2 + lib/modules/radarr/core/database.dart | 21 + lib/modules/radarr/core/dialogs.dart | 39 + lib/modules/radarr/core/extensions.dart | 1 + .../core/extensions/radarr_queue_record.dart | 75 + lib/modules/radarr/core/state.dart | 42 + .../widgets/search_bar_filter_button.dart | 2 + .../widgets/search_bar_sort_button.dart | 2 + lib/modules/radarr/routes/queue.dart | 1 + lib/modules/radarr/routes/queue/route.dart | 62 +- lib/modules/radarr/routes/queue/widgets.dart | 1 + .../routes/queue/widgets/queue_tile.dart | 140 + .../widgets/search_bar_filter_button.dart | 2 + .../widgets/search_bar_sort_button.dart | 2 + .../sabnzbd/widgets/history_hide_button.dart | 2 + .../settings/routes/configuration.dart | 2 +- lib/modules/settings/routes/settings.dart | 2 +- lib/modules/settings/routes/system/route.dart | 31 +- .../routes/queue/widgets/queue_tile.dart | 13 +- .../widgets/appbar_filter_button.dart | 2 + .../releases/widgets/appbar_sort_button.dart | 2 + .../widgets/search_bar_filter_button.dart | 2 + .../widgets/search_bar_sort_button.dart | 2 + localization/radarr/en.json | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- macos/Podfile.lock | 18 +- pubspec.lock | 67 +- pubspec.yaml | 20 +- vendor/font_awesome_flutter/.gitignore | 12 + vendor/font_awesome_flutter/LICENSE | 23 + vendor/font_awesome_flutter/icons.json.gpg | Bin 0 -> 2003795 bytes .../lib/font_awesome_flutter.dart | 52408 ++++++++++++++++ .../lib/fonts/fa-brands-400.ttf.gpg | Bin 0 -> 91200 bytes .../lib/fonts/fa-duotone-900.ttf.gpg | Bin 0 -> 260374 bytes .../lib/fonts/fa-light-300.ttf.gpg | Bin 0 -> 247215 bytes .../lib/fonts/fa-regular-400.ttf.gpg | Bin 0 -> 226293 bytes .../lib/fonts/fa-solid-900.ttf.gpg | Bin 0 -> 184816 bytes .../lib/src/fa_duotone_icon.dart | 71 + .../font_awesome_flutter/lib/src/fa_icon.dart | 148 + .../lib/src/icon_data.dart | 85 + vendor/font_awesome_flutter/pubspec.yaml | 49 + .../tool/generate_example.dart | 87 + .../tool/generate_font.dart | 204 + vendor/font_awesome_flutter/tool/update.bat | 25 + vendor/font_awesome_flutter/tool/update.sh | 29 + 72 files changed, 53764 insertions(+), 105 deletions(-) create mode 100644 lib/modules/radarr/core/extensions/radarr_queue_record.dart create mode 100644 lib/modules/radarr/routes/queue/widgets.dart create mode 100644 lib/modules/radarr/routes/queue/widgets/queue_tile.dart create mode 100755 vendor/font_awesome_flutter/.gitignore create mode 100644 vendor/font_awesome_flutter/LICENSE create mode 100644 vendor/font_awesome_flutter/icons.json.gpg create mode 100644 vendor/font_awesome_flutter/lib/font_awesome_flutter.dart create mode 100644 vendor/font_awesome_flutter/lib/fonts/fa-brands-400.ttf.gpg create mode 100644 vendor/font_awesome_flutter/lib/fonts/fa-duotone-900.ttf.gpg create mode 100644 vendor/font_awesome_flutter/lib/fonts/fa-light-300.ttf.gpg create mode 100644 vendor/font_awesome_flutter/lib/fonts/fa-regular-400.ttf.gpg create mode 100644 vendor/font_awesome_flutter/lib/fonts/fa-solid-900.ttf.gpg create mode 100644 vendor/font_awesome_flutter/lib/src/fa_duotone_icon.dart create mode 100644 vendor/font_awesome_flutter/lib/src/fa_icon.dart create mode 100644 vendor/font_awesome_flutter/lib/src/icon_data.dart create mode 100755 vendor/font_awesome_flutter/pubspec.yaml create mode 100644 vendor/font_awesome_flutter/tool/generate_example.dart create mode 100644 vendor/font_awesome_flutter/tool/generate_font.dart create mode 100644 vendor/font_awesome_flutter/tool/update.bat create mode 100755 vendor/font_awesome_flutter/tool/update.sh diff --git a/.blackbox/blackbox-files.txt b/.blackbox/blackbox-files.txt index dad57f33..aa5764b2 100644 --- a/.blackbox/blackbox-files.txt +++ b/.blackbox/blackbox-files.txt @@ -4,3 +4,9 @@ android/key.properties ios/ExportOptions.plist ios/GoogleService-Info.plist macos/GoogleService-Info.plist +vendor/font_awesome_flutter/icons.json +vendor/font_awesome_flutter/lib/fonts/fa-brands-400.ttf +vendor/font_awesome_flutter/lib/fonts/fa-duotone-900.ttf +vendor/font_awesome_flutter/lib/fonts/fa-light-300.ttf +vendor/font_awesome_flutter/lib/fonts/fa-regular-400.ttf +vendor/font_awesome_flutter/lib/fonts/fa-solid-900.ttf diff --git a/.gitignore b/.gitignore index 34373b5b..47a9537f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,9 +48,16 @@ ios/build/ /.blackbox/pubring.gpg~ /.blackbox/pubring.kbx~ /.blackbox/secring.gpg -/ios/ExportOptions.plist -/ios/GoogleService-Info.plist /android/key.properties /android/app/google-services.json /android/key.jks +/ios/ExportOptions.plist +/ios/GoogleService-Info.plist /macos/GoogleService-Info.plist +/vendor/font_awesome_flutter/lib/fonts/fa-brands-400.ttf +/vendor/font_awesome_flutter/lib/fonts/fa-duotone-900.ttf +/vendor/font_awesome_flutter/lib/fonts/fa-light-300.ttf +/vendor/font_awesome_flutter/lib/fonts/fa-regular-400.ttf +/vendor/font_awesome_flutter/lib/fonts/fa-solid-900.ttf +/vendor/font_awesome_flutter/lib/fonts/fa-thin-100.ttf +/vendor/font_awesome_flutter/icons.json diff --git a/CHANGELOG.md b/CHANGELOG.md index cf466de3..89d4c2bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,16 +3,23 @@ ## GitHub #### NEW +- `[Radarr/Queue]` Ability to view and manage the queue #### TWEAKS #### FIXES - `[Radarr/Catalogue]` Update filters to more closely match the web GUI filtering +- `[UI/UX]` Search and filter/sort buttons now have fixed heights to have consistency across platforms +- `[UI/UX]` Show click cursor on all clickable tiles +- `[UI/UX]` Show two buttons per line on expandable tiles #### PLATFORM SPECIFIC - `[macOS]` (new) Added LunaSea icon - `[macOS]` (fix) Set application/window title - `[macOS]` (fix) Add platform compatability checks for Firebase Analytics & in-app purchases +- `[macOS]` (fix) Utilize package_info_plus to allow internal version checking +- `[macOS]` (fix) Hide incompatible settings options + --- ## v5.0.0 (50000014) diff --git a/assets/localization/en.json b/assets/localization/en.json index 1e17cf83..633c7aa1 100644 --- a/assets/localization/en.json +++ b/assets/localization/en.json @@ -49,6 +49,7 @@ "radarr.InCinemasIn": "In Cinemas in {}", "radarr.InCinemasToday": "In Cinemas Today", "radarr.Interactive": "Interactive", + "radarr.Language": "Language", "radarr.Languages": "Languages", "radarr.ManualImport": "Manual Import", "radarr.ManualImportDescription": "Import Movies from the Filesystem", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 10dcd7bb..d4cc1a84 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -66,7 +66,7 @@ PODS: - Firebase/Analytics (= 7.3.0) - firebase_core - Flutter - - firebase_auth (1.0.2): + - firebase_auth (1.0.3): - Firebase/Auth (= 7.3.0) - firebase_core - Flutter @@ -81,7 +81,7 @@ PODS: - Firebase/Messaging (= 7.3.0) - firebase_core - Flutter - - firebase_storage (8.0.2): + - firebase_storage (8.0.3): - Firebase/Storage (= 7.3.0) - firebase_core - Flutter @@ -187,7 +187,7 @@ PODS: - nanopb/encode (2.30906.0) - network_info_plus (0.0.1): - Flutter - - package_info (0.0.1): + - package_info_plus (0.4.5): - Flutter - path_provider (0.0.1): - Flutter @@ -222,7 +222,7 @@ DEPENDENCIES: - GoogleUtilities - in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`) - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - - package_info (from `.symlinks/plugins/package_info/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - quick_actions (from `.symlinks/plugins/quick_actions/ios`) - share (from `.symlinks/plugins/share/ios`) @@ -281,8 +281,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/in_app_purchase/ios" network_info_plus: :path: ".symlinks/plugins/network_info_plus/ios" - package_info: - :path: ".symlinks/plugins/package_info/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" quick_actions: @@ -307,11 +307,11 @@ SPEC CHECKSUMS: file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Firebase: 26223c695fe322633274198cb19dca8cb7e54416 firebase_analytics: ee4abb07bfa857e7c95d97144efe9eb68be414cd - firebase_auth: 9e9e667b904ee5701ab50330a5718cce0bf1a469 + firebase_auth: c2c27e1081671b02f90981e70dad54722198491f firebase_core: b5d81dfd4fb2d6f700e67de34d9a633ae325c4e9 firebase_crashlytics: 3b8018f6c88a59be95f9e2f38f04e5c0f05e0c6a firebase_messaging: 7547c99f31466371f9cfcb733d5a1bf32a819872 - firebase_storage: 78b9c7d73a4e75a9bed8697fab8424979e31d7dd + firebase_storage: 2ce2146c1fa764e68330e6998fa386522c95d199 FirebaseAnalytics: 2580c2d62535ae7b644143d48941fcc239ea897a FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 @@ -330,7 +330,7 @@ SPEC CHECKSUMS: in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6 nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 network_info_plus: b78876159360f5580608c2cea620d6ceffabd0ad - package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 quick_actions: 6cb2390c4dab0e737c94573c27e18d9666710720 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0fad1d61..800249e8 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -452,7 +452,7 @@ "${BUILT_PRODUCTS_DIR}/in_app_purchase/in_app_purchase.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/network_info_plus/network_info_plus.framework", - "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", + "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", "${BUILT_PRODUCTS_DIR}/share/share.framework", @@ -483,7 +483,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/network_info_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework", diff --git a/lib/core/dialogs.dart b/lib/core/dialogs.dart index 21a2405f..402edeac 100644 --- a/lib/core/dialogs.dart +++ b/lib/core/dialogs.dart @@ -91,6 +91,25 @@ class LunaDialogs { ); } + Future showMessages(BuildContext context, List messages) async { + if((messages ?? []).isEmpty) return textPreview(context, 'Messages', 'No messages found'); + await LunaDialog.dialog( + context: context, + title: 'Messages', + cancelButtonText: 'Close', + content: List.generate( + messages.length, + (index) => LunaDialog.tile( + text: messages[index], + icon: Icons.info_outline_rounded, + iconColor: LunaColours.accent, + ), + ), + contentPadding: LunaDialog.listDialogContentPadding(), + ); + } + + /// **Will be removed in future** /// /// Show a delete catalogue with all files warning dialog. diff --git a/lib/core/system/firebase/analytics.dart b/lib/core/system/firebase/analytics.dart index 4f4a0857..7884f720 100644 --- a/lib/core/system/firebase/analytics.dart +++ b/lib/core/system/firebase/analytics.dart @@ -29,5 +29,7 @@ class LunaFirebaseAnalytics { } /// Log an "app_open" event. - Future appOpened() async => instance.logAppOpen(); + Future appOpened() async { + if(isPlatformCompatible) instance.logAppOpen(); + } } diff --git a/lib/core/system/firebase/crashlytics.dart b/lib/core/system/firebase/crashlytics.dart index 7f130037..d87411ee 100644 --- a/lib/core/system/firebase/crashlytics.dart +++ b/lib/core/system/firebase/crashlytics.dart @@ -1,8 +1,12 @@ +import 'dart:io'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:lunasea/core.dart'; class LunaFirebaseCrashlytics { + /// Returns true if Firebase Analytics is compatible with this build system/platform. + static bool get isPlatformCompatible => !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows; + /// Returns an instance of [FirebaseCrashlytics]. /// /// Throws an error if [LunaFirebase.initialize] has not been called. @@ -11,8 +15,10 @@ class LunaFirebaseCrashlytics { /// Set the enabled state of Firebase Crashlytics. /// /// If `enabled` is set to false, force-disables Analytics. - void setEnabledState({ bool enabled = true }) { - bool state = kReleaseMode && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data; - instance.setCrashlyticsCollectionEnabled(state && enabled); + void setEnabledState() { + if(isPlatformCompatible) { + bool state = kReleaseMode && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data; + instance.setCrashlyticsCollectionEnabled(state); + } } } diff --git a/lib/core/system/quick_actions.dart b/lib/core/system/quick_actions.dart index 365e0783..75e4287f 100644 --- a/lib/core/system/quick_actions.dart +++ b/lib/core/system/quick_actions.dart @@ -1,20 +1,23 @@ +import 'dart:io'; import 'package:lunasea/core.dart'; import 'package:quick_actions/quick_actions.dart'; class LunaQuickActions { static final QuickActions _quickActions = QuickActions(); + static bool get isPlatformCompatible => Platform.isAndroid || Platform.isIOS; + /// Initialize the quick actions by setting the action handler. /// /// Required before the handler can actually receive any intents. void initialize() { - _quickActions.initialize((action) => _handler(action)); + if(isPlatformCompatible) _quickActions.initialize((action) => _handler(action)); setShortcutItems(); } /// Sets the shortcut items by checking the database and enabling the respective action if enabled. void setShortcutItems() { - _quickActions.setShortcutItems([ + if(isPlatformCompatible) _quickActions.setShortcutItems([ if(LunaDatabaseValue.QUICK_ACTIONS_SEARCH.data) LunaModule.SEARCH.shortcutItem, if(LunaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LunaModule.LIDARR.shortcutItem, if(LunaDatabaseValue.QUICK_ACTIONS_RADARR.data) LunaModule.RADARR.shortcutItem, diff --git a/lib/core/ui/appbar.dart b/lib/core/ui/appbar.dart index f3c6e42f..14792858 100644 --- a/lib/core/ui/appbar.dart +++ b/lib/core/ui/appbar.dart @@ -204,7 +204,9 @@ class _State extends State { }, ); return InkWell( - child: Icon(Icons.arrow_back_ios_rounded), + child: Center( + child: Icon(Icons.arrow_back_ios_rounded), + ), onTap: () async { HapticFeedback.lightImpact(); Navigator.of(context).lunaSafetyPop(); diff --git a/lib/core/ui/bottom_bar/navigation_bar.dart b/lib/core/ui/bottom_bar/navigation_bar.dart index f28a6fb3..cacd4545 100644 --- a/lib/core/ui/bottom_bar/navigation_bar.dart +++ b/lib/core/ui/bottom_bar/navigation_bar.dart @@ -66,21 +66,25 @@ class _State extends State { child: GNav( gap: 8.0, iconSize: 24.0, - padding: EdgeInsets.fromLTRB(18.0, 10.0, 12.0, 10.0), duration: Duration(milliseconds: LunaUI.ANIMATION_SPEED), tabBackgroundColor: Theme.of(context).canvasColor.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY), activeColor: LunaColours.accent, tabs: List.generate(widget.icons.length, (index) => GButton( icon: widget.icons[index], text: widget.titles[index], + active: _index == index, iconSize: 22.0, haptic: true, + padding: _index == index + ? EdgeInsets.all(10.0).add(EdgeInsets.only(left: 6.0)) + : EdgeInsets.all(10.0), iconColor: Colors.white, textStyle: TextStyle( fontWeight: LunaUI.FONT_WEIGHT_BOLD, fontSize: LunaUI.FONT_SIZE_NAVIGATION_BAR, color: Colors.white, ), + iconActiveColor: LunaColours.accent, leading: widget.leadingOnTab == null ? null : widget.leadingOnTab[index], )).toList(), tabActiveBorder: LunaUI.shouldUseBorder ? Border.all(color: Colors.white12) : null, diff --git a/lib/core/ui/card.dart b/lib/core/ui/card.dart index 007952fd..65b4e61f 100644 --- a/lib/core/ui/card.dart +++ b/lib/core/ui/card.dart @@ -10,9 +10,15 @@ class LunaCard extends Card { Color color, Decoration decoration, double height, + double width, }) : super( key: key, - child: Container(child: child, decoration: decoration, height: height), + child: Container( + child: child, + decoration: decoration, + height: height, + width: width, + ), margin: margin, color: color == null ? Theme.of(context).primaryColor : color, shape: LunaUI.shapeBorder, diff --git a/lib/core/ui/icons/icon_custom.dart b/lib/core/ui/icons/icon_custom.dart index 41d73972..9f31d56d 100644 --- a/lib/core/ui/icons/icon_custom.dart +++ b/lib/core/ui/icons/icon_custom.dart @@ -1,4 +1,5 @@ import 'package:flutter/widgets.dart'; +// export 'package:font_awesome_flutter/font_awesome_flutter.dart'; class LunaIcons { LunaIcons._(); diff --git a/lib/core/ui/input_bar.dart b/lib/core/ui/input_bar.dart index 3610464c..490ece43 100644 --- a/lib/core/ui/input_bar.dart +++ b/lib/core/ui/input_bar.dart @@ -3,6 +3,7 @@ import 'package:lunasea/core.dart'; class LunaTextInputBar extends StatefulWidget { static const double appBarHeight = 62.0; + static const double appBarInnerHeight = 62.0-13.0; static const EdgeInsets appBarMargin = const EdgeInsets.fromLTRB(12.0, 1.0, 12.0, 12.0); final TextEditingController controller; @@ -56,6 +57,7 @@ class _State extends State { child: widget.isFormField ? _isForm : _isNotForm, ), ), + height: LunaTextInputBar.appBarInnerHeight, color: Theme.of(context).canvasColor, ); diff --git a/lib/core/ui/list_tile/list_tile.dart b/lib/core/ui/list_tile/list_tile.dart index 1c2ffa20..ac4c3a30 100644 --- a/lib/core/ui/list_tile/list_tile.dart +++ b/lib/core/ui/list_tile/list_tile.dart @@ -26,6 +26,7 @@ class LunaListTile extends Card { subtitle: subtitle, trailing: trailing, leading: leading, + mouseCursor: onTap != null || onLongPress != null ? SystemMouseCursors.click : null, contentPadding: contentPadding ? customContentPadding ?? EdgeInsets.symmetric(vertical: 6.0, horizontal: 12.0) : null, ), borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), diff --git a/lib/core/ui/list_view/reorderable_list_view.dart b/lib/core/ui/list_view/reorderable_list_view.dart index aeebc056..e2bec61b 100644 --- a/lib/core/ui/list_view/reorderable_list_view.dart +++ b/lib/core/ui/list_view/reorderable_list_view.dart @@ -7,6 +7,7 @@ class LunaReorderableListView extends StatelessWidget { final ScrollPhysics physics; final ScrollController controller; final void Function(int, int) onReorder; + final bool buildDefaultDragHandles; LunaReorderableListView({ Key key, @@ -15,6 +16,7 @@ class LunaReorderableListView extends StatelessWidget { @required this.onReorder, this.padding, this.physics = const AlwaysScrollableScrollPhysics(), + this.buildDefaultDragHandles = false, }) : super(key: key); @override @@ -28,6 +30,7 @@ class LunaReorderableListView extends StatelessWidget { padding: padding ?? MediaQuery.of(context).padding.add(EdgeInsets.symmetric(vertical: 8.0)), physics: physics, onReorder: onReorder, + buildDefaultDragHandles: buildDefaultDragHandles, ), ); } diff --git a/lib/core/ui/list_view/reorderable_list_view_builder.dart b/lib/core/ui/list_view/reorderable_list_view_builder.dart index 15ab96b5..84f1da98 100644 --- a/lib/core/ui/list_view/reorderable_list_view_builder.dart +++ b/lib/core/ui/list_view/reorderable_list_view_builder.dart @@ -8,6 +8,7 @@ class LunaReorderableListViewBuilder extends StatelessWidget { final ScrollPhysics physics; final ScrollController controller; final void Function(int, int) onReorder; + final bool buildDefaultDragHandles; LunaReorderableListViewBuilder({ Key key, @@ -17,6 +18,7 @@ class LunaReorderableListViewBuilder extends StatelessWidget { @required this.onReorder, this.padding, this.physics = const AlwaysScrollableScrollPhysics(), + this.buildDefaultDragHandles = false, }) : super(key: key) { assert(itemCount != null); assert(itemBuilder != null); @@ -34,6 +36,7 @@ class LunaReorderableListViewBuilder extends StatelessWidget { itemCount: itemCount, itemBuilder: itemBuilder, onReorder: onReorder, + buildDefaultDragHandles: buildDefaultDragHandles, ), ); } diff --git a/lib/core/ui/theme.dart b/lib/core/ui/theme.dart index d9a5a2a5..a8aa83d0 100644 --- a/lib/core/ui/theme.dart +++ b/lib/core/ui/theme.dart @@ -20,7 +20,7 @@ class LunaTheme { accentColor: LunaColours.accent, highlightColor: LunaColours.splash.withOpacity(0.15), cardColor: LunaColours.secondary, - hoverColor: LunaColours.splash.withOpacity(0.10), + hoverColor: LunaColours.accent.withOpacity(0.15), splashColor: LunaColours.accent.withOpacity(0.30), dialogBackgroundColor: LunaColours.secondary, toggleableActiveColor: LunaColours.accent, @@ -43,7 +43,7 @@ class LunaTheme { accentColor: LunaColours.accent, highlightColor: LunaColours.splash.withOpacity(0.15), cardColor: Colors.black, - hoverColor: LunaColours.splash.withOpacity(0.10), + hoverColor: LunaColours.accent.withOpacity(0.15), splashColor: LunaColours.accent.withOpacity(0.30), dialogBackgroundColor: Colors.black, toggleableActiveColor: LunaColours.accent, diff --git a/lib/core/utilities/changelog.dart b/lib/core/utilities/changelog.dart index 04d8c11b..22cef090 100644 --- a/lib/core/utilities/changelog.dart +++ b/lib/core/utilities/changelog.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; class LunaChangelog { /// Check if the current build number is not equal to the stored build number. @@ -10,7 +10,7 @@ class LunaChangelog { Future checkAndShowChangelog() async { PackageInfo.fromPlatform() .then((package) { - if(AlertsDatabaseValue.CHANGELOG.data != package.buildNumber) { + if(package != null && AlertsDatabaseValue.CHANGELOG.data != package.buildNumber) { AlertsDatabaseValue.CHANGELOG.put(package.buildNumber); showChangelog(package.version, package.buildNumber); } @@ -32,7 +32,8 @@ class LunaChangelog { body: LunaListViewModal( shrinkWrap: false, children: [ - LunaHeader(text: '$version ($buildNumber)', subtitle: changelog.motd), + if(version != null && buildNumber != null) LunaHeader(text: '$version ($buildNumber)', subtitle: changelog.motd), + if(version == null || buildNumber == null) LunaHeader(text: 'LunaSea', subtitle: changelog.motd), if((changelog.changesNew?.length ?? 0) != 0) LunaHeader(text: 'New'), if((changelog.changesNew?.length ?? 0) != 0) LunaTableCard( content: List.generate( diff --git a/lib/core/utilities/logger.dart b/lib/core/utilities/logger.dart index 074249ac..4bef459b 100644 --- a/lib/core/utilities/logger.dart +++ b/lib/core/utilities/logger.dart @@ -61,6 +61,7 @@ class LunaLogger { Future recordCrashlyticsError(dynamic error, StackTrace stackTrace) async { if( kReleaseMode && + LunaFirebaseCrashlytics.isPlatformCompatible && LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data && !(error is DioError) @@ -75,6 +76,7 @@ class LunaLogger { Future recordCrashlyticsFlutterError(FlutterErrorDetails details) async { if( kReleaseMode && + LunaFirebaseCrashlytics.isPlatformCompatible && LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data && !(error is DioError) diff --git a/lib/core/widgets/expandable_list_tile.dart b/lib/core/widgets/expandable_list_tile.dart index 5596eb83..084addaf 100644 --- a/lib/core/widgets/expandable_list_tile.dart +++ b/lib/core/widgets/expandable_list_tile.dart @@ -133,14 +133,22 @@ class _State extends State { padding: EdgeInsets.symmetric(horizontal: 12.0), )).toList(), if(widget.expandedTableButtons != null) Padding( - child: Row( + child: Wrap( children: [ - ...widget.expandedTableButtons.map((button) => Expanded( - child: Padding( - child: button, - padding: EdgeInsets.zero, - ), - )).toList(), + ...List.generate( + widget.expandedTableButtons.length, + (index) { + double widthFactor = 0.5; + if( + index == (widget.expandedTableButtons.length-1) && + widget.expandedTableButtons.length.isOdd + ) widthFactor = 1; + return FractionallySizedBox( + child: widget.expandedTableButtons[index], + widthFactor: widthFactor, + ); + } + ), ], ), padding: EdgeInsets.symmetric(horizontal: 6.0), diff --git a/lib/modules/dashboard/routes/calendar/widgets/calendar.dart b/lib/modules/dashboard/routes/calendar/widgets/calendar.dart index d1b51553..d9bde899 100644 --- a/lib/modules/dashboard/routes/calendar/widgets/calendar.dart +++ b/lib/modules/dashboard/routes/calendar/widgets/calendar.dart @@ -129,16 +129,6 @@ class _State extends State { weekendStyle: weekdayTitleStyle, weekdayStyle: weekdayTitleStyle, ), - headerStyle: HeaderStyle( - titleTextStyle: TextStyle( - fontSize: LunaUI.FONT_SIZE_HEADER, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - centerHeaderTitle: true, - formatButtonVisible: false, - leftChevronIcon: Icon(Icons.arrow_back_ios_rounded), - rightChevronIcon: Icon(Icons.arrow_forward_ios_rounded), - ), initialCalendarFormat: (DashboardDatabaseValue.CALENDAR_STARTING_SIZE.data as CalendarStartingSize).data, availableCalendarFormats: const { CalendarFormat.month : 'Month', CalendarFormat.twoWeeks : '2 Weeks', CalendarFormat.week : 'Week'}, diff --git a/lib/modules/lidarr/widgets/catalogue_hide_button.dart b/lib/modules/lidarr/widgets/catalogue_hide_button.dart index 5461285d..ca507db9 100644 --- a/lib/modules/lidarr/widgets/catalogue_hide_button.dart +++ b/lib/modules/lidarr/widgets/catalogue_hide_button.dart @@ -29,6 +29,8 @@ class _State extends State { borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart index c8e0c21f..2ceb48c5 100644 --- a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart +++ b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart @@ -18,6 +18,8 @@ class _State extends State { @override Widget build(BuildContext context) => LunaCard( context: context, + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, child: Consumer( builder: (context, model, _) => LunaPopupMenuButton( tooltip: 'Sort Catalogue', diff --git a/lib/modules/lidarr/widgets/releases_hide_button.dart b/lib/modules/lidarr/widgets/releases_hide_button.dart index 4109ac85..08067691 100644 --- a/lib/modules/lidarr/widgets/releases_hide_button.dart +++ b/lib/modules/lidarr/widgets/releases_hide_button.dart @@ -26,6 +26,8 @@ class _State extends State { onPressed: () => model.hideRejectedReleases = !model.hideRejectedReleases, ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: LunaTextInputBar.appBarMargin.subtract(EdgeInsets.only(left: 12.0)), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/lidarr/widgets/releases_sorting_button.dart b/lib/modules/lidarr/widgets/releases_sorting_button.dart index f854e4f9..c3d35891 100644 --- a/lib/modules/lidarr/widgets/releases_sorting_button.dart +++ b/lib/modules/lidarr/widgets/releases_sorting_button.dart @@ -18,6 +18,8 @@ class _State extends State { @override Widget build(BuildContext context) => LunaCard( context: context, + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, child: Consumer( builder: (context, model, _) => LunaPopupMenuButton( tooltip: 'Sort Releases', diff --git a/lib/modules/nzbget/widgets/history_hide_button.dart b/lib/modules/nzbget/widgets/history_hide_button.dart index d7053076..3b7c9f36 100644 --- a/lib/modules/nzbget/widgets/history_hide_button.dart +++ b/lib/modules/nzbget/widgets/history_hide_button.dart @@ -26,6 +26,8 @@ class _State extends State { onPressed: () => model.historyHideFailed = !model.historyHideFailed, ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.only(left: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/core/database.dart b/lib/modules/radarr/core/database.dart index 3c6c1537..8859c80f 100644 --- a/lib/modules/radarr/core/database.dart +++ b/lib/modules/radarr/core/database.dart @@ -44,6 +44,9 @@ class RadarrDatabase extends LunaModuleDatabase { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: case RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE: + case RadarrDatabaseValue.QUEUE_REFRESH_RATE: + case RadarrDatabaseValue.QUEUE_BLACKLIST: + case RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT: case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: case RadarrDatabaseValue.CONTENT_PAGE_SIZE: data[value.key] = value.data; break; @@ -76,6 +79,9 @@ class RadarrDatabase extends LunaModuleDatabase { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: case RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE: + case RadarrDatabaseValue.QUEUE_REFRESH_RATE: + case RadarrDatabaseValue.QUEUE_BLACKLIST: + case RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT: case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: case RadarrDatabaseValue.CONTENT_PAGE_SIZE: value.put(config[key]); break; @@ -103,6 +109,9 @@ class RadarrDatabase extends LunaModuleDatabase { case 'RADARR_ADD_MOVIE_DEFAULT_TAGS': return RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS; case 'RADARR_ADD_DISCOVER_USE_SUGGESTIONS': return RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS; case 'RADARR_MANUAL_IMPORT_DEFAULT_MODE': return RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE; + case 'RADARR_QUEUE_REFRESH_RATE': return RadarrDatabaseValue.QUEUE_REFRESH_RATE; + case 'RADARR_QUEUE_BLACKLIST': return RadarrDatabaseValue.QUEUE_BLACKLIST; + case 'RADARR_QUEUE_REMOVE_FROM_CLIENT': return RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT; case 'RADARR_REMOVE_MOVIE_IMPORT_LIST': return RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST; case 'RADARR_REMOVE_MOVIE_DELETE_FILES': return RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES; case 'RADARR_CONTENT_PAGE_SIZE': return RadarrDatabaseValue.CONTENT_PAGE_SIZE; @@ -129,6 +138,9 @@ enum RadarrDatabaseValue { ADD_MOVIE_DEFAULT_TAGS, ADD_DISCOVER_USE_SUGGESTIONS, MANUAL_IMPORT_DEFAULT_MODE, + QUEUE_REFRESH_RATE, + QUEUE_BLACKLIST, + QUEUE_REMOVE_FROM_CLIENT, REMOVE_MOVIE_IMPORT_LIST, REMOVE_MOVIE_DELETE_FILES, CONTENT_PAGE_SIZE, @@ -154,6 +166,9 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: return 'RADARR_ADD_MOVIE_DEFAULT_TAGS'; case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: return 'RADARR_ADD_DISCOVER_USE_SUGGESTIONS'; case RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE: return 'RADARR_MANUAL_IMPORT_DEFAULT_MODE'; + case RadarrDatabaseValue.QUEUE_REFRESH_RATE: return 'RADARR_QUEUE_REFRESH_RATE'; + case RadarrDatabaseValue.QUEUE_BLACKLIST: return 'RADARR_RADARR_QUEUE_BLACKLIST'; + case RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT: return 'RADARR_QUEUE_REMOVE_FROM_CLIENT'; case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: return 'RADARR_REMOVE_MOVIE_IMPORT_LIST'; case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: return 'RADARR_REMOVE_MOVIE_DELETE_FILES'; case RadarrDatabaseValue.CONTENT_PAGE_SIZE: return 'RADARR_CONTENT_PAGE_SIZE'; @@ -181,6 +196,9 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: return _box.get(this.key, defaultValue: []); case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: return _box.get(this.key, defaultValue: true); case RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE: return _box.get(this.key, defaultValue: RadarrImportMode.COPY.value); + case RadarrDatabaseValue.QUEUE_REFRESH_RATE: return _box.get(this.key, defaultValue: 60); + case RadarrDatabaseValue.QUEUE_BLACKLIST: return _box.get(this.key, defaultValue: false); + case RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT: return _box.get(this.key, defaultValue: false); case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: return _box.get(this.key, defaultValue: false); case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: return _box.get(this.key, defaultValue: false); case RadarrDatabaseValue.CONTENT_PAGE_SIZE: return _box.get(this.key, defaultValue: 25); @@ -208,6 +226,9 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: if(value is List) box.put(this.key, value); return; case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: if(value is bool) box.put(this.key, value); return; case RadarrDatabaseValue.MANUAL_IMPORT_DEFAULT_MODE: if(value is RadarrImportMode) box.put(this.key, value.value); return; + case RadarrDatabaseValue.QUEUE_REFRESH_RATE: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.QUEUE_BLACKLIST: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT: if(value is bool) box.put(this.key, value); return; case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: if(value is bool) box.put(this.key, value); return; case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: if(value is bool) box.put(this.key, value); return; case RadarrDatabaseValue.CONTENT_PAGE_SIZE: if(value is int) box.put(this.key, value); return; diff --git a/lib/modules/radarr/core/dialogs.dart b/lib/modules/radarr/core/dialogs.dart index 3c424541..dd0aded6 100644 --- a/lib/modules/radarr/core/dialogs.dart +++ b/lib/modules/radarr/core/dialogs.dart @@ -427,6 +427,45 @@ class RadarrDialogs { ); } + Future confirmDeleteQueue(BuildContext context) async { + bool _flag = false; + + void _setValues(bool flag) { + _flag = flag; + Navigator.of(context, rootNavigator: true).pop(); + } + + await LunaDialog.dialog( + context: context, + title: 'Remove From Queue', + buttons: [ + LunaDialog.button( + text: 'Remove', + textColor: LunaColours.red, + onPressed: () => _setValues(true), + ), + ], + content: [ + RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT.listen( + builder: (context, box, _) => LunaDialog.checkbox( + title: 'Remove From Client', + value: RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT.data, + onChanged: (selected) => RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT.put(selected), + ), + ), + RadarrDatabaseValue.QUEUE_BLACKLIST.listen( + builder: (context, box, _) => LunaDialog.checkbox( + title: 'Blacklist Release', + value: RadarrDatabaseValue.QUEUE_BLACKLIST.data, + onChanged: (selected) => RadarrDatabaseValue.QUEUE_BLACKLIST.put(selected), + ), + ), + ], + contentPadding: LunaDialog.listDialogContentPadding(), + ); + return _flag; + } + Future setAddTags(BuildContext context) async { await showDialog( context: context, diff --git a/lib/modules/radarr/core/extensions.dart b/lib/modules/radarr/core/extensions.dart index 24667f4d..b9bf282d 100644 --- a/lib/modules/radarr/core/extensions.dart +++ b/lib/modules/radarr/core/extensions.dart @@ -8,5 +8,6 @@ export 'extensions/radarr_manual_import.dart'; export 'extensions/radarr_movie_file.dart'; export 'extensions/radarr_movie.dart'; export 'extensions/radarr_quality_profile.dart'; +export 'extensions/radarr_queue_record.dart'; export 'extensions/radarr_release.dart'; export 'extensions/radarr_system_status.dart'; diff --git a/lib/modules/radarr/core/extensions/radarr_queue_record.dart b/lib/modules/radarr/core/extensions/radarr_queue_record.dart new file mode 100644 index 00000000..dc27f3ea --- /dev/null +++ b/lib/modules/radarr/core/extensions/radarr_queue_record.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/radarr.dart'; + +extension LunaRadarrQueueRecord on RadarrQueueRecord { + String get lunaQuality { + return this.quality?.quality?.name ?? LunaUI.TEXT_EMDASH; + } + + String get lunaLanguage { + if((this?.languages?.length ?? 0) == 0) return LunaUI.TEXT_EMDASH; + if(this.languages.length == 1) return this.languages[0].name ?? LunaUI.TEXT_EMDASH; + return 'Multi-Language'; + } + + String lunaTitle(RadarrMovie movie) { + String title = movie?.title ?? LunaUI.TEXT_EMDASH; + String year = movie?.lunaYear; + return '$title ($year)'; + } + + String get lunaDownloadClient { + if((this?.downloadClient ?? '').isNotEmpty) return this.downloadClient; + return LunaUI.TEXT_EMDASH; + } + + String get lunaIndexer { + if((this?.indexer ?? '').isNotEmpty) return this.indexer; + return LunaUI.TEXT_EMDASH; + } + + Color get lunaProtocolColor { + if(this.protocol == RadarrProtocol.USENET) return LunaColours.accent; + return LunaColours.blue; + } + + int get lunaPercentageComplete { + if(this.sizeLeft == null || this.size == null || this.size == 0) return 0; + double sizeFetched = this.size - this.sizeLeft; + return ((sizeFetched/this.size)*100).round(); + } + + IconData get lunaStatusIcon { + switch(this.status) { + case RadarrQueueRecordStatus.DELAY: return Icons.access_time_rounded; + case RadarrQueueRecordStatus.DOWNLOAD_CLIENT_UNAVAILABLE: return Icons.access_time_rounded; + case RadarrQueueRecordStatus.FAILED: return Icons.cloud_download_rounded; + case RadarrQueueRecordStatus.PAUSED: return Icons.pause_rounded; + case RadarrQueueRecordStatus.QUEUED: return Icons.cloud_rounded; + case RadarrQueueRecordStatus.WARNING: return Icons.cloud_download_rounded; + case RadarrQueueRecordStatus.COMPLETED: return Icons.download_done_rounded; + case RadarrQueueRecordStatus.DOWNLOADING: return Icons.cloud_download_rounded; + default: return Icons.cloud_download_rounded; + } + } + + Color get lunaStatusColor { + Color color = Colors.white; + if(this.status == RadarrQueueRecordStatus.COMPLETED) switch(this.trackedDownloadState) { + case RadarrTrackedDownloadState.FAILED_PENDING: color = LunaColours.red; break; + case RadarrTrackedDownloadState.IMPORT_PENDING: color = LunaColours.purple; break; + case RadarrTrackedDownloadState.IMPORTING: color = LunaColours.purple; break; + default: break; + } + if(this.trackedDownloadStatus == RadarrTrackedDownloadStatus.WARNING) color = LunaColours.orange; + switch(this.status) { + case RadarrQueueRecordStatus.DOWNLOAD_CLIENT_UNAVAILABLE: color = LunaColours.orange; break; + case RadarrQueueRecordStatus.FAILED: color = LunaColours.red; break; + case RadarrQueueRecordStatus.WARNING: color = LunaColours.orange; break; + default: break; + } + if(this.trackedDownloadStatus == RadarrTrackedDownloadStatus.ERROR) color = LunaColours.red; + return color; + } +} diff --git a/lib/modules/radarr/core/state.dart b/lib/modules/radarr/core/state.dart index cfb618c6..9039af9e 100644 --- a/lib/modules/radarr/core/state.dart +++ b/lib/modules/radarr/core/state.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; @@ -5,6 +7,12 @@ class RadarrState extends LunaModuleState { RadarrState() { reset(); } + + @override + void dispose() { + _getQueueTimer?.cancel(); + super.dispose(); + } @override void reset() { @@ -17,6 +25,7 @@ class RadarrState extends LunaModuleState { _qualityDefinitions = null; _languages = null; _tags = null; + _queue = null; // Reinitialize resetProfile(); if(_enabled) { @@ -26,6 +35,7 @@ class RadarrState extends LunaModuleState { fetchLanguages(); fetchTags(); fetchMovies(); + fetchQueue(); } notifyListeners(); } @@ -259,6 +269,38 @@ class RadarrState extends LunaModuleState { if(_api != null) _tags = _api.tag.getAll(); notifyListeners(); } + + ///////////// + /// QUEUE /// + ///////////// + + /// Timer to handle refreshing queue data + Timer _getQueueTimer; + + void createQueueTimer() => _getQueueTimer = Timer.periodic( + Duration(seconds: RadarrDatabaseValue.QUEUE_REFRESH_RATE.data), + (_) => fetchQueue(), + ); + + void cancelQueueTimer() => _getQueueTimer?.cancel(); + + + Future _queue; + Future get queue => _queue; + set queue(Future queue) { + assert(queue != null); + _queue = queue; + notifyListeners(); + } + + void fetchQueue() { + cancelQueueTimer(); + if(_api != null) { + _queue = _api.queue.get(); + createQueueTimer(); + } + notifyListeners(); + } ////////////// /// IMAGES /// diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart index 0a310cfe..60dffa2e 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart @@ -42,6 +42,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart index ed816a32..e47ff653 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart @@ -59,6 +59,8 @@ class _State extends State { ), margin: EdgeInsets.zero, color: Theme.of(context).canvasColor, + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, ); } } \ No newline at end of file diff --git a/lib/modules/radarr/routes/queue.dart b/lib/modules/radarr/routes/queue.dart index 6a579cde..29f788da 100644 --- a/lib/modules/radarr/routes/queue.dart +++ b/lib/modules/radarr/routes/queue.dart @@ -1 +1,2 @@ export 'queue/route.dart'; +export 'queue/widgets.dart'; diff --git a/lib/modules/radarr/routes/queue/route.dart b/lib/modules/radarr/routes/queue/route.dart index f0eaeec3..626d75cb 100644 --- a/lib/modules/radarr/routes/queue/route.dart +++ b/lib/modules/radarr/routes/queue/route.dart @@ -15,21 +15,79 @@ class _RadarrQueueRoute extends StatefulWidget { State createState() => _State(); } -class _State extends State<_RadarrQueueRoute> { +class _State extends State<_RadarrQueueRoute> with LunaLoadCallbackMixin, LunaScrollControllerMixin { final GlobalKey _scaffoldKey = GlobalKey(); + final GlobalKey _refreshKey = GlobalKey(); @override Widget build(BuildContext context) { return LunaScaffold( scaffoldKey: _scaffoldKey, appBar: _appBar(), - body: LunaMessage.goBack(context: context, text: 'lunasea.ComingSoon'.tr()), + body: _body(), ); } + @override + Future loadCallback() async { + if(context.read().enabled) { + await context.read().api.command.refreshMonitoredDownloads(); + context.read().fetchQueue(); + } + } + Widget _appBar() { return LunaAppBar( title: 'radarr.Queue'.tr(), + scrollControllers: [scrollController], + ); + } + + Widget _body() { + return LunaRefreshIndicator( + key: _refreshKey, + context: context, + onRefresh: loadCallback, + child: FutureBuilder( + future: Future.wait([ + context.select((RadarrState state) => state.queue), + context.select((RadarrState state) => state.movies), + ]), + builder: (context, AsyncSnapshot snapshot) { + if(snapshot.hasError) { + if(snapshot.connectionState != ConnectionState.waiting) LunaLogger().error( + 'Unable to fetch Radarr queue', + snapshot.error, + snapshot.stackTrace, + ); + return LunaMessage.error(onTap: _refreshKey.currentState?.show); + } + if(snapshot.hasData) return _list(snapshot.data[0], snapshot.data[1]); + return LunaLoader(); + }, + ), + ); + } + + Widget _list(RadarrQueue queue, List movies) { + if((queue?.records?.length ?? 0) == 0) return LunaMessage( + text: 'Empty Queue', + buttonText: 'lunasea.Refresh'.tr(), + onTap: _refreshKey.currentState?.show, + ); + return LunaListViewBuilder( + controller: scrollController, + itemCount: queue.records.length, + itemBuilder: (context, index) { + RadarrMovie movie = movies.firstWhere( + (movie) => movie.id == queue.records[index].movieId, + orElse: () => null, + ); + return RadarrQueueTile( + record: queue.records[index], + movie: movie, + ); + } ); } } diff --git a/lib/modules/radarr/routes/queue/widgets.dart b/lib/modules/radarr/routes/queue/widgets.dart new file mode 100644 index 00000000..f6ee9af5 --- /dev/null +++ b/lib/modules/radarr/routes/queue/widgets.dart @@ -0,0 +1 @@ +export 'widgets/queue_tile.dart'; diff --git a/lib/modules/radarr/routes/queue/widgets/queue_tile.dart b/lib/modules/radarr/routes/queue/widgets/queue_tile.dart new file mode 100644 index 00000000..aed20dac --- /dev/null +++ b/lib/modules/radarr/routes/queue/widgets/queue_tile.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/radarr.dart'; + +class RadarrQueueTile extends StatelessWidget { + final RadarrQueueRecord record; + final RadarrMovie movie; + + RadarrQueueTile({ + Key key, + @required this.record, + @required this.movie, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: context.watch().movies, + builder: (context, AsyncSnapshot> snapshot) { + RadarrMovie movie; + if(snapshot.hasData) movie = snapshot.data.firstWhere( + (element) => element.id == record.movieId, + orElse: () => null, + ); + return LunaExpandableListTile( + title: record.title, + collapsedSubtitle1: _subtitle1(), + collapsedSubtitle2: _subtitle2(), + expandedHighlightedNodes: _highlightedNodes(), + expandedTableContent: _tableContent(movie), + expandedTableButtons: _tableButtons(context), + collapsedTrailing: LunaIconButton( + icon: record.lunaStatusIcon, + color: record.lunaStatusColor, + ), + onLongPress: () async => RadarrMoviesDetailsRouter().navigateTo(context, movieId: record.movieId), + ); + }, + ); + } + + TextSpan _subtitle1() { + return TextSpan(text: record?.lunaTitle(movie) ?? LunaUI.TEXT_EMDASH); + } + + TextSpan _subtitle2() { + return TextSpan( + children: [ + TextSpan( + text: record.lunaQuality, + style: TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: record?.timeLeft ?? LunaUI.TEXT_EMDASH), + ], + ); + } + + List _tableContent(RadarrMovie movie) { + return [ + LunaTableContent(title: 'radarr.Movie'.tr(), body: record?.lunaTitle(movie)), + LunaTableContent(title: 'radarr.Languages'.tr(), body: record.lunaLanguage), + LunaTableContent(title: 'Client', body: record.lunaDownloadClient), + LunaTableContent(title: 'Indexer', body: record.lunaIndexer), + LunaTableContent(title: 'radarr.Size'.tr(), body: record.size.toInt().lunaBytesToString()), + LunaTableContent(title: 'Time Left', body: record.timeLeft ?? LunaUI.TEXT_EMDASH), + ]; + } + + List _highlightedNodes() { + return [ + LunaHighlightedNode( + text: record.protocol?.readable ?? LunaUI.TEXT_EMDASH, + backgroundColor: LunaColours.blue, + ), + LunaHighlightedNode( + text: record.lunaQuality, + backgroundColor: LunaColours.accent, + ), + if((record.customFormats?.length ?? 0) != 0) for(int i=0; i _tableButtons(BuildContext context) { + return [ + if((record?.statusMessages ?? []).isNotEmpty) LunaButton.text( + icon: Icons.messenger_outline_rounded, + text: 'Messages', + onTap: () async { + LunaDialogs().showMessages( + context, + record.statusMessages.map((status) => status.messages.join('\n')).toList(), + ); + }, + ), + if( + record?.status == RadarrQueueRecordStatus.COMPLETED && + record?.trackedDownloadStatus == RadarrTrackedDownloadStatus.WARNING && + (record?.outputPath ?? '').isNotEmpty + ) LunaButton.text( + icon: Icons.download_done_rounded, + text: 'radarr.Import'.tr(), + onTap: () async => RadarrManualImportDetailsRouter().navigateTo(context, path: record.outputPath) + ), + LunaButton.text( + icon: Icons.delete_rounded, + color: LunaColours.red, + text: 'Remove', + onTap: () async { + if(context.read().enabled) { + bool result = await RadarrDialogs().confirmDeleteQueue(context); + if(result) { + await context.read().api.queue.delete( + id: record.id, + blacklist: RadarrDatabaseValue.QUEUE_BLACKLIST.data, + removeFromClient: RadarrDatabaseValue.QUEUE_REMOVE_FROM_CLIENT.data, + ); + context.read().api.command.refreshMonitoredDownloads() + .then((_) => context.read().fetchQueue()); + } + } + }, + ), + ]; + } +} diff --git a/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart b/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart index d489835e..c419719e 100644 --- a/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart +++ b/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart @@ -44,6 +44,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 14.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart b/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart index 60555681..fd9ee10e 100644 --- a/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart +++ b/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart @@ -61,6 +61,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 13.5), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sabnzbd/widgets/history_hide_button.dart b/lib/modules/sabnzbd/widgets/history_hide_button.dart index 6d8729ef..a922289f 100644 --- a/lib/modules/sabnzbd/widgets/history_hide_button.dart +++ b/lib/modules/sabnzbd/widgets/history_hide_button.dart @@ -26,6 +26,8 @@ class _State extends State { onPressed: () => model.historyHideFailed = !model.historyHideFailed, ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.only(left: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/settings/routes/configuration.dart b/lib/modules/settings/routes/configuration.dart index 7398cbe2..34b7245d 100644 --- a/lib/modules/settings/routes/configuration.dart +++ b/lib/modules/settings/routes/configuration.dart @@ -70,7 +70,7 @@ class _State extends State<_SettingsConfigurationRoute> with LunaScrollControlle }, ), ), - LunaListTile( + if(LunaQuickActions.isPlatformCompatible) LunaListTile( context: context, title: LunaText.title(text: 'Quick Actions'), subtitle: LunaText.subtitle(text: 'Quick Actions on the Home Screen'), diff --git a/lib/modules/settings/routes/settings.dart b/lib/modules/settings/routes/settings.dart index 9464a303..38a961a3 100644 --- a/lib/modules/settings/routes/settings.dart +++ b/lib/modules/settings/routes/settings.dart @@ -64,7 +64,7 @@ class _State extends State<_SettingsHomeRoute> with LunaScrollControllerMixin { onTap: () async => SettingsProfilesRouter().navigateTo(context), ), LunaDivider(), - LunaListTile( + if(LunaInAppPurchases.isPlatformCompatible) LunaListTile( context: context, title: LunaText.title(text: 'Donations'), subtitle: LunaText.subtitle(text: 'Donate to the Developer'), diff --git a/lib/modules/settings/routes/system/route.dart b/lib/modules/settings/routes/system/route.dart index fb65deec..844398a9 100644 --- a/lib/modules/settings/routes/system/route.dart +++ b/lib/modules/settings/routes/system/route.dart @@ -2,7 +2,7 @@ import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; class SettingsSystemRouter extends SettingsPageRouter { SettingsSystemRouter() : super('/settings/system'); @@ -45,9 +45,9 @@ class _State extends State<_SettingsSystemRoute> with LunaScrollControllerMixin SettingsSystemBackupRestoreBackupTile(), SettingsSystemBackupRestoreRestoreTile(), LunaDivider(), - _enableAnalytics(), - _enableCrashlytics(), - LunaDivider(), + if(LunaFirebaseAnalytics.isPlatformCompatible) _enableAnalytics(), + if(LunaFirebaseCrashlytics.isPlatformCompatible) _enableCrashlytics(), + if(LunaFirebaseCrashlytics.isPlatformCompatible || LunaFirebaseAnalytics.isPlatformCompatible) LunaDivider(), _language(), _clearConfiguration(), ], @@ -57,17 +57,18 @@ class _State extends State<_SettingsSystemRoute> with LunaScrollControllerMixin Widget _versionInformation() { return FutureBuilder( future: PackageInfo.fromPlatform(), - builder: (context, AsyncSnapshot snapshot) => LunaListTile( - context: context, - title: LunaText.title( - text: snapshot.hasData - ? 'Version: ${snapshot.data.version} (${snapshot.data.buildNumber})' - : 'Version: Loading...', - ), - subtitle: LunaText.subtitle(text: 'View Recent Changes'), - trailing: LunaIconButton(icon: Icons.system_update), - onTap: () async => LunaChangelog().showChangelog(snapshot.data.version, snapshot.data.buildNumber), - ), + builder: (context, AsyncSnapshot snapshot) { + String version = 'Loading${LunaUI.TEXT_ELLIPSIS}'; + if(snapshot.hasError) version = 'Unknown'; + if(snapshot.hasData) version = '${snapshot.data.version} (${snapshot.data.buildNumber})'; + return LunaListTile( + context: context, + title: LunaText.title(text: 'Version: $version'), + subtitle: LunaText.subtitle(text: 'View Recent Changes'), + trailing: LunaIconButton(icon: Icons.system_update), + onTap: () async => LunaChangelog().showChangelog(snapshot.data?.version, snapshot.data?.buildNumber), + ); + } ); } diff --git a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart index 130131e6..31a95749 100644 --- a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart +++ b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart @@ -91,7 +91,7 @@ class _State extends State { children: [ LunaHighlightedNode( text: widget.record?.protocol?.lunaCapitalizeFirstLetters() ?? LunaUI.TEXT_EMDASH, - backgroundColor: widget.record.protocol == 'torrent' ? LunaColours.purple : LunaColours.blue, + backgroundColor: LunaColours.blue, ), LunaHighlightedNode( text: widget.record?.quality?.quality?.name ?? 'Unknown', @@ -167,15 +167,14 @@ class _State extends State { text: 'Messages', icon: Icons.messenger_outline_rounded, onTap: () async { - String messages = widget.record.statusMessages.fold('', (warnings, status) { - warnings += status.messages.fold('', (message, element) => message += '\n${LunaUI.TEXT_BULLET} $element'); - return warnings.trim(); - }); - LunaDialogs().textPreview(context, 'Messages', messages); + LunaDialogs().showMessages( + context, + widget.record.statusMessages.map((status) => status.messages.join('\n')).toList(), + ); }, ), LunaButton.text( - text: 'Delete', + text: 'Remove', icon: Icons.delete_rounded, color: LunaColours.red, onTap: () async => _deleteQueueRecord(), diff --git a/lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart b/lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart index 284d83dc..87fefff4 100644 --- a/lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart +++ b/lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart @@ -43,6 +43,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: LunaTextInputBar.appBarMargin.subtract(EdgeInsets.only(left: 12.0)), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart b/lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart index c6f608a3..051f83a8 100644 --- a/lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart +++ b/lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart @@ -60,6 +60,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: LunaTextInputBar.appBarMargin.subtract(EdgeInsets.only(left: 12.0)), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart b/lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart index 9b2f3cb4..f2d2ab29 100644 --- a/lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart +++ b/lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart @@ -43,6 +43,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart b/lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart index 4301626d..1860b528 100644 --- a/lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart +++ b/lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart @@ -60,6 +60,8 @@ class _State extends State { ), ), ), + height: LunaTextInputBar.appBarInnerHeight, + width: LunaTextInputBar.appBarInnerHeight, margin: EdgeInsets.zero, color: Theme.of(context).canvasColor, ); diff --git a/localization/radarr/en.json b/localization/radarr/en.json index cc1de367..907bb0f3 100644 --- a/localization/radarr/en.json +++ b/localization/radarr/en.json @@ -33,6 +33,7 @@ "radarr.InCinemasIn": "In Cinemas in {}", "radarr.InCinemasToday": "In Cinemas Today", "radarr.Interactive": "Interactive", + "radarr.Language": "Language", "radarr.Languages": "Languages", "radarr.ManualImport": "Manual Import", "radarr.ManualImportDescription": "Import Movies from the Filesystem", diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a9aba3d0..4817e850 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,7 +13,7 @@ import firebase_crashlytics import firebase_messaging import firebase_storage import network_info_plus_macos -import package_info +import package_info_plus_macos import path_provider_macos import shared_preferences_macos import url_launcher_macos diff --git a/macos/Podfile.lock b/macos/Podfile.lock index ea35aa63..f2d1a851 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -243,7 +243,7 @@ PODS: - Firebase/Storage (7.3.0): - Firebase/CoreOnly - FirebaseStorage (~> 7.3.0) - - firebase_auth (1.0.2): + - firebase_auth (1.0.3): - Firebase/Auth (~> 7.3.0) - Firebase/CoreOnly (~> 7.3.0) - firebase_core @@ -261,7 +261,7 @@ PODS: - Firebase/Messaging (~> 7.3.0) - firebase_core - FlutterMacOS - - firebase_storage (8.0.2): + - firebase_storage (8.0.3): - Firebase/CoreOnly (~> 7.3.0) - Firebase/Storage (~> 7.3.0) - firebase_core @@ -372,7 +372,7 @@ PODS: - network_info_plus_macos (0.0.1): - FlutterMacOS - Reachability - - package_info (0.0.1): + - package_info_plus_macos (0.0.1): - FlutterMacOS - path_provider_macos (0.0.1): - FlutterMacOS @@ -393,7 +393,7 @@ DEPENDENCIES: - firebase_storage (from `Flutter/ephemeral/.symlinks/plugins/firebase_storage/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - network_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/network_info_plus_macos/macos`) - - package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`) + - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`) - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) @@ -441,8 +441,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral network_info_plus_macos: :path: Flutter/ephemeral/.symlinks/plugins/network_info_plus_macos/macos - package_info: - :path: Flutter/ephemeral/.symlinks/plugins/package_info/macos + package_info_plus_macos: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos path_provider_macos: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos shared_preferences_macos: @@ -456,11 +456,11 @@ SPEC CHECKSUMS: cloud_firestore: 2f06cee99ba40ed1ed79792bafa13e5727bcb706 connectivity_plus_macos: dbac1dc9b113dfb69a5691831313b1d8ac1c8269 Firebase: 26223c695fe322633274198cb19dca8cb7e54416 - firebase_auth: a17717c1f074d844ed176534dd588537293d7b27 + firebase_auth: ba042208860f086aead3c50fcacff096262854e8 firebase_core: 5c0a84649819db6738f6d159548762a2def9cb50 firebase_crashlytics: 748de8459d6b018d3cca37e6ed7f8c2fbd86d4cd firebase_messaging: 044c01f581b49f873a217792b3e2006ba7be8b9f - firebase_storage: 42cc28b760954e1a2810565ad0ffccfcd3b3fa49 + firebase_storage: e0b723ec5030607a97de3f269c6bb7145de3d193 FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350 @@ -479,7 +479,7 @@ SPEC CHECKSUMS: leveldb-library: 50c7b45cbd7bf543c81a468fe557a16ae3db8729 nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 network_info_plus_macos: 38ec4a578ecdfcddab609f61da3026d0b32900e5 - package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 + package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 diff --git a/pubspec.lock b/pubspec.lock index 9b7916b2..c8d1ed91 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "19.0.0" + version: "20.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.4.0" archive: dependency: transitive description: @@ -343,21 +343,21 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.0.2" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" firebase_core: dependency: "direct main" description: @@ -420,7 +420,7 @@ packages: name: firebase_storage url: "https://pub.dartlang.org" source: hosted - version: "8.0.2" + version: "8.0.3" firebase_storage_platform_interface: dependency: transitive description: @@ -582,7 +582,7 @@ packages: name: in_app_purchase url: "https://pub.dartlang.org" source: hosted - version: "0.5.1+2" + version: "0.5.1+3" infinite_scroll_pagination: dependency: "direct main" description: @@ -709,13 +709,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" - package_info: + package_info_plus: dependency: "direct main" description: - name: package_info + name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "1.0.0" + package_info_plus_linux: + dependency: transitive + description: + name: package_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_info_plus_macos: + dependency: transitive + description: + name: package_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_info_plus_web: + dependency: transitive + description: + name: package_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_info_plus_windows: + dependency: transitive + description: + name: package_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -855,14 +890,14 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.1" radarr: dependency: "direct main" description: - name: radarr - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" + path: "/Users/jagandeepbrar/Git/Packages/radarr" + relative: false + source: path + version: "2.1.0" share: dependency: "direct main" description: @@ -1098,7 +1133,7 @@ packages: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.3" + version: "3.0.4" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 49e7df2e..27e67461 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,36 +13,38 @@ dependencies: connectivity_plus: ^1.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS convert: ^3.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS dio: ^4.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS - easy_localization: ^3.0.0 # ANDROID IOS LINUX MACOS WEB + easy_localization: ^3.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS encrypt: ^5.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS expandable: ^5.0.1 # ANDROID IOS LINUX MACOS WEB WINDOWS file_picker: ^3.0.1 # ANDROID IOS WEB firebase_analytics: ^7.1.1 # ANDROID IOS WEB - firebase_auth: ^1.0.2 # ANDROID IOS MACOS WEB + firebase_auth: ^1.0.3 # ANDROID IOS MACOS WEB firebase_core: ^1.0.3 # ANDROID IOS MACOS WEB firebase_crashlytics: ^2.0.0 # ANDROID IOS MACOS firebase_messaging: ^9.1.1 # ANDROID IOS MACOS WEB - firebase_storage: ^8.0.2 # ANDROID IOS MACOS WEB + firebase_storage: ^8.0.3 # ANDROID IOS MACOS WEB flash: ^1.5.1 # ANDROID IOS LINUX MACOS WEB WINDOWS fluro: ^2.0.3 # ANDROID IOS LINUX MACOS WEB WINDOWS flutter_spinkit: ^5.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS fl_chart: ^0.35.0 # ANDROID IOS LINUX MACOS WEB WINDOWS + # font_awesome_flutter: # ANDROID IOS LINUX MACOS WEB WINDOWS + # path: vendor/font_awesome_flutter google_nav_bar: ^5.0.3 # ANDROID IOS LINUX MACOS WEB WINDOWS hive: ^2.0.2 # ANDROID IOS LINUX MACOS WEB WINDOWS hive_flutter: ^1.0.0 # ANDROID IOS LINUX MACOS WINDOWS - in_app_purchase: ^0.5.1+2 # ANDROID IOS + in_app_purchase: ^0.5.1+3 # ANDROID IOS infinite_scroll_pagination: ^3.0.1 # ANDROID IOS LINUX MACOS WEB WINDOWS intl: ^0.17.0 # ANDROID IOS LINUX MACOS WEB WINDOWS modal_bottom_sheet: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS network_info_plus: ^1.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS - package_info: ^2.0.0 # ANDROID IOS MACOS + package_info_plus: ^1.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS path_provider: ^2.0.1 # ANDROID IOS LINUX MACOS WINDOWS percent_indicator: ^3.0.1 # ANDROID IOS LINUX MACOS WEB WINDOWS provider: ^5.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS quick_actions: ^0.6.0 # ANDROID IOS - radarr: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS - # radarr: - # path: /Users/jagandeepbrar/Git/Packages/radarr + # radarr: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS + radarr: + path: /Users/jagandeepbrar/Git/Packages/radarr share: ^2.0.1 # ANDROID IOS sonarr: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS stack_trace: ^1.10.0 # ANDROID IOS LINUX MACOS WEB WINDOWS @@ -51,7 +53,7 @@ dependencies: tautulli: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS tuple: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS url_launcher: ^6.0.3 # ANDROID IOS LINUX MACOS WEB WINDOWS - uuid: ^3.0.3 # ANDROID IOS LINUX MACOS WEB WINDOWS + uuid: ^3.0.4 # ANDROID IOS LINUX MACOS WEB WINDOWS wake_on_lan: ^2.0.1+1 # ANDROID IOS LINUX MACOS WINDOWS xml: ^5.1.0 # ANDROID IOS LINUX MACOS WEB WINDOWS diff --git a/vendor/font_awesome_flutter/.gitignore b/vendor/font_awesome_flutter/.gitignore new file mode 100755 index 00000000..f8fded0a --- /dev/null +++ b/vendor/font_awesome_flutter/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +.atom/ +.idea +.packages +.dart_tool/ +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock +.flutter-plugins +local.properties diff --git a/vendor/font_awesome_flutter/LICENSE b/vendor/font_awesome_flutter/LICENSE new file mode 100644 index 00000000..95e09cf0 --- /dev/null +++ b/vendor/font_awesome_flutter/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2017 Brian Egan +Font Awesome Icons by @fontawesome - https://fontawesome.com +License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/font_awesome_flutter/icons.json.gpg b/vendor/font_awesome_flutter/icons.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..432cfc46c333421ca3200905d5cbb40a6f485a1c GIT binary patch literal 2003795 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

L7ilmrr-X~s3+Zvmt*PpnSQ{=UdOxW*5 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

AQ&>s=NO@HSo0{7vu0|87!@_MVZBS; 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