diff --git a/.gitignore b/.gitignore index 24958ac0..36599d0c 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,8 @@ _changelog.txt # Build Configurations ExportOptions.plist key.properties + +# Firebase +android/app/google-services.json +ios/GoogleService-Info.plist +output/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 140b6780..96bd7e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # LunaSea Changelog +## [Beta/TestFlight] v4.2.0 (40200001) + +#### NEW +- `[Settings/Modules]` Add an information/help button with module descriptions and links + +#### TWEAKS +- `[Radarr]` Always show the amount of days content will be available instead of limiting it to only content in the next 30 days +- `[Settings]` Moved "Backup & Restore" and "Logs" to "System" section +- `[Settings]` Merged "Customization" and "Modules" sections into "Configuration" +- `[Settings/Sonarr]` Removed the need to enable Sonarr to test the connection +- `[Settings/Tautulli]` Removed the need to enable Tautulli to test the connection + +#### FIXES +- `[IAPs]` Ensure all IAPs are marked as "consumed" +- `[Flutter]` Updated packages +- `[Logging]` Updated Sentry to v4 framework to improve capturing fatal/crashing bugs +- `[Settings/Logs]` Hide exception and stack trace buttons when an error is not available +- `[Sonarr/History]` Fixed history fetching the oldest entries, not the newest +- `[State]` Correctly clear state when clearing LunaSea's configuration +- `[Tautulli/Activity]` Fixed consistency of hardware transcoding indicator compared to the web UI +- `[UI/Divider]` Fix consistency of divider width across regular and AMOLED dark theme + +--- + ## [Stable/Production] v4.1.1 (40101001) #### NEW diff --git a/README.md b/README.md index 03351f31..b0bcaf1b 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,19 @@ [![Play Store](https://img.shields.io/badge/Play%20Store-v4.1.1-red?style=for-the-badge&logo=google-play&color=%234CAF50&logoColor=white)](https://play.google.com/store/apps/details?id=app.lunasea.lunasea) [![Subreddit](https://img.shields.io/reddit/subreddit-subscribers/LunaSeaApp?label=reddit&logo=reddit&color=%23F44336&logoColor=white&style=for-the-badge)](https://www.reddit.com/r/LunaSeaApp) -![LunaSea](https://uploads-ssl.webflow.com/5fcfe91dceef944b8c203a63/5fd235627cda4b4967b528d8_banner.png) +![LunaSea](https://uploads-ssl.webflow.com/5fcfe91dceef944b8c203a63/5fd4d46b86a0830f10cad146_lunasea_splash_2.png) -![Screenshot](https://uploads-ssl.webflow.com/5fcfe91dceef944b8c203a63/5fd235d3153d39465529f5eb_hero.png) - -LunaSea is a fully featured, open source self-hosted software controller! Focused on giving you a seamless experience between all of your self-hosted media software, LunaSea supports: +LunaSea is a fully featured, open source self-hosted controller! Focused on giving you a seamless experience between all of your self-hosted media software, LunaSea supports: - Lidarr - Radarr - Sonarr -- SABnzbd - NZBGet -- NZBHydra2 +- SABnzbd - Newznab Indexer Searching -- Wake on LAN +- NZBHydra2 - Tautulli +- Wake on LAN LunaSea even comes with support for multiple instances of applications using profiles, backing up and restoring your configuration to your filesystem, an AMOLED black theme, and more! @@ -33,6 +31,7 @@ LunaSea even comes with support for multiple instances of applications using pro 3. [Flutter Framework (Stable Channel)](https://flutter.dev/) 4. [XCode](https://apps.apple.com/ca/app/xcode/id497799835?mt=12) 5. [Developer Certificate Configured](https://github.com/LunaSeaApp/LunaSea/wiki/Setup-of-Development-Certificate) +6. [FlutterFire for IOS Configured](https://firebase.flutter.dev/docs/installation/ios/) #### Developing @@ -74,6 +73,7 @@ LunaSea even comes with support for multiple instances of applications using pro 1. [Android SDK/Android Studio Installed & Configured](https://developer.android.com/studio) 2. [Flutter Framework (Stable Channel)](https://flutter.dev/) 3. [Keystore Configured](https://github.com/LunaSeaApp/LunaSea/wiki/Configure-Keystore) +4. [FlutterFire for Android Configured](https://firebase.flutter.dev/docs/installation/android/) #### Developing diff --git a/android/app/build.gradle b/android/app/build.gradle index ba40e65e..1db81ab9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,6 +23,7 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'com.google.gms.google-services' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def keystoreProperties = new Properties() diff --git a/android/build.gradle b/android/build.gradle index dae7f94a..d438c50c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.3' } } diff --git a/assets/rive/coding.flr b/assets/rive/coding.flr deleted file mode 100644 index ba2a3966..00000000 Binary files a/assets/rive/coding.flr and /dev/null differ diff --git a/ios/Podfile b/ios/Podfile index 77bf8364..2f7df640 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -30,25 +30,12 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! - + pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '6.33.0' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ - '$(inherited)', - 'PERMISSION_SPEECH_RECOGNIZER=0', - 'PERMISSION_SENSORS=0', - 'PERMISSION_MEDIA_LIBRARY=0', - 'PERMISSION_EVENTS=0', - 'PERMISSION_CONTACTS=0', - 'PERMISSION_REMINDERS=0', - 'PERMISSION_MICROPHONE=0' - ] - end end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0d627dad..28858305 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,9 @@ PODS: + - cloud_firestore (0.14.4): + - Firebase/CoreOnly (~> 6.33.0) + - Firebase/Firestore (~> 6.33.0) + - firebase_core + - Flutter - DKImagePickerController/Core (4.3.2): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -33,20 +38,92 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter + - Firebase/Auth (6.33.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 6.9.2) + - Firebase/CoreOnly (6.33.0): + - FirebaseCore (= 6.10.3) + - Firebase/Firestore (6.33.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 1.18.0) + - Firebase/Storage (6.33.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 3.9.0) + - firebase_auth (0.18.4-1): + - Firebase/Auth (~> 6.33.0) + - Firebase/CoreOnly (~> 6.33.0) + - firebase_core + - Flutter + - firebase_core (0.5.3): + - Firebase/CoreOnly (~> 6.33.0) + - Flutter + - firebase_storage (5.2.0): + - Firebase/CoreOnly (~> 6.33.0) + - Firebase/Storage (~> 6.33.0) + - firebase_core + - Flutter + - FirebaseAuth (6.9.2): + - FirebaseCore (~> 6.10) + - GoogleUtilities/AppDelegateSwizzler (~> 6.7) + - GoogleUtilities/Environment (~> 6.7) + - GTMSessionFetcher/Core (~> 1.1) + - FirebaseCore (6.10.3): + - FirebaseCoreDiagnostics (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - FirebaseCoreDiagnostics (1.7.0): + - GoogleDataTransport (~> 7.4) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - nanopb (~> 1.30906.0) + - FirebaseFirestore (1.18.0) + - FirebaseStorage (3.9.1): + - FirebaseCore (~> 6.10) + - GTMSessionFetcher/Core (~> 1.1) - Flutter (1.0.0) + - GoogleDataTransport (7.5.1): + - nanopb (~> 1.30906.0) + - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (6.7.2): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (6.7.2)" + - GoogleUtilities/Reachability (6.7.2): + - GoogleUtilities/Logger + - GTMSessionFetcher/Core (1.5.0) - in_app_purchase (0.0.1): - Flutter + - nanopb (1.30906.0): + - nanopb/decode (= 1.30906.0) + - nanopb/encode (= 1.30906.0) + - nanopb/decode (1.30906.0) + - nanopb/encode (1.30906.0) - package_info (0.0.1): - Flutter - path_provider (0.0.1): - Flutter - "permission_handler (5.0.1+1)": - Flutter + - PromisesObjC (1.2.11) - quick_actions (0.0.1): - Flutter - SDWebImage (5.10.0): - SDWebImage/Core (= 5.10.0) - SDWebImage/Core (5.10.0) + - Sentry (6.0.12): + - Sentry/Core (= 6.0.12) + - Sentry/Core (6.0.12) + - sentry_flutter (0.0.1): + - Flutter + - Sentry (~> 6.0.10) - share (0.0.1): - Flutter - SwiftyGif (5.3.0) @@ -54,13 +131,19 @@ PODS: - Flutter DEPENDENCIES: + - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) + - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `6.33.0`) - Flutter (from `Flutter`) - in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - quick_actions (from `.symlinks/plugins/quick_actions/ios`) + - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share (from `.symlinks/plugins/share/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) @@ -68,12 +151,34 @@ SPEC REPOS: trunk: - DKImagePickerController - DKPhotoGallery + - Firebase + - FirebaseAuth + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseStorage + - GoogleDataTransport + - GoogleUtilities + - GTMSessionFetcher + - nanopb + - PromisesObjC - SDWebImage + - Sentry - SwiftyGif EXTERNAL SOURCES: + cloud_firestore: + :path: ".symlinks/plugins/cloud_firestore/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_storage: + :path: ".symlinks/plugins/firebase_storage/ios" + FirebaseFirestore: + :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git + :tag: 6.33.0 Flutter: :path: Flutter in_app_purchase: @@ -86,26 +191,50 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/permission_handler/ios" quick_actions: :path: ".symlinks/plugins/quick_actions/ios" + sentry_flutter: + :path: ".symlinks/plugins/sentry_flutter/ios" share: :path: ".symlinks/plugins/share/ios" url_launcher: :path: ".symlinks/plugins/url_launcher/ios" +CHECKOUT OPTIONS: + FirebaseFirestore: + :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git + :tag: 6.33.0 + SPEC CHECKSUMS: + cloud_firestore: b8c0e15fa49dfff87c2817d288b577e5dca2df13 DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 + Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 + firebase_auth: d5159db3873478d1ac839af7b10d2f831516136a + firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 + firebase_storage: a023e199edb807d8481c8aa722c8516f462ffab2 + FirebaseAuth: c92d49ada7948d1a23466e3db17bc4c2039dddc3 + FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd + FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 + FirebaseFirestore: 4e00bcfe64c4cb493124b87e249abe6c580403c7 + FirebaseStorage: 15e0f15ef3c7fec3d1899d68623e47d4447066b4 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6 + nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 + PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f quick_actions: 6cb2390c4dab0e737c94573c27e18d9666710720 SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee + Sentry: 8ffcfe3416e0a58b90bb1ae319e8e8a13022a03e + sentry_flutter: e57e33f56d5791e4effa48e85e44505367686b27 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef -PODFILE CHECKSUM: d583357f6c19d22241654320250048fb085838b3 +PODFILE CHECKSUM: 1b77370c3c4c6c5f98e1d8ea21a13d3289f277f1 COCOAPODS: 1.10.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 46e93f31..4125f5f7 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 9C2991242453FE3100B93AB2 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C2991232453FE3100B93AB2 /* StoreKit.framework */; }; + 9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -47,6 +48,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C0A396F23E0F00900EC8127 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 9C2991232453FE3100B93AB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; + 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; A046209115F73D5EC348E242 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D5A8F6AE14D184A58D8A7331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E2F8912DE8B8A6F72277E96C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; @@ -79,6 +81,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -153,6 +156,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, FB187DB152E6AC8A61142056 /* [CP] Embed Pods Frameworks */, + 5BA92B8D22335C6E222F251E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -205,6 +209,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -227,6 +232,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 5BA92B8D22335C6E222F251E /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/FirebaseFirestore/FirebaseFirestore/Resources/gRPCCertificates-Cpp.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -273,13 +296,19 @@ "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", + "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", "${BUILT_PRODUCTS_DIR}/in_app_purchase/in_app_purchase.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", + "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", "${BUILT_PRODUCTS_DIR}/share/share.framework", "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", ); @@ -288,13 +317,19 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.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}/sentry_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", ); diff --git a/lib/core.dart b/lib/core.dart index 0cbc0895..c51f311d 100644 --- a/lib/core.dart +++ b/lib/core.dart @@ -1,20 +1,22 @@ -export 'core/constants.dart'; +export 'core/adapters.dart'; export 'core/configuration.dart'; +export 'core/constants.dart'; export 'core/database.dart'; export 'core/dialogs.dart'; export 'core/encryption.dart'; export 'core/extensions.dart'; -export 'core/filesystem.dart'; export 'core/image_cache.dart'; +export 'core/filesystem.dart'; +export 'core/firebase.dart'; export 'core/in_app_purchases.dart'; export 'core/logger.dart'; export 'core/luna_ui.dart'; -export 'core/module_map.dart'; +export 'core/module_metadata.dart'; export 'core/networking.dart'; export 'core/profile.dart'; -export 'core/providers.dart'; export 'core/quick_actions.dart'; export 'core/router.dart'; +export 'core/state.dart'; export 'core/theme.dart'; export 'core/types.dart'; export 'core/ui.dart'; diff --git a/lib/core/database/adapters.dart b/lib/core/adapters.dart similarity index 66% rename from lib/core/database/adapters.dart rename to lib/core/adapters.dart index b7ee04c5..7907b13d 100644 --- a/lib/core/database/adapters.dart +++ b/lib/core/adapters.dart @@ -1,3 +1,2 @@ export 'adapters/indexer.dart'; -export 'adapters/lunasea.dart'; export 'adapters/profile.dart'; diff --git a/lib/core/adapters/indexer.dart b/lib/core/adapters/indexer.dart new file mode 100644 index 00000000..2df2a721 --- /dev/null +++ b/lib/core/adapters/indexer.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; + +part 'indexer.g.dart'; + +/// Hive database object containing all information on an indexer +@HiveType(typeId: 1, adapterName: 'IndexerHiveObjectAdapter') +class IndexerHiveObject extends HiveObject { + /// Create a new [IndexerHiveObject] object with all fields set to empty values('', false, 0, {}, etc.) + factory IndexerHiveObject.empty() => IndexerHiveObject( + displayName: '', + host: '', + key: '', + headers: {}, + ); + + /// Create a new [IndexerHiveObject] from another [IndexerHiveObject] (deep-copy). + factory IndexerHiveObject.fromIndexerHiveObject(IndexerHiveObject indexer) => IndexerHiveObject( + displayName: indexer.displayName, + host: indexer.host, + key: indexer.key, + headers: indexer.headers, + ); + + /// Create a new [IndexerHiveObject] from a map where the keys map 1-to-1. + /// + /// - Does _not_ do type checking, and will throw an error if the type is invalid. + /// - If the key is null, sets to the "empty" value + factory IndexerHiveObject.fromMap(Map indexer) => IndexerHiveObject( + displayName: indexer['displayName'] ?? '', + host: indexer['host'] ?? '', + key: indexer['key'] ?? '', + headers: indexer['headers'] ?? {}, + ); + + IndexerHiveObject({ + @required this.displayName, + @required this.host, + @required this.key, + @required this.headers, + }); + + @override + String toString() => toMap().toString(); + + Map toMap() => { + "displayName": displayName ?? '', + "host": host ?? '', + "key": key ?? '', + "headers": headers ?? {}, + }; + + @HiveField(0) + String displayName; + @HiveField(1) + String host; + @HiveField(2) + String key; + @HiveField(3) + Map headers; +} diff --git a/lib/core/database/adapters/indexer.g.dart b/lib/core/adapters/indexer.g.dart similarity index 100% rename from lib/core/database/adapters/indexer.g.dart rename to lib/core/adapters/indexer.g.dart diff --git a/lib/core/adapters/profile.dart b/lib/core/adapters/profile.dart new file mode 100644 index 00000000..541a3b3a --- /dev/null +++ b/lib/core/adapters/profile.dart @@ -0,0 +1,415 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules.dart'; + +part 'profile.g.dart'; + +/// Hive database object containing all profile fields +@HiveType(typeId: 0, adapterName: 'ProfileHiveObjectAdapter') +class ProfileHiveObject extends HiveObject { + /// Create a new [ProfileHiveObject] object with all fields set to empty values('', false, 0, {}, etc.) + factory ProfileHiveObject.empty() => ProfileHiveObject( + //Lidarr + lidarrEnabled: false, + lidarrHost: '', + lidarrKey: '', + lidarrHeaders: {}, + //Radarr + radarrEnabled: false, + radarrHost: '', + radarrKey: '', + radarrHeaders: {}, + //Sonarr + sonarrEnabled: false, + sonarrHost: '', + sonarrKey: '', + sonarrVersion3: false, + sonarrHeaders: {}, + //SABnzbd + sabnzbdEnabled: false, + sabnzbdHost: '', + sabnzbdKey: '', + sabnzbdHeaders: {}, + //NZBGet + nzbgetEnabled: false, + nzbgetHost: '', + nzbgetUser: '', + nzbgetPass: '', + nzbgetBasicAuth: false, + nzbgetHeaders: {}, + //Wake on LAN + wakeOnLANEnabled: false, + wakeOnLANBroadcastAddress: '', + wakeOnLANMACAddress: '', + //Tautulli + tautulliEnabled: false, + tautulliHost: '', + tautulliKey: '', + tautulliHeaders: {}, + //Ombi + ombiEnabled: false, + ombiHost: '', + ombiKey: '', + ombiHeaders: {}, + ); + + /// Create a new [ProfileHiveObject] from another [ProfileHiveObject] (deep-copy). + factory ProfileHiveObject.fromProfileHiveObject(ProfileHiveObject profile) => ProfileHiveObject( + //Lidarr + lidarrEnabled: profile.lidarrEnabled, + lidarrHost: profile.lidarrHost, + lidarrKey: profile.lidarrKey, + lidarrHeaders: profile.lidarrHeaders, + //Radarr + radarrEnabled: profile.radarrEnabled, + radarrHost: profile.radarrHost, + radarrKey: profile.radarrKey, + radarrHeaders: profile.radarrHeaders, + //Sonarr + sonarrEnabled: profile.sonarrEnabled, + sonarrHost: profile.sonarrHost, + sonarrKey: profile.sonarrKey, + sonarrVersion3: profile.sonarrVersion3, + sonarrHeaders: profile.sonarrHeaders, + //SABnzbd + sabnzbdEnabled: profile.sabnzbdEnabled, + sabnzbdHost: profile.sabnzbdHost, + sabnzbdKey: profile.sabnzbdKey, + sabnzbdHeaders: profile.sabnzbdHeaders, + //NZBGet + nzbgetEnabled: profile.nzbgetEnabled, + nzbgetHost: profile.nzbgetHost, + nzbgetUser: profile.nzbgetUser, + nzbgetPass: profile.nzbgetPass, + nzbgetBasicAuth: profile.nzbgetBasicAuth, + nzbgetHeaders: profile.nzbgetHeaders, + //Wake On LAN + wakeOnLANEnabled: profile.wakeOnLANEnabled, + wakeOnLANBroadcastAddress: profile.wakeOnLANBroadcastAddress, + wakeOnLANMACAddress: profile.wakeOnLANMACAddress, + //Tautulli + tautulliEnabled: profile.tautulliEnabled, + tautulliHost: profile.tautulliHost, + tautulliKey: profile.tautulliKey, + tautulliHeaders: profile.tautulliHeaders, + //Ombi + ombiEnabled: profile.ombiEnabled, + ombiHost: profile.ombiHost, + ombiKey: profile.ombiKey, + ombiHeaders: profile.ombiHeaders, + ); + + /// Create a new [ProfileHiveObject] from a map where the keys map 1-to-1. + /// + /// - Does _not_ do type checking, and will throw an error if the type is invalid. + /// - If the key is null, sets to the "empty" value + factory ProfileHiveObject.fromMap(Map profile) => ProfileHiveObject( + //Sonarr + sonarrEnabled: profile['sonarrEnabled'] ?? false, + sonarrHost: profile['sonarrHost'] ?? '', + sonarrKey: profile['sonarrKey'] ?? '', + sonarrVersion3: profile['sonarrVersion3'] ?? false, + sonarrHeaders: profile['sonarrHeaders'] ?? {}, + //Radarr + radarrEnabled: profile['radarrEnabled'] ?? false, + radarrHost: profile['radarrHost'] ?? '', + radarrKey: profile['radarrKey'] ?? '', + radarrHeaders: profile['radarrHeaders'] ?? {}, + //Lidarr + lidarrEnabled: profile['lidarrEnabled'] ?? false, + lidarrHost: profile['lidarrHost'] ?? '', + lidarrKey: profile['lidarrKey'] ?? '', + lidarrHeaders: profile['lidarrHeaders'] ?? {}, + //SABnzbd + sabnzbdEnabled: profile['sabnzbdEnabled'] ?? false, + sabnzbdHost: profile['sabnzbdHost'] ?? '', + sabnzbdKey: profile['sabnzbdKey'] ?? '', + sabnzbdHeaders: profile['sabnzbdHeaders'] ?? {}, + //NZBGet + nzbgetEnabled: profile['nzbgetEnabled'] ?? false, + nzbgetHost: profile['nzbgetHost'] ?? '', + nzbgetUser: profile['nzbgetUser'] ?? '', + nzbgetPass: profile['nzbgetPass'] ?? '', + nzbgetBasicAuth: profile['nzbgetBasicAuth'] ?? false, + nzbgetHeaders: profile['nzbgetHeaders'] ?? {}, + //Wake on LAN + wakeOnLANEnabled: profile['wakeOnLANEnabled'] ?? false, + wakeOnLANBroadcastAddress: profile['wakeOnLANBroadcastAddress'] ?? '', + wakeOnLANMACAddress: profile['wakeOnLANMACAddress'] ?? '', + //Tautulli + tautulliEnabled: profile['tautulliEnabled'] ?? false, + tautulliHost: profile['tautulliHost'] ?? '', + tautulliKey: profile['tautulliKey'] ?? '', + tautulliHeaders: profile['tautulliHeaders'] ?? {}, + //Ombi + ombiEnabled: profile['ombiEnabled'] ?? false, + ombiHost: profile['ombiHost'] ?? '', + ombiKey: profile['ombiKey'] ?? '', + ombiHeaders: profile['ombiHeaders'] ?? {}, + ); + + + ProfileHiveObject({ + //Lidarr + @required this.lidarrEnabled, + @required this.lidarrHost, + @required this.lidarrKey, + @required this.lidarrHeaders, + //Radarr + @required this.radarrEnabled, + @required this.radarrHost, + @required this.radarrKey, + @required this.radarrHeaders, + //Sonarr + @required this.sonarrEnabled, + @required this.sonarrHost, + @required this.sonarrKey, + @required this.sonarrVersion3, + @required this.sonarrHeaders, + //SABnzbd + @required this.sabnzbdEnabled, + @required this.sabnzbdHost, + @required this.sabnzbdKey, + @required this.sabnzbdHeaders, + //NZBGet + @required this.nzbgetEnabled, + @required this.nzbgetHost, + @required this.nzbgetUser, + @required this.nzbgetPass, + @required this.nzbgetBasicAuth, + @required this.nzbgetHeaders, + //Wake On LAN + @required this.wakeOnLANEnabled, + @required this.wakeOnLANBroadcastAddress, + @required this.wakeOnLANMACAddress, + //Tautulli + @required this.tautulliEnabled, + @required this.tautulliHost, + @required this.tautulliKey, + @required this.tautulliHeaders, + //Ombi + @required this.ombiEnabled, + @required this.ombiHost, + @required this.ombiKey, + @required this.ombiHeaders, + }); + + @override + String toString() => toMap().toString(); + + Map toMap() => { + "key": key, + //Sonarr + "sonarrEnabled": sonarrEnabled, + "sonarrHost": sonarrHost, + "sonarrKey": sonarrKey, + "sonarrVersion3": sonarrVersion3, + "sonarrHeaders": sonarrHeaders, + //Radarr + "radarrEnabled": radarrEnabled, + "radarrHost": radarrHost, + "radarrKey": radarrKey, + "radarrHeaders": radarrHeaders, + //Lidarr + "lidarrEnabled": lidarrEnabled, + "lidarrHost": lidarrHost, + "lidarrKey": lidarrKey, + "lidarrHeaders": lidarrHeaders, + //SABnzbd + "sabnzbdEnabled": sabnzbdEnabled, + "sabnzbdHost": sabnzbdHost, + "sabnzbdKey": sabnzbdKey, + "sabnzbdHeaders": sabnzbdHeaders, + //NZBGet + "nzbgetEnabled": nzbgetEnabled, + "nzbgetHost": nzbgetHost, + "nzbgetUser": nzbgetUser, + "nzbgetPass": nzbgetPass, + "nzbgetBasicAuth": nzbgetBasicAuth, + "nzbgetHeaders": nzbgetHeaders, + //Wake On LAN + "wakeOnLANEnabled": wakeOnLANEnabled, + "wakeOnLANBroadcastAddress": wakeOnLANBroadcastAddress, + "wakeOnLANMACAddress": wakeOnLANMACAddress, + //Tautulli + "tautulliEnabled": tautulliEnabled, + "tautulliHost": tautulliHost, + "tautulliKey": tautulliKey, + "tautulliHeaders": tautulliHeaders, + //Ombi + "ombiEnabled": ombiEnabled, + "ombiHost": ombiHost, + "ombiKey": ombiKey, + "ombiHeaders": ombiHeaders, + }; + + //Lidarr + @HiveField(0) + bool lidarrEnabled; + @HiveField(1) + String lidarrHost; + @HiveField(2) + String lidarrKey; + @HiveField(26) + Map lidarrHeaders; + + Map getLidarr() => { + 'enabled': lidarrEnabled ?? false, + 'host': lidarrHost ?? '', + 'key': lidarrKey ?? '', + 'headers': lidarrHeaders ?? {}, + }; + + //Radarr + @HiveField(3) + bool radarrEnabled; + @HiveField(4) + String radarrHost; + @HiveField(5) + String radarrKey; + @HiveField(27) + Map radarrHeaders; + + Map getRadarr() => { + 'enabled': radarrEnabled ?? false, + 'host': radarrHost ?? '', + 'key': radarrKey ?? '', + 'headers': radarrHeaders ?? {}, + }; + + //Sonarr + @HiveField(6) + bool sonarrEnabled; + @HiveField(7) + String sonarrHost; + @HiveField(8) + String sonarrKey; + @HiveField(21) + bool sonarrVersion3; + @HiveField(28) + Map sonarrHeaders; + + Map getSonarr() => { + 'enabled': sonarrEnabled ?? false, + 'host': sonarrHost ?? '', + 'key': sonarrKey ?? '', + 'v3': sonarrVersion3 ?? false, + 'headers': sonarrHeaders ?? {}, + }; + + //SABnzbd + @HiveField(9) + bool sabnzbdEnabled; + @HiveField(10) + String sabnzbdHost; + @HiveField(11) + String sabnzbdKey; + @HiveField(29) + Map sabnzbdHeaders; + + Map getSABnzbd() => { + 'enabled': sabnzbdEnabled ?? false, + 'host': sabnzbdHost ?? '', + 'key': sabnzbdKey ?? '', + 'headers': sabnzbdHeaders ?? {}, + }; + + //NZBGet + @HiveField(12) + bool nzbgetEnabled; + @HiveField(13) + String nzbgetHost; + @HiveField(14) + String nzbgetUser; + @HiveField(15) + String nzbgetPass; + @HiveField(22) + bool nzbgetBasicAuth; + @HiveField(30) + Map nzbgetHeaders; + + Map getNZBGet() => { + 'enabled': nzbgetEnabled ?? false, + 'host': nzbgetHost ?? '', + 'user': nzbgetUser ?? '', + 'pass': nzbgetPass ?? '', + 'basic_auth': nzbgetBasicAuth ?? false, + 'headers': nzbgetHeaders ?? {}, + }; + + //Wake On LAN + @HiveField(23) + bool wakeOnLANEnabled; + @HiveField(24) + String wakeOnLANBroadcastAddress; + @HiveField(25) + String wakeOnLANMACAddress; + + Map getWakeOnLAN() => { + 'enabled': wakeOnLANEnabled ?? false, + 'broadcastAddress': wakeOnLANBroadcastAddress ?? '', + 'MACAddress': wakeOnLANMACAddress ?? '', + }; + + //Tautulli + @HiveField(31) + bool tautulliEnabled; + @HiveField(32) + String tautulliHost; + @HiveField(33) + String tautulliKey; + @HiveField(35) + Map tautulliHeaders; + + Map getTautulli() => { + 'enabled': tautulliEnabled ?? false, + 'host': tautulliHost ?? '', + 'key': tautulliKey ?? '', + 'headers': tautulliHeaders ?? {}, + }; + + //Ombi + @HiveField(36) + bool ombiEnabled; + @HiveField(37) + String ombiHost; + @HiveField(38) + String ombiKey; + @HiveField(39) + Map ombiHeaders; + + Map getOmbi() => { + 'enabled': ombiEnabled ?? false, + 'host': ombiHost ?? '', + 'key': ombiKey ?? '', + 'headers': ombiHeaders ?? {}, + }; + + List get enabledModules => [ + ...enabledAutomationModules, + ...enabledClientModules, + ...enabledMonitoringModules, + ]; + + List get enabledAutomationModules => [ + if(lidarrEnabled ?? false) LidarrConstants.MODULE_KEY, + if(radarrEnabled ?? false) RadarrConstants.MODULE_KEY, + if(sonarrEnabled ?? false) SonarrConstants.MODULE_KEY, + ]; + + List get enabledClientModules => [ + if(nzbgetEnabled ?? false) NZBGetConstants.MODULE_KEY, + if(sabnzbdEnabled ?? false) SABnzbdConstants.MODULE_KEY, + ]; + + List get enabledMonitoringModules => [ + if(tautulliEnabled ?? false) TautulliConstants.MODULE_KEY, + if(ombiEnabled ?? false) OmbiConstants.MODULE_KEY, + ]; + + bool get anyAutomationEnabled => enabledAutomationModules.isNotEmpty; + bool get anyClientsEnabled => enabledClientModules.isNotEmpty; + bool get anyMonitoringEnabled => enabledMonitoringModules.isNotEmpty; + bool get anythingEnabled => anyAutomationEnabled || anyClientsEnabled || anyMonitoringEnabled; +} diff --git a/lib/core/database/adapters/profile.g.dart b/lib/core/adapters/profile.g.dart similarity index 100% rename from lib/core/database/adapters/profile.g.dart rename to lib/core/adapters/profile.g.dart diff --git a/lib/core/configuration.dart b/lib/core/configuration.dart index 5e6a84be..eda86c3d 100644 --- a/lib/core/configuration.dart +++ b/lib/core/configuration.dart @@ -1,2 +1,103 @@ -export 'configuration/export.dart'; -export 'configuration/import.dart'; +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings/core.dart' show SettingsDatabase, SettingsConstants; +import 'package:lunasea/modules/home/core.dart' show HomeDatabase, HomeConstants; +import 'package:lunasea/modules/search/core.dart' show SearchDatabase, SearchConstants; +import 'package:lunasea/modules/lidarr/core.dart' show LidarrDatabase, LidarrConstants; +import 'package:lunasea/modules/radarr/core.dart' show RadarrDatabase, RadarrConstants; +import 'package:lunasea/modules/sonarr/core.dart' show SonarrDatabase, SonarrConstants; +import 'package:lunasea/modules/nzbget/core.dart' show NZBGetDatabase, NZBGetConstants; +import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdDatabase, SABnzbdConstants; +import 'package:lunasea/modules/ombi/core.dart' show OmbiDatabase, OmbiConstants; +import 'package:lunasea/modules/tautulli/core.dart' show TautulliDatabase, TautulliConstants; + +class LunaConfiguration { + /// Returns a list of all profiles converted to a map. + List> _getProfiles() { + List> _data = []; + for(var key in Database.profilesBox.keys) _data.add(Database.profilesBox.get(key).toMap()); + return _data; + } + + /// Given a list of map objects, creates or updates profiles for each object. + void _setProfiles(List data) { + Box box = Database.profilesBox; + for(Map profile in data) box.put(profile['key'], ProfileHiveObject.fromMap(profile)); + } + + /// Returns a list of all indexers converted to a map. + List> _getIndexers() { + List> _data = []; + for(var key in Database.indexersBox.keys) _data.add(Database.indexersBox.get(key).toMap()); + return _data; + } + + /// Given a list of map objects, creates or updates indexers for each object. + void _setIndexers(List data) { + Box box = Database.indexersBox; + for(Map indexer in data) box.add(IndexerHiveObject.fromMap(indexer)); + } + + /// Import the entire configuration from a JSON-encoded string (typically read through a `.lunasea` backup file). + /// + /// - Clears all boxes + /// - Calls `_setProfiles()` and `_setIndexers()` + /// - Calls `import()` on all module databases, which implement [LunaDatabase]. + /// - Resets the application state + /// + /// On a failed import, resets LunaSea back to the default/base state + Future import(BuildContext context, String data) async { + Map config = json.decode(data); + Database.clearAllBoxes(); + try { + // Set the profilers, indexer, and global LunaSea boxes + if(config['profiles'] != null) _setProfiles(config['profiles']); + if(config['indexers'] != null) _setIndexers(config['indexers']); + if(config['lunasea'] != null) LunaDatabase().import(config['lunasea']); + // General + if(config[SettingsConstants.MODULE_KEY] != null) SettingsDatabase().import(config[SettingsConstants.MODULE_KEY]); + if(config[HomeConstants.MODULE_KEY] != null) HomeDatabase().import(config[HomeConstants.MODULE_KEY]); + if(config[SearchConstants.MODULE_KEY] != null) SearchDatabase().import(config[SearchConstants.MODULE_KEY]); + // Automation + if(config[LidarrConstants.MODULE_KEY] != null) LidarrDatabase().import(config[LidarrConstants.MODULE_KEY]); + if(config[RadarrConstants.MODULE_KEY] != null) RadarrDatabase().import(config[RadarrConstants.MODULE_KEY]); + if(config[SonarrConstants.MODULE_KEY] != null) SonarrDatabase().import(config[SonarrConstants.MODULE_KEY]); + // Clients + if(config[NZBGetConstants.MODULE_KEY] != null) NZBGetDatabase().import(config[NZBGetConstants.MODULE_KEY]); + if(config[SABnzbdConstants.MODULE_KEY] != null) SABnzbdDatabase().import(config[SABnzbdConstants.MODULE_KEY]); + //Monitoring + if(config[OmbiConstants.MODULE_KEY] != null) OmbiDatabase().import(config[OmbiConstants.MODULE_KEY]); + if(config[TautulliConstants.MODULE_KEY] != null) TautulliDatabase().import(config[TautulliConstants.MODULE_KEY]); + } catch (error, stack) { + LunaLogger().error('Failed to import configuration, resetting to default', error, stack); + Database.setDefaults(); + } + // Reset the entire app's state + LunaState.reset(context); + } + + /// Converts the entire application configuration/database into a JSON string. + /// + /// - Calls `_getProfiles()` and `_getIndexers()` + /// - Calls `export()` on all module databases, which implement [LunaModuleDatabase]. + String export() => json.encode({ + "profiles": _getProfiles(), + "indexers": _getIndexers(), + "lunasea": LunaDatabase().export(), + // General + SettingsConstants.MODULE_KEY: SettingsDatabase().export(), + HomeConstants.MODULE_KEY: HomeDatabase().export(), + SearchConstants.MODULE_KEY: SearchDatabase().export(), + // Automation + LidarrConstants.MODULE_KEY: LidarrDatabase().export(), + RadarrConstants.MODULE_KEY: RadarrDatabase().export(), + SonarrConstants.MODULE_KEY: SonarrDatabase().export(), + // Clients + NZBGetConstants.MODULE_KEY: NZBGetDatabase().export(), + SABnzbdConstants.MODULE_KEY: SABnzbdDatabase().export(), + // Monitoring + OmbiConstants.MODULE_KEY: OmbiDatabase().export(), + TautulliConstants.MODULE_KEY: TautulliDatabase().export(), + }); +} diff --git a/lib/core/configuration/export.dart b/lib/core/configuration/export.dart deleted file mode 100644 index ce17bdff..00000000 --- a/lib/core/configuration/export.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:convert'; -import 'package:lunasea/core/database.dart'; - -class Export { - Export._(); - - static bool validateType(dynamic value) { - Type _type = value.runtimeType; - if( - _type == bool || - _type == String || - _type == int || - _type == double - ) return true; - return false; - } - - static Map get _lunasea { - Map _data = {}; - _data['profile'] = LunaSeaDatabaseValue.ENABLED_PROFILE.data; - return _data; - } - - static List get _profiles { - Box _box = Database.profilesBox; - List _data = []; - for(var key in _box.keys) { - ProfileHiveObject profile = _box.get(key); - _data.add(profile.toMap()); - } - return _data; - } - - static List get _indexers { - Box _box = Database.indexersBox; - List _data = []; - for(var key in _box.keys) { - IndexerHiveObject indexer = _box.get(key); - _data.add(indexer.toMap()); - } - return _data; - } - - static String export() { - return json.encode({ - "lunasea": _lunasea, - "profiles": _profiles, - "indexers": _indexers, - }); - } -} \ No newline at end of file diff --git a/lib/core/configuration/import.dart b/lib/core/configuration/import.dart deleted file mode 100644 index bd3317c3..00000000 --- a/lib/core/configuration/import.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/core/database.dart'; - -class Import { - Import._(); - - static void _setLunaSea(BuildContext context, Map data) { - String _name = data['lunasea']['profile']; - Map _profile = (data['profiles'] as List).firstWhere((element) => element['key'] == _name, orElse: () => null); - if(_profile == null) _name = (data['profiles'] as List)[0]['key']; - LunaSeaDatabaseValue.ENABLED_PROFILE.put(_name); - } - - static void _setProfiles(List data) { - Box _box = Database.profilesBox; - for(Map profile in data) { - _box.put(profile['key'], ProfileHiveObject( - //Sonarr - sonarrEnabled: profile['sonarrEnabled'] ?? false, - sonarrHost: profile['sonarrHost'] ?? '', - sonarrKey: profile['sonarrKey'] ?? '', - sonarrVersion3: profile['sonarrVersion3'] ?? false, - sonarrHeaders: profile['sonarrHeaders'] ?? {}, - //Radarr - radarrEnabled: profile['radarrEnabled'] ?? false, - radarrHost: profile['radarrHost'] ?? '', - radarrKey: profile['radarrKey'] ?? '', - radarrHeaders: profile['radarrHeaders'] ?? {}, - //Lidarr - lidarrEnabled: profile['lidarrEnabled'] ?? false, - lidarrHost: profile['lidarrHost'] ?? '', - lidarrKey: profile['lidarrKey'] ?? '', - lidarrHeaders: profile['lidarrHeaders'] ?? {}, - //SABnzbd - sabnzbdEnabled: profile['sabnzbdEnabled'] ?? false, - sabnzbdHost: profile['sabnzbdHost'] ?? '', - sabnzbdKey: profile['sabnzbdKey'] ?? '', - sabnzbdHeaders: profile['sabnzbdHeaders'] ?? {}, - //NZBGet - nzbgetEnabled: profile['nzbgetEnabled'] ?? false, - nzbgetHost: profile['nzbgetHost'] ?? '', - nzbgetUser: profile['nzbgetUser'] ?? '', - nzbgetPass: profile['nzbgetPass'] ?? '', - nzbgetBasicAuth: profile['nzbgetBasicAuth'] ?? false, - nzbgetHeaders: profile['nzbgetHeaders'] ?? {}, - //Wake on LAN - wakeOnLANEnabled: profile['wakeOnLANEnabled'] ?? false, - wakeOnLANBroadcastAddress: profile['wakeOnLANBroadcastAddress'] ?? '', - wakeOnLANMACAddress: profile['wakeOnLANMACAddress'] ?? '', - //Tautulli - tautulliEnabled: profile['tautulliEnabled'] ?? false, - tautulliHost: profile['tautulliHost'] ?? '', - tautulliKey: profile['tautulliKey'] ?? '', - tautulliHeaders: profile['tautulliHeaders'] ?? {}, - //Ombi - ombiEnabled: profile['ombiEnabled'] ?? false, - ombiHost: profile['ombiHost'] ?? '', - ombiKey: profile['ombiKey'] ?? '', - ombiHeaders: profile['ombiHeaders'] ?? {}, - )); - } - } - - static void _setIndexers(List data) { - Box _box = Database.indexersBox; - for(Map indexer in data) { - _box.add(IndexerHiveObject( - displayName: indexer['displayName'] ?? '', - host: indexer['host'] ?? '', - key: indexer['key'] ?? '', - headers: indexer['headers'] ?? {}, - )); - } - } - - static bool _validate(Map config) { - if( - config['profiles'] != null && - config['indexers'] != null && - config['lunasea'] != null - ) return true; - return false; - } - - static void _clearBoxes() { - Database.clearIndexersBox(); - Database.clearLunaSeaBox(); - Database.clearProfilesBox(); - } - - static Future import(BuildContext context, String data) async { - Map _config = json.decode(data); - if(_validate(_config)) { - _clearBoxes(); - _setProfiles(_config['profiles']); - _setIndexers(_config['indexers']); - _setLunaSea(context, _config); - LunaProvider.reset(context); - return true; - } - return false; - } -} diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 8065dbf9..cc4318a0 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -1,32 +1,29 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/module_map.dart'; -import 'package:lunasea/modules.dart' show - LidarrConstants, - RadarrConstants, - SonarrConstants, - NZBGetConstants, - SABnzbdConstants, - SearchConstants, - SettingsConstants, - WakeOnLANConstants, - TautulliConstants; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/lidarr/core.dart' show LidarrConstants; +import 'package:lunasea/modules/radarr/core.dart' show RadarrConstants; +import 'package:lunasea/modules/sonarr/core.dart' show SonarrConstants; +import 'package:lunasea/modules/nzbget/core.dart' show NZBGetConstants; +import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdConstants; +import 'package:lunasea/modules/search/core.dart' show SearchConstants; +import 'package:lunasea/modules/settings/core.dart' show SettingsConstants; +import 'package:lunasea/modules/wake_on_lan/core.dart' show WakeOnLANConstants; +import 'package:lunasea/modules/tautulli/core.dart' show TautulliConstants; class Constants { Constants._(); static const APPLICATION_NAME = 'LunaSea'; - static const SENTRY_DSN = 'https://511f76efcf714ecfb5ed6b26b5819bd6@o426090.ingest.sentry.io/5367513'; - - static const Map MODULE_MAP = { - LidarrConstants.MODULE_KEY: LidarrConstants.MODULE_MAP, - RadarrConstants.MODULE_KEY: RadarrConstants.MODULE_MAP, - SonarrConstants.MODULE_KEY: SonarrConstants.MODULE_MAP, - NZBGetConstants.MODULE_KEY: NZBGetConstants.MODULE_MAP, - SABnzbdConstants.MODULE_KEY: SABnzbdConstants.MODULE_MAP, - SearchConstants.MODULE_KEY: SearchConstants.MODULE_MAP, - SettingsConstants.MODULE_KEY: SettingsConstants.MODULE_MAP, - WakeOnLANConstants.MODULE_KEY: WakeOnLANConstants.MODULE_MAP, - TautulliConstants.MODULE_KEY: TautulliConstants.MODULE_MAP, + static const Map MODULE_METADATA = { + LidarrConstants.MODULE_KEY: LidarrConstants.MODULE_METADATA, + RadarrConstants.MODULE_KEY: RadarrConstants.MODULE_METADATA, + SonarrConstants.MODULE_KEY: SonarrConstants.MODULE_METADATA, + NZBGetConstants.MODULE_KEY: NZBGetConstants.MODULE_METADATA, + SABnzbdConstants.MODULE_KEY: SABnzbdConstants.MODULE_METADATA, + SearchConstants.MODULE_KEY: SearchConstants.MODULE_METADATA, + SettingsConstants.MODULE_KEY: SettingsConstants.MODULE_METADATA, + WakeOnLANConstants.MODULE_KEY: WakeOnLANConstants.MODULE_METADATA, + TautulliConstants.MODULE_KEY: TautulliConstants.MODULE_METADATA, }; static const EMPTY_MAP = {}; @@ -51,21 +48,14 @@ class Constants { static const UI_FONT_SIZE_SUBTITLE = 13.0; static const UI_FONT_SIZE_GRAPH_LEGEND = 10.0; - static const CONFIGURATION_INVALID = '<>'; - static const ENCRYPTION_FAILURE = '<>'; static const NO_SERVICES_ENABLED = '<>'; - static const CHECK_LOGS_MESSAGE = 'Check the logs for more details'; - - static const BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb']; - static const BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']; static const URL_DOCUMENTATION = 'https://docs.lunasea.app'; - static const URL_GITHUB = 'https://github.com/JagandeepBrar/LunaSea'; + static const URL_GITHUB = 'https://github.com/CometTools/LunaSea'; static const URL_REDDIT = 'https://www.reddit.com/r/LunaSeaApp'; static const URL_WEBSITE = 'https://www.lunasea.app'; static const URL_FEEDBACK = 'https://feedback.lunasea.app'; - static const URL_LICENSES = 'https://docs.lunasea.app/other-resources/licenses'; - static const URL_CHANGELOG = 'https://docs.lunasea.app/development/changelog'; + static const URL_CHANGELOG = 'https://github.com/CometTools/LunaSea/blob/master/CHANGELOG.md'; static const URL_DISCORD = 'https://discord.com/invite/8MH2N3h'; static const URL_SENTRY = 'https://sentry.io'; static const URL_TESTFLIGHT = 'https://testflight.apple.com/join/WWXaybra'; diff --git a/lib/core/database.dart b/lib/core/database.dart index 88aa827a..53c45880 100644 --- a/lib/core/database.dart +++ b/lib/core/database.dart @@ -1,2 +1,3 @@ export 'database/database.dart'; -export 'database/adapters.dart'; +export 'database/luna_database.dart'; +export 'database/module_database.dart'; diff --git a/lib/core/database/adapters/indexer.dart b/lib/core/database/adapters/indexer.dart deleted file mode 100644 index 0aa6b30d..00000000 --- a/lib/core/database/adapters/indexer.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; - -part 'indexer.g.dart'; - -@HiveType(typeId: 1, adapterName: 'IndexerHiveObjectAdapter') -class IndexerHiveObject extends HiveObject { - factory IndexerHiveObject.empty() { - return IndexerHiveObject( - displayName: '', - host: '', - key: '', - headers: {}, - ); - } - - factory IndexerHiveObject.from(IndexerHiveObject obj) { - return IndexerHiveObject( - displayName: obj.displayName, - host: obj.host, - key: obj.key, - headers: obj.headers, - ); - } - - IndexerHiveObject({ - @required this.displayName, - @required this.host, - @required this.key, - @required this.headers, - }); - - @override - String toString() { - return toMap().toString(); - } - - Map toMap() { - return { - "displayName": displayName ?? '', - "host": host ?? '', - "key": key ?? '', - "headers": headers ?? {}, - }; - } - - @HiveField(0) - String displayName; - @HiveField(1) - String host; - @HiveField(2) - String key; - @HiveField(3) - Map headers; -} diff --git a/lib/core/database/adapters/lunasea.dart b/lib/core/database/adapters/lunasea.dart deleted file mode 100644 index f4f164b8..00000000 --- a/lib/core/database/adapters/lunasea.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:lunasea/core/database/database.dart'; -import 'package:lunasea/core/extensions.dart'; - -class LunaSeaDatabase { - LunaSeaDatabase._(); - - static void registerAdapters() { - Hive.registerAdapter(LSBrowsersAdapter()); - } -} - -enum LunaSeaDatabaseValue { - ENABLED_PROFILE, - ENABLED_SENTRY, - CLIENT_IDENTIFIER, - THEME_AMOLED, - THEME_AMOLED_BORDER, - THEME_IMAGE_BACKGROUND_OPACITY, - SELECTED_BROWSER, - DRAWER_GROUP_MODULES, - DRAWER_EXPAND_AUTOMATION, - DRAWER_EXPAND_CLIENTS, - DRAWER_EXPAND_MONITORING, - QUICK_ACTIONS_LIDARR, - QUICK_ACTIONS_RADARR, - QUICK_ACTIONS_SONARR, - QUICK_ACTIONS_NZBGET, - QUICK_ACTIONS_SABNZBD, - QUICK_ACTIONS_TAUTULLI, - QUICK_ACTIONS_SEARCH, - USE_24_HOUR_TIME, -} - -extension LunaSeaDatabaseValueExtension on LunaSeaDatabaseValue { - String get key { - switch(this) { - case LunaSeaDatabaseValue.ENABLED_PROFILE: return 'profile'; - case LunaSeaDatabaseValue.ENABLED_SENTRY: return 'LUNASEA_ENABLED_SENTRY'; - case LunaSeaDatabaseValue.THEME_AMOLED: return 'LUNASEA_THEME_AMOLED'; - case LunaSeaDatabaseValue.THEME_AMOLED_BORDER: return 'LUNASEA_THEME_AMOLED_BORDER'; - case LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY'; - case LunaSeaDatabaseValue.SELECTED_BROWSER: return 'LUNASEA_SELECTED_BROWSER'; - case LunaSeaDatabaseValue.CLIENT_IDENTIFIER: return 'LUNASEA_CLIENT_IDENTIFIER'; - case LunaSeaDatabaseValue.DRAWER_GROUP_MODULES: return 'LUNASEA_DRAWER_GROUP_MODULES'; - case LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return 'LUNASEA_DRAWER_EXPAND_AUTOMATION'; - case LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS: return 'LUNASEA_DRAWER_EXPAND_CLIENTS'; - case LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING: return 'LUNASEA_DRAWER_EXPAND_MONITORING'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR: return 'LUNASEA_QUICK_ACTIONS_LIDARR'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR: return 'LUNASEA_QUICK_ACTIONS_RADARR'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR: return 'LUNASEA_QUICK_ACTIONS_SONARR'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET: return 'LUNASEA_QUICK_ACTIONS_NZBGET'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD: return 'LUNASEA_QUICK_ACTIONS_SABNZBD'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return 'LUNASEA_QUICK_ACTIONS_TAUTULLI'; - case LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH: return 'LUNASEA_QUICK_ACTIONS_SEARCH'; - case LunaSeaDatabaseValue.USE_24_HOUR_TIME: return 'LUNASEA_USE_24_HOUR_TIME'; - } - throw Exception('key not found'); - } - - dynamic get data { - final _box = Database.lunaSeaBox; - switch(this) { - case LunaSeaDatabaseValue.ENABLED_PROFILE: return _box.get(this.key, defaultValue: 'default'); - case LunaSeaDatabaseValue.SELECTED_BROWSER: return _box.get(this.key, defaultValue: LSBrowsers.APPLE_SAFARI); - case LunaSeaDatabaseValue.THEME_AMOLED: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.THEME_AMOLED_BORDER: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return _box.get(this.key, defaultValue: 10); - case LunaSeaDatabaseValue.ENABLED_SENTRY: return _box.get(this.key, defaultValue: true); - case LunaSeaDatabaseValue.CLIENT_IDENTIFIER: return _box.get(this.key, defaultValue: null); - case LunaSeaDatabaseValue.DRAWER_GROUP_MODULES: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return _box.get(this.key, defaultValue: true); - case LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS: return _box.get(this.key, defaultValue: true); - case LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING: return _box.get(this.key, defaultValue: true); - case LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH: return _box.get(this.key, defaultValue: false); - case LunaSeaDatabaseValue.USE_24_HOUR_TIME: return _box.get(this.key, defaultValue: false); - } - throw Exception('data not found'); - } - - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); -} diff --git a/lib/core/database/adapters/profile.dart b/lib/core/database/adapters/profile.dart deleted file mode 100644 index 191cdfb6..00000000 --- a/lib/core/database/adapters/profile.dart +++ /dev/null @@ -1,376 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules.dart'; - -part 'profile.g.dart'; - -//Next HiveField ID: 40 - -/** - * Dead Fields - * 16, 17, 18, 19, 20, 34 - */ - -/// Hive database object containing all profile fields -@HiveType(typeId: 0, adapterName: 'ProfileHiveObjectAdapter') -class ProfileHiveObject extends HiveObject { - factory ProfileHiveObject.empty() { - return ProfileHiveObject( - //Lidarr - lidarrEnabled: false, - lidarrHost: '', - lidarrKey: '', - lidarrHeaders: {}, - //Radarr - radarrEnabled: false, - radarrHost: '', - radarrKey: '', - radarrHeaders: {}, - //Sonarr - sonarrEnabled: false, - sonarrHost: '', - sonarrKey: '', - sonarrVersion3: false, - sonarrHeaders: {}, - //SABnzbd - sabnzbdEnabled: false, - sabnzbdHost: '', - sabnzbdKey: '', - sabnzbdHeaders: {}, - //NZBGet - nzbgetEnabled: false, - nzbgetHost: '', - nzbgetUser: '', - nzbgetPass: '', - nzbgetBasicAuth: false, - nzbgetHeaders: {}, - //Wake on LAN - wakeOnLANEnabled: false, - wakeOnLANBroadcastAddress: '', - wakeOnLANMACAddress: '', - //Tautulli - tautulliEnabled: false, - tautulliHost: '', - tautulliKey: '', - tautulliHeaders: {}, - //Ombi - ombiEnabled: false, - ombiHost: '', - ombiKey: '', - ombiHeaders: {}, - ); - } - - factory ProfileHiveObject.from(ProfileHiveObject obj) { - return ProfileHiveObject( - //Lidarr - lidarrEnabled: obj.lidarrEnabled, - lidarrHost: obj.lidarrHost, - lidarrKey: obj.lidarrKey, - lidarrHeaders: obj.lidarrHeaders, - //Radarr - radarrEnabled: obj.radarrEnabled, - radarrHost: obj.radarrHost, - radarrKey: obj.radarrKey, - radarrHeaders: obj.radarrHeaders, - //Sonarr - sonarrEnabled: obj.sonarrEnabled, - sonarrHost: obj.sonarrHost, - sonarrKey: obj.sonarrKey, - sonarrVersion3: obj.sonarrVersion3, - sonarrHeaders: obj.sonarrHeaders, - //SABnzbd - sabnzbdEnabled: obj.sabnzbdEnabled, - sabnzbdHost: obj.sabnzbdHost, - sabnzbdKey: obj.sabnzbdKey, - sabnzbdHeaders: obj.sabnzbdHeaders, - //NZBGet - nzbgetEnabled: obj.nzbgetEnabled, - nzbgetHost: obj.nzbgetHost, - nzbgetUser: obj.nzbgetUser, - nzbgetPass: obj.nzbgetPass, - nzbgetBasicAuth: obj.nzbgetBasicAuth, - nzbgetHeaders: obj.nzbgetHeaders, - //Wake On LAN - wakeOnLANEnabled: obj.wakeOnLANEnabled, - wakeOnLANBroadcastAddress: obj.wakeOnLANBroadcastAddress, - wakeOnLANMACAddress: obj.wakeOnLANMACAddress, - //Tautulli - tautulliEnabled: obj.tautulliEnabled, - tautulliHost: obj.tautulliHost, - tautulliKey: obj.tautulliKey, - tautulliHeaders: obj.tautulliHeaders, - //Ombi - ombiEnabled: obj.ombiEnabled, - ombiHost: obj.ombiHost, - ombiKey: obj.ombiKey, - ombiHeaders: obj.ombiHeaders, - ); - } - - ProfileHiveObject({ - //Lidarr - @required this.lidarrEnabled, - @required this.lidarrHost, - @required this.lidarrKey, - @required this.lidarrHeaders, - //Radarr - @required this.radarrEnabled, - @required this.radarrHost, - @required this.radarrKey, - @required this.radarrHeaders, - //Sonarr - @required this.sonarrEnabled, - @required this.sonarrHost, - @required this.sonarrKey, - @required this.sonarrVersion3, - @required this.sonarrHeaders, - //SABnzbd - @required this.sabnzbdEnabled, - @required this.sabnzbdHost, - @required this.sabnzbdKey, - @required this.sabnzbdHeaders, - //NZBGet - @required this.nzbgetEnabled, - @required this.nzbgetHost, - @required this.nzbgetUser, - @required this.nzbgetPass, - @required this.nzbgetBasicAuth, - @required this.nzbgetHeaders, - //Wake On LAN - @required this.wakeOnLANEnabled, - @required this.wakeOnLANBroadcastAddress, - @required this.wakeOnLANMACAddress, - //Tautulli - @required this.tautulliEnabled, - @required this.tautulliHost, - @required this.tautulliKey, - @required this.tautulliHeaders, - //Ombi - @required this.ombiEnabled, - @required this.ombiHost, - @required this.ombiKey, - @required this.ombiHeaders, - }); - - @override - String toString() => toMap().toString(); - - Map toMap() { - return { - "key": key, - //Sonarr - "sonarrEnabled": sonarrEnabled, - "sonarrHost": sonarrHost, - "sonarrKey": sonarrKey, - "sonarrVersion3": sonarrVersion3, - "sonarrHeaders": sonarrHeaders, - //Radarr - "radarrEnabled": radarrEnabled, - "radarrHost": radarrHost, - "radarrKey": radarrKey, - "radarrHeaders": radarrHeaders, - //Lidarr - "lidarrEnabled": lidarrEnabled, - "lidarrHost": lidarrHost, - "lidarrKey": lidarrKey, - "lidarrHeaders": lidarrHeaders, - //SABnzbd - "sabnzbdEnabled": sabnzbdEnabled, - "sabnzbdHost": sabnzbdHost, - "sabnzbdKey": sabnzbdKey, - "sabnzbdHeaders": sabnzbdHeaders, - //NZBGet - "nzbgetEnabled": nzbgetEnabled, - "nzbgetHost": nzbgetHost, - "nzbgetUser": nzbgetUser, - "nzbgetPass": nzbgetPass, - "nzbgetBasicAuth": nzbgetBasicAuth, - "nzbgetHeaders": nzbgetHeaders, - //Wake On LAN - "wakeOnLANEnabled": wakeOnLANEnabled, - "wakeOnLANBroadcastAddress": wakeOnLANBroadcastAddress, - "wakeOnLANMACAddress": wakeOnLANMACAddress, - //Tautulli - "tautulliEnabled": tautulliEnabled, - "tautulliHost": tautulliHost, - "tautulliKey": tautulliKey, - "tautulliHeaders": tautulliHeaders, - //Ombi - "ombiEnabled": ombiEnabled, - "ombiHost": ombiHost, - "ombiKey": ombiKey, - "ombiHeaders": ombiHeaders, - }; - } - - //Lidarr - @HiveField(0) - bool lidarrEnabled; - @HiveField(1) - String lidarrHost; - @HiveField(2) - String lidarrKey; - @HiveField(26) - Map lidarrHeaders; - - Map getLidarr() => { - 'enabled': lidarrEnabled ?? false, - 'host': lidarrHost ?? '', - 'key': lidarrKey ?? '', - 'headers': lidarrHeaders ?? {}, - }; - - //Radarr - @HiveField(3) - bool radarrEnabled; - @HiveField(4) - String radarrHost; - @HiveField(5) - String radarrKey; - @HiveField(27) - Map radarrHeaders; - - Map getRadarr() => { - 'enabled': radarrEnabled ?? false, - 'host': radarrHost ?? '', - 'key': radarrKey ?? '', - 'headers': radarrHeaders ?? {}, - }; - - //Sonarr - @HiveField(6) - bool sonarrEnabled; - @HiveField(7) - String sonarrHost; - @HiveField(8) - String sonarrKey; - @HiveField(21) - bool sonarrVersion3; - @HiveField(28) - Map sonarrHeaders; - - Map getSonarr() => { - 'enabled': sonarrEnabled ?? false, - 'host': sonarrHost ?? '', - 'key': sonarrKey ?? '', - 'v3': sonarrVersion3 ?? false, - 'headers': sonarrHeaders ?? {}, - }; - - //SABnzbd - @HiveField(9) - bool sabnzbdEnabled; - @HiveField(10) - String sabnzbdHost; - @HiveField(11) - String sabnzbdKey; - @HiveField(29) - Map sabnzbdHeaders; - - Map getSABnzbd() => { - 'enabled': sabnzbdEnabled ?? false, - 'host': sabnzbdHost ?? '', - 'key': sabnzbdKey ?? '', - 'headers': sabnzbdHeaders ?? {}, - }; - - //NZBGet - @HiveField(12) - bool nzbgetEnabled; - @HiveField(13) - String nzbgetHost; - @HiveField(14) - String nzbgetUser; - @HiveField(15) - String nzbgetPass; - @HiveField(22) - bool nzbgetBasicAuth; - @HiveField(30) - Map nzbgetHeaders; - - Map getNZBGet() => { - 'enabled': nzbgetEnabled ?? false, - 'host': nzbgetHost ?? '', - 'user': nzbgetUser ?? '', - 'pass': nzbgetPass ?? '', - 'basic_auth': nzbgetBasicAuth ?? false, - 'headers': nzbgetHeaders ?? {}, - }; - - //Wake On LAN - @HiveField(23) - bool wakeOnLANEnabled; - @HiveField(24) - String wakeOnLANBroadcastAddress; - @HiveField(25) - String wakeOnLANMACAddress; - - Map getWakeOnLAN() => { - 'enabled': wakeOnLANEnabled ?? false, - 'broadcastAddress': wakeOnLANBroadcastAddress ?? '', - 'MACAddress': wakeOnLANMACAddress ?? '', - }; - - //Tautulli - @HiveField(31) - bool tautulliEnabled; - @HiveField(32) - String tautulliHost; - @HiveField(33) - String tautulliKey; - @HiveField(35) - Map tautulliHeaders; - - Map getTautulli() => { - 'enabled': tautulliEnabled ?? false, - 'host': tautulliHost ?? '', - 'key': tautulliKey ?? '', - 'headers': tautulliHeaders ?? {}, - }; - - //Ombi - @HiveField(36) - bool ombiEnabled; - @HiveField(37) - String ombiHost; - @HiveField(38) - String ombiKey; - @HiveField(39) - Map ombiHeaders; - - Map getOmbi() => { - 'enabled': ombiEnabled ?? false, - 'host': ombiHost ?? '', - 'key': ombiKey ?? '', - 'headers': ombiHeaders ?? {}, - }; - - List get enabledModules => [ - ...enabledAutomationModules, - ...enabledClientModules, - ...enabledMonitoringModules, - ]; - - List get enabledAutomationModules => [ - if(lidarrEnabled ?? false) LidarrConstants.MODULE_KEY, - if(radarrEnabled ?? false) RadarrConstants.MODULE_KEY, - if(sonarrEnabled ?? false) SonarrConstants.MODULE_KEY, - ]; - - List get enabledClientModules => [ - if(nzbgetEnabled ?? false) NZBGetConstants.MODULE_KEY, - if(sabnzbdEnabled ?? false) SABnzbdConstants.MODULE_KEY, - ]; - - List get enabledMonitoringModules => [ - if(tautulliEnabled ?? false) TautulliConstants.MODULE_KEY, - if(ombiEnabled ?? false) OmbiConstants.MODULE_KEY, - ]; - - bool get anyAutomationEnabled => enabledAutomationModules.isNotEmpty; - bool get anyClientsEnabled => enabledClientModules.isNotEmpty; - bool get anyMonitoringEnabled => enabledMonitoringModules.isNotEmpty; - bool get anythingEnabled => anyAutomationEnabled || anyClientsEnabled || anyMonitoringEnabled; -} diff --git a/lib/core/database/database.dart b/lib/core/database/database.dart index 78b2be89..4ec8a849 100644 --- a/lib/core/database/database.dart +++ b/lib/core/database/database.dart @@ -1,68 +1,76 @@ // Imports import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; -import './adapters.dart'; -import 'package:lunasea/modules.dart' show - HomeDatabase, - SearchDatabase, - SettingsDatabase, - LidarrDatabase, - RadarrDatabase, - SonarrDatabase, - NZBGetDatabase, - SABnzbdDatabase; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/home/core.dart' show HomeDatabase; +import 'package:lunasea/modules/search/core.dart' show SearchDatabase; +import 'package:lunasea/modules/settings/core.dart' show SettingsDatabase; +import 'package:lunasea/modules/lidarr/core.dart' show LidarrDatabase; +import 'package:lunasea/modules/radarr/core.dart' show RadarrDatabase; +import 'package:lunasea/modules/sonarr/core.dart' show SonarrDatabase; +import 'package:lunasea/modules/nzbget/core.dart' show NZBGetDatabase; +import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdDatabase; +import 'package:lunasea/modules/ombi/core.dart' show OmbiDatabase; +import 'package:lunasea/modules/tautulli/core.dart' show TautulliDatabase; // Exports export 'package:hive/hive.dart'; export 'package:hive_flutter/hive_flutter.dart'; -/// Next HiveType: 16 -/// -/// Dead Fields: - -class Database { - Database._(); +class Database { + static const String _DATABASE_PATH = 'database'; + /// Initialize the database. + /// + /// - Initializes Hive database path + /// - Register all adapters + /// - Open all boxes + /// - Set default database state of necessary static Future initialize() async { - await Hive.initFlutter('database'); + await Hive.initFlutter(_DATABASE_PATH); _registerAdapters(); await _openBoxes(); if(profilesBox.keys.length == 0) setDefaults(); } + /// Deinitialize the database by closing all open hive boxes. static Future deinitialize() async => await Hive.close(); + /// Registers all necessary object adapters for Hive. static void _registerAdapters() { //Core Hive.registerAdapter(IndexerHiveObjectAdapter()); Hive.registerAdapter(ProfileHiveObjectAdapter()); //General - LunaSeaDatabase.registerAdapters(); - HomeDatabase.registerAdapters(); - SearchDatabase.registerAdapters(); - SettingsDatabase.registerAdapters(); + LunaDatabase().registerAdapters(); + HomeDatabase().registerAdapters(); + SearchDatabase().registerAdapters(); + SettingsDatabase().registerAdapters(); //Automation - LidarrDatabase.registerAdapters(); - RadarrDatabase.registerAdapters(); - SonarrDatabase.registerAdapters(); + LidarrDatabase().registerAdapters(); + RadarrDatabase().registerAdapters(); + SonarrDatabase().registerAdapters(); //Clients - NZBGetDatabase.registerAdapters(); - SABnzbdDatabase.registerAdapters(); + NZBGetDatabase().registerAdapters(); + SABnzbdDatabase().registerAdapters(); + //Monitoring + OmbiDatabase().registerAdapters(); + TautulliDatabase().registerAdapters(); } + /// Open all Hive boxes for reading. static Future _openBoxes() async { await Hive.openBox('lunasea'); await Hive.openBox('indexers'); await Hive.openBox('profiles'); } + /// Set the default state for all hive boxes. static void setDefaults() { //Clear all the boxes - clearLunaSeaBox(); - clearProfilesBox(); - clearIndexersBox(); + clearAllBoxes(); //Set default profile & enabled profile profilesBox.put('default', ProfileHiveObject.empty()); - lunaSeaBox.put(LunaSeaDatabaseValue.ENABLED_PROFILE.key, 'default'); + lunaSeaBox.put(LunaDatabaseValue.ENABLED_PROFILE.key, 'default'); } //Get boxes @@ -74,6 +82,11 @@ class Database { static void clearLunaSeaBox() => lunaSeaBox.deleteAll(lunaSeaBox.keys); static void clearProfilesBox() => profilesBox.deleteAll(profilesBox.keys); static void clearIndexersBox() => indexersBox.deleteAll(indexersBox.keys); + static void clearAllBoxes() { + clearLunaSeaBox(); + clearProfilesBox(); + clearIndexersBox(); + } //Profile values static String get currentProfile => lunaSeaBox.get('profile'); diff --git a/lib/core/database/luna_database.dart b/lib/core/database/luna_database.dart new file mode 100644 index 00000000..f05ddad5 --- /dev/null +++ b/lib/core/database/luna_database.dart @@ -0,0 +1,194 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaDatabase extends LunaModuleDatabase { + @override + void registerAdapters() { + Hive.registerAdapter(LunaBrowserAdapter()); + } + + @override + Map export() { + Map data = {}; + for(LunaDatabaseValue value in LunaDatabaseValue.values) { + switch(value) { + // Non-primitive values + case LunaDatabaseValue.SELECTED_BROWSER: data[value.key] = (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).key; break; + // Primitive values + case LunaDatabaseValue.ENABLED_PROFILE: + case LunaDatabaseValue.ENABLED_SENTRY: + case LunaDatabaseValue.THEME_AMOLED: + case LunaDatabaseValue.THEME_AMOLED_BORDER: + case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: + case LunaDatabaseValue.DRAWER_GROUP_MODULES: + case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: + case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: + case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: + case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: + case LunaDatabaseValue.QUICK_ACTIONS_RADARR: + case LunaDatabaseValue.QUICK_ACTIONS_SONARR: + case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: + case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: + case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: + case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: + case LunaDatabaseValue.USE_24_HOUR_TIME: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + LunaDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Non-primitive values + case LunaDatabaseValue.SELECTED_BROWSER: value.put(LunaBrowser.APPLE_SAFARI.fromKey(config[key])); break; + // Primitive values + case LunaDatabaseValue.ENABLED_PROFILE: + case LunaDatabaseValue.ENABLED_SENTRY: + case LunaDatabaseValue.THEME_AMOLED: + case LunaDatabaseValue.THEME_AMOLED_BORDER: + case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: + case LunaDatabaseValue.DRAWER_GROUP_MODULES: + case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: + case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: + case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: + case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: + case LunaDatabaseValue.QUICK_ACTIONS_RADARR: + case LunaDatabaseValue.QUICK_ACTIONS_SONARR: + case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: + case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: + case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: + case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: + case LunaDatabaseValue.USE_24_HOUR_TIME: value.put(config[key]); break; + } + } + } + + LunaDatabaseValue valueFromKey(String key) { + switch(key) { + case 'profile': return LunaDatabaseValue.ENABLED_PROFILE; + case 'LUNASEA_ENABLED_SENTRY': return LunaDatabaseValue.ENABLED_SENTRY; + case 'LUNASEA_THEME_AMOLED': return LunaDatabaseValue.THEME_AMOLED; + case 'LUNASEA_THEME_AMOLED_BORDER': return LunaDatabaseValue.THEME_AMOLED_BORDER; + case 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY': return LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY; + case 'LUNASEA_SELECTED_BROWSER': return LunaDatabaseValue.SELECTED_BROWSER; + case 'LUNASEA_DRAWER_GROUP_MODULES': return LunaDatabaseValue.DRAWER_GROUP_MODULES; + case 'LUNASEA_DRAWER_EXPAND_AUTOMATION': return LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION; + case 'LUNASEA_DRAWER_EXPAND_CLIENTS': return LunaDatabaseValue.DRAWER_EXPAND_CLIENTS; + case 'LUNASEA_DRAWER_EXPAND_MONITORING': return LunaDatabaseValue.DRAWER_EXPAND_MONITORING; + case 'LUNASEA_QUICK_ACTIONS_LIDARR': return LunaDatabaseValue.QUICK_ACTIONS_LIDARR; + case 'LUNASEA_QUICK_ACTIONS_RADARR': return LunaDatabaseValue.QUICK_ACTIONS_RADARR; + case 'LUNASEA_QUICK_ACTIONS_SONARR': return LunaDatabaseValue.QUICK_ACTIONS_SONARR; + case 'LUNASEA_QUICK_ACTIONS_NZBGET': return LunaDatabaseValue.QUICK_ACTIONS_NZBGET; + case 'LUNASEA_QUICK_ACTIONS_SABNZBD': return LunaDatabaseValue.QUICK_ACTIONS_SABNZBD; + case 'LUNASEA_QUICK_ACTIONS_TAUTULLI': return LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI; + case 'LUNASEA_QUICK_ACTIONS_SEARCH': return LunaDatabaseValue.QUICK_ACTIONS_SEARCH; + case 'LUNASEA_USE_24_HOUR_TIME': return LunaDatabaseValue.USE_24_HOUR_TIME; + default: return null; + } + } +} + +enum LunaDatabaseValue { + ENABLED_PROFILE, + ENABLED_SENTRY, + THEME_AMOLED, + THEME_AMOLED_BORDER, + THEME_IMAGE_BACKGROUND_OPACITY, + SELECTED_BROWSER, + DRAWER_GROUP_MODULES, + DRAWER_EXPAND_AUTOMATION, + DRAWER_EXPAND_CLIENTS, + DRAWER_EXPAND_MONITORING, + QUICK_ACTIONS_LIDARR, + QUICK_ACTIONS_RADARR, + QUICK_ACTIONS_SONARR, + QUICK_ACTIONS_NZBGET, + QUICK_ACTIONS_SABNZBD, + QUICK_ACTIONS_TAUTULLI, + QUICK_ACTIONS_SEARCH, + USE_24_HOUR_TIME, +} + +extension LunaDatabaseValueExtension on LunaDatabaseValue { + String get key { + switch(this) { + case LunaDatabaseValue.ENABLED_PROFILE: return 'profile'; + case LunaDatabaseValue.ENABLED_SENTRY: return 'LUNASEA_ENABLED_SENTRY'; + case LunaDatabaseValue.THEME_AMOLED: return 'LUNASEA_THEME_AMOLED'; + case LunaDatabaseValue.THEME_AMOLED_BORDER: return 'LUNASEA_THEME_AMOLED_BORDER'; + case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY'; + case LunaDatabaseValue.SELECTED_BROWSER: return 'LUNASEA_SELECTED_BROWSER'; + case LunaDatabaseValue.DRAWER_GROUP_MODULES: return 'LUNASEA_DRAWER_GROUP_MODULES'; + case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return 'LUNASEA_DRAWER_EXPAND_AUTOMATION'; + case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: return 'LUNASEA_DRAWER_EXPAND_CLIENTS'; + case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: return 'LUNASEA_DRAWER_EXPAND_MONITORING'; + case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: return 'LUNASEA_QUICK_ACTIONS_LIDARR'; + case LunaDatabaseValue.QUICK_ACTIONS_RADARR: return 'LUNASEA_QUICK_ACTIONS_RADARR'; + case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return 'LUNASEA_QUICK_ACTIONS_SONARR'; + case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return 'LUNASEA_QUICK_ACTIONS_NZBGET'; + case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return 'LUNASEA_QUICK_ACTIONS_SABNZBD'; + case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return 'LUNASEA_QUICK_ACTIONS_TAUTULLI'; + case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return 'LUNASEA_QUICK_ACTIONS_SEARCH'; + case LunaDatabaseValue.USE_24_HOUR_TIME: return 'LUNASEA_USE_24_HOUR_TIME'; + } + throw Exception('key not found'); + } + + dynamic get data { + final box = Database.lunaSeaBox; + switch(this) { + case LunaDatabaseValue.ENABLED_PROFILE: return box.get(this.key, defaultValue: 'default'); + case LunaDatabaseValue.SELECTED_BROWSER: return box.get(this.key, defaultValue: LunaBrowser.APPLE_SAFARI); + case LunaDatabaseValue.THEME_AMOLED: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.THEME_AMOLED_BORDER: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return box.get(this.key, defaultValue: 10); + case LunaDatabaseValue.ENABLED_SENTRY: return box.get(this.key, defaultValue: true); + case LunaDatabaseValue.DRAWER_GROUP_MODULES: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return box.get(this.key, defaultValue: true); + case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: return box.get(this.key, defaultValue: true); + case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: return box.get(this.key, defaultValue: true); + case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_RADARR: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return box.get(this.key, defaultValue: false); + case LunaDatabaseValue.USE_24_HOUR_TIME: return box.get(this.key, defaultValue: false); + } + throw Exception('data not found'); + } + + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case LunaDatabaseValue.ENABLED_PROFILE: if(value.runtimeType == String) box.put(this.key, value); return; + case LunaDatabaseValue.ENABLED_SENTRY: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.THEME_AMOLED: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.THEME_AMOLED_BORDER: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: if(value.runtimeType == int) box.put(this.key, value); return; + case LunaDatabaseValue.SELECTED_BROWSER: if(value.runtimeType == LunaBrowser) box.put(this.key, value); return; + case LunaDatabaseValue.DRAWER_GROUP_MODULES: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_RADARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_SONARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: if(value.runtimeType == bool) box.put(this.key, value); return; + case LunaDatabaseValue.USE_24_HOUR_TIME: if(value.runtimeType == bool) box.put(this.key, value); return; + } + LunaLogger().warning('LunaDatabaseValueExtension', 'put', 'Attempted to enter data for invalid LunaDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); +} diff --git a/lib/core/database/module_database.dart b/lib/core/database/module_database.dart new file mode 100644 index 00000000..effc4ef7 --- /dev/null +++ b/lib/core/database/module_database.dart @@ -0,0 +1,13 @@ +abstract class LunaModuleDatabase { + /// Register all adapters necessary for this module + void registerAdapters(); + + /// Export the current module's database into a [Map] object + Map export(); + + /// Receiving a map object, import a configuration + void import(Map config); + + /// Return the Database enumerator value given the key + dynamic valueFromKey(String value); +} diff --git a/lib/core/dialogs.dart b/lib/core/dialogs.dart index 808fcb80..7d04e837 100644 --- a/lib/core/dialogs.dart +++ b/lib/core/dialogs.dart @@ -3,9 +3,14 @@ import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; class LunaDialogs { - LunaDialogs._(); - - static Future> editText(BuildContext context, String title, { String prefill = '', List extraText }) async { + /// Show an an edit text prompt. + /// + /// Can pass in [prefill] String to prefill the [TextFormField]. Can also pass in a list of [TextSpan] tp show text above the field. + /// + /// Returns list containing: + /// - 0: Flag (true if they hit save, false if they cancelled the prompt) + /// - 1: Value from the [TextEditingController]. + Future> editText(BuildContext context, String dialogTitle, { String prefill = '', List extraText }) async { bool _flag = false; final _formKey = GlobalKey(); final _textController = TextEditingController()..text = prefill; @@ -19,7 +24,7 @@ class LunaDialogs { await LSDialog.dialog( context: context, - title: title, + title: dialogTitle, buttons: [ LSDialog.button( text: 'Save', @@ -32,7 +37,7 @@ class LunaDialogs { key: _formKey, child: LSDialog.textFormInput( controller: _textController, - title: title, + title: dialogTitle, onSubmitted: (_) => _setValues(true), validator: (_) => null, ), @@ -43,7 +48,10 @@ class LunaDialogs { return [_flag, _textController.text]; } - static Future textPreview(BuildContext context, String title, String text, { bool alignLeft = false }) async { + /// Show a text preview dialog. + /// + /// Can pass in boolean [alignLeft] to left align the text in the dialog (useful for bulleted lists) + Future textPreview(BuildContext context, String dialogTitle, String text, { bool alignLeft = false }) async { await showDialog( context: context, builder: (context) => AlertDialog( @@ -63,7 +71,7 @@ class LunaDialogs { }, ), ], - title: LSDialog.title(text: title), + title: LSDialog.title(text: dialogTitle), content: LSDialog.content( children: [ LSDialog.textContent( @@ -75,14 +83,17 @@ class LunaDialogs { ], ), contentPadding: LSDialog.textDialogContentPadding(), - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), ); } - static Future> deleteCatalogueWithFiles(BuildContext context, String title) async { + /// **Will be removed in future** + /// + /// Show a delete catalogue with all files warning dialog. + Future> deleteCatalogueWithFiles(BuildContext context, String moduleTitle) async { bool _flag = false; void _setValues(bool flag) { @@ -101,7 +112,7 @@ class LunaDialogs { ), ], content: [ - LSDialog.textContent(text: 'Are you sure you want to delete all the files and folders for $title?'), + LSDialog.textContent(text: 'Are you sure you want to delete all the files and folders for $moduleTitle?'), ], contentPadding: LSDialog.textDialogContentPadding(), ); diff --git a/lib/core/encryption.dart b/lib/core/encryption.dart index 2eb1e560..41652cc5 100644 --- a/lib/core/encryption.dart +++ b/lib/core/encryption.dart @@ -2,32 +2,42 @@ import 'package:encrypt/encrypt.dart'; import 'package:lunasea/core.dart'; class LunaEncryption { - LunaEncryption._(); + static const int _IV_LENGTH = 16; + static const int _KEY_LENGTH = 32; + static const String _PAD_VALUE = '0'; + static const String ENCRYPTION_FAILURE = '<>'; - static String encrypt(String encryptionKey, String data) { + /// Encrypt a given string using a given encryption key, and return the encrypted string. + /// + /// The key is duped, and either trimmed or padded (with _PAD_VALUE) to 32 characters. + String encrypt(String encryptionKey, String data) { try { - String _key = (encryptionKey+encryptionKey).padRight(32, '0').substring(0, 32); - Key key = Key.fromUtf8(_key); - IV iv = IV.fromLength(16); + String morphedKey = (encryptionKey+encryptionKey).padRight(_KEY_LENGTH, _PAD_VALUE).substring(0, _KEY_LENGTH); + Key key = Key.fromUtf8(morphedKey); + IV iv = IV.fromLength(_IV_LENGTH); final _encrypter = Encrypter(AES(key)); final _encrypted = _encrypter.encrypt(data, iv: iv).base64; return _encrypted; - } catch (e) { - LunaLogger.error('package:lunasea/core/configuration/encryption.dart', 'encrypt', 'Encryption error', e, StackTrace.current); + } catch (error, stack) { + LunaLogger().error('Failed to decrypted "$data" with using key "$encryptionKey"', error, stack); } - return Constants.ENCRYPTION_FAILURE; + return ENCRYPTION_FAILURE; } - static String decrypt(String decryptionKey, String data) { + /// Decrypt a given string using a given encryption key, and return the decrypted string. + /// + /// The key is duped, and either trimmed or padded (with _PAD_VALUE) to 32 characters. + String decrypt(String decryptionKey, String data) { try { - String _key = (decryptionKey+decryptionKey).padRight(32, '0').substring(0, 32); - Key key = Key.fromUtf8(_key); - IV iv = IV.fromLength(16); + String morphedKey = (decryptionKey+decryptionKey).padRight(_KEY_LENGTH, _PAD_VALUE).substring(0, _KEY_LENGTH); + Key key = Key.fromUtf8(morphedKey); + IV iv = IV.fromLength(_IV_LENGTH); final _encrypter = Encrypter(AES(key)); return _encrypter.decrypt64(data, iv: iv); - } catch (e) { - LunaLogger.error('package:lunasea/core/configuration/encryption.dart', 'decrypt', 'Decryption error', e, StackTrace.current); + } catch (error) { + /// Do not log as error (to Sentry), since a decrpytion error is very likely a user problem + LunaLogger().warning('LunaEncryption', 'decrypt', 'Failed to decrypted "$data" with using key "$decryptionKey"'); } - return Constants.ENCRYPTION_FAILURE; + return ENCRYPTION_FAILURE; } } diff --git a/lib/core/extensions.dart b/lib/core/extensions.dart index d9cb977e..5cb2ad29 100644 --- a/lib/core/extensions.dart +++ b/lib/core/extensions.dart @@ -1,7 +1,7 @@ -export 'extensions/integer.dart'; export 'extensions/datetime.dart'; export 'extensions/double.dart'; export 'extensions/duration.dart'; -export 'extensions/in_app_purchases.dart'; -export 'extensions/flog.dart'; +export 'extensions/flog_log_level.dart'; +export 'extensions/iap_product_details.dart'; +export 'extensions/int.dart'; export 'extensions/string.dart'; diff --git a/lib/core/extensions/datetime.dart b/lib/core/extensions/datetime.dart index 131dd21e..393a2f85 100644 --- a/lib/core/extensions/datetime.dart +++ b/lib/core/extensions/datetime.dart @@ -2,39 +2,42 @@ import 'package:intl/intl.dart'; import 'package:lunasea/core.dart'; extension DateTimeExtension on DateTime { - //ignore: non_constant_identifier_names - DateTime lsDateTime_floor() => DateTime(this.year, this.month, this.day); + /// Get the floor of a date. + /// + /// The floor is a new [DateTime] object with only the year, month, and day copied from the original source. + DateTime get lunaFloor => DateTime(this.year, this.month, this.day); - //ignore: non_constant_identifier_names - String lsDateTime_ageString(DateTime date) { - if(this == null || date == null) return 'Unknown'; - Duration diff = this.difference(date); - if(diff.inDays >= 1) { - return '${diff.inDays} ${diff.inDays == 1 ? 'Day' : 'Days'} Ago'; - } else if(diff.inHours >= 1) { - return '${diff.inHours} ${diff.inHours == 1 ? 'Hour' : 'Hours'} Ago'; - } else if(diff.inMinutes >= 1) { - return '${diff.inMinutes} ${diff.inMinutes == 1 ? 'Minute' : 'Minutes'} Ago'; - } + /// Returns a string representation of the "age" of the [DateTime] object. + /// + /// Compares to [DateTime.now().toLocal()] to calculate the age. + String get lunaAge { + if(this == null) return 'Unknown'; + Duration diff = DateTime.now().toLocal().difference(this); + if(diff.inDays >= 1) return '${diff.inDays} ${diff.inDays == 1 ? 'Day' : 'Days'} Ago'; + if(diff.inHours >= 1) return '${diff.inHours} ${diff.inHours == 1 ? 'Hour' : 'Hours'} Ago'; + if(diff.inMinutes >= 1) return '${diff.inMinutes} ${diff.inMinutes == 1 ? 'Minute' : 'Minutes'} Ago'; return '${diff.inSeconds} ${diff.inSeconds == 1 ? 'Second' : 'Seconds'} Ago'; } - //ignore: non_constant_identifier_names - String lsDateTime_upcomingString(DateTime date, { bool shouldLimit = true, int limit = 30 }) { - if(this == null || date == null) return 'Unknown'; - Duration diff = date.difference(this); + /// Returns a string representation of how far in the future the day is. + /// + /// Compares to [DateTime.now().toLocal()] to calculate the upcoming time. + String get lunaUpcomingDays { + if(this == null) return 'Unknown'; + Duration diff = this.difference(DateTime.now().toLocal()); if(diff.inDays == 0) return 'Today'; - if(shouldLimit && diff.inDays > limit) return ''; return 'In ${diff.inDays} ${diff.inDays == 1 ? "Day" : "Days"}'; } - /// Returns just the time as a String. - /// 3 PM will return either as 15:00 (24 hour style) or 3:00 PM depending on the configured database option - //ignore: non_constant_identifier_names - String get lsDateTime_time => LunaSeaDatabaseValue.USE_24_HOUR_TIME.data - ? DateFormat.Hm().format(this) - : DateFormat.jm().format(this); + /// Returns just the time as a string. + /// + /// 3 PM will return either 15:00 (24 hour style) or 3:00 PM depending on the configured database option. + String get lunaTime => LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat.Hm().format(this) : DateFormat.jm().format(this); + + /// Returns just the date as a string. + /// + /// Formatted as YYYY-MM-DD + String get lunaDate => '${this.year.toString().padLeft(4, '0')}-${this.month.toString().padLeft(2, '0')}-${this.day.toString().padLeft(2, '0')}'; + - //ignore: non_constant_identifier_names - String get lsDateTime_date => '${this.year.toString().padLeft(4, '0')}-${this.month.toString().padLeft(2, '0')}-${this.day.toString().padLeft(2, '0')}'; } diff --git a/lib/core/extensions/double.dart b/lib/core/extensions/double.dart index 49341792..ba622dba 100644 --- a/lib/core/extensions/double.dart +++ b/lib/core/extensions/double.dart @@ -1 +1,13 @@ -export 'double/time.dart'; +import 'package:lunasea/core.dart'; + +extension DoubleExtension on double { + /// Given a double (of hours), return a string signifying how long ago it occurred. + /// + /// Time greater than 48 hours => "x Days Ago" else "x Hours Ago" + String lunaHoursToAge() { + if(this == null) return Constants.TEXT_EMDASH; + int days = (this/24).floor(); + if(this > 48) return '$days Days Ago'; + return this == 1 ? '1 Hour Ago' : '${this.toStringAsFixed(1)} Hours Ago'; + } +} diff --git a/lib/core/extensions/double/time.dart b/lib/core/extensions/double/time.dart deleted file mode 100644 index fcd5624b..00000000 --- a/lib/core/extensions/double/time.dart +++ /dev/null @@ -1,9 +0,0 @@ -extension DoubleTimeExtension on double { - // ignore: non_constant_identifier_names - String lsTime_releaseAgeString() { - if(this == null) return ''; - int days = (this/24).floor(); - if(this > 48) return days == 1 ? '1 Day Ago' : '$days Days Ago'; - return this == 1 ? '1 Hour Ago' : '${this.toStringAsFixed(1)} Hours Ago'; - } -} diff --git a/lib/core/extensions/duration.dart b/lib/core/extensions/duration.dart index bae20937..b542217e 100644 --- a/lib/core/extensions/duration.dart +++ b/lib/core/extensions/duration.dart @@ -1,35 +1,31 @@ import 'package:lunasea/core.dart'; extension DurationExtension on Duration { - /// Creates a HH:MM:SS timestamp - //ignore: non_constant_identifier_names - String lsDuration_timestamp() { - String hours = this.inHours.toString().padLeft(2, '0'); - String minutes = (this.inMinutes%60).toString().padLeft(2, '0'); - String seconds = (this.inSeconds%60).toString().padLeft(2, '0'); - return [ - if(hours != '00') '$hours:', - '$minutes:', - '$seconds', - ].join(); - } + /// Return a string representation of the timestamp. + /// + /// Format example: HH:MM:SS or MM:SS if the duration is less than one hour + String get lunaTimestamp => [ + if(this.inHours != 0) this.inHours.toString().padLeft(2, '0'), + (this.inMinutes%60).toString().padLeft(2, '0'), + (this.inSeconds%60).toString().padLeft(2, '0'), + ].join(':'); - - /// Creates a full timestamp, akin to 1 Day, 23 Hours, 10 Minutes - //ignore: non_constant_identifier_names - String lsDuration_fullTimestamp() { + /// Returns a string representation of the timestamp as words. + /// + /// Format example: 1 Day, 23 Hours, 10 Minutes + String get lunaTimestampWords { String days, hours, minutes; - if(this.inDays == 1) days = '1 Day '; - if(this.inDays > 1) days = '${this.inDays.toString()} Days '; - if(this.inHours == 1) hours = '1 Hour '; - if(this.inHours > 1) hours = '${(this.inHours%24).toString()} Hours '; - if(this.inMinutes == 1) minutes = '1 Minute '; - if(this.inMinutes > 1) minutes = '${(this.inMinutes%60).toString()} Minutes '; + if(this.inDays == 1) days = '1 Day'; + if(this.inDays > 1) days = '${this.inDays.toString()} Days'; + if(this.inHours == 1) hours = '1 Hour'; + if(this.inHours > 1) hours = '${(this.inHours%24).toString()} Hours'; + if(this.inMinutes == 1) minutes = '1 Minute'; + if(this.inMinutes > 1) minutes = '${(this.inMinutes%60).toString()} Minutes'; return [ if(days != null) days, if(hours != null) hours, if(minutes != null) minutes, if(minutes == null) Constants.TEXT_EMDASH, - ].join(); + ].join(' '); } } diff --git a/lib/core/extensions/flog.dart b/lib/core/extensions/flog.dart deleted file mode 100644 index a164105c..00000000 --- a/lib/core/extensions/flog.dart +++ /dev/null @@ -1 +0,0 @@ -export 'flog/log_level.dart'; diff --git a/lib/core/extensions/flog/log_level.dart b/lib/core/extensions/flog/log_level.dart deleted file mode 100644 index d013891b..00000000 --- a/lib/core/extensions/flog/log_level.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:f_logs/f_logs.dart' as FLog; - -extension FLogLogLevelExtension on FLog.LogLevel { - //ignore: non_constant_identifier_names - String lsFLogLogLevel_name() => this.toString().substring(9); - - //ignore: non_constant_identifier_names - Color lsFLogLogLevel_color() { - switch(this.toString()) { - case 'LogLevel.WARNING': return Colors.orange; - case 'LogLevel.ERROR': return Colors.red; - case 'LogLevel.FATAL': return LunaColours.accent; - default: return Colors.blueGrey; - } - } -} \ No newline at end of file diff --git a/lib/core/extensions/flog_log_level.dart b/lib/core/extensions/flog_log_level.dart new file mode 100644 index 00000000..d03868e7 --- /dev/null +++ b/lib/core/extensions/flog_log_level.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:f_logs/f_logs.dart'; + +extension FLogLogLevelExtension on LogLevel { + /// Return a readable string version of the log level. + String get lunaName => this.toString().substring(9); + + /// Return a [Color] corresponding to the log level. + Color get lunaColour { + switch(this) { + case LogLevel.WARNING: return LunaColours.orange; + case LogLevel.ERROR: return LunaColours.red; + case LogLevel.FATAL: return LunaColours.accent; + default: return LunaColours.blueGrey; + } + } +} \ No newline at end of file diff --git a/lib/core/extensions/in_app_purchases.dart b/lib/core/extensions/iap_product_details.dart similarity index 75% rename from lib/core/extensions/in_app_purchases.dart rename to lib/core/extensions/iap_product_details.dart index 47cb7cee..003a32de 100644 --- a/lib/core/extensions/in_app_purchases.dart +++ b/lib/core/extensions/iap_product_details.dart @@ -3,28 +3,27 @@ import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:lunasea/core.dart'; extension ProductDetailsExtension on ProductDetails { - //ignore: non_constant_identifier_names - IconData get ls_Icon { + /// Returns [IconData] icon corresponding to the IAP + IconData get lunaIcon { switch(this.id) { case LunaInAppPurchases.IAP_ID_DONATION_01: return Icons.local_drink; case LunaInAppPurchases.IAP_ID_DONATION_03: return Icons.local_cafe; case LunaInAppPurchases.IAP_ID_DONATION_05: return Icons.local_bar; case LunaInAppPurchases.IAP_ID_DONATION_10: return Icons.fastfood; + case LunaInAppPurchases.IAP_UPGRADE_PRO: return Icons.upgrade; default: return Icons.attach_money; } } - //ignore: non_constant_identifier_names - String get ls_Name { + /// Returns a readable name for the corresponding IAP + String get lunaName { switch(this.id) { case LunaInAppPurchases.IAP_ID_DONATION_01: return 'Buy Me A Soda'; case LunaInAppPurchases.IAP_ID_DONATION_03: return 'Buy Me A Coffee'; case LunaInAppPurchases.IAP_ID_DONATION_05: return 'Buy Me A Beer'; case LunaInAppPurchases.IAP_ID_DONATION_10: return 'Buy Me A Burger'; + case LunaInAppPurchases.IAP_UPGRADE_PRO: return 'Upgrade to LunaSea Pro'; default: return 'Unknown In-App Purchase'; } } - - //ignore: non_constant_identifier_names - String get ls_Description => '${this.price}'; -} \ No newline at end of file +} diff --git a/lib/core/extensions/int.dart b/lib/core/extensions/int.dart new file mode 100644 index 00000000..dd0696eb --- /dev/null +++ b/lib/core/extensions/int.dart @@ -0,0 +1,76 @@ +extension IntegerBytesExtension on int { + static const _BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb']; + static const _BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']; + + String _bytesToString({ int decimals = 2, position = 0, bool bytes = true }) { + if(this == null || this <= 0) return '${0.toStringAsFixed(decimals)} B'; + int chunk = bytes ? 1024 : 1000; + double size = this.toDouble(); + while(size > chunk) { + size /= chunk; + position++; + } + return '${size.toStringAsFixed(decimals)} ${bytes ? _BYTE_SIZES[position] : _BIT_SIZES[position]}'; + } + + /// Given an integer of the number of bytes/bits, return a string representation. + String lunaBytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 0, bytes: bytes); + + /// Given an integer of the number of kilobytes/kilobits, return a string representation. + String lunaKilobytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 1, bytes: bytes); + + /// Given an integer of the number of megabytes/megabits, return a string representation. + String lunaMegabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 2, bytes: bytes); + + /// Given an integer of the number of gigabytes/gigabits, return a string representation. + String lunaGigabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 3, bytes: bytes); +} + +extension IntegerTimeExtension on int { + static const int _MINUTE_IN_SECONDS = 60; + static const int _HOUR_IN_SECONDS = 60*60; + static const int _DAY_IN_SECONDS = 60*60*24; + + /// Given an integer duration, converts it to a readable string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input. + /// + /// Only contains the highest factor (2.5 Days would return 2 Days). + String lunaDuration({ int divisor = 1, int multiplier = 1 }) { + if(this == null) return ''; + int duration = ((this*multiplier)/divisor).floor(); + int days = (duration/_DAY_IN_SECONDS).floor(); + if(days > 0) return '$days ${days == 1 ? 'Day' : 'Days'}'; + int hours = (duration/_HOUR_IN_SECONDS).floor(); + if(hours > 0) return '$hours ${hours == 1 ? 'Hour' : 'Hours'}'; + int minutes = (duration/_MINUTE_IN_SECONDS).floor(); + if(minutes > 0) return '$minutes ${minutes == 1 ? 'Minute' : 'Minutes'}'; + return '$duration ${duration == 1 ? 'Second' : 'Seconds'}'; + } + + /// Given an integer duration, converts it to a readable runtime string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input. + /// + /// Format example: 1h 23m + String lunaRuntime({ int divisor = 1, int multiplier = 1 }) { + if(this == null || this == 0) return ''; + double runtime = ((this*multiplier)/divisor); + if(runtime < 60) return '${runtime.floor()}m'; + return '${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m'; + } + + /// Given an integer duration, converts it to a readable timestamp string. Assumes the duration is in seconds, use the `divisor` and `multiplier` variables to easily modify the input. + /// + /// Format example: 1:23:45 (1 hour, 23 minutes, and 45 seconds) + String lunaTimestamp({ int divisor = 1, int multiplier = 1 }) { + if(this == null) return ''; + int duration = ((this*multiplier)/divisor).floor(); + int hours = 0, minutes = 0; + while(duration >= _HOUR_IN_SECONDS) { + duration -= _HOUR_IN_SECONDS; + hours++; + } + while(duration >= _MINUTE_IN_SECONDS) { + duration -= _MINUTE_IN_SECONDS; + minutes++; + } + return '${hours.toString().padLeft(1, '0')}:${minutes.toString().padLeft(2, '0')}:${duration.toString().padLeft(2, '0')}'; + } +} diff --git a/lib/core/extensions/integer.dart b/lib/core/extensions/integer.dart deleted file mode 100644 index c789d032..00000000 --- a/lib/core/extensions/integer.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'integer/bytes.dart'; -export 'integer/time.dart'; diff --git a/lib/core/extensions/integer/bytes.dart b/lib/core/extensions/integer/bytes.dart deleted file mode 100644 index 3469b827..00000000 --- a/lib/core/extensions/integer/bytes.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:lunasea/core/constants.dart'; - -extension IntegerBytesExtension on int { - String _bytesToString({ int decimals = 2, position = 0, bool bytes = true }) { - if(this == null || this <= 0) return '${0.toStringAsFixed(decimals)} B'; - int chunk = bytes ? 1024 : 1000; - double size = this.toDouble(); - while(size > chunk) { - size /= chunk; - position++; - } - return '${size.toStringAsFixed(decimals)} ${bytes ? Constants.BYTE_SIZES[position] : Constants.BIT_SIZES[position]}'; - } - - // ignore: non_constant_identifier_names - String lsBytes_BytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 0, bytes: bytes); - // ignore: non_constant_identifier_names - String lsBytes_KilobytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 1, bytes: bytes); - // ignore: non_constant_identifier_names - String lsBytes_MegabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 2, bytes: bytes); - // ignore: non_constant_identifier_names - String lsBytes_GigabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 3, bytes: bytes); -} diff --git a/lib/core/extensions/integer/time.dart b/lib/core/extensions/integer/time.dart deleted file mode 100644 index e4791e7c..00000000 --- a/lib/core/extensions/integer/time.dart +++ /dev/null @@ -1,47 +0,0 @@ -const DAY_IN_SECONDS = 60*60*24; -const HOUR_IN_SECONDS = 60*60; -const MINUTE_IN_SECONDS = 60; - -extension IntegerTimeExtension on int { - // ignore: non_constant_identifier_names - String lsTime_runtimeString({ bool dot = false, int divisor = 1, int multipler = 1 }) { - if(this == null || this == 0) return ''; - double runtime = ((this*multipler)/divisor); - if(runtime < 60) return dot - ? '\t•\t${runtime.floor()}m' - : '${runtime.floor()}m'; - return dot - ? '\t•\t${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m' - : '${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m'; - } - - // ignore: non_constant_identifier_names - String lsTime_timestampString({ int divisor = 1, int multipler = 1 }) { - if(this == null) return ''; - int seconds = ((this*multipler)/divisor).floor(); - int hours = 0; - int minutes = 0; - while(seconds >= 3600) { - seconds -= 3600; - hours++; - } - while(seconds >= 60) { - seconds -= 60; - minutes++; - } - return '${hours.toString().padLeft(1, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; - } - - // ignore: non_constant_identifier_names - String lsTime_durationString({ int divisor = 1, int multiplier = 1 }) { - if(this == null) return ''; - int duration = ((this*multiplier)/divisor).floor(); - int days = (duration/DAY_IN_SECONDS).floor(); - if(days > 0) return days == 1 ? '1 Day' : '$days Days'; - int hours = (duration/HOUR_IN_SECONDS).floor(); - if(hours > 0) return hours == 1 ? '1 Hour' : '$hours Hours'; - int minutes = (duration/MINUTE_IN_SECONDS).floor(); - if(minutes > 0) return minutes == 1 ? '1 Minute' : '$minutes Minutes'; - return duration == 1 ? '1 Second' : '$duration Seconds'; - } -} \ No newline at end of file diff --git a/lib/core/extensions/string.dart b/lib/core/extensions/string.dart index fdf34b8a..b19c69c1 100644 --- a/lib/core/extensions/string.dart +++ b/lib/core/extensions/string.dart @@ -1,3 +1,58 @@ -export 'string/language.dart'; -export 'string/links.dart'; -export 'string/slugs.dart'; +import 'package:lunasea/core.dart'; +import 'package:url_launcher/url_launcher.dart'; + +extension StringExtension on String { + /// Returns a string with all first letters of each word capitalized + /// + /// Default word splitting pattern is by a space. + String lunaCapitalizeFirstLetters({ Pattern pattern = ' ' }) { + List split = this.split(pattern); + for(var i=0; i 'lunasea-flutter'; + String lunaConvertToSlug() => this.toLowerCase().replaceAll(RegExp(r'[\ \.]'), '-').replaceAll(RegExp(r'[^a-zA-Z0-9\-]'), '').trim(); +} + +extension StringLinksExtension on String { + Future _openLink(String url) async { + try { + //Attempt to launch the link forced as a universal link + if(await _launchUniversalLink(url)) return; + //If that fails and we're using a custom browser, format the link for the correct browser and attempt to launch + LunaBrowser _browser = LunaDatabaseValue.SELECTED_BROWSER.data; + if(_browser != LunaBrowser.APPLE_SAFARI && await _launchCustomBrowser(_browser.formatted(url))) return; + //If all else fails, just launch it in Safari/stock browser + await _launch(url); + } catch (error, stack) { + LunaLogger().error('Unable to open link: $url', error, stack); + } + } + + Future _launchUniversalLink(String url) async => await launch(url, forceSafariVC: false, universalLinksOnly: true); + Future _launchCustomBrowser(String url) async => await launch(url, forceSafariVC: false); + Future _launch(String url) async => await launch(url); + + /// Attempt to open this string as a URL in a browser. + Future lunaOpenGenericLink() async => await _openLink(this); + + /// Attach this string as an ID/title to IMDB and attempt to launch it as a URL. + Future lunaOpenIMDB() async => await _openLink('https://www.imdb.com/title/$this'); + + /// Attach this string as a video ID to YouTube and attempt to launch it as a URL. + Future lunaOpenYouTube() async => await _openLink('https://www.youtube.com/watch?v=$this'); + + /// Attach this string as a movie ID to TheMovieDB and attempt to launch it as a URL. + Future lunaOpenTheMovieDB() async => await _openLink('https://www.themoviedb.org/movie/$this'); + + /// Attach this string as a series ID to TheTVDB and attempt to launch it as a URL. + Future lunaOpenTheTVDB() async => await _openLink('https://www.thetvdb.com/?id=$this&tab=series'); + + /// Attach this string as a series ID to TVMaze and attempt to launch it as a URL. + Future lunaOpenTVMaze() async => await _openLink('https://www.tvmaze.com/shows/$this'); +} diff --git a/lib/core/extensions/string/language.dart b/lib/core/extensions/string/language.dart deleted file mode 100644 index cbdfedee..00000000 --- a/lib/core/extensions/string/language.dart +++ /dev/null @@ -1,10 +0,0 @@ -extension StringLanguageExtension on String { - // ignore: non_constant_identifier_names - lsLanguage_Capitalize() { - List split = this.split(' '); - for(var i=0; i _openLink(String url) async { - try { - //Attempt to launch the link forced as a universal link - if(await _launchUniversalLink(url)) return; - //If that fails and we're using a custom browser, format the link for the correct browser and attempt to launch - LSBrowsers _browser = LunaSeaDatabaseValue.SELECTED_BROWSER.data; - if( - _browser != LSBrowsers.APPLE_SAFARI && - await _launchCustomBrowser(_browser.formatted(url)) - ) return; - //If all else fails, just launch it in Safari - await _launch(url); - } catch (e) {} - } - - Future _launchUniversalLink(String url) async => await launch( - url, - forceSafariVC: false, - universalLinksOnly: true, - ); - - Future _launchCustomBrowser(String url) async => await launch( - url, - forceSafariVC: false, - ); - - Future _launch(String url) async => await launch(url); - - // ignore: non_constant_identifier_names - Future lsLinks_OpenLink() async => await _openLink(this); - // ignore: non_constant_identifier_names - Future lsLinks_OpenIMDB() async => await _openLink('https://www.imdb.com/title/$this'); - // ignore: non_constant_identifier_names - Future lsLinks_OpenYoutube() async => await _openLink('https://www.youtube.com/watch?v=$this'); - // ignore: non_constant_identifier_names - Future lsLinks_OpenMovieDB() async => await _openLink('https://www.themoviedb.org/movie/$this'); - // ignore: non_constant_identifier_names - Future lsLinks_OpenTVDB() async => await _openLink('https://www.thetvdb.com/?id=$this&tab=series'); - // ignore: non_constant_identifier_names - Future lsLinks_OpenTVMaze() async => await _openLink('https://www.tvmaze.com/shows/$this'); -} diff --git a/lib/core/extensions/string/slugs.dart b/lib/core/extensions/string/slugs.dart deleted file mode 100644 index a5de36c8..00000000 --- a/lib/core/extensions/string/slugs.dart +++ /dev/null @@ -1,10 +0,0 @@ -extension StringSlugsExtension on String { - // ignore: non_constant_identifier_names - String lsSlugs_ConvertToSlug() { - return this - .toLowerCase() - .replaceAll(RegExp(r'[\ \.]'), '-') - .replaceAll(RegExp(r'[^a-zA-Z0-9\-]'), '') - .trim(); - } -} diff --git a/lib/core/filesystem.dart b/lib/core/filesystem.dart index 247af4d4..d0b07aa2 100644 --- a/lib/core/filesystem.dart +++ b/lib/core/filesystem.dart @@ -3,18 +3,17 @@ import 'package:share/share.dart'; import 'package:path_provider/path_provider.dart'; class LunaFileSystem { - LunaFileSystem._(); - - static Future _getTempFilePath(String filename) async { - Directory tempDir = await getTemporaryDirectory(); - return '${tempDir.path}/$filename'; - } - - static Future exportFileToTemporaryStorage(String name, String data) async { - String _file = await _getTempFilePath(name); + /// Export a given string to the OS-level share sheet with the given name. + /// + /// Temporarily writes the string as a [File] to the temporary storage directory on the OS. + /// + /// Temporary storage is eventually cleared by the OS, but is more than enough time to save/send via the share sheet. + Future exportStringToShareSheet(String name, String data) async { + Directory tempDirectory = await getTemporaryDirectory(); + String path = '${tempDirectory.path}/$name'; //Write the NZB to the filesystem - File file = File(_file); + File file = File(path); await file?.writeAsString(data); - await Share.shareFiles([_file]); + await Share.shareFiles([path]); } } diff --git a/lib/core/firebase.dart b/lib/core/firebase.dart new file mode 100644 index 00000000..a33f33c4 --- /dev/null +++ b/lib/core/firebase.dart @@ -0,0 +1,13 @@ +import 'package:firebase_core/firebase_core.dart'; + +class LunaFirebase { + /// Return an instance of [FirebaseApp]. + /// + /// Throws an error if [LunaFirebase.initialize] has not been called. + FirebaseApp get app => Firebase.app(); + + /// Initialize Firebase: Calls [Firebase.initializeApp()]. + /// + /// This must be called before anything accesses Firebase services, or an exception will be thrown. + static Future initialize({ String name }) async => Firebase.initializeApp(); +} diff --git a/lib/core/image_cache.dart b/lib/core/image_cache.dart index 8b315629..1fb34eee 100644 --- a/lib/core/image_cache.dart +++ b/lib/core/image_cache.dart @@ -1,8 +1,7 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/painting.dart'; class LunaImageCache { - LunaImageCache._(); - + /// Initialize the image cache by setting [ImageCache]'s maximumSize and maximumSizeBytes. static void initialize() { ImageCache().maximumSize = 1000; ImageCache().maximumSizeBytes = 128 << 20; diff --git a/lib/core/in_app_purchases.dart b/lib/core/in_app_purchases.dart index 4f78dc4f..9298abcd 100644 --- a/lib/core/in_app_purchases.dart +++ b/lib/core/in_app_purchases.dart @@ -2,40 +2,75 @@ import 'dart:async'; import 'package:in_app_purchase/in_app_purchase.dart'; class LunaInAppPurchases { + // Donation IAP IDs static const IAP_ID_DONATION_01 = 'donation_01'; static const IAP_ID_DONATION_03 = 'donation_03'; static const IAP_ID_DONATION_05 = 'donation_05'; static const IAP_ID_DONATION_10 = 'donation_10'; - - static const IAP_IDS = [ + static const IAP_DONATION_IDS = [ IAP_ID_DONATION_01, IAP_ID_DONATION_03, IAP_ID_DONATION_05, IAP_ID_DONATION_10, ]; - static final InAppPurchaseConnection connection = InAppPurchaseConnection.instance; - static StreamSubscription> purchaseStream; - static List products = []; - static bool available = true; + // Upgrade IAP IDs + static const IAP_UPGRADE_PRO = 'upgrade_01'; + static const IAP_UPGRADE_IDS = [ + IAP_UPGRADE_PRO, + ]; + + static InAppPurchaseConnection get connection => InAppPurchaseConnection.instance; + static StreamSubscription> _purchaseStream; + static List donationIAPs = []; + static List upgradeIAPs = []; + static bool isAvailable = false; + /// Initialize the in-app purchases connection. + /// + /// - Enables pending purchase + /// - Attach [InAppPurchaseConnection.instance.purchaseUpdateStream] listener for automatically completing IAP purchases + /// - Fetch and store donation IAPs static Future initialize() async { InAppPurchaseConnection.enablePendingPurchases(); - purchaseStream = connection.purchaseUpdatedStream.listen((data) => LunaInAppPurchases._purchasedCallback(data)); - available = await connection.isAvailable(); - ProductDetailsResponse _resp = await connection.queryProductDetails(Set.from(IAP_IDS)); - products = _resp.productDetails; - products.sort((a, b) => a.id.compareTo(b.id)); + isAvailable = await connection.isAvailable(); + if(isAvailable) { + // Open listener + _purchaseStream = connection.purchaseUpdatedStream.listen((data) => LunaInAppPurchases._purchasedCallback(data)); + // Load donation IAPs + ProductDetailsResponse donation = await connection.queryProductDetails(Set.from(IAP_DONATION_IDS)); + donationIAPs = donation?.productDetails ?? []; + donationIAPs.sort((a, b) => a?.id?.compareTo(b?.id) ?? 0); + // Load upgrade IAPs + ProductDetailsResponse upgrade = await connection.queryProductDetails(Set.from(IAP_UPGRADE_IDS)); + upgradeIAPs = upgrade?.productDetails ?? []; + upgradeIAPs.sort((a, b) => a?.id?.compareTo(b?.id) ?? 0); + } } - static Future deinitialize() async { - purchaseStream?.cancel(); - } + /// Deinitialize the in-app purchases connection. + /// + /// - Cleanly cancel/close the [InAppPurchaseConnection.instance.purchaseUpdateStream] listener + static Future deinitialize() async => _purchaseStream?.cancel(); + /// Callback function for [purchaseUpdatedStream]. + /// + /// **Donations**: + /// - Automatically completes the purchase, as there is no actual product to deliver + /// + /// **Account Upgrade**: + /// - Create a listener to the user's account in Firestore + /// - Upgrade their account + /// - Validate upgrade + /// - Mark as completed, close listener static Future _purchasedCallback(List purchases) async { for(var purchase in purchases) { - if(purchase.pendingCompletePurchase) await connection.completePurchase(purchase); + /// Handle donations by always completing the purchase + if(IAP_DONATION_IDS.contains(purchase.productID)) { + if(purchase.pendingCompletePurchase) await connection.completePurchase(purchase); + } + /// Handle account upgrades + if(IAP_UPGRADE_IDS.contains(purchase.productID)) {} } } } - diff --git a/lib/core/logger.dart b/lib/core/logger.dart index ff1bf391..2d7b2dad 100644 --- a/lib/core/logger.dart +++ b/lib/core/logger.dart @@ -1,88 +1,79 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core/constants.dart'; -import 'package:lunasea/core/database.dart'; -import 'package:sentry/sentry.dart'; -import 'package:stack_trace/stack_trace.dart'; +import 'package:dio/dio.dart'; import 'package:f_logs/f_logs.dart' show FLog, DataLogType, FormatType, LogsConfig; -export 'package:dio/dio.dart' show DioError; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:stack_trace/stack_trace.dart'; class LunaLogger { - LunaLogger._(); - static final SentryClient _sentry = SentryClient(dsn: Constants.SENTRY_DSN); + static const SENTRY_DSN = 'https://511f76efcf714ecfb5ed6b26b5819bd6@o426090.ingest.sentry.io/5367513'; + static const CHECK_LOGS_MESSAGE = 'Check the logs for more details'; + /// Initialize the logger by setting the timestamp format and capturing errors on [FlutterError.onError]. static void initialize() { LogsConfig config = FLog.getDefaultConfigurations() ..formatType = FormatType.FORMAT_SQUARE ..timestampFormat = 'MMMM dd, y - hh:mm:ss a'; FLog.applyConfigurations(config); FlutterError.onError = (FlutterErrorDetails details, { bool forceReport = false }) async { - bool inDebugMode = false; - assert(inDebugMode = true); - if (inDebugMode) { - FlutterError.dumpErrorToConsole(details, forceReport: forceReport); - } + if (kDebugMode) FlutterError.dumpErrorToConsole(details, forceReport: forceReport); Zone.current.handleUncaughtError(details.exception, details.stack); }; } - static void debug(String className, String methodName, String text, {DataLogType type = DataLogType.DEFAULT}) { - FLog.debug( - className: className, - methodName: methodName, - text: text, - dataLogType: type.toString(), - ); - } + /// Log a new debug-level log. + void debug(String text) => FLog.debug( + text: text, + dataLogType: DataLogType.DEFAULT.toString(), + ); - static void warning(String className, String methodName, String text, {DataLogType type = DataLogType.DEFAULT}) { - FLog.warning( - className: className, - methodName: methodName, - text: text, - dataLogType: type.toString(), - ); - } + /// Log a new warning-level log. + void warning(String className, String methodName, String text) => FLog.warning( + className: className, + methodName: methodName, + text: text, + dataLogType: DataLogType.DEFAULT.toString(), + ); - static void error(String className, String methodName, String text, dynamic error, StackTrace stack, { - DataLogType type = DataLogType.DEFAULT, - bool uploadToSentry = true, - }) { + /// Log a new error-level log. + void error(String text, dynamic error, StackTrace stack) { + Trace _trace = stack == null ? null : Trace.from(stack); FLog.error( - className: className, - methodName: methodName, + className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown', + methodName: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown', text: text, exception: error, stacktrace: stack, - dataLogType: type.toString(), + dataLogType: DataLogType.DEFAULT.toString(), ); - if(uploadToSentry && LunaSeaDatabaseValue.ENABLED_SENTRY.data) _sentry.captureException( - exception: error, + if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException( + error, stackTrace: stack, ); } - static void fatal(dynamic error, StackTrace stack, { - DataLogType type = DataLogType.DEFAULT, - bool uploadToSentry = true, - }) { - Trace _trace = Trace.from(stack); + /// Log a new fatal-level log. + void fatal(dynamic error, StackTrace stack) { + Trace _trace = stack == null ? null : Trace.from(stack); FLog.fatal( - className: _trace.frames.length >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown', - methodName: _trace.frames.length >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown', + className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown', + methodName: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown', text: error.toString(), exception: error, stacktrace: stack, - dataLogType: type.toString(), + dataLogType: DataLogType.DEFAULT.toString(), ); - if(LunaSeaDatabaseValue.ENABLED_SENTRY.data) _sentry.captureException( - exception: error, + if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException( + error, stackTrace: stack, ); } - static Future exportLogs() async => FLog.exportLogs(); - - static Future clearLogs() async => FLog.clearLogs(); + /// Export all logs, and return the [File] object containing the log file. + Future exportLogs() async => FLog.exportLogs(); + /// Clear all logs currently saved to the database. + Future clearLogs() async => FLog.clearLogs(); } diff --git a/lib/core/luna_ui/decoration/card_decoration.dart b/lib/core/luna_ui/decoration/card_decoration.dart index cd7cbc6d..5dc0cf64 100644 --- a/lib/core/luna_ui/decoration/card_decoration.dart +++ b/lib/core/luna_ui/decoration/card_decoration.dart @@ -3,20 +3,19 @@ import 'package:lunasea/core.dart'; // ignore: non_constant_identifier_names Decoration LunaCardDecoration({ @required String uri, @required Map headers }) { - if(LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null; - double _opacity = (LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int)/100; + if(LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null; return BoxDecoration( image: DecorationImage( image: NetworkImage( uri, headers: Map.from(headers), ), - onError: (error, stack) => LunaLogger.warning( - 'LunaCardDecoration', - 'DecorationImage', - 'Failed to fetch background image: $uri', + // To prevent excessive logs, just handle an error loading the image with an empty callback + onError: (error, stack) {}, + colorFilter: ColorFilter.mode( + Colors.black.withOpacity((LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int)/100), + BlendMode.dstATop, ), - colorFilter: ColorFilter.mode(Colors.black.withOpacity(_opacity), BlendMode.dstATop), fit: BoxFit.cover, ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), diff --git a/lib/core/luna_ui/snackbar.dart b/lib/core/luna_ui/snackbar.dart index 8849a92f..04c495b6 100644 --- a/lib/core/luna_ui/snackbar.dart +++ b/lib/core/luna_ui/snackbar.dart @@ -52,7 +52,7 @@ Future showLunaSnackBar({ horizontalDismissDirection: HorizontalDismissDirection.horizontal, margin: EdgeInsets.all(_PADDING), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - borderColor: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? Colors.white12 : Colors.transparent, child: FlashBar( diff --git a/lib/core/luna_ui/snackbar/snackbar_error.dart b/lib/core/luna_ui/snackbar/snackbar_error.dart index 82cf7e76..35ba0655 100644 --- a/lib/core/luna_ui/snackbar/snackbar_error.dart +++ b/lib/core/luna_ui/snackbar/snackbar_error.dart @@ -6,12 +6,12 @@ Future showLunaErrorSnackBar({ @required BuildContext context, @required String title, dynamic error, - String message = Constants.CHECK_LOGS_MESSAGE, + String message = LunaLogger.CHECK_LOGS_MESSAGE, }) async => showLunaSnackBar( context: context, title: title, message: message, type: LunaSnackbarType.ERROR, showButton: error != null, - buttonOnPressed: () async => LunaDialogs.textPreview(context, 'Error', error.toString()), + buttonOnPressed: () async => LunaDialogs().textPreview(context, 'Error', error.toString()), ); diff --git a/lib/core/module_map.dart b/lib/core/module_map.dart deleted file mode 100644 index 97e23376..00000000 --- a/lib/core/module_map.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class LunaModuleMap { - final String name; - final String description; - final String settingsDescription; - final String route; - final IconData icon; - final Color color; - - const LunaModuleMap({ - @required this.name, - @required this.description, - @required this.settingsDescription, - @required this.route, - @required this.icon, - @required this.color, - }); -} diff --git a/lib/core/module_metadata.dart b/lib/core/module_metadata.dart new file mode 100644 index 00000000..dcd3108e --- /dev/null +++ b/lib/core/module_metadata.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +/// [LunaModuleMetadata] stores general information about a module, including: +/// - Name +/// - Descrption +/// - Base/home route +/// - Links +/// - Icon +/// - Brand colour +/// - etc. +class LunaModuleMetadata { + final String name; + final String description; + final String settingsDescription; + final String helpMessage; + final String route; + final String website; + final String github; + final IconData icon; + final Color color; + + const LunaModuleMetadata({ + @required this.name, + @required this.description, + @required this.settingsDescription, + @required this.helpMessage, + @required this.route, + @required this.website, + @required this.github, + @required this.icon, + @required this.color, + }); +} diff --git a/lib/core/networking.dart b/lib/core/networking.dart index 2cbbe827..ca2edd20 100644 --- a/lib/core/networking.dart +++ b/lib/core/networking.dart @@ -1,8 +1,14 @@ import 'dart:io'; class LunaNetworking extends HttpOverrides { - static void initialize() => HttpOverrides.global = LunaNetworking(); + /// Initialize the default HTTP client by setting [HttpOverrides]'s global HTTP client to [LunaNetworking]. + static void initialize() { + HttpOverrides.global = LunaNetworking(); + } + /// Overrides the default [createHttpClient] function and ensures all HTTPS connections allow bad certificate connections. + /// + /// Returns a new [HttpClient] using the given [context]. @override HttpClient createHttpClient(SecurityContext context) { final HttpClient client = super.createHttpClient(context); diff --git a/lib/core/profile.dart b/lib/core/profile.dart index 88504cf5..abebc95b 100644 --- a/lib/core/profile.dart +++ b/lib/core/profile.dart @@ -2,17 +2,22 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; class LunaProfile { - LunaProfile._(); + LunaProfile(); - static Future changeProfile(BuildContext context, String profile) async { - if(LunaSeaDatabaseValue.ENABLED_PROFILE.data != profile) { + /// Safely change profiles. + /// + /// Does this safely by: + /// - Ensures that the passed in profile isn't already enabled + /// - Ensures that the profile exists + Future safelyChangeProfiles(BuildContext context, String profile) async { + if(LunaDatabaseValue.ENABLED_PROFILE.data != profile) { if(Database.profilesBox.containsKey(profile)) { - LunaSeaDatabaseValue.ENABLED_PROFILE.put(profile); - LunaProvider.reset(context); + LunaDatabaseValue.ENABLED_PROFILE.put(profile); + LunaState.reset(context); LSSnackBar(context: context, title: 'Changed Profile', message: profile); return true; } else { - LunaLogger.warning('LunaProfile', 'changeProfile', 'Attempted to change profile to unknown profile: $profile'); + LunaLogger().warning('LunaProfile', 'changeProfile', 'Attempted to change profile to unknown profile: $profile'); } } return false; diff --git a/lib/core/quick_actions.dart b/lib/core/quick_actions.dart index 36c2e9c3..e0be1ac7 100644 --- a/lib/core/quick_actions.dart +++ b/lib/core/quick_actions.dart @@ -6,48 +6,63 @@ import 'package:lunasea/main.dart'; export 'package:quick_actions/quick_actions.dart' show ShortcutItem; class LunaQuickActions { + LunaQuickActions._(); + static final QuickActions _quickActions = QuickActions(); - LunaQuickActions._(); - + /// Initialize the quick actions by setting the action handler. + /// + /// Required before the handler can actually receive any intents. static void initialize(BuildContext context) { _quickActions.initialize((action) => _handler(context, action)); setShortcutItems(); } + /// Sets the shortcut items by checking the database and enabling the respective action if enabled. static void setShortcutItems() { _quickActions.setShortcutItems([ - if(LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH.data) SearchConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LidarrConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR.data) RadarrConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR.data) SonarrConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET.data) NZBGetConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) SABnzbdConstants.MODULE_QUICK_ACTION, - if(LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) TautulliConstants.MODULE_QUICK_ACTION, + // General + if(LunaDatabaseValue.QUICK_ACTIONS_SEARCH.data) SearchConstants.MODULE_QUICK_ACTION, + // Automation + if(LunaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LidarrConstants.MODULE_QUICK_ACTION, + if(LunaDatabaseValue.QUICK_ACTIONS_RADARR.data) RadarrConstants.MODULE_QUICK_ACTION, + if(LunaDatabaseValue.QUICK_ACTIONS_SONARR.data) SonarrConstants.MODULE_QUICK_ACTION, + // Clients + if(LunaDatabaseValue.QUICK_ACTIONS_NZBGET.data) NZBGetConstants.MODULE_QUICK_ACTION, + if(LunaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) SABnzbdConstants.MODULE_QUICK_ACTION, + // Monitoring + if(LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) TautulliConstants.MODULE_QUICK_ACTION, ]); } + /// The actual shortcut command handler static void _handler(BuildContext context, String action) { if(action != null) { switch(action) { + // General case SearchConstants.MODULE_KEY: _pushSearch(); break; + // Automation case LidarrConstants.MODULE_KEY: _pushLidarr(); break; case RadarrConstants.MODULE_KEY: _pushRadarr(); break; case SonarrConstants.MODULE_KEY: _pushSonarr(); break; + // Clients case NZBGetConstants.MODULE_KEY: _pushNZBGet(); break; case SABnzbdConstants.MODULE_KEY: _pushSABnzbd(); break; + // Monitoring case TautulliConstants.MODULE_KEY: _pushTautulli(); break; - case OmbiConstants.MODULE_KEY: _pushOmbi(); break; } } } + // General static void _pushSearch() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Search.ROUTE_NAME, (Route route) => false); + // Automation static void _pushLidarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Lidarr.ROUTE_NAME, (Route route) => false); static void _pushRadarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Radarr.ROUTE_NAME, (Route route) => false); static void _pushSonarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(SonarrHomeRouter.route(), (Route route) => false); + // Clients static void _pushNZBGet() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(NZBGet.ROUTE_NAME, (Route route) => false); static void _pushSABnzbd() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(SABnzbd.ROUTE_NAME, (Route route) => false); - static void _pushOmbi() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(OmbiHomeRouter.route(), (Route route) => false); + // Monitoring static void _pushTautulli() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(TautulliHomeRouter.route(), (Route route) => false); } diff --git a/lib/core/router.dart b/lib/core/router.dart index ce5e7ea6..70afbf13 100644 --- a/lib/core/router.dart +++ b/lib/core/router.dart @@ -1,110 +1,3 @@ -import 'package:flutter/material.dart'; -import 'package:fluro/fluro.dart'; -import 'package:lunasea/modules/home/routes.dart'; -import 'package:lunasea/modules/search/routes.dart'; -import 'package:lunasea/modules/lidarr/routes.dart'; -import 'package:lunasea/modules/radarr/routes.dart'; -import 'package:lunasea/modules/nzbget/routes.dart'; -import 'package:lunasea/modules/sabnzbd/routes.dart'; -import 'package:lunasea/modules/settings.dart' show SettingsRouter; -import 'package:lunasea/modules/sonarr.dart' show SonarrRouter; -import 'package:lunasea/modules/ombi.dart' show OmbiRouter; -import 'package:lunasea/modules/tautulli.dart' show TautulliRouter; - -class LunaRouter { - static FluroRouter router = FluroRouter(); - - LunaRouter._(); - - static void intialize() { - SettingsRouter.initialize(router); - SonarrRouter.initialize(router); - OmbiRouter.initialize(router); - TautulliRouter.initialize(router); - } - - static TransitionType get transitionType => TransitionType.native; - - static Map get routes => { - ..._home, - ..._search, - ..._lidarr, - ..._radarr, - ..._sabnzbd, - ..._nzbget, - }; - - static Map get _home => { - // / - Home.ROUTE_NAME: (context) => Home(), - // /home/* - HomeCalendar.ROUTE_NAME: (context) => HomeCalendar(refreshIndicatorKey: null), - HomeQuickAccess.ROUTE_NAME: (context) => HomeQuickAccess(), - }; - - static Map get _search => { - // /search - Search.ROUTE_NAME: (context) => Search(), - // /search/* - SearchSearch.ROUTE_NAME: (context) => SearchSearch(), - SearchCategories.ROUTE_NAME: (context) => SearchCategories(), - SearchSubcategories.ROUTE_NAME: (context) => SearchSubcategories(), - SearchResults.ROUTE_NAME: (context) => SearchResults(), - }; - - static Map get _lidarr => { - // /lidarr - Lidarr.ROUTE_NAME: (context) => Lidarr(), - // /lidarr/* - LidarrCatalogue.ROUTE_NAME: (context) => LidarrCatalogue(refreshIndicatorKey: null, refreshAllPages: null), - LidarrMissing.ROUTE_NAME: (context) => LidarrMissing(refreshIndicatorKey: null, refreshAllPages: null), - LidarrHistory.ROUTE_NAME: (context) => LidarrHistory(refreshIndicatorKey: null, refreshAllPages: null), - // /lidarr/add/* - LidarrAddSearch.ROUTE_NAME: (context) => LidarrAddSearch(), - LidarrAddDetails.ROUTE_NAME: (context) => LidarrAddDetails(), - // /lidarr/edit/* - LidarrEditArtist.ROUTE_NAME: (context) => LidarrEditArtist(), - // /lidarr/details/* - LidarrDetailsAlbum.ROUTE_NAME: (context) => LidarrDetailsAlbum(), - LidarrDetailsArtist.ROUTE_NAME: (context) => LidarrDetailsArtist(), - // /lidarr/search/* - LidarrSearchResults.ROUTE_NAME: (context) => LidarrSearchResults(), - }; - - static Map get _radarr => { - // /radarr - Radarr.ROUTE_NAME: (context) => Radarr(), - // /radarr/* - RadarrCatalogue.ROUTE_NAME: (context) => RadarrCatalogue(refreshIndicatorKey: null, refreshAllPages: null), - RadarrMissing.ROUTE_NAME: (context) => RadarrMissing(refreshIndicatorKey: null, refreshAllPages: null), - RadarrUpcoming.ROUTE_NAME: (context) => RadarrUpcoming(refreshIndicatorKey: null, refreshAllPages: null), - RadarrHistory.ROUTE_NAME: (context) => RadarrHistory(refreshIndicatorKey: null, refreshAllPages: null), - // /radarr/add/* - RadarrAddSearch.ROUTE_NAME: (context) => RadarrAddSearch(), - RadarrAddDetails.ROUTE_NAME: (context) => RadarrAddDetails(), - // /radarr/details/* - RadarrDetailsMovie.ROUTE_NAME: (context) => RadarrDetailsMovie(), - RadarrEditMovie.ROUTE_NAME: (context) => RadarrEditMovie(), - RadarrSearchResults.ROUTE_NAME: (context) => RadarrSearchResults(), - }; - - static Map get _nzbget => { - // /nzbget - NZBGet.ROUTE_NAME: (context) => NZBGet(), - // /nzbget/* - NZBGetHistory.ROUTE_NAME: (context) => NZBGetHistory(refreshIndicatorKey: null), - NZBGetQueue.ROUTE_NAME: (context) => NZBGetQueue(refreshIndicatorKey: null), - NZBGetStatistics.ROUTE_NAME: (context) => NZBGetStatistics(), - }; - - static Map get _sabnzbd => { - // /sabnzbd - SABnzbd.ROUTE_NAME: (context) => SABnzbd(), - // /sabnzbd/* - SABnzbdHistory.ROUTE_NAME: (context) => SABnzbdHistory(refreshIndicatorKey: null), - SABnzbdQueue.ROUTE_NAME: (context) => SABnzbdQueue(refreshIndicatorKey: null), - SABnzbdStatistics.ROUTE_NAME: (context) => SABnzbdStatistics(), - // /sabnzbd/history/* - SABnzbdHistoryStages.ROUTE_NAME: (context) => SABnzbdHistoryStages(), - }; -} +export 'router/module_router.dart'; +export 'router/page_router.dart'; +export 'router/router.dart'; diff --git a/lib/core/router/module_router.dart b/lib/core/router/module_router.dart new file mode 100644 index 00000000..1c1b7d36 --- /dev/null +++ b/lib/core/router/module_router.dart @@ -0,0 +1,6 @@ +import 'package:fluro/fluro.dart'; + +abstract class LunaModuleRouter { + /// Given the [FluroRouter], define all routes and handlers required for the module. + void defineAllRoutes(FluroRouter router); +} diff --git a/lib/core/router/page_router.dart b/lib/core/router/page_router.dart new file mode 100644 index 00000000..fe0c6425 --- /dev/null +++ b/lib/core/router/page_router.dart @@ -0,0 +1,33 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +abstract class LunaPageRouter { + /// The full string representation of the route without the named parameters being swapped out + final String fullRoute; + + /// Use [FluroRouter] to navigate to the page + Future navigateTo(BuildContext context) => LunaRouter.router.navigateTo(context, fullRoute); + + /// Get a string representation of the route with any named parameters inserted + String route() => fullRoute; + + /// Used to define the route in the passed in router. + /// + /// If the route requires no parameters, call [noParameterRouteDefinition] with the route as the [Widget] parameter. + void defineRoute(FluroRouter router); + + /// Define the route in the passed in router without any parameters required. + /// + /// Already has a definition that: + /// - Calls [FluroRouter.define] with the [fullRoute] + /// - Creates a basic [Handler] + /// - Sets the transition type to [LunaRouter.transitionType] + void noParameterRouteDefinition(FluroRouter router, StatefulWidget widget) => router.define( + fullRoute, + handler: Handler(handlerFunc: (context, params) => widget), + transitionType: LunaRouter.transitionType, + ); + + LunaPageRouter(this.fullRoute); +} diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart new file mode 100644 index 00000000..007abed1 --- /dev/null +++ b/lib/core/router/router.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:fluro/fluro.dart'; +import 'package:lunasea/modules/home/routes.dart'; +import 'package:lunasea/modules/search/routes.dart'; +import 'package:lunasea/modules/lidarr/routes.dart'; +import 'package:lunasea/modules/radarr/routes.dart'; +import 'package:lunasea/modules/nzbget/routes.dart'; +import 'package:lunasea/modules/sabnzbd/routes.dart'; +import 'package:lunasea/modules.dart' show OmbiRouter, SettingsRouter, SonarrRouter, TautulliRouter; + +class LunaRouter { + static FluroRouter router = FluroRouter(); + static TransitionType get transitionType => TransitionType.native; + + /// Calls `defineAllRoutes()` on all module routers that implement [LunaModuleRouter]. + static void intialize() { + SettingsRouter().defineAllRoutes(router); + SonarrRouter().defineAllRoutes(router); + OmbiRouter().defineAllRoutes(router); + TautulliRouter().defineAllRoutes(router); + } + + /// **Will be removed when all module routers are integrated.** + /// + /// Returns a map of all module routes. + static Map get routes => { + Home.ROUTE_NAME: (context) => Home(), + ..._search, + ..._lidarr, + ..._radarr, + ..._sabnzbd, + ..._nzbget, + }; + + static Map get _search => { + // /search + Search.ROUTE_NAME: (context) => Search(), + // /search/* + SearchSearch.ROUTE_NAME: (context) => SearchSearch(), + SearchCategories.ROUTE_NAME: (context) => SearchCategories(), + SearchSubcategories.ROUTE_NAME: (context) => SearchSubcategories(), + SearchResults.ROUTE_NAME: (context) => SearchResults(), + }; + + static Map get _lidarr => { + // /lidarr + Lidarr.ROUTE_NAME: (context) => Lidarr(), + // /lidarr/add/* + LidarrAddSearch.ROUTE_NAME: (context) => LidarrAddSearch(), + LidarrAddDetails.ROUTE_NAME: (context) => LidarrAddDetails(), + // /lidarr/edit/* + LidarrEditArtist.ROUTE_NAME: (context) => LidarrEditArtist(), + // /lidarr/details/* + LidarrDetailsAlbum.ROUTE_NAME: (context) => LidarrDetailsAlbum(), + LidarrDetailsArtist.ROUTE_NAME: (context) => LidarrDetailsArtist(), + // /lidarr/search/* + LidarrSearchResults.ROUTE_NAME: (context) => LidarrSearchResults(), + }; + + static Map get _radarr => { + // /radarr + Radarr.ROUTE_NAME: (context) => Radarr(), + // /radarr/add/* + RadarrAddSearch.ROUTE_NAME: (context) => RadarrAddSearch(), + RadarrAddDetails.ROUTE_NAME: (context) => RadarrAddDetails(), + // /radarr/details/* + RadarrDetailsMovie.ROUTE_NAME: (context) => RadarrDetailsMovie(), + RadarrEditMovie.ROUTE_NAME: (context) => RadarrEditMovie(), + RadarrSearchResults.ROUTE_NAME: (context) => RadarrSearchResults(), + }; + + static Map get _nzbget => { + NZBGet.ROUTE_NAME: (context) => NZBGet(), + NZBGetStatistics.ROUTE_NAME: (context) => NZBGetStatistics(), + }; + + static Map get _sabnzbd => { + SABnzbd.ROUTE_NAME: (context) => SABnzbd(), + SABnzbdStatistics.ROUTE_NAME: (context) => SABnzbdStatistics(), + SABnzbdHistoryStages.ROUTE_NAME: (context) => SABnzbdHistoryStages(), + }; +} diff --git a/lib/core/state.dart b/lib/core/state.dart new file mode 100644 index 00000000..22c651f1 --- /dev/null +++ b/lib/core/state.dart @@ -0,0 +1,2 @@ +export 'state/module_state.dart'; +export 'state/state.dart'; diff --git a/lib/core/types/state_global.dart b/lib/core/state/module_state.dart similarity index 76% rename from lib/core/types/state_global.dart rename to lib/core/state/module_state.dart index c5f72d52..ccd2adcf 100644 --- a/lib/core/types/state_global.dart +++ b/lib/core/state/module_state.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -abstract class LunaGlobalState extends ChangeNotifier { +abstract class LunaModuleState extends ChangeNotifier { /// Reset the state back to the default void reset(); diff --git a/lib/core/providers.dart b/lib/core/state/state.dart similarity index 65% rename from lib/core/providers.dart rename to lib/core/state/state.dart index 0082384a..0b9b950c 100644 --- a/lib/core/providers.dart +++ b/lib/core/state/state.dart @@ -1,24 +1,21 @@ -//Exports -export 'package:provider/provider.dart'; -//Imports import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules.dart' show - HomeState, - SearchState, - SettingsState, - LidarrState, - RadarrState, - SonarrState, - NZBGetState, - SABnzbdState, - OmbiState, - TautulliState; +import 'package:lunasea/modules/home/core.dart' show HomeState; +import 'package:lunasea/modules/search/core.dart' show SearchState; +import 'package:lunasea/modules/settings/core.dart' show SettingsState; +import 'package:lunasea/modules/lidarr/core.dart' show LidarrState; +import 'package:lunasea/modules/radarr/core.dart' show RadarrState; +import 'package:lunasea/modules/sonarr/core.dart' show SonarrState; +import 'package:lunasea/modules/nzbget/core.dart' show NZBGetState; +import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdState; +import 'package:lunasea/modules/ombi/core.dart' show OmbiState; +import 'package:lunasea/modules/tautulli/core.dart' show TautulliState; +export 'package:provider/provider.dart'; -class LunaProvider { - LunaProvider._(); +class LunaState { + LunaState._(); + /// Calls `.reset()` on all states which extend [LunaModuleState]. static void reset(BuildContext context) { // General Provider.of(context, listen: false)?.reset(); @@ -36,6 +33,9 @@ class LunaProvider { Provider.of(context, listen: false)?.reset(); } + /// Returns a [MultiProvider] with the provided child. + /// + /// The [MultiProvider] has a [ChangeNotifierProvider] provider added for each module global state object. static MultiProvider providers({ @required Widget child }) => MultiProvider( providers: [ // General diff --git a/lib/core/theme.dart b/lib/core/theme.dart index 02942f55..7ef6c878 100644 --- a/lib/core/theme.dart +++ b/lib/core/theme.dart @@ -3,16 +3,14 @@ import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; class LunaTheme { - LunaTheme._(); - - static ThemeData get darkTheme => LunaSeaDatabaseValue.THEME_AMOLED.data - ? _pureBlackTheme() - : _midnightTheme(); + /// Returns the active [ThemeData] by checking the theme database value. + static ThemeData activeTheme() { + return LunaDatabaseValue.THEME_AMOLED.data ? _pureBlackTheme() : _midnightTheme(); + } + /// Midnight theme (Default) static ThemeData _midnightTheme() { - const _textStyle = TextStyle( - color: Colors.white, - ); + const _textStyle = TextStyle(color: Colors.white); return ThemeData( brightness: Brightness.dark, canvasColor: LunaColours.primary, @@ -22,13 +20,7 @@ class LunaTheme { cardColor: LunaColours.secondary, splashColor: LunaColours.splash, dialogBackgroundColor: LunaColours.secondary, - dividerColor: LunaColours.accent.withAlpha(0), toggleableActiveColor: LunaColours.accent, - dividerTheme: DividerThemeData( - color: LunaColours.accent, - indent: 100.0, - endIndent: 100.0, - ), iconTheme: IconThemeData( color: Colors.white, ), @@ -52,10 +44,9 @@ class LunaTheme { ); } + /// AMOLED/Pure black theme static ThemeData _pureBlackTheme() { - const _textStyle = TextStyle( - color: Colors.white, - ); + const _textStyle = TextStyle(color: Colors.white); return ThemeData( brightness: Brightness.dark, canvasColor: Colors.black, @@ -65,13 +56,7 @@ class LunaTheme { cardColor: Colors.black, splashColor: LunaColours.splash, dialogBackgroundColor: Colors.black, - dividerColor: LunaColours.accent.withAlpha(0), toggleableActiveColor: LunaColours.accent, - dividerTheme: DividerThemeData( - color: LunaColours.accent, - indent: 72.0, - endIndent: 72.0, - ), iconTheme: IconThemeData( color: Colors.white, ), diff --git a/lib/core/types.dart b/lib/core/types.dart index 7966ed0f..2d170b34 100644 --- a/lib/core/types.dart +++ b/lib/core/types.dart @@ -1,4 +1,3 @@ -export 'types/api.dart'; -export 'types/loading.dart'; +export 'types/browser.dart'; +export 'types/loading_state.dart'; export 'types/sorter.dart'; -export 'types/state_global.dart'; diff --git a/lib/core/types/api.dart b/lib/core/types/api.dart deleted file mode 100644 index 47a2a389..00000000 --- a/lib/core/types/api.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract class API { - void logWarning(String methodName, String text); - void logError(String methodName, String text, Object error, StackTrace trace, { bool uploadToSentry = true }); - Future testConnection(); -} diff --git a/lib/core/types/browser.dart b/lib/core/types/browser.dart new file mode 100644 index 00000000..e2929869 --- /dev/null +++ b/lib/core/types/browser.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +part 'browser.g.dart'; + +@HiveType(typeId: 11, adapterName: 'LunaBrowserAdapter') +enum LunaBrowser { + @HiveField(0) + APPLE_SAFARI, + @HiveField(1) + BRAVE_BROWSER, + @HiveField(2) + GOOGLE_CHROME, + @HiveField(3) + MICROSOFT_EDGE, + @HiveField(4) + MOZILLA_FIREFOX, +} + +extension LunaBrowserExtension on LunaBrowser { + String get name { + switch(this) { + case LunaBrowser.APPLE_SAFARI: return 'Apple Safari'; + case LunaBrowser.BRAVE_BROWSER: return 'Brave Browser'; + case LunaBrowser.GOOGLE_CHROME: return 'Google Chrome'; + case LunaBrowser.MICROSOFT_EDGE: return 'Microsoft Edge'; + case LunaBrowser.MOZILLA_FIREFOX: return 'Mozilla Firefox'; + default: return null; + } + } + + String get key { + switch(this) { + case LunaBrowser.APPLE_SAFARI: return 'applesafari'; + case LunaBrowser.BRAVE_BROWSER: return 'bravebrowser'; + case LunaBrowser.GOOGLE_CHROME: return 'googlechrome'; + case LunaBrowser.MICROSOFT_EDGE: return 'microsoftedge'; + case LunaBrowser.MOZILLA_FIREFOX: return 'mozillafirefox'; + default: return null; + } + } + + LunaBrowser fromKey(String key) { + switch(key) { + case 'applesafari': return LunaBrowser.APPLE_SAFARI; + case 'bravebrowser': return LunaBrowser.BRAVE_BROWSER; + case 'googlechrome': return LunaBrowser.GOOGLE_CHROME; + case 'microsoftedge': return LunaBrowser.MICROSOFT_EDGE; + case 'mozillafirefox': return LunaBrowser.MOZILLA_FIREFOX; + default: return null; + } + } + + IconData get icon { + switch(this) { + case LunaBrowser.APPLE_SAFARI: return CustomIcons.safari; + case LunaBrowser.BRAVE_BROWSER: return CustomIcons.bravebrowser; + case LunaBrowser.GOOGLE_CHROME: return CustomIcons.chrome; + case LunaBrowser.MICROSOFT_EDGE: return CustomIcons.microsoftedge; + case LunaBrowser.MOZILLA_FIREFOX: return CustomIcons.firefox; + default: return null; + } + } + + String formatted(String url) { + bool isHttps = url.substring(0, 8) == 'https://'; + switch(this) { + case LunaBrowser.BRAVE_BROWSER: return 'brave://open-url?url=$url'; + case LunaBrowser.GOOGLE_CHROME: return isHttps + ? url.replaceFirst('https://', 'googlechromes://') + : url.replaceFirst('http://', 'googlechrome://'); + case LunaBrowser.MICROSOFT_EDGE: return isHttps + ? url.replaceFirst('https://', 'microsoft-edge-https://') + : url.replaceFirst('http://', 'microsoft-edge-http://'); + case LunaBrowser.MOZILLA_FIREFOX: return 'firefox://open-url?url=$url'; + case LunaBrowser.APPLE_SAFARI: + default: return url; + } + } +} \ No newline at end of file diff --git a/lib/core/extensions/string/links.g.dart b/lib/core/types/browser.g.dart similarity index 59% rename from lib/core/extensions/string/links.g.dart rename to lib/core/types/browser.g.dart index abcb0e08..3adf715a 100644 --- a/lib/core/extensions/string/links.g.dart +++ b/lib/core/types/browser.g.dart @@ -1,49 +1,49 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'links.dart'; +part of 'browser.dart'; // ************************************************************************** // TypeAdapterGenerator // ************************************************************************** -class LSBrowsersAdapter extends TypeAdapter { +class LunaBrowserAdapter extends TypeAdapter { @override final int typeId = 11; @override - LSBrowsers read(BinaryReader reader) { + LunaBrowser read(BinaryReader reader) { switch (reader.readByte()) { case 0: - return LSBrowsers.APPLE_SAFARI; + return LunaBrowser.APPLE_SAFARI; case 1: - return LSBrowsers.BRAVE_BROWSER; + return LunaBrowser.BRAVE_BROWSER; case 2: - return LSBrowsers.GOOGLE_CHROME; + return LunaBrowser.GOOGLE_CHROME; case 3: - return LSBrowsers.MICROSOFT_EDGE; + return LunaBrowser.MICROSOFT_EDGE; case 4: - return LSBrowsers.MOZILLA_FIREFOX; + return LunaBrowser.MOZILLA_FIREFOX; default: return null; } } @override - void write(BinaryWriter writer, LSBrowsers obj) { + void write(BinaryWriter writer, LunaBrowser obj) { switch (obj) { - case LSBrowsers.APPLE_SAFARI: + case LunaBrowser.APPLE_SAFARI: writer.writeByte(0); break; - case LSBrowsers.BRAVE_BROWSER: + case LunaBrowser.BRAVE_BROWSER: writer.writeByte(1); break; - case LSBrowsers.GOOGLE_CHROME: + case LunaBrowser.GOOGLE_CHROME: writer.writeByte(2); break; - case LSBrowsers.MICROSOFT_EDGE: + case LunaBrowser.MICROSOFT_EDGE: writer.writeByte(3); break; - case LSBrowsers.MOZILLA_FIREFOX: + case LunaBrowser.MOZILLA_FIREFOX: writer.writeByte(4); break; } @@ -55,7 +55,7 @@ class LSBrowsersAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is LSBrowsersAdapter && + other is LunaBrowserAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/core/types/loading.dart b/lib/core/types/loading_state.dart similarity index 100% rename from lib/core/types/loading.dart rename to lib/core/types/loading_state.dart diff --git a/lib/core/ui.dart b/lib/core/ui.dart index 494e99f5..7d87a423 100644 --- a/lib/core/ui.dart +++ b/lib/core/ui.dart @@ -17,7 +17,6 @@ export 'ui/messages.dart'; export 'ui/navigation_bar.dart'; export 'ui/network_image.dart'; export 'ui/refresh_indicator.dart'; -export 'ui/rive.dart'; export 'ui/shape.dart'; export 'ui/snackbar.dart'; export 'ui/sticky_header.dart'; diff --git a/lib/core/ui/appbar/appbar_dropdown.dart b/lib/core/ui/appbar/appbar_dropdown.dart index b3fcd6fe..08bb77ad 100644 --- a/lib/core/ui/appbar/appbar_dropdown.dart +++ b/lib/core/ui/appbar/appbar_dropdown.dart @@ -11,7 +11,7 @@ Widget LSAppBarDropdown({ ? LunaAppBar(context: context, title: title, actions: actions, hideLeading: true) : AppBar( title: PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), child: Wrap( @@ -28,7 +28,7 @@ Widget LSAppBarDropdown({ ), ], ), - onSelected: (result) => LunaProfile.changeProfile(context, result), + onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result), itemBuilder: (context) { return >[for(String profile in profiles) PopupMenuItem( value: profile, diff --git a/lib/core/ui/card/card.dart b/lib/core/ui/card/card.dart index 7e17ed2d..1ec82da8 100644 --- a/lib/core/ui/card/card.dart +++ b/lib/core/ui/card/card.dart @@ -19,8 +19,8 @@ class LSCard extends StatelessWidget { @override Widget build(BuildContext context) => ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.THEME_AMOLED.key, - LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key, + LunaDatabaseValue.THEME_AMOLED.key, + LunaDatabaseValue.THEME_AMOLED_BORDER.key, ]), builder: (context, box, widget) => Card( child: Container( @@ -34,7 +34,7 @@ class LSCard extends StatelessWidget { color: color == null ? Theme.of(context).primaryColor : color, - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), diff --git a/lib/core/ui/card/card_tile.dart b/lib/core/ui/card/card_tile.dart index 8f6e01a2..7379a2c6 100644 --- a/lib/core/ui/card/card_tile.dart +++ b/lib/core/ui/card/card_tile.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:lunasea/core/constants.dart'; class LSCardTile extends StatelessWidget { final Widget title; @@ -34,8 +33,8 @@ class LSCardTile extends StatelessWidget { @override Widget build(BuildContext context) => ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.THEME_AMOLED.key, - LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key, + LunaDatabaseValue.THEME_AMOLED.key, + LunaDatabaseValue.THEME_AMOLED_BORDER.key, ]), builder: (context, box, widget) => Card( child: Container( @@ -63,7 +62,7 @@ class LSCardTile extends StatelessWidget { : Constants.UI_CARD_MARGIN : customMargin, elevation: Constants.UI_ELEVATION, - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), color: color == null diff --git a/lib/core/ui/description_block.dart b/lib/core/ui/description_block.dart index 5273fbd6..9c504074 100644 --- a/lib/core/ui/description_block.dart +++ b/lib/core/ui/description_block.dart @@ -62,7 +62,7 @@ class _State extends State { ], ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () => LunaDialogs.textPreview(context, widget.title, widget.description.trim() ?? 'No summary is available.'), + onTap: () => LunaDialogs().textPreview(context, widget.title, widget.description.trim() ?? 'No summary is available.'), ), ); } diff --git a/lib/core/ui/dialogs.dart b/lib/core/ui/dialogs.dart index 4d531aa4..fb2134ef 100644 --- a/lib/core/ui/dialogs.dart +++ b/lib/core/ui/dialogs.dart @@ -210,7 +210,7 @@ abstract class LSDialog { title: LSDialog.title(text: title), content: LSDialog.content(children: content), contentPadding: contentPadding, - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), diff --git a/lib/core/ui/divider.dart b/lib/core/ui/divider.dart index 22cd2cfe..8789f3e3 100644 --- a/lib/core/ui/divider.dart +++ b/lib/core/ui/divider.dart @@ -6,5 +6,7 @@ class LSDivider extends StatelessWidget { Widget build(BuildContext context) => Divider( thickness: 1.0, color: LunaColours.splash, + indent: 60.0, + endIndent: 60.0, ); } diff --git a/lib/core/ui/drawer/drawer.dart b/lib/core/ui/drawer/drawer.dart index 6764817f..01056d52 100644 --- a/lib/core/ui/drawer/drawer.dart +++ b/lib/core/ui/drawer/drawer.dart @@ -4,6 +4,6 @@ import 'package:lunasea/core.dart'; // ignore: non_constant_identifier_names Widget LSDrawer({ @required String page, -}) => LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data +}) => LunaDatabaseValue.DRAWER_GROUP_MODULES.data ? LSDrawerCategories(page: page) : LSDrawerNoCategories(page: page); diff --git a/lib/core/ui/drawer/drawer_categories.dart b/lib/core/ui/drawer/drawer_categories.dart index f83ccb72..a2e9a021 100644 --- a/lib/core/ui/drawer/drawer_categories.dart +++ b/lib/core/ui/drawer/drawer_categories.dart @@ -12,12 +12,12 @@ class LSDrawerCategories extends StatelessWidget { @override Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, lunaBox, widget) { return ValueListenableBuilder( valueListenable: Database.indexersBox.listenable(), builder: (context, indexerBox, widget) { - ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaSeaDatabaseValue.ENABLED_PROFILE.key)); + ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaDatabaseValue.ENABLED_PROFILE.key)); return Drawer( child: ListView( children: _getDrawerEntries(context, profile, (indexerBox as Box).length > 0), @@ -61,14 +61,14 @@ class LSDrawerCategories extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), ), - initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data, + initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data, children: List.generate( Database.currentProfileObject.enabledAutomationModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].name, padLeft: true, ), ), @@ -81,14 +81,14 @@ class LSDrawerCategories extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), ), - initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS.data, + initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_CLIENTS.data, children: List.generate( Database.currentProfileObject.enabledClientModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].name, padLeft: true, ), ), @@ -101,14 +101,14 @@ class LSDrawerCategories extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), ), - initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING.data, + initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_MONITORING.data, children: List.generate( Database.currentProfileObject.enabledMonitoringModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].name, padLeft: true, ), ), @@ -152,9 +152,9 @@ class LSDrawerCategories extends StatelessWidget { @required BuildContext context, }) { return ListTile( - leading: LSIcon(icon: Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].icon), + leading: LSIcon(icon: Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].icon), title: Text( - Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].name, + Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].name, style: TextStyle( color: Colors.white, fontSize: Constants.UI_FONT_SIZE_SUBTITLE, diff --git a/lib/core/ui/drawer/drawer_header.dart b/lib/core/ui/drawer/drawer_header.dart index 41d3253b..f7003061 100644 --- a/lib/core/ui/drawer/drawer_header.dart +++ b/lib/core/ui/drawer/drawer_header.dart @@ -5,19 +5,19 @@ import 'package:lunasea/core.dart'; Widget LSDrawerHeader() => UserAccountsDrawerHeader( accountName: LSTitle(text: Constants.APPLICATION_NAME), accountEmail: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, lunaBox, widget) => ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), builder: (context, profilesBox, widget) => Padding( child: PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), child: Row( mainAxisSize: MainAxisSize.max, children: [ LSSubtitle( - text: LunaSeaDatabaseValue.ENABLED_PROFILE.data, + text: LunaDatabaseValue.ENABLED_PROFILE.data, ), LSIcon( icon: Icons.arrow_drop_down, @@ -26,7 +26,7 @@ Widget LSDrawerHeader() => UserAccountsDrawerHeader( ), ], ), - onSelected: (result) => LunaProfile.changeProfile(context, result), + onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result), itemBuilder: (context) { return >[for(String profile in (profilesBox as Box).keys) PopupMenuItem( value: profile, diff --git a/lib/core/ui/drawer/drawer_nocategories.dart b/lib/core/ui/drawer/drawer_nocategories.dart index 5936751a..d8912db5 100644 --- a/lib/core/ui/drawer/drawer_nocategories.dart +++ b/lib/core/ui/drawer/drawer_nocategories.dart @@ -12,12 +12,12 @@ class LSDrawerNoCategories extends StatelessWidget { @override Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, lunaBox, widget) { return ValueListenableBuilder( valueListenable: Database.indexersBox.listenable(), builder: (context, indexerBox, widget) { - ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaSeaDatabaseValue.ENABLED_PROFILE.key)); + ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaDatabaseValue.ENABLED_PROFILE.key)); return Drawer( child: ListView( children: _getDrawerEntries(context, profile, (indexerBox as Box).length > 0), @@ -57,27 +57,27 @@ class LSDrawerNoCategories extends StatelessWidget { Database.currentProfileObject.enabledAutomationModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].name, ), ), ...List.generate( Database.currentProfileObject.enabledClientModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].name, ), ), ...List.generate( Database.currentProfileObject.enabledMonitoringModules.length, (index) => _buildEntry( context: context, - route: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].route, - icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].icon, - title: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].name, + route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].route, + icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].icon, + title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].name, ), ), ]; @@ -116,9 +116,9 @@ class LSDrawerNoCategories extends StatelessWidget { @required BuildContext context, }) { return ListTile( - leading: LSIcon(icon: Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].icon), + leading: LSIcon(icon: Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].icon), title: Text( - Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].name, + Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].name, style: TextStyle( color: Colors.white, fontSize: Constants.UI_FONT_SIZE_SUBTITLE, diff --git a/lib/core/ui/messages/generic.dart b/lib/core/ui/messages/generic.dart index daa9648f..f22ae918 100644 --- a/lib/core/ui/messages/generic.dart +++ b/lib/core/ui/messages/generic.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/constants.dart'; import 'package:lunasea/core.dart'; class LSGenericMessage extends StatelessWidget { @@ -19,8 +18,8 @@ class LSGenericMessage extends StatelessWidget { @override Widget build(BuildContext context) => ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.THEME_AMOLED.key, - LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key, + LunaDatabaseValue.THEME_AMOLED.key, + LunaDatabaseValue.THEME_AMOLED_BORDER.key, ]), builder: (context, box, child) => Column( mainAxisAlignment: MainAxisAlignment.center, @@ -30,7 +29,7 @@ class LSGenericMessage extends StatelessWidget { child: child, margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), elevation: Constants.UI_ELEVATION, - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), diff --git a/lib/core/ui/messages/not_enabled.dart b/lib/core/ui/messages/not_enabled.dart index 401194da..6e82a243 100644 --- a/lib/core/ui/messages/not_enabled.dart +++ b/lib/core/ui/messages/not_enabled.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/constants.dart'; -import './generic.dart'; +import 'package:lunasea/core.dart'; class LSNotEnabled extends StatelessWidget { final String service; diff --git a/lib/core/ui/messages/typewriter.dart b/lib/core/ui/messages/typewriter.dart index dbb3b462..69ffe2ef 100644 --- a/lib/core/ui/messages/typewriter.dart +++ b/lib/core/ui/messages/typewriter.dart @@ -45,8 +45,8 @@ class _State extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) => ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.THEME_AMOLED.key, - LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key, + LunaDatabaseValue.THEME_AMOLED.key, + LunaDatabaseValue.THEME_AMOLED_BORDER.key, ]), builder: (context, box, child) => Column( mainAxisAlignment: MainAxisAlignment.center, @@ -56,7 +56,7 @@ class _State extends State with TickerProviderStateMixin { child: child, margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), elevation: Constants.UI_ELEVATION, - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), diff --git a/lib/core/ui/rive.dart b/lib/core/ui/rive.dart deleted file mode 100644 index 4ceaa9a5..00000000 --- a/lib/core/ui/rive.dart +++ /dev/null @@ -1,8 +0,0 @@ -class RiveAnimations { - RiveAnimations._(); - - static const CODER = { - 'path': 'assets/rive/coding.flr', - 'animation': 'coding', - }; -} diff --git a/lib/core/ui/snackbar.dart b/lib/core/ui/snackbar.dart index ff024d34..d54a8aa5 100644 --- a/lib/core/ui/snackbar.dart +++ b/lib/core/ui/snackbar.dart @@ -14,7 +14,7 @@ const _LEFT_PADDING = 8.0; Future LSSnackBar({ @required BuildContext context, @required String title, - String message = Constants.CHECK_LOGS_MESSAGE, + String message = LunaLogger.CHECK_LOGS_MESSAGE, Duration duration, SNACKBAR_TYPE type = SNACKBAR_TYPE.info, bool showButton = false, @@ -44,7 +44,7 @@ Future LSSnackBar({ horizontalDismissDirection: HorizontalDismissDirection.horizontal, margin: EdgeInsets.all(8.0), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - borderColor: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? Colors.white12 : Colors.transparent, child: FlashBar( diff --git a/lib/core/ui/text/header.dart b/lib/core/ui/text/header.dart index ef11e77a..ab376e34 100644 --- a/lib/core/ui/text/header.dart +++ b/lib/core/ui/text/header.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:lunasea/core/ui/colors.dart'; class LSHeader extends StatelessWidget { final String text; diff --git a/lib/core/uuid.dart b/lib/core/uuid.dart index b2af6d3e..cc315930 100644 --- a/lib/core/uuid.dart +++ b/lib/core/uuid.dart @@ -1,6 +1,11 @@ import 'package:uuid/uuid.dart'; class LunaUUID { - static final _generator = Uuid(); - static String get uuid => _generator.v4(); + static final Uuid _generator = Uuid(); + + /// Generate a new v4 UUID with the base configuration. + String get uuid => _generator.v4(); + + /// Return the [Uuid] instance. + Uuid get generator => _generator; } diff --git a/lib/main.dart b/lib/main.dart index ee4af4f9..def5c412 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,30 +2,47 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; /// LunaSea Entry Point: Initialize & Run Application -/// Runs app in guarded zone to attempt to capture fatal (crashing) errors -void main() async { - await _init(); - runZonedGuarded( - () => runApp(LunaBIOS()), - (Object error, StackTrace stack) => LunaLogger.fatal(error, stack), - ); -} +/// +/// Runs app in Sentry guarded zone to attempt to capture fatal (crashing) errors +Future main() async => await SentryFlutter.init( + (options) => options + ..dsn = LunaLogger.SENTRY_DSN, + appRunner: () async { + await _init(); + runApp(LunaBIOS()); + } +); +/// Initializes LunaSea before running the BIOS Widget. +/// +/// Sets up (in order): +/// - System UI Overlay Styling +/// - Logger +/// - Network +/// - Image Cache +/// - Router +/// - Firebase +/// - IAPs +/// - Database +/// +/// Does not call [WidgetsFlutterBinding.ensureInitialized()] as that is called during Sentry's initialization. +/// If Sentry is removed, you should call that method to prevent black-screen flashing on launch. Future _init() async { - WidgetsFlutterBinding.ensureInitialized(); - //Set system UI style (navbar, statusbar) + //Set system UI overlay style (navbar, statusbar) SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( systemNavigationBarColor: Colors.black, systemNavigationBarDividerColor: Colors.black, statusBarColor: Colors.transparent, )); //LunaSea initialization - LunaNetworking.initialize(); LunaLogger.initialize(); + LunaNetworking.initialize(); LunaImageCache.initialize(); LunaRouter.intialize(); + await LunaFirebase.initialize(); await LunaInAppPurchases.initialize(); await Database.initialize(); } @@ -39,17 +56,20 @@ class LunaBIOS extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaProvider.providers( + Widget build(BuildContext context) => LunaState.providers( child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.THEME_AMOLED.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.THEME_AMOLED.key]), builder: (context, box, _) { return MaterialApp( - navigatorKey: LunaBIOS.navigatorKey, routes: LunaRouter.routes, onGenerateRoute: LunaRouter.router.generator, - darkTheme: LunaTheme.darkTheme, - theme: LunaTheme.darkTheme, + darkTheme: LunaTheme.activeTheme(), + theme: LunaTheme.activeTheme(), title: Constants.APPLICATION_NAME, + navigatorKey: LunaBIOS.navigatorKey, + navigatorObservers: [ + SentryNavigatorObserver(), + ], ); } ), diff --git a/lib/modules/home/core/adapters/calendar_starting_day.dart b/lib/modules/home/core/adapters/calendar_starting_day.dart index a8d425f3..3dce584e 100644 --- a/lib/modules/home/core/adapters/calendar_starting_day.dart +++ b/lib/modules/home/core/adapters/calendar_starting_day.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/core/database/database.dart'; +import 'package:lunasea/core.dart'; import 'package:table_calendar/table_calendar.dart'; part 'calendar_starting_day.g.dart'; @@ -31,7 +31,7 @@ extension CalendarStartingDayExtension on CalendarStartingDay { case CalendarStartingDay.FRIDAY: return StartingDayOfWeek.friday; case CalendarStartingDay.SATURDAY: return StartingDayOfWeek.saturday; case CalendarStartingDay.SUNDAY: return StartingDayOfWeek.sunday; - default: return StartingDayOfWeek.monday; + default: return null; } } @@ -44,7 +44,33 @@ extension CalendarStartingDayExtension on CalendarStartingDay { case CalendarStartingDay.FRIDAY: return 'Friday'; case CalendarStartingDay.SATURDAY: return 'Saturday'; case CalendarStartingDay.SUNDAY: return 'Sunday'; - default: return 'Unknown Starting Day'; + default: return null; + } + } + + String get key { + switch(this) { + case CalendarStartingDay.MONDAY: return 'mon'; + case CalendarStartingDay.TUESDAY: return 'tue'; + case CalendarStartingDay.WEDNESDAY: return 'wed'; + case CalendarStartingDay.THURSDAY: return 'thu'; + case CalendarStartingDay.FRIDAY: return 'fri'; + case CalendarStartingDay.SATURDAY: return 'sat'; + case CalendarStartingDay.SUNDAY: return 'sun'; + default: return null; + } + } + + CalendarStartingDay fromKey(String key) { + switch(key) { + case 'mon': return CalendarStartingDay.MONDAY; + case 'tue': return CalendarStartingDay.TUESDAY; + case 'wed': return CalendarStartingDay.WEDNESDAY; + case 'thu': return CalendarStartingDay.THURSDAY; + case 'fri': return CalendarStartingDay.FRIDAY; + case 'sat': return CalendarStartingDay.SATURDAY; + case 'sun': return CalendarStartingDay.SUNDAY; + default: return null; } } } \ No newline at end of file diff --git a/lib/modules/home/core/adapters/calendar_starting_size.dart b/lib/modules/home/core/adapters/calendar_starting_size.dart index cc2ce3bb..55125c12 100644 --- a/lib/modules/home/core/adapters/calendar_starting_size.dart +++ b/lib/modules/home/core/adapters/calendar_starting_size.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/database/database.dart'; +import 'package:lunasea/core.dart'; import 'package:table_calendar/table_calendar.dart'; part 'calendar_starting_size.g.dart'; @@ -17,28 +17,46 @@ enum CalendarStartingSize { extension CalendarStartingSizeExtension on CalendarStartingSize { CalendarFormat get data { switch(this) { + case CalendarStartingSize.ONE_WEEK: return CalendarFormat.week; case CalendarStartingSize.TWO_WEEKS: return CalendarFormat.twoWeeks; case CalendarStartingSize.ONE_MONTH: return CalendarFormat.month; - case CalendarStartingSize.ONE_WEEK: - default: return CalendarFormat.week; + default: return null; } } String get name { switch(this) { + case CalendarStartingSize.ONE_WEEK: return 'One Week'; case CalendarStartingSize.TWO_WEEKS: return 'Two Weeks'; case CalendarStartingSize.ONE_MONTH: return 'One Month'; - case CalendarStartingSize.ONE_WEEK: - default: return 'One Week'; + default: return null; + } + } + + String get key { + switch(this) { + case CalendarStartingSize.ONE_WEEK: return 'oneweek'; + case CalendarStartingSize.TWO_WEEKS: return 'twoweeks'; + case CalendarStartingSize.ONE_MONTH: return 'onemonth'; + default: return null; } } IconData get icon { switch(this) { + case CalendarStartingSize.ONE_WEEK: return Icons.photo_size_select_small; case CalendarStartingSize.TWO_WEEKS: return Icons.photo_size_select_large; case CalendarStartingSize.ONE_MONTH: return Icons.photo_size_select_actual; - case CalendarStartingSize.ONE_WEEK: - default: return Icons.photo_size_select_small; + default: return null; + } + } + + CalendarStartingSize fromKey(String key) { + switch(key) { + case 'oneweek': return CalendarStartingSize.ONE_WEEK; + case 'twoweeks': return CalendarStartingSize.TWO_WEEKS; + case 'onemonth': return CalendarStartingSize.ONE_MONTH; + default: return null; } } } diff --git a/lib/modules/home/core/adapters/calendar_starting_type.dart b/lib/modules/home/core/adapters/calendar_starting_type.dart index 1946a36d..61fd5a6c 100644 --- a/lib/modules/home/core/adapters/calendar_starting_type.dart +++ b/lib/modules/home/core/adapters/calendar_starting_type.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:lunasea/core/database/database.dart'; part 'calendar_starting_type.g.dart'; @@ -13,27 +12,35 @@ enum CalendarStartingType { } extension CalendarStartingTypeExtension on CalendarStartingType { - String get data { - switch(this) { - case CalendarStartingType.SCHEDULE: return 'schedule'; - case CalendarStartingType.CALENDAR: - default: return 'calendar'; - } - } - String get name { switch(this) { case CalendarStartingType.SCHEDULE: return 'Schedule'; - case CalendarStartingType.CALENDAR: - default: return 'Calendar'; + case CalendarStartingType.CALENDAR: return 'Calendar'; + default: return null; + } + } + + String get key { + switch(this) { + case CalendarStartingType.SCHEDULE: return 'schedule'; + case CalendarStartingType.CALENDAR: return 'calendar'; + default: return null; } } IconData get icon { switch(this) { case CalendarStartingType.SCHEDULE: return Icons.calendar_view_day; - case CalendarStartingType.CALENDAR: - default: return CustomIcons.calendar; + case CalendarStartingType.CALENDAR: return CustomIcons.calendar; + default: return null; + } + } + + CalendarStartingType fromKey(String key) { + switch(key) { + case 'schedule': return CalendarStartingType.SCHEDULE; + case 'calendar': return CalendarStartingType.CALENDAR; + default: return null; } } } \ No newline at end of file diff --git a/lib/modules/home/core/api/api.dart b/lib/modules/home/core/api/api.dart index 9c07de90..73411c10 100644 --- a/lib/modules/home/core/api/api.dart +++ b/lib/modules/home/core/api/api.dart @@ -4,7 +4,7 @@ import 'package:intl/intl.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/home.dart'; -class CalendarAPI extends API { +class CalendarAPI { final Map lidarr; final Map radarr; final Map sonarr; @@ -23,22 +23,7 @@ class CalendarAPI extends API { ); } - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'package:lunasea/core/api/calendar/api.dart', - methodName, - 'Home: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); - - void logWarning(String methodName, String text) => LunaLogger.warning( - 'package:lunasea/core/api/calendar/api.dart', - methodName, - 'Home: $text', - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Home: $text', error, trace); Future testConnection() async => true; @@ -80,7 +65,7 @@ class CalendarAPI extends API { Response response = await _client.get('calendar'); if(response.data.length > 0) { for(var entry in response.data) { - DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.lsDateTime_floor(); + DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.lunaFloor; if(date != null) { List day = map[date] ?? []; day.add(CalendarLidarrData( @@ -94,10 +79,8 @@ class CalendarAPI extends API { } } } - } on DioError catch (error, stack) { - logError('_getLidarrUpcoming', 'Failed to fetch Lidarr upcoming content', error, stack, uploadToSentry: false); } catch (error, stack) { - logError('_getLidarrUpcoming', 'Failed to fetch Lidarr upcoming content', error, stack); + logError('Failed to fetch Lidarr upcoming content', error, stack); } return; } @@ -121,7 +104,7 @@ class CalendarAPI extends API { Response response = await _client.get('calendar'); if(response.data.length > 0) { for(var entry in response.data) { - DateTime date = DateTime.tryParse(entry['physicalRelease'] ?? '')?.toLocal()?.lsDateTime_floor(); + DateTime date = DateTime.tryParse(entry['physicalRelease'] ?? '')?.toLocal()?.lunaFloor; if(date != null) { List day = map[date] ?? []; day.add(CalendarRadarrData( @@ -136,10 +119,8 @@ class CalendarAPI extends API { } } } - } on DioError catch (error, stack) { - logError('_getRadarrUpcoming', 'Failed to fetch Radarr upcoming content', error, stack, uploadToSentry: false); } catch (error, stack) { - logError('_getRadarrUpcoming', 'Failed to fetch Radarr upcoming content', error, stack); + logError('Failed to fetch Radarr upcoming content', error, stack); } return; } @@ -163,7 +144,7 @@ class CalendarAPI extends API { Response response = await _client.get('calendar'); if(response.data.length > 0) { for(var entry in response.data) { - DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.lsDateTime_floor(); + DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.lunaFloor; if(date != null) { List day = map[date] ?? []; day.add(CalendarSonarrData( @@ -181,10 +162,8 @@ class CalendarAPI extends API { } } } - } on DioError catch (error, stack) { - logError('_getSonarrUpcoming', 'Failed to fetch Sonarr upcoming content', error, stack, uploadToSentry: false); } catch (error, stack) { - logError('_getSonarrUpcoming', 'Failed to fetch Sonarr upcoming content', error, stack); + logError('Failed to fetch Sonarr upcoming content', error, stack); } return; } diff --git a/lib/modules/home/core/api/data/lidarr.dart b/lib/modules/home/core/api/data/lidarr.dart index cba6a775..dfa2796e 100644 --- a/lib/modules/home/core/api/data/lidarr.dart +++ b/lib/modules/home/core/api/data/lidarr.dart @@ -72,7 +72,7 @@ class CalendarLidarrData extends CalendarData { Future trailingOnPress(BuildContext context) async { await LidarrAPI.from(Database.currentProfileObject).searchAlbums([id]) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: albumTitle)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } @override diff --git a/lib/modules/home/core/api/data/radarr.dart b/lib/modules/home/core/api/data/radarr.dart index 05aef824..d40ed69f 100644 --- a/lib/modules/home/core/api/data/radarr.dart +++ b/lib/modules/home/core/api/data/radarr.dart @@ -19,9 +19,7 @@ class CalendarRadarrData extends CalendarData { @required this.runtime, }): super(id, title); - String get runtimeString { - return runtime.lsTime_runtimeString(dot: true); - } + String get runtimeString => '\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}'; TextSpan get subtitle => TextSpan( style: TextStyle( @@ -75,7 +73,7 @@ class CalendarRadarrData extends CalendarData { Future trailingOnPress(BuildContext context) async { await RadarrAPI.from(Database.currentProfileObject).automaticSearchMovie(id) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } @override diff --git a/lib/modules/home/core/api/data/sonarr.dart b/lib/modules/home/core/api/data/sonarr.dart index f223a08e..e6a80d38 100644 --- a/lib/modules/home/core/api/data/sonarr.dart +++ b/lib/modules/home/core/api/data/sonarr.dart @@ -93,7 +93,7 @@ class CalendarSonarrData extends CalendarData { String get airTimeString { if(airTimeObject != null) { - return LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + return LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat.Hm().format(airTimeObject) : DateFormat('hh:mm\na').format(airTimeObject); } @@ -110,13 +110,10 @@ class CalendarSonarrData extends CalendarData { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'CalendarSonarrData', - 'trailingOnPress', + LunaLogger().error( 'Failed to search for episode: $id', error, stack, - uploadToSentry: !(error is DioError), ); LSSnackBar( context: context, diff --git a/lib/modules/home/core/constants.dart b/lib/modules/home/core/constants.dart index 7b22cd25..e3a3c12f 100644 --- a/lib/modules/home/core/constants.dart +++ b/lib/modules/home/core/constants.dart @@ -6,18 +6,21 @@ class HomeConstants { static const MODULE_KEY = 'home'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Home', description: 'Home', settingsDescription: 'Configure the Home Screen', + helpMessage: '', icon: CustomIcons.home, route: '/', color: Color(LunaColours.ACCENT_COLOR), + website: '', + github: '', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/home/core/database.dart b/lib/modules/home/core/database.dart index 93efbdf7..a9082df7 100644 --- a/lib/modules/home/core/database.dart +++ b/lib/modules/home/core/database.dart @@ -1,15 +1,75 @@ +import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; -import 'package:lunasea/core/database/database.dart'; -import './adapters.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/home/core.dart'; -class HomeDatabase { - HomeDatabase._(); - - static void registerAdapters() { +class HomeDatabase extends LunaModuleDatabase { + @override + void registerAdapters() { Hive.registerAdapter(CalendarStartingDayAdapter()); Hive.registerAdapter(CalendarStartingSizeAdapter()); Hive.registerAdapter(CalendarStartingTypeAdapter()); } + + @override + Map export() { + Map data = {}; + for(HomeDatabaseValue value in HomeDatabaseValue.values) { + switch(value) { + // Non-primitive values + case HomeDatabaseValue.CALENDAR_STARTING_DAY: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_DAY.data as CalendarStartingDay).key; break; + case HomeDatabaseValue.CALENDAR_STARTING_SIZE: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_SIZE.data as CalendarStartingSize).key; break; + case HomeDatabaseValue.CALENDAR_STARTING_TYPE: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_TYPE.data as CalendarStartingType).key; break; + // Primitive values + case HomeDatabaseValue.NAVIGATION_INDEX: + case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: + case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: + case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: + case HomeDatabaseValue.MODULES_BRAND_COLOURS: + case HomeDatabaseValue.CALENDAR_DAYS_PAST: + case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + HomeDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Non-primitive values + case HomeDatabaseValue.CALENDAR_STARTING_DAY: value.put(CalendarStartingDay.MONDAY.fromKey(config[key])); break; + case HomeDatabaseValue.CALENDAR_STARTING_SIZE: value.put(CalendarStartingSize.ONE_WEEK.fromKey(config[key])); break; + case HomeDatabaseValue.CALENDAR_STARTING_TYPE: value.put(CalendarStartingType.CALENDAR.fromKey(config[key])); break; + // Primitive values + case HomeDatabaseValue.NAVIGATION_INDEX: + case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: + case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: + case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: + case HomeDatabaseValue.MODULES_BRAND_COLOURS: + case HomeDatabaseValue.CALENDAR_DAYS_PAST: + case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: value.put(config[key]); break; + } + } + } + + @override + HomeDatabaseValue valueFromKey(String key) { + switch(key) { + case 'HOME_NAVIGATION_INDEX': return HomeDatabaseValue.NAVIGATION_INDEX; + case 'HOME_CALENDAR_STARTING_DAY': return HomeDatabaseValue.CALENDAR_STARTING_DAY; + case 'HOME_CALENDAR_STARTING_SIZE': return HomeDatabaseValue.CALENDAR_STARTING_SIZE; + case 'HOME_CALENDAR_STARTING_TYPE': return HomeDatabaseValue.CALENDAR_STARTING_TYPE; + case 'HOME_CALENDAR_ENABLE_LIDARR': return HomeDatabaseValue.CALENDAR_ENABLE_LIDARR; + case 'HOME_CALENDAR_ENABLE_RADARR': return HomeDatabaseValue.CALENDAR_ENABLE_RADARR; + case 'HOME_CALENDAR_ENABLE_SONARR': return HomeDatabaseValue.CALENDAR_ENABLE_SONARR; + case 'HOME_MODULES_BRAND_COLOURS': return HomeDatabaseValue.MODULES_BRAND_COLOURS; + case 'HOME_CALENDAR_DAYS_PAST': return HomeDatabaseValue.CALENDAR_DAYS_PAST; + case 'HOME_CALENDAR_DAYS_FUTURE': return HomeDatabaseValue.CALENDAR_DAYS_FUTURE; + default: return null; + } + } } enum HomeDatabaseValue { @@ -43,21 +103,41 @@ extension HomeDatabaseValueExtension on HomeDatabaseValue { } dynamic get data { - final _box = Database.lunaSeaBox; + final box = Database.lunaSeaBox; switch(this) { - case HomeDatabaseValue.NAVIGATION_INDEX: return _box.get(this.key, defaultValue: 0); - case HomeDatabaseValue.CALENDAR_STARTING_DAY: return _box.get(this.key, defaultValue: CalendarStartingDay.MONDAY); - case HomeDatabaseValue.CALENDAR_STARTING_SIZE: return _box.get(this.key, defaultValue: CalendarStartingSize.ONE_WEEK); - case HomeDatabaseValue.CALENDAR_STARTING_TYPE: return _box.get(this.key, defaultValue: CalendarStartingType.CALENDAR); - case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: return _box.get(this.key, defaultValue: true); - case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: return _box.get(this.key, defaultValue: true); - case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: return _box.get(this.key, defaultValue: true); - case HomeDatabaseValue.MODULES_BRAND_COLOURS: return _box.get(this.key, defaultValue: false); - case HomeDatabaseValue.CALENDAR_DAYS_PAST: return _box.get(this.key, defaultValue: 14); - case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: return _box.get(this.key, defaultValue: 14); + case HomeDatabaseValue.NAVIGATION_INDEX: return box.get(this.key, defaultValue: 0); + case HomeDatabaseValue.CALENDAR_STARTING_DAY: return box.get(this.key, defaultValue: CalendarStartingDay.MONDAY); + case HomeDatabaseValue.CALENDAR_STARTING_SIZE: return box.get(this.key, defaultValue: CalendarStartingSize.ONE_WEEK); + case HomeDatabaseValue.CALENDAR_STARTING_TYPE: return box.get(this.key, defaultValue: CalendarStartingType.CALENDAR); + case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: return box.get(this.key, defaultValue: true); + case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: return box.get(this.key, defaultValue: true); + case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: return box.get(this.key, defaultValue: true); + case HomeDatabaseValue.MODULES_BRAND_COLOURS: return box.get(this.key, defaultValue: false); + case HomeDatabaseValue.CALENDAR_DAYS_PAST: return box.get(this.key, defaultValue: 14); + case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: return box.get(this.key, defaultValue: 14); } throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case HomeDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_STARTING_DAY: if(value.runtimeType == CalendarStartingDay) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_STARTING_SIZE: if(value.runtimeType == CalendarStartingSize) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_STARTING_TYPE: if(value.runtimeType == CalendarStartingType) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: if(value.runtimeType == bool) box.put(this.key, value); return; + case HomeDatabaseValue.MODULES_BRAND_COLOURS: if(value.runtimeType == bool) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_DAYS_PAST: if(value.runtimeType == int) box.put(this.key, value); return; + case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('HomeDatabaseValueExtension', 'put', 'Attempted to enter data for invalid HomeDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/home/core/state/global.dart b/lib/modules/home/core/state/global.dart index edd5e420..dbb4b27e 100644 --- a/lib/modules/home/core/state/global.dart +++ b/lib/modules/home/core/state/global.dart @@ -1,7 +1,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/home.dart'; -class HomeState extends LunaGlobalState { +class HomeState extends LunaModuleState { @override void reset() {} diff --git a/lib/modules/home/routes/calendar.dart b/lib/modules/home/routes/calendar.dart index d8f1bd7f..d825bcf1 100644 --- a/lib/modules/home/routes/calendar.dart +++ b/lib/modules/home/routes/calendar.dart @@ -31,7 +31,7 @@ class _State extends State with AutomaticKeepAliveClientMixin { } Future _refresh() async { - _today = DateTime.now().lsDateTime_floor(); + _today = DateTime.now().lunaFloor; setState(() { _future = widget.api.getUpcoming(_today); }); diff --git a/lib/modules/home/routes/home.dart b/lib/modules/home/routes/home.dart index 6b04c5a8..748a0807 100644 --- a/lib/modules/home/routes/home.dart +++ b/lib/modules/home/routes/home.dart @@ -26,7 +26,7 @@ class _State extends State { Widget build(BuildContext context) => WillPopScope( onWillPop: _willPopScope, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, lunaBox, widget) { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(keys: [Database.currentProfile]), diff --git a/lib/modules/home/routes/quick_access.dart b/lib/modules/home/routes/quick_access.dart index b7b35712..493d096c 100644 --- a/lib/modules/home/routes/quick_access.dart +++ b/lib/modules/home/routes/quick_access.dart @@ -34,22 +34,22 @@ class _State extends State with AutomaticKeepAliveClientMixin { itemBuilder: (context, index) { //Check if indexer search should be included at the top if(index == 0 && _hasIndexers) return HomeSummaryTile( - title: Constants.MODULE_MAP[SearchConstants.MODULE_KEY].name, - subtitle: Constants.MODULE_MAP[SearchConstants.MODULE_KEY].description, - icon: Constants.MODULE_MAP[SearchConstants.MODULE_KEY].icon, + title: Constants.MODULE_METADATA[SearchConstants.MODULE_KEY].name, + subtitle: Constants.MODULE_METADATA[SearchConstants.MODULE_KEY].description, + icon: Constants.MODULE_METADATA[SearchConstants.MODULE_KEY].icon, index: index, - route: Constants.MODULE_MAP[SearchConstants.MODULE_KEY].route, - color: Constants.MODULE_MAP[SearchConstants.MODULE_KEY].color, + route: Constants.MODULE_METADATA[SearchConstants.MODULE_KEY].route, + color: Constants.MODULE_METADATA[SearchConstants.MODULE_KEY].color, ); if(index == _serviceCount-1) return HomeSummaryTile( - title: Constants.MODULE_MAP[SettingsConstants.MODULE_KEY].name, - subtitle: Constants.MODULE_MAP[SettingsConstants.MODULE_KEY].description, - icon: Constants.MODULE_MAP[SettingsConstants.MODULE_KEY].icon, + title: Constants.MODULE_METADATA[SettingsConstants.MODULE_KEY].name, + subtitle: Constants.MODULE_METADATA[SettingsConstants.MODULE_KEY].description, + icon: Constants.MODULE_METADATA[SettingsConstants.MODULE_KEY].icon, index: index, - route: Constants.MODULE_MAP[SettingsConstants.MODULE_KEY].route, - color: Constants.MODULE_MAP[SettingsConstants.MODULE_KEY].color, + route: Constants.MODULE_METADATA[SettingsConstants.MODULE_KEY].route, + color: Constants.MODULE_METADATA[SettingsConstants.MODULE_KEY].color, ); - LunaModuleMap data = Constants.MODULE_MAP[widget.profile.enabledModules[_hasIndexers ? index-1 : index]]; + LunaModuleMetadata data = Constants.MODULE_METADATA[widget.profile.enabledModules[_hasIndexers ? index-1 : index]]; if(data != null) return HomeSummaryTile( title: data.name, subtitle: data.description, diff --git a/lib/modules/lidarr/core/api/api.dart b/lib/modules/lidarr/core/api/api.dart index e539fbe7..aee02d80 100644 --- a/lib/modules/lidarr/core/api/api.dart +++ b/lib/modules/lidarr/core/api/api.dart @@ -3,7 +3,7 @@ import 'package:dio/dio.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; -class LidarrAPI extends API { +class LidarrAPI { final Map _values; final Dio _dio; @@ -29,22 +29,7 @@ class LidarrAPI extends API { ); } - void logWarning(String methodName, String text) => LunaLogger.warning( - 'package:lunasea/core/api/lidarr/api.dart', - methodName, - 'Lidarr: $text', - ); - - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'package:lunasea/core/api/lidarr/api.dart', - methodName, - 'Lidarr: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Lidarr: $text', error, trace); bool get enabled => _values['enabled']; String get host => _values['host']; @@ -82,10 +67,10 @@ class LidarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getAllArtists', 'Failed to fetch artists', error, stack, uploadToSentry: false); + logError('Failed to fetch artists', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAllArtists', 'Failed to fetch artists', error, stack); + logError('Failed to fetch artists', error, stack); return Future.error(error); } } @@ -99,10 +84,10 @@ class LidarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getAllArtistIDs', 'Failed to fetch artist IDs', error, stack, uploadToSentry: false); + logError('Failed to fetch artist IDs', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAllArtistIDs', 'Failed to fetch artist IDs', error, stack); + logError('Failed to fetch artist IDs', error, stack); return Future.error(error); } } @@ -118,10 +103,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('refreshArtist', 'Failed to refresh artist ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to refresh artist ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('refreshArtist', 'Failed to refresh artist ($artistID)', error, stack); + logError('Failed to refresh artist ($artistID)', error, stack); return Future.error(error); } } @@ -152,10 +137,10 @@ class LidarrAPI extends API { sizeOnDisk: response.data['statistics'] != null ? response.data['statistics']['sizeOnDisk'] ?? 0 : 0, ); } on DioError catch (error, stack) { - logError('getArtist', 'Failed to fetch artist ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to fetch artist ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getArtist', 'Failed to fetch artist ($artistID)', error, stack); + logError('Failed to fetch artist ($artistID)', error, stack); return Future.error(error); } } @@ -170,10 +155,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('removeArtist', 'Failed to remove artist ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to remove artist ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('removeArtist', 'Failed to remove artist ($artistID)', error, stack); + logError('Failed to remove artist ($artistID)', error, stack); return Future.error(error); } } @@ -194,10 +179,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('editArtist', 'Failed to edit artist ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to edit artist ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('editArtist', 'Failed to edit artist ($artistID)', error, stack); + logError('Failed to edit artist ($artistID)', error, stack); return Future.error(error); } } @@ -228,10 +213,10 @@ class LidarrAPI extends API { }); return entries; } on DioError catch (error, stack) { - logError('getArtistAlbums', 'Failed to fetch albums ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to fetch albums ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getArtistAlbums', 'Failed to fetch albums ($artistID)', error, stack); + logError('Failed to fetch albums ($artistID)', error, stack); return Future.error(error); } } @@ -253,10 +238,10 @@ class LidarrAPI extends API { releaseDate: response.data[0]['releaseDate'] ?? '', ); } on DioError catch (error, stack) { - logError('getAlbum', 'Failed to fetch album ($albumID)', error, stack, uploadToSentry: false); + logError('Failed to fetch album ($albumID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAlbum', 'Failed to fetch album ($albumID)', error, stack); + logError('Failed to fetch album ($albumID)', error, stack); return Future.error(error); } } @@ -282,10 +267,10 @@ class LidarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getAlbumTracks', 'Failed to fetch album tracks ($albumID)', error, stack, uploadToSentry: false); + logError('Failed to fetch album tracks ($albumID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAlbumTracks', 'Failed to fetch album tracks ($albumID)', error, stack); + logError('Failed to fetch album tracks ($albumID)', error, stack); return Future.error(error); } } @@ -302,10 +287,10 @@ class LidarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack, uploadToSentry: false); + logError('Failed to fetch quality profiles', error, stack); return Future.error(error); } catch (error, stack) { - logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack); + logError('Failed to fetch quality profiles', error, stack); return Future.error(error); } } @@ -322,10 +307,10 @@ class LidarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getMetadataProfiles', 'Failed to fetch metadata profiles', error, stack, uploadToSentry: false); + logError('Failed to fetch metadata profiles', error, stack); return Future.error(error); } catch (error, stack) { - logError('getMetadataProfiles', 'Failed to fetch metadata profiles', error, stack); + logError('Failed to fetch metadata profiles', error, stack); return Future.error(error); } } @@ -343,10 +328,10 @@ class LidarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getRootFolders', 'Failed to fetch root folders', error, stack, uploadToSentry: false); + logError('Failed to fetch root folders', error, stack); return Future.error(error); } catch (error, stack) { - logError('getRootFolders', 'Failed to fetch root folders', error, stack); + logError('Failed to fetch root folders', error, stack); return Future.error(error); } } @@ -436,10 +421,10 @@ class LidarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack, uploadToSentry: false); + logError('Failed to fetch history', error, stack); return Future.error(error); } catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack); + logError('Failed to fetch history', error, stack); return Future.error(error); } } @@ -468,10 +453,10 @@ class LidarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getMissing', 'Failed to fetch missing albums', error, stack, uploadToSentry: false); + logError('Failed to fetch missing albums', error, stack); return Future.error(error); } catch (error, stack) { - logError('getMissing', 'Failed to fetch missing albums', error, stack); + logError('Failed to fetch missing albums', error, stack); return Future.error(error); } } @@ -487,10 +472,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('searchAlbums', 'Failed to search for albums (${albums.toString()})', error, stack, uploadToSentry: false); + logError('Failed to search for albums (${albums.toString()})', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchAlbums', 'Failed to search for albums (${albums.toString()})', error, stack); + logError('Failed to search for albums (${albums.toString()})', error, stack); return Future.error(error); } } @@ -507,10 +492,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('searchAllMissing', 'Failed to search for all missing albums', error, stack, uploadToSentry: false); + logError('Failed to search for all missing albums', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchAllMissing', 'Failed to search for all missing albums', error, stack); + logError('Failed to search for all missing albums', error, stack); return Future.error(error); } } @@ -525,10 +510,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('updateLibrary', 'Failed to update library', error, stack, uploadToSentry: false); + logError('Failed to update library', error, stack); return Future.error(error); } catch (error, stack) { - logError('updateLibrary', 'Failed to update library', error, stack); + logError('Failed to update library', error, stack); return Future.error(error); } } @@ -543,10 +528,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack, uploadToSentry: false); + logError('Failed to trigger RSS sync', error, stack); return Future.error(error); } catch (error, stack) { - logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack); + logError('Failed to trigger RSS sync', error, stack); return Future.error(error); } } @@ -561,10 +546,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('triggerBackup', 'Failed to backup database', error, stack, uploadToSentry: false); + logError('Failed to backup database', error, stack); return Future.error(error); } catch (error, stack) { - logError('triggerBackup', 'Failed to backup database', error, stack); + logError('Failed to backup database', error, stack); return Future.error(error); } } @@ -580,10 +565,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('toggleArtistMonitored', 'Failed to toggle artist monitored status ($artistID)', error, stack, uploadToSentry: false); + logError('Failed to toggle artist monitored status ($artistID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('toggleArtistMonitored', 'Failed to toggle artist monitored status ($artistID)', error, stack); + logError('Failed to toggle artist monitored status ($artistID)', error, stack); return Future.error(error); } } @@ -604,10 +589,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('toggleAlbumMonitored', 'Failed to toggle album monitored status ($albumID)', error, stack, uploadToSentry: false); + logError('Failed to toggle album monitored status ($albumID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('toggleAlbumMonitored', 'Failed to toggle album monitored status ($albumID)', error, stack); + logError('Failed to toggle album monitored status ($albumID)', error, stack); return Future.error(error); } } @@ -635,10 +620,10 @@ class LidarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('searchArtists', 'Failed to search ($search)', error, stack, uploadToSentry: false); + logError('Failed to search ($search)', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchArtists', 'Failed to search ($search)', error, stack); + logError('Failed to search ($search)', error, stack); return Future.error(error); } } @@ -662,10 +647,10 @@ class LidarrAPI extends API { ); return response.data['id']; } on DioError catch (error, stack) { - logError('addArtist', 'Failed to add artist (${entry.title})', error, stack, uploadToSentry: false); + logError('Failed to add artist (${entry.title})', error, stack); return Future.error(error); } catch (error, stack) { - logError('addArtist', 'Failed to add artist (${entry.title})', error, stack); + logError('Failed to add artist (${entry.title})', error, stack); return Future.error(error); } } @@ -699,10 +684,10 @@ class LidarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getReleases', 'Failed to fetch releases ($albumID)', error, stack, uploadToSentry: false); + logError('Failed to fetch releases ($albumID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getReleases', 'Failed to fetch releases ($albumID)', error, stack); + logError('Failed to fetch releases ($albumID)', error, stack); return Future.error(error); } } @@ -718,10 +703,10 @@ class LidarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('downloadRelease', 'Failed to download release ($guid)', error, stack, uploadToSentry: false); + logError('Failed to download release ($guid)', error, stack); return Future.error(error); } catch (error, stack) { - logError('downloadRelease', 'Failed to download release ($guid)', error, stack); + logError('Failed to download release ($guid)', error, stack); return Future.error(error); } } diff --git a/lib/modules/lidarr/core/api/data/album.dart b/lib/modules/lidarr/core/api/data/album.dart index 5b99a410..eecfe559 100644 --- a/lib/modules/lidarr/core/api/data/album.dart +++ b/lib/modules/lidarr/core/api/data/album.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/database.dart'; +import 'package:lunasea/core.dart'; import 'package:intl/intl.dart'; class LidarrAlbumData { diff --git a/lib/modules/lidarr/core/api/data/catalogue.dart b/lib/modules/lidarr/core/api/data/catalogue.dart index d859ef66..5e421c10 100644 --- a/lib/modules/lidarr/core/api/data/catalogue.dart +++ b/lib/modules/lidarr/core/api/data/catalogue.dart @@ -71,7 +71,7 @@ class LidarrCatalogueData { case LidarrCatalogueSorting.type: return artistType; case LidarrCatalogueSorting.dateAdded: return dateAdded; case LidarrCatalogueSorting.size: - case LidarrCatalogueSorting.alphabetical: return sizeOnDisk?.lsBytes_BytesToString(); + case LidarrCatalogueSorting.alphabetical: return sizeOnDisk?.lunaBytesToString(); } return ''; } diff --git a/lib/modules/lidarr/core/constants.dart b/lib/modules/lidarr/core/constants.dart index a452f989..ef0bad49 100644 --- a/lib/modules/lidarr/core/constants.dart +++ b/lib/modules/lidarr/core/constants.dart @@ -6,13 +6,16 @@ class LidarrConstants { static const MODULE_KEY = 'lidarr'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Lidarr', description: 'Manage Music', settingsDescription: 'Configure Lidarr', + helpMessage: 'Lidarr is a music collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new tracks from your favorite artists and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.', icon: CustomIcons.music, route: '/lidarr', color: Color(0xFF159552), + website: 'https://lidarr.audio', + github: 'https://github.com/Lidarr/Lidarr', ); static const Map EVENT_TYPE_MESSAGES = { @@ -28,6 +31,6 @@ class LidarrConstants { //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } \ No newline at end of file diff --git a/lib/modules/lidarr/core/database.dart b/lib/modules/lidarr/core/database.dart index 3e68b2f0..614f8dff 100644 --- a/lib/modules/lidarr/core/database.dart +++ b/lib/modules/lidarr/core/database.dart @@ -1,15 +1,63 @@ +import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart' hide LidarrDatabaseValueExtension; -class LidarrDatabase { - LidarrDatabase._(); - - static void registerAdapters() { +class LidarrDatabase extends LunaModuleDatabase { + @override + void registerAdapters() { Hive.registerAdapter(LidarrQualityProfileAdapter()); Hive.registerAdapter(LidarrMetadataProfileAdapter()); Hive.registerAdapter(LidarrRootFolderAdapter()); } + + @override + Map export() { + Map data = {}; + for(LidarrDatabaseValue value in LidarrDatabaseValue.values) { + switch(value) { + // Primitive values + case LidarrDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + // Non-exported values + case LidarrDatabaseValue.ADD_MONITORED: + case LidarrDatabaseValue.ADD_ALBUM_FOLDERS: + case LidarrDatabaseValue.ADD_QUALITY_PROFILE: + case LidarrDatabaseValue.ADD_METADATA_PROFILE: + case LidarrDatabaseValue.ADD_ROOT_FOLDER: break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + LidarrDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case LidarrDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + // Non-imported values + case LidarrDatabaseValue.ADD_MONITORED: + case LidarrDatabaseValue.ADD_ALBUM_FOLDERS: + case LidarrDatabaseValue.ADD_QUALITY_PROFILE: + case LidarrDatabaseValue.ADD_METADATA_PROFILE: + case LidarrDatabaseValue.ADD_ROOT_FOLDER: break; + } + } + } + + @override + LidarrDatabaseValue valueFromKey(String key) { + switch(key) { + case 'LIDARR_NAVIGATION_INDEX': return LidarrDatabaseValue.NAVIGATION_INDEX; + case 'LIDARR_ADD_MONITORED': return LidarrDatabaseValue.ADD_MONITORED; + case 'LIDARR_ADD_ALBUM_FOLDERS': return LidarrDatabaseValue.ADD_ALBUM_FOLDERS; + case 'LIDARR_ADD_QUALITY_PROFILE': return LidarrDatabaseValue.ADD_QUALITY_PROFILE; + case 'LIDARR_ADD_METADATA_PROFILE': return LidarrDatabaseValue.ADD_METADATA_PROFILE; + case 'LIDARR_ADD_ROOT_FOLDER': return LidarrDatabaseValue.ADD_ROOT_FOLDER; + default: return null; + } + } } enum LidarrDatabaseValue { @@ -47,5 +95,21 @@ extension LidarrDatabaseValueExtension on LidarrDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case LidarrDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + case LidarrDatabaseValue.ADD_MONITORED: if(value.runtimeType == bool) box.put(this.key, value); return; + case LidarrDatabaseValue.ADD_ALBUM_FOLDERS: if(value.runtimeType == bool) box.put(this.key, value); return; + case LidarrDatabaseValue.ADD_QUALITY_PROFILE: if(value.runtimeType == LidarrQualityProfile) box.put(this.key, value); return; + case LidarrDatabaseValue.ADD_METADATA_PROFILE: if(value.runtimeType == LidarrMetadataProfile) box.put(this.key, value); return; + case LidarrDatabaseValue.ADD_ROOT_FOLDER: if(value.runtimeType == LidarrRootFolder) box.put(this.key, value); return; + } + LunaLogger().warning('LidarrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid LidarrDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/lidarr/core/dialogs.dart b/lib/modules/lidarr/core/dialogs.dart index 809801d3..5766edf0 100644 --- a/lib/modules/lidarr/core/dialogs.dart +++ b/lib/modules/lidarr/core/dialogs.dart @@ -193,7 +193,7 @@ class LidarrDialogs { text: folders[index].path, subtitle: LSDialog.richText( children: [ - LSDialog.bolded(text: folders[index].freeSpace.lsBytes_BytesToString()), + LSDialog.bolded(text: folders[index].freeSpace.lunaBytesToString()), ], ), icon: Icons.folder, diff --git a/lib/modules/lidarr/core/sorting/catalogue.dart b/lib/modules/lidarr/core/sorting/catalogue.dart index 20b4a624..ac665855 100644 --- a/lib/modules/lidarr/core/sorting/catalogue.dart +++ b/lib/modules/lidarr/core/sorting/catalogue.dart @@ -12,8 +12,6 @@ enum LidarrCatalogueSorting { } extension LidarrCatalogueSortingExtension on LidarrCatalogueSorting { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case LidarrCatalogueSorting.alphabetical: return 'abc'; @@ -43,7 +41,7 @@ extension LidarrCatalogueSortingExtension on LidarrCatalogueSorting { List sort( List data, bool ascending - ) => _sorter.byType(data, this, ascending); + ) => _Sorter().byType(data, this, ascending); } class _Sorter extends LunaSorter { diff --git a/lib/modules/lidarr/core/sorting/releases.dart b/lib/modules/lidarr/core/sorting/releases.dart index 6f70594a..0969ce39 100644 --- a/lib/modules/lidarr/core/sorting/releases.dart +++ b/lib/modules/lidarr/core/sorting/releases.dart @@ -11,8 +11,6 @@ enum LidarrReleasesSorting { } extension LidarrReleasesSortingExtension on LidarrReleasesSorting { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case LidarrReleasesSorting.age: return 'age'; @@ -40,7 +38,7 @@ extension LidarrReleasesSortingExtension on LidarrReleasesSorting { List sort( List data, bool ascending - ) => _sorter.byType(data, this, ascending); + ) => _Sorter().byType(data, this, ascending); } class _Sorter extends LunaSorter { diff --git a/lib/modules/lidarr/core/state/global.dart b/lib/modules/lidarr/core/state/global.dart index c44529eb..afdad816 100644 --- a/lib/modules/lidarr/core/state/global.dart +++ b/lib/modules/lidarr/core/state/global.dart @@ -1,7 +1,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; -class LidarrState extends LunaGlobalState { +class LidarrState extends LunaModuleState { LidarrState() { reset(); } diff --git a/lib/modules/lidarr/routes/add_details.dart b/lib/modules/lidarr/routes/add_details.dart index f5fb3722..761f7fc4 100644 --- a/lib/modules/lidarr/routes/add_details.dart +++ b/lib/modules/lidarr/routes/add_details.dart @@ -113,7 +113,7 @@ class _State extends State { title: 'No Discogs Page Available', message: 'No Discogs URL is available', ) - : _arguments.data.discogsLink.lsLinks_OpenLink() + : _arguments.data.discogsLink.lunaOpenGenericLink() ) ], ); @@ -253,6 +253,6 @@ class _State extends State { search: search, ) .then((id) => Navigator.of(context).pop(['artist_added', _arguments.data.title, id])) - .catchError((_) => LSSnackBar(context: context, title: search ? 'Failed to Add Artist (With Search)' : 'Failed to Add Artist', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: search ? 'Failed to Add Artist (With Search)' : 'Failed to Add Artist', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } diff --git a/lib/modules/lidarr/routes/details_album.dart b/lib/modules/lidarr/routes/details_album.dart index 1af3147c..091e2c72 100644 --- a/lib/modules/lidarr/routes/details_album.dart +++ b/lib/modules/lidarr/routes/details_album.dart @@ -113,7 +113,7 @@ class _State extends State { LidarrAPI _api = LidarrAPI.from(Database.currentProfileObject); _api.searchAlbums([_arguments.albumID]) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: _arguments.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _manualSearch() async => Navigator.of(context).pushNamed( diff --git a/lib/modules/lidarr/routes/edit_artist.dart b/lib/modules/lidarr/routes/edit_artist.dart index 89e6982d..dda82db9 100644 --- a/lib/modules/lidarr/routes/edit_artist.dart +++ b/lib/modules/lidarr/routes/edit_artist.dart @@ -158,7 +158,7 @@ class _State extends State { ); Future _changePath() async { - List _values = await LunaDialogs.editText(context, 'Artist Path', prefill: _path); + List _values = await LunaDialogs().editText(context, 'Artist Path', prefill: _path); if(_values[0] && mounted) setState(() => _path = _values[1]); } @@ -192,6 +192,6 @@ class _State extends State { _arguments.entry.albumFolders = _albumFolders; Navigator.of(context).pop([true]); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } diff --git a/lib/modules/lidarr/routes/lidarr.dart b/lib/modules/lidarr/routes/lidarr.dart index 6c002af0..e2836bda 100644 --- a/lib/modules/lidarr/routes/lidarr.dart +++ b/lib/modules/lidarr/routes/lidarr.dart @@ -40,7 +40,7 @@ class _State extends State { } }, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, box, widget) { if(_profileState != Database.currentProfileObject.toString()) _refreshProfile(); return Scaffold( @@ -75,13 +75,13 @@ class _State extends State { Widget get _body => PageView( controller: _pageController, - children: _api.enabled ? _tabs : List.generate(_tabs.length, (_) => LSNotEnabled(LidarrConstants.MODULE_MAP.name)), + children: _api.enabled ? _tabs : List.generate(_tabs.length, (_) => LSNotEnabled(LidarrConstants.MODULE_METADATA.name)), onPageChanged: _onPageChanged, ); Widget get _appBar => LSAppBarDropdown( context: context, - title: LidarrConstants.MODULE_MAP.name, + title: LidarrConstants.MODULE_METADATA.name, profiles: Database.profilesBox.keys.fold([], (value, element) { if((Database.profilesBox.get(element) as ProfileHiveObject).lidarrEnabled) value.add(element); @@ -124,34 +124,34 @@ class _State extends State { _refreshAllPages(); break; } - default: LunaLogger.warning('Lidarr', '_enterAddArtist', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('Lidarr', '_enterAddArtist', 'Unknown Case: ${result[0]}'); } } Future _handlePopup() async { List values = await LidarrDialogs.globalSettings(context); if(values[0]) switch(values[1]) { - case 'web_gui': await _api.host?.toString()?.lsLinks_OpenLink(); break; + case 'web_gui': await _api.host?.toString()?.lunaOpenGenericLink(); break; case 'update_library': await _api.updateLibrary() .then((_) => LSSnackBar(context: context, title: 'Updating Library...', message: 'Updating your library in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update Library', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update Library', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'rss_sync': await _api.triggerRssSync() .then((_) => LSSnackBar(context: context, title: 'Running RSS Sync...', message: 'Running RSS sync in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Run RSS Sync', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Run RSS Sync', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'backup': await _api.triggerBackup() .then((_) => LSSnackBar(context: context, title: 'Backing Up Database...', message: 'Backing up database in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Backup Database', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Backup Database', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'missing_search': { List values = await LidarrDialogs.searchAllMissing(context); if(values[0]) await _api.searchAllMissing() .then((_) => LSSnackBar(context: context, title: 'Searching...', message: 'Search for all missing albums')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; } - default: LunaLogger.warning('Lidarr', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('Lidarr', '_handlePopup', 'Unknown Case: ${values[1]}'); } } diff --git a/lib/modules/lidarr/widgets/add_search_result_tile.dart b/lib/modules/lidarr/widgets/add_search_result_tile.dart index 117b983f..deb889a5 100644 --- a/lib/modules/lidarr/widgets/add_search_result_tile.dart +++ b/lib/modules/lidarr/widgets/add_search_result_tile.dart @@ -42,7 +42,7 @@ class LidarrAddSearchResultTile extends StatelessWidget { ); if(result != null) switch(result[0]) { case 'artist_added': Navigator.of(context).pop(result); break; - default: LunaLogger.warning('LidarrAddSearchResultTile', '_enterDetails', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('LidarrAddSearchResultTile', '_enterDetails', 'Unknown Case: ${result[0]}'); } } } diff --git a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart index 568762b7..9ef7e0a6 100644 --- a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart +++ b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, model, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/lidarr/widgets/catalogue_tile.dart b/lib/modules/lidarr/widgets/catalogue_tile.dart index 95c9daac..5bada024 100644 --- a/lib/modules/lidarr/widgets/catalogue_tile.dart +++ b/lib/modules/lidarr/widgets/catalogue_tile.dart @@ -69,7 +69,7 @@ class _State extends State { LSSnackBar( context: context, title: widget.data.monitored ? 'Failed to Stop Monitoring' : 'Failed to Monitor', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); }); @@ -94,7 +94,7 @@ class _State extends State { widget.refresh(); break; } - default: LunaLogger.warning('LidarrCatalogueTile', '_enterArtist', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('LidarrCatalogueTile', '_enterArtist', 'Unknown Case: ${result[0]}'); } } @@ -104,7 +104,7 @@ class _State extends State { case 'refresh_artist': _refreshArtist(); break; case 'edit_artist': _enterEditArtist(); break; case 'remove_artist': _removeArtist(); break; - default: LunaLogger.warning('LidarrCatalogueTile', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); + default: LunaLogger().warning('LidarrCatalogueTile', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); } } @@ -125,7 +125,7 @@ class _State extends State { final _api = LidarrAPI.from(Database.currentProfileObject); await _api.refreshArtist(widget.data.artistID) .then((_) => LSSnackBar(context: context, title: 'Refreshing...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _removeArtist() async { @@ -133,14 +133,14 @@ class _State extends State { List values = await LidarrDialogs.deleteArtist(context); if(values[0]) { if(values[1]) { - values = await LunaDialogs.deleteCatalogueWithFiles(context, widget.data.title); + values = await LunaDialogs().deleteCatalogueWithFiles(context, widget.data.title); if(values[0]) { await _api.removeArtist(widget.data.artistID, deleteFiles: true) .then((_) { LSSnackBar(context: context, title: 'Removed (With Data)', message: widget.data.title, type: SNACKBAR_TYPE.success); widget.refresh(); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove (With Data)', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove (With Data)', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } else { await _api.removeArtist(widget.data.artistID, deleteFiles: false) @@ -148,7 +148,7 @@ class _State extends State { LSSnackBar(context: context, title: 'Removed', message: widget.data.title, type: SNACKBAR_TYPE.success); widget.refresh(); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } } diff --git a/lib/modules/lidarr/widgets/details_album_tile.dart b/lib/modules/lidarr/widgets/details_album_tile.dart index b6243289..eb67fb82 100644 --- a/lib/modules/lidarr/widgets/details_album_tile.dart +++ b/lib/modules/lidarr/widgets/details_album_tile.dart @@ -90,7 +90,7 @@ class _State extends State { widget.refreshState(); LSSnackBar(context: context, title: widget.data.monitored ? 'Monitoring' : 'No Longer Monitoring', message: widget.data.title, type: SNACKBAR_TYPE.success); }) - .catchError((_) => LSSnackBar(context: context, title: widget.data.monitored ? 'Failed to Stop Monitoring' : 'Failed to Monitor', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: widget.data.monitored ? 'Failed to Stop Monitoring' : 'Failed to Monitor', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _enterAlbum() async => await Navigator.of(context).pushNamed( diff --git a/lib/modules/lidarr/widgets/details_album_track_tile.dart b/lib/modules/lidarr/widgets/details_album_track_tile.dart index cbc0486f..5d9cdfda 100644 --- a/lib/modules/lidarr/widgets/details_album_track_tile.dart +++ b/lib/modules/lidarr/widgets/details_album_track_tile.dart @@ -30,7 +30,7 @@ class _State extends State { ), children: [ TextSpan( - text: '${widget?.data?.duration?.lsTime_timestampString(divisor: 1000)}\n', + text: '${widget?.data?.duration?.lunaTimestamp(divisor: 1000)}\n', ), widget?.data?.file(widget.monitored), ] diff --git a/lib/modules/lidarr/widgets/details_edit_button.dart b/lib/modules/lidarr/widgets/details_edit_button.dart index f8bfd37f..c2ce0e09 100644 --- a/lib/modules/lidarr/widgets/details_edit_button.dart +++ b/lib/modules/lidarr/widgets/details_edit_button.dart @@ -30,7 +30,7 @@ class _State extends State { case 'refresh_artist': _refreshArtist(context); break; case 'edit_artist': _enterEditArtist(context); break; case 'remove_artist': _removeArtist(context); break; - default: LunaLogger.warning('LidarrDetailsEditButton', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); + default: LunaLogger().warning('LidarrDetailsEditButton', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); } } @@ -51,7 +51,7 @@ class _State extends State { final _api = LidarrAPI.from(Database.currentProfileObject); await _api.refreshArtist(widget.data.artistID) .then((_) => LSSnackBar(context: context, title: 'Refreshing...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _removeArtist(BuildContext context) async { @@ -59,16 +59,16 @@ class _State extends State { List values = await LidarrDialogs.deleteArtist(context); if(values[0]) { if(values[1]) { - values = await LunaDialogs.deleteCatalogueWithFiles(context, widget.data.title); + values = await LunaDialogs().deleteCatalogueWithFiles(context, widget.data.title); if(values[0]) { await _api.removeArtist(widget.data.artistID, deleteFiles: true) .then((_) => widget.remove(true)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove (With Data)', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove (With Data)', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } else { await _api.removeArtist(widget.data.artistID) .then((_) => widget.remove(false)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } } diff --git a/lib/modules/lidarr/widgets/details_overview.dart b/lib/modules/lidarr/widgets/details_overview.dart index 94132359..387510e3 100644 --- a/lib/modules/lidarr/widgets/details_overview.dart +++ b/lib/modules/lidarr/widgets/details_overview.dart @@ -36,7 +36,7 @@ class _State extends State with AutomaticKeepAliveClientM LSCardTile( title: LSTitle(text: 'Artist Path', centerText: true), subtitle: LSSubtitle(text: widget?.data?.path ?? 'Unknown', centerText: true), - onTap: () => LunaDialogs.textPreview(context, 'Artist Path', widget?.data?.path ?? 'Unknown'), + onTap: () => LunaDialogs().textPreview(context, 'Artist Path', widget?.data?.path ?? 'Unknown'), ), LSContainerRow( children: [ @@ -87,7 +87,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(16.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.bandsintownURI?.lsLinks_OpenLink(), + onTap: () async => await widget.data?.bandsintownURI?.lunaOpenGenericLink(), ), reducedMargin: true, ), @@ -103,7 +103,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(19.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.discogsURI?.lsLinks_OpenLink(), + onTap: () async => await widget.data?.discogsURI?.lunaOpenGenericLink(), ), reducedMargin: true, ), @@ -119,7 +119,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(20.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.lastfmURI?.lsLinks_OpenLink(), + onTap: () async => await widget.data?.lastfmURI?.lunaOpenGenericLink(), ), reducedMargin: true, ), diff --git a/lib/modules/lidarr/widgets/missing_tile.dart b/lib/modules/lidarr/widgets/missing_tile.dart index dab30567..c4bb3833 100644 --- a/lib/modules/lidarr/widgets/missing_tile.dart +++ b/lib/modules/lidarr/widgets/missing_tile.dart @@ -65,7 +65,7 @@ class _State extends State { final _api = LidarrAPI.from(Database.currentProfileObject); await _api.searchAlbums([widget.entry.albumID]) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: widget.entry.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _interactiveSearch() async => Navigator.of(context).pushNamed( diff --git a/lib/modules/lidarr/widgets/releases_sorting_button.dart b/lib/modules/lidarr/widgets/releases_sorting_button.dart index 56946845..2052fbbb 100644 --- a/lib/modules/lidarr/widgets/releases_sorting_button.dart +++ b/lib/modules/lidarr/widgets/releases_sorting_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, model, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/lidarr/widgets/search_result_tile.dart b/lib/modules/lidarr/widgets/search_result_tile.dart index c4c04083..ff6a99d7 100644 --- a/lib/modules/lidarr/widgets/search_result_tile.dart +++ b/lib/modules/lidarr/widgets/search_result_tile.dart @@ -36,7 +36,7 @@ class LidarrSearchResultTile extends StatelessWidget { runSpacing: 10.0, children: [ LSTextHighlighted( - text: data.protocol.lsLanguage_Capitalize(), + text: data.protocol.lunaCapitalizeFirstLetters(), bgColor: data.isTorrent ? LunaColours.purple : LunaColours.blue, @@ -61,9 +61,9 @@ class LidarrSearchResultTile extends StatelessWidget { ), ), TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'), - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}\t•\t'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}\t•\t'), TextSpan(text: '${data.indexer}\n'), - TextSpan(text: '${data.ageHours.lsTime_releaseAgeString() ?? 'Unknown'}'), + TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}'), ], ), ), @@ -122,7 +122,7 @@ class LidarrSearchResultTile extends StatelessWidget { : LunaColours.blue, fontWeight: FontWeight.bold, ), - text: data.protocol.lsLanguage_Capitalize(), + text: data.protocol.lunaCapitalizeFirstLetters(), ), if(data.isTorrent) TextSpan( text: ' (${data.seeders}/${data.leechers})', @@ -132,9 +132,9 @@ class LidarrSearchResultTile extends StatelessWidget { ), ), TextSpan(text: '\t•\t${data.indexer}\t•\t'), - TextSpan(text: '${data.ageHours.lsTime_releaseAgeString() ?? 'Unknown'}\n'), + TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}\n'), TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'), - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}'), ], ), ), @@ -172,7 +172,7 @@ class LidarrSearchResultTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Start Downloading', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -182,6 +182,6 @@ class LidarrSearchResultTile extends StatelessWidget { for(var i=0; i _values; final Dio _dio; @@ -35,17 +35,7 @@ class NZBGetAPI extends API { ); } - void logWarning(String methodName, String text) => LunaLogger.warning('NZBGetAPI', methodName, 'NZBGet: $text'); - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'NZBGetAPI', - methodName, - 'NZBGet: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('NZBGet: $text', error, trace); bool get enabled => _values['enabled']; String get host => _values['host']; @@ -79,11 +69,8 @@ class NZBGetAPI extends API { remainingLow: response.data['result']['RemainingSizeLo'] ?? 0, speedlimit: response.data['result']['DownloadLimit'] ?? 0, ); - } on DioError catch (error, stack) { - logError('getStatus', 'Failed to fetch status', error, stack, uploadToSentry: false); - return Future.error(error, stack); } catch (error, stack) { - logError('getStatus', 'Failed to fetch status', error, stack); + logError('Failed to fetch status', error, stack); return Future.error(error, stack); } } @@ -106,10 +93,10 @@ class NZBGetAPI extends API { scanPaused: response.data['result']['ScanPaused'] ?? true, ); } on DioError catch (error, stack) { - logError('getStatistics', 'Failed to fetch statistics', error, stack, uploadToSentry: false); + logError('Failed to fetch statistics', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('getStatistics', 'Failed to fetch statistics', error, stack); + logError('Failed to fetch statistics', error, stack); return Future.error(error, stack); } } @@ -137,10 +124,10 @@ class NZBGetAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getLogs', 'Failed to fetch logs ($amount)', error, stack, uploadToSentry: false); + logError('Failed to fetch logs ($amount)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('getLogs', 'Failed to fetch logs ($amount)', error, stack); + logError('Failed to fetch logs ($amount)', error, stack); return Future.error(error, stack); } } @@ -170,10 +157,10 @@ class NZBGetAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getQueue', 'Failed to fetch queue', error, stack, uploadToSentry: false); + logError('Failed to fetch queue', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('getQueue', 'Failed to fetch queue', error, stack); + logError('Failed to fetch queue', error, stack); return Future.error(error, stack); } } @@ -204,10 +191,10 @@ class NZBGetAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack, uploadToSentry: false); + logError('Failed to fetch history', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack); + logError('Failed to fetch history', error, stack); return Future.error(error, stack); } } @@ -221,10 +208,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('pauseQueue', 'Failed to pause queue', error, stack, uploadToSentry: false); + logError('Failed to pause queue', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('pauseQueue', 'Failed to pause queue', error, stack); + logError('Failed to pause queue', error, stack); return Future.error(error, stack); } } @@ -242,10 +229,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('pauseQueueFor', 'Failed to pause queue for $minutes minutes', error, stack, uploadToSentry: false); + logError('Failed to pause queue for $minutes minutes', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('pauseQueueFor', 'Failed to pause queue for $minutes minutes', error, stack); + logError('Failed to pause queue for $minutes minutes', error, stack); return Future.error(error, stack); } } @@ -259,10 +246,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('resumeQueue', 'Failed to resume queue', error, stack, uploadToSentry: false); + logError('Failed to resume queue', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('resumeQueue', 'Failed to resume queue', error, stack); + logError('Failed to resume queue', error, stack); return Future.error(error, stack); } } @@ -283,10 +270,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('moveQueue', 'Failed to move queue entry ($id, $offset)', error, stack, uploadToSentry: false); + logError('Failed to move queue entry ($id, $offset)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('moveQueue', 'Failed to move queue entry ($id, $offset)', error, stack); + logError('Failed to move queue entry ($id, $offset)', error, stack); return Future.error(error, stack); } } @@ -307,10 +294,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('pauseSingleJob', 'Failed to pause job ($id)', error, stack, uploadToSentry: false); + logError('Failed to pause job ($id)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('pauseSingleJob', 'Failed to pause job ($id)', error, stack); + logError('Failed to pause job ($id)', error, stack); return Future.error(error, stack); } } @@ -331,10 +318,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('resumeSingleJob', 'Failed to resume job ($id)', error, stack, uploadToSentry: false); + logError('Failed to resume job ($id)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('resumeSingleJob', 'Failed to resume job ($id)', error, stack); + logError('Failed to resume job ($id)', error, stack); return Future.error(error, stack); } } @@ -355,10 +342,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('deleteJob', 'Failed to delete job ($id)', error, stack, uploadToSentry: false); + logError('Failed to delete job ($id)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('deleteJob', 'Failed to delete job ($id)', error, stack); + logError('Failed to delete job ($id)', error, stack); return Future.error(error, stack); } } @@ -379,10 +366,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('renameJob', 'Failed to rename job ($id, $name)', error, stack, uploadToSentry: false); + logError('Failed to rename job ($id, $name)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('renameJob', 'Failed to rename job ($id, $name)', error, stack); + logError('Failed to rename job ($id, $name)', error, stack); return Future.error(error, stack); } } @@ -403,10 +390,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('setJobPriority', 'Failed to set job priority ($id, ${priority.name})', error, stack, uploadToSentry: false); + logError('Failed to set job priority ($id, ${priority.name})', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('setJobPriority', 'Failed to set job priority ($id, ${priority.name})', error, stack); + logError('Failed to set job priority ($id, ${priority.name})', error, stack); return Future.error(error, stack); } } @@ -427,10 +414,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('setJobCategory', 'Failed to set job category ($id, ${category.name})', error, stack, uploadToSentry: false); + logError('Failed to set job category ($id, ${category.name})', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('setJobCategory', 'Failed to set job category ($id, ${category.name})', error, stack); + logError('Failed to set job category ($id, ${category.name})', error, stack); return Future.error(error, stack); } } @@ -451,10 +438,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('setJobPassword', 'Failed to set job password ($id, $password)', error, stack, uploadToSentry: false); + logError('Failed to set job password ($id, $password)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('setJobPassword', 'Failed to set job password ($id, $password)', error, stack); + logError('Failed to set job password ($id, $password)', error, stack); return Future.error(error, stack); } } @@ -475,10 +462,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('deleteHistoryEntry', 'Failed to delete history entry ($id, $hide)', error, stack, uploadToSentry: false); + logError('Failed to delete history entry ($id, $hide)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('deleteHistoryEntry', 'Failed to delete history entry ($id, $hide)', error, stack); + logError('Failed to delete history entry ($id, $hide)', error, stack); return Future.error(error, stack); } } @@ -499,10 +486,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('retryHistoryEntry', 'Failed to retry history entry ($id)', error, stack, uploadToSentry: false); + logError('Failed to retry history entry ($id)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('retryHistoryEntry', 'Failed to retry history entry ($id)', error, stack); + logError('Failed to retry history entry ($id)', error, stack); return Future.error(error, stack); } } @@ -526,10 +513,10 @@ class NZBGetAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getCategories', 'Failed to fetch categories', error, stack, uploadToSentry: false); + logError('Failed to fetch categories', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('getCategories', 'Failed to fetch categories', error, stack); + logError('Failed to fetch categories', error, stack); return Future.error(error, stack); } } @@ -550,10 +537,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('sortQueue', 'Failed to sort queue (${sort.name})', error, stack, uploadToSentry: false); + logError('Failed to sort queue (${sort.name})', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('sortQueue', 'Failed to sort queue (${sort.name})', error, stack); + logError('Failed to sort queue (${sort.name})', error, stack); return Future.error(error, stack); } } @@ -581,10 +568,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] > 0) return true; throw(Error()); } on DioError catch (error, stack) { - logError('uploadURL', 'Failed to add NZB by URL ($url)', error, stack, uploadToSentry: false); + logError('Failed to add NZB by URL ($url)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('uploadURL', 'Failed to add NZB by URL ($url)', error, stack); + logError('Failed to add NZB by URL ($url)', error, stack); return Future.error(error, stack); } } @@ -613,10 +600,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] > 0) return true; throw(Error()); } on DioError catch (error, stack) { - logError('uploadFile', 'Failed to add NZB by file ($name)', error, stack, uploadToSentry: false); + logError('Failed to add NZB by file ($name)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('uploadFile', 'Failed to add NZB by file ($name)', error, stack); + logError('Failed to add NZB by file ($name)', error, stack); return Future.error(error, stack); } } @@ -633,10 +620,10 @@ class NZBGetAPI extends API { if(response.data['result'] != null && response.data['result'] == true) return true; throw(Error()); } on DioError catch (error, stack) { - logError('setSpeedLimit', 'Failed to set speed limit ($limit)', error, stack, uploadToSentry: false); + logError('Failed to set speed limit ($limit)', error, stack); return Future.error(error, stack); } catch (error, stack) { - logError('setSpeedLimit', 'Failed to set speed limit ($limit)', error, stack); + logError('Failed to set speed limit ($limit)', error, stack); return Future.error(error, stack); } } diff --git a/lib/modules/nzbget/core/api/data/history.dart b/lib/modules/nzbget/core/api/data/history.dart index 18aa5c21..eb0f4c4f 100644 --- a/lib/modules/nzbget/core/api/data/history.dart +++ b/lib/modules/nzbget/core/api/data/history.dart @@ -36,12 +36,12 @@ class NZBGetHistoryData { return '0.00 MB/s'; } else { int speed = (downloaded/downloadTime).floor(); - return '${speed?.lsBytes_BytesToString()}/s'; + return '${speed?.lunaBytesToString()}/s'; } } String get sizeReadable { - return downloaded?.lsBytes_BytesToString(); + return downloaded?.lunaBytesToString(); } DateTime get timestampObject { @@ -49,7 +49,7 @@ class NZBGetHistoryData { } String get completeTime { - return now.lsDateTime_ageString(timestampObject); + return timestampObject?.lunaAge ?? 'Unknown'; } String get healthString { diff --git a/lib/modules/nzbget/core/api/data/log.dart b/lib/modules/nzbget/core/api/data/log.dart index 70bf748d..2e26c958 100644 --- a/lib/modules/nzbget/core/api/data/log.dart +++ b/lib/modules/nzbget/core/api/data/log.dart @@ -16,7 +16,7 @@ class NZBGetLogData { }); String get timestamp { - return LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + return LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat('MMMM dd, y - HH:mm').format(DateTime.fromMillisecondsSinceEpoch(time*1000)) : DateFormat('MMMM dd, y - hh:mm:ss a').format(DateTime.fromMillisecondsSinceEpoch(time*1000)); } diff --git a/lib/modules/nzbget/core/api/data/queue.dart b/lib/modules/nzbget/core/api/data/queue.dart index 929820e2..90fe8829 100644 --- a/lib/modules/nzbget/core/api/data/queue.dart +++ b/lib/modules/nzbget/core/api/data/queue.dart @@ -41,7 +41,7 @@ class NZBGetQueueData { } String get timestamp { - return (queueSeconds+remainingTime).lsTime_timestampString() == '0:00:00' ? '―' : (queueSeconds+remainingTime).lsTime_timestampString(); + return (queueSeconds+remainingTime).lunaTimestamp() == '0:00:00' ? '―' : (queueSeconds+remainingTime).lunaTimestamp(); } String get statusString { diff --git a/lib/modules/nzbget/core/api/data/statistics.dart b/lib/modules/nzbget/core/api/data/statistics.dart index 2dd2c695..9f805d1f 100644 --- a/lib/modules/nzbget/core/api/data/statistics.dart +++ b/lib/modules/nzbget/core/api/data/statistics.dart @@ -33,19 +33,19 @@ class NZBGetStatisticsData { } String get freeSpaceString { - return freeSpace?.lsBytes_BytesToString(decimals: 1); + return freeSpace?.lunaBytesToString(decimals: 1); } String get downloadedString { - return downloaded?.lsBytes_BytesToString(decimals: 1); + return downloaded?.lunaBytesToString(decimals: 1); } String get uptimeString { - return uptimeSeconds.lsTime_durationString(); + return uptimeSeconds.lunaDuration(); } String get speedLimitString { - String limit = speedLimit?.lsBytes_BytesToString(); + String limit = speedLimit?.lunaBytesToString(); return limit == '0.00 B' ? 'No Limit Set' : '$limit/s'; } } diff --git a/lib/modules/nzbget/core/api/data/status.dart b/lib/modules/nzbget/core/api/data/status.dart index 0a533683..35f04d2d 100644 --- a/lib/modules/nzbget/core/api/data/status.dart +++ b/lib/modules/nzbget/core/api/data/status.dart @@ -21,18 +21,18 @@ class NZBGetStatusData { } String get currentSpeed { - return '${speed?.lsBytes_BytesToString(decimals: 1)}/s'; + return '${speed?.lunaBytesToString(decimals: 1)}/s'; } String get remainingString { - return remaining?.lsBytes_BytesToString(decimals: 1); + return remaining?.lunaBytesToString(decimals: 1); } String get timeLeft { - return speed == 0 ? '0:00:00' : ((remaining/speed).floor()).lsTime_timestampString(); + return speed == 0 ? '0:00:00' : ((remaining/speed).floor()).lunaTimestamp(); } String get speedlimitString { - return speedlimit == 0 ? 'Unlimited' : speedlimit?.lsBytes_BytesToString(decimals: 0); + return speedlimit == 0 ? 'Unlimited' : speedlimit?.lunaBytesToString(decimals: 0); } } diff --git a/lib/modules/nzbget/core/constants.dart b/lib/modules/nzbget/core/constants.dart index 67c77913..a8ccb36e 100644 --- a/lib/modules/nzbget/core/constants.dart +++ b/lib/modules/nzbget/core/constants.dart @@ -6,18 +6,21 @@ class NZBGetConstants { static const MODULE_KEY = 'nzbget'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'NZBGet', description: 'Manage Usenet Downloads', settingsDescription: 'Configure NZBGet', + helpMessage: 'NZBGet is a binary downloader, which downloads files from Usenet based on information given in nzb-files.', icon: CustomIcons.nzbget, route: '/nzbget', color: Color(0xFF42D535), + website: 'https://nzbget.net', + github: 'https://github.com/nzbget/nzbget', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/nzbget/core/database.dart b/lib/modules/nzbget/core/database.dart index 612cddc4..30cbafe8 100644 --- a/lib/modules/nzbget/core/database.dart +++ b/lib/modules/nzbget/core/database.dart @@ -1,9 +1,40 @@ -import 'package:lunasea/core/database.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; -class NZBGetDatabase { - NZBGetDatabase._(); +class NZBGetDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} - static void registerAdapters() {} + @override + Map export() { + Map data = {}; + for(NZBGetDatabaseValue value in NZBGetDatabaseValue.values) { + switch(value) { + // Primitive values + case NZBGetDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + NZBGetDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case NZBGetDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + } + } + } + + @override + NZBGetDatabaseValue valueFromKey(String key) { + switch(key) { + case 'NZBGET_NAVIGATION_INDEX': return NZBGetDatabaseValue.NAVIGATION_INDEX; + default: return null; + } + } } enum NZBGetDatabaseValue { @@ -26,5 +57,16 @@ extension NZBGetDatabaseValueExtension on NZBGetDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case NZBGetDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('NZBGetDatabaseValueExtension', 'put', 'Attempted to enter data for invalid NZBGetDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/nzbget/core/state/global.dart b/lib/modules/nzbget/core/state/global.dart index 41f483ef..266b76f5 100644 --- a/lib/modules/nzbget/core/state/global.dart +++ b/lib/modules/nzbget/core/state/global.dart @@ -1,6 +1,6 @@ import 'package:lunasea/core.dart'; -class NZBGetState extends LunaGlobalState { +class NZBGetState extends LunaModuleState { NZBGetState() { reset(); } diff --git a/lib/modules/nzbget/routes/nzbget.dart b/lib/modules/nzbget/routes/nzbget.dart index 1650a069..2e26380f 100644 --- a/lib/modules/nzbget/routes/nzbget.dart +++ b/lib/modules/nzbget/routes/nzbget.dart @@ -40,7 +40,7 @@ class _State extends State { } }, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, box, widget) { if(_profileState != Database.currentProfileObject.toString()) _refreshProfile(); return Scaffold( @@ -96,11 +96,11 @@ class _State extends State { Future _handlePopup() async { List values = await NZBGetDialogs.globalSettings(context); if(values[0]) switch(values[1]) { - case 'web_gui': _api.host.lsLinks_OpenLink(); break; + case 'web_gui': _api.host.lunaOpenGenericLink(); break; case 'add_nzb': _addNZB(); break; case 'sort': _sort(); break; case 'server_details': _serverDetails(); break; - default: LunaLogger.warning('NZBGet', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('NZBGet', '_handlePopup', 'Unknown Case: ${values[1]}'); } } @@ -109,7 +109,7 @@ class _State extends State { if(values[0]) switch(values[1]) { case 'link': _addByURL(); break; case 'file': _addByFile(); break; - default: LunaLogger.warning('NZBGet', '_addNZB', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('NZBGet', '_addNZB', 'Unknown Case: ${values[1]}'); } } @@ -125,7 +125,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Upload NZB', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -154,22 +154,12 @@ class _State extends State { message: _name, ); }) - .catchError((error, stack) { - LunaLogger.error( - 'NZBGet', - '_addByFile', - 'Failed to add NZB file', - error, - stack, - uploadToSentry: !(error is DioError), - ); - showLunaErrorSnackBar( - context: context, - title: 'Failed to Upload NZB', - message: Constants.CHECK_LOGS_MESSAGE, - error: error, - ); - }); + .catchError((error, stack) => showLunaErrorSnackBar( + context: context, + title: 'Failed to Upload NZB', + message: LunaLogger.CHECK_LOGS_MESSAGE, + error: error, + )); } else { showLunaErrorSnackBar( context: context, @@ -178,7 +168,7 @@ class _State extends State { ); } } catch (error, stack) { - LunaLogger.error('NZBGet', '_addByFile', 'Failed to add NZB by file', error, stack); + LunaLogger().error('Failed to add NZB by file', error, stack); showLunaErrorSnackBar( context: context, title: 'Failed to Upload NZB', @@ -202,7 +192,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Sort Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/nzbget/routes/queue.dart b/lib/modules/nzbget/routes/queue.dart index 62d96f60..9a71e86b 100644 --- a/lib/modules/nzbget/routes/queue.dart +++ b/lib/modules/nzbget/routes/queue.dart @@ -155,7 +155,7 @@ class _State extends State with TickerProviderStateMixin, Automatic .catchError((_) => LSSnackBar( context: context, title: 'Failed to Move Job', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); }, diff --git a/lib/modules/nzbget/widgets/app_bar_stats.dart b/lib/modules/nzbget/widgets/app_bar_stats.dart index a5940e9b..f3cd61cf 100644 --- a/lib/modules/nzbget/widgets/app_bar_stats.dart +++ b/lib/modules/nzbget/widgets/app_bar_stats.dart @@ -61,13 +61,13 @@ class NZBGetAppBarStats extends StatelessWidget { .then((_) => LSSnackBar( context: context, title: 'Speed Limit Set', - message: 'Set to ${(values[1] as int).lsBytes_KilobytesToString(decimals: 0)}/s', + message: 'Set to ${(values[1] as int).lunaKilobytesToString(decimals: 0)}/s', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Speed Limit', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); break; @@ -76,13 +76,13 @@ class NZBGetAppBarStats extends StatelessWidget { .then((_) => LSSnackBar( context: context, title: 'Speed Limit Set', - message: 'Set to ${(values[1] as int).lsBytes_KilobytesToString(decimals: 0)}/s', + message: 'Set to ${(values[1] as int).lunaKilobytesToString(decimals: 0)}/s', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Speed Limit', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/nzbget/widgets/history_tile.dart b/lib/modules/nzbget/widgets/history_tile.dart index 8907d771..0dcadd97 100644 --- a/lib/modules/nzbget/widgets/history_tile.dart +++ b/lib/modules/nzbget/widgets/history_tile.dart @@ -164,7 +164,7 @@ class NZBGetHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Retry Job', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); break; @@ -174,7 +174,7 @@ class NZBGetHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Hide History', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); break; @@ -183,7 +183,7 @@ class NZBGetHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Delete History', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -199,7 +199,7 @@ class NZBGetHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Delete History', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/nzbget/widgets/log_tile.dart b/lib/modules/nzbget/widgets/log_tile.dart index 7d238ac5..4973327f 100644 --- a/lib/modules/nzbget/widgets/log_tile.dart +++ b/lib/modules/nzbget/widgets/log_tile.dart @@ -14,6 +14,6 @@ class NZBGetLogTile extends StatelessWidget { title: LSTitle(text: data.text), subtitle: LSSubtitle(text: data.timestamp), trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => LunaDialogs.textPreview(context, 'Log Entry', data.text), + onTap: () async => LunaDialogs().textPreview(context, 'Log Entry', data.text), ); } diff --git a/lib/modules/nzbget/widgets/queue_fab.dart b/lib/modules/nzbget/widgets/queue_fab.dart index b9003da6..54be9b38 100644 --- a/lib/modules/nzbget/widgets/queue_fab.dart +++ b/lib/modules/nzbget/widgets/queue_fab.dart @@ -103,13 +103,13 @@ class _State extends State with TickerProviderStateMixin { .then((_) => LSSnackBar( context: context, title: 'Pausing Queue', - message: 'For ${(values[1] as int).lsTime_durationString(multiplier: 60)}', + message: 'For ${(values[1] as int).lunaDuration(multiplier: 60)}', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } else { @@ -117,13 +117,13 @@ class _State extends State with TickerProviderStateMixin { .then((_) => LSSnackBar( context: context, title: 'Pausing Queue', - message: 'For ${(values[1] as int).lsTime_durationString(multiplier: 60)}', + message: 'For ${(values[1] as int).lunaDuration(multiplier: 60)}', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -141,7 +141,7 @@ class _State extends State with TickerProviderStateMixin { LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); }); @@ -158,7 +158,7 @@ class _State extends State with TickerProviderStateMixin { LSSnackBar( context: context, title: 'Failed to Resume Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); }); diff --git a/lib/modules/nzbget/widgets/queue_tile.dart b/lib/modules/nzbget/widgets/queue_tile.dart index c47c04d0..1514ae9a 100644 --- a/lib/modules/nzbget/widgets/queue_tile.dart +++ b/lib/modules/nzbget/widgets/queue_tile.dart @@ -69,7 +69,7 @@ class _State extends State { case 'password': _helper._password(); break; case 'rename': _helper._rename(); break; case 'delete': _helper._delete(); break; - default: LunaLogger.warning('NZBGetQueueTile', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('NZBGetQueueTile', '_handlePopup', 'Unknown Case: ${values[1]}'); } } } @@ -99,7 +99,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Pause Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -116,7 +116,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Resume Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -135,7 +135,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Set Category', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -153,7 +153,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Set Priority', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -171,7 +171,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Rename Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -189,7 +189,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Delete Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -207,7 +207,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Set Job Password', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/ombi/core/constants.dart b/lib/modules/ombi/core/constants.dart index 8f220467..755ca75f 100644 --- a/lib/modules/ombi/core/constants.dart +++ b/lib/modules/ombi/core/constants.dart @@ -6,18 +6,21 @@ class OmbiConstants { static const String MODULE_KEY = 'ombi'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Ombi', description: 'Manage Requests for Media', settingsDescription: 'Configure Ombi', + helpMessage: 'Ombi is a self-hosted web application that automatically gives your shared Plex or Emby users the ability to request content by themselves! Ombi can be linked to multiple TV Show and Movie DVR tools to create a seamless end-to-end experience for your users.', icon: CustomIcons.tautulli, route: '/ombi', color: Color(0xFFD4782C), + website: 'https://ombi.io', + github: 'https://github.com/tidusjar/Ombi', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/ombi/core/database.dart b/lib/modules/ombi/core/database.dart index 176c6e6a..6912afdc 100644 --- a/lib/modules/ombi/core/database.dart +++ b/lib/modules/ombi/core/database.dart @@ -1,9 +1,40 @@ +import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class OmbiDatabase { - OmbiDatabase._(); +class OmbiDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} + + @override + Map export() { + Map data = {}; + for(OmbiDatabaseValue value in OmbiDatabaseValue.values) { + switch(value) { + // Primitive values + case OmbiDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + } + } + return data; + } - static void registerAdapters() {} + @override + void import(Map config) { + for(String key in config.keys) { + OmbiDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case OmbiDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + } + } + } + + @override + OmbiDatabaseValue valueFromKey(String key) { + switch(key) { + case 'OMBI_NAVIGATION_INDEX': return OmbiDatabaseValue.NAVIGATION_INDEX; + default: return null; + } + } } enum OmbiDatabaseValue { @@ -26,5 +57,16 @@ extension OmbiDatabaseValueExtension on OmbiDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case OmbiDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('OmbiDatabaseValueExtension', 'put', 'Attempted to enter data for invalid OmbiDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/ombi/core/router.dart b/lib/modules/ombi/core/router.dart index dd6ac6ce..5bf32261 100644 --- a/lib/modules/ombi/core/router.dart +++ b/lib/modules/ombi/core/router.dart @@ -1,10 +1,10 @@ import 'package:fluro/fluro.dart'; +import 'package:lunasea/core.dart'; import 'package:lunasea/modules/ombi.dart'; -class OmbiRouter { - OmbiRouter._(); - - static void initialize(FluroRouter router) { +class OmbiRouter extends LunaModuleRouter { + @override + void defineAllRoutes(FluroRouter router) { OmbiHomeRouter.defineRoutes(router); } } diff --git a/lib/modules/ombi/core/state.dart b/lib/modules/ombi/core/state.dart index c1f9535e..4bf628b0 100644 --- a/lib/modules/ombi/core/state.dart +++ b/lib/modules/ombi/core/state.dart @@ -1,6 +1,6 @@ import 'package:lunasea/core.dart'; -class OmbiState extends LunaGlobalState { +class OmbiState extends LunaModuleState { @override void reset() {} } diff --git a/lib/modules/ombi/modules/ombi/route.dart b/lib/modules/ombi/modules/ombi/route.dart index 7bc34c19..7fdd7d85 100644 --- a/lib/modules/ombi/modules/ombi/route.dart +++ b/lib/modules/ombi/modules/ombi/route.dart @@ -35,7 +35,7 @@ class _State extends State<_OmbiHomeRoute> { Widget build(BuildContext context) => WillPopScope( onWillPop: _onWillPop, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaSeaDatabaseValue.ENABLED_PROFILE.key ]), + valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaDatabaseValue.ENABLED_PROFILE.key ]), builder: (context, box, _) => Scaffold( key: _scaffoldKey, drawer: _drawer, diff --git a/lib/modules/radarr/core/api/api.dart b/lib/modules/radarr/core/api/api.dart index 35b1f0b9..37f18cdc 100644 --- a/lib/modules/radarr/core/api/api.dart +++ b/lib/modules/radarr/core/api/api.dart @@ -3,7 +3,7 @@ import 'package:dio/dio.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; -class RadarrAPI extends API { +class RadarrAPI { final Map _values; final Dio _dio; @@ -29,17 +29,7 @@ class RadarrAPI extends API { ); } - void logWarning(String methodName, String text) => LunaLogger.warning('package:lunasea/core/api/radarr/api.dart', methodName, 'Radarr: $text'); - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'package:lunasea/core/api/radarr/api.dart', - methodName, - 'Radarr: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Radarr: $text', error, trace); bool get enabled => _values['enabled']; String get host => _values['host']; @@ -69,10 +59,10 @@ class RadarrAPI extends API { ); return response.data['id']; } on DioError catch (error, stack) { - logError('addMovie', 'Failed to add movie (${entry.title})', error, stack, uploadToSentry: false); + logError('Failed to add movie (${entry.title})', error, stack); return Future.error(error); } catch (error, stack) { - logError('addMovie', 'Failed to add movie (${entry.title})', error, stack); + logError('Failed to add movie (${entry.title})', error, stack); return Future.error(error); } } @@ -88,10 +78,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('refreshMovie', 'Failed to refresh movie ($id)', error, stack, uploadToSentry: false); + logError('Failed to refresh movie ($id)', error, stack); return Future.error(error); } catch (error, stack) { - logError('refreshMovie', 'Failed to refresh movie ($id)', error, stack); + logError('Failed to refresh movie ($id)', error, stack); return Future.error(error); } } @@ -106,10 +96,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('updateLibrary', 'Failed to update library', error, stack, uploadToSentry: false); + logError('Failed to update library', error, stack); return Future.error(error); } catch (error, stack) { - logError('updateLibrary', 'Failed to update library', error, stack); + logError('Failed to update library', error, stack); return Future.error(error); } } @@ -124,10 +114,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack, uploadToSentry: false); + logError('Failed to trigger RSS sync', error, stack); return Future.error(error); } catch (error, stack) { - logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack); + logError('Failed to trigger RSS sync', error, stack); return Future.error(error); } } @@ -142,10 +132,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('triggerBackup', 'Failed to backup database', error, stack, uploadToSentry: false); + logError('Failed to backup database', error, stack); return Future.error(error); } catch (error, stack) { - logError('triggerBackup', 'Failed to backup database', error, stack); + logError('Failed to backup database', error, stack); return Future.error(error); } } @@ -165,10 +155,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('editMovie', 'Failed to edit movie ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to edit movie ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('editMovie', 'Failed to edit movie ($movieID)', error, stack); + logError('Failed to edit movie ($movieID)', error, stack); return Future.error(error); } } @@ -184,10 +174,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('removeMovie', 'Failed to remove movie ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to remove movie ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('removeMovie', 'Failed to remove movie ($movieID)', error, stack); + logError('Failed to remove movie ($movieID)', error, stack); return Future.error(error); } } @@ -227,10 +217,10 @@ class RadarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getAllMovies', 'Failed to fetch all movies', error, stack, uploadToSentry: false); + logError('Failed to fetch all movies', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAllMovies', 'Failed to fetch all movies', error, stack); + logError('Failed to fetch all movies', error, stack); return Future.error(error); } } @@ -244,10 +234,10 @@ class RadarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getAllMovieIDs', 'Failed to fetch all movie IDs', error, stack, uploadToSentry: false); + logError('Failed to fetch all movie IDs', error, stack); return Future.error(error); } catch (error, stack) { - logError('getAllMovieIDs', 'Failed to fetch all movie IDs', error, stack); + logError('Failed to fetch all movie IDs', error, stack); return Future.error(error); } } @@ -283,10 +273,10 @@ class RadarrAPI extends API { staticPath: response.data['pathState'] == 'static' ? true : false, ); } on DioError catch (error, stack) { - logError('getMovie', 'Failed to fetch movie ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to fetch movie ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getMovie', 'Failed to fetch movie ($movieID)', error, stack); + logError('Failed to fetch movie ($movieID)', error, stack); return Future.error(error); } } @@ -296,10 +286,10 @@ class RadarrAPI extends API { await _dio.delete('moviefile/$movieID'); return true; } on DioError catch (error, stack) { - logError('removeMovieFile', 'Failed to remove movie file ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to remove movie file ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('removeMovieFile', 'Failed to remove movie file ($movieID)', error, stack); + logError('Failed to remove movie file ($movieID)', error, stack); return Future.error(error); } } @@ -334,10 +324,10 @@ class RadarrAPI extends API { }); return _entries; } on DioError catch (error, stack) { - logError('getMissing', 'Failed to fetch missing movies', error, stack, uploadToSentry: false); + logError('Failed to fetch missing movies', error, stack); return Future.error(error); } catch (error, stack) { - logError('getMissing', 'Failed to fetch missing movies', error, stack); + logError('Failed to fetch missing movies', error, stack); return Future.error(error); } } @@ -393,10 +383,10 @@ class RadarrAPI extends API { }); return [_cinemaEntries, _announcedEntries].expand((x) => x).toList(); } on DioError catch (error, stack) { - logError('getUpcoming', 'Failed to fetch upcoming movies', error, stack, uploadToSentry: false); + logError('Failed to fetch upcoming movies', error, stack); return Future.error(error); } catch (error, stack) { - logError('getUpcoming', 'Failed to fetch upcoming movies', error, stack); + logError('Failed to fetch upcoming movies', error, stack); return Future.error(error); } } @@ -470,10 +460,10 @@ class RadarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack, uploadToSentry: false); + logError('Failed to fetch history', error, stack); return Future.error(error); } catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack); + logError('Failed to fetch history', error, stack); return Future.error(error); } } @@ -489,10 +479,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('searchMissingMovies', 'Failed to search for missing movies (${movieIDs.toString()})', error, stack, uploadToSentry: false); + logError('Failed to search for missing movies (${movieIDs.toString()})', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchMissingMovies', 'Failed to search for missing movies (${movieIDs.toString()})', error, stack); + logError('Failed to search for missing movies (${movieIDs.toString()})', error, stack); return Future.error(error); } } @@ -509,10 +499,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('searchAllMissing', 'Failed to search for all missing movies', error, stack, uploadToSentry: false); + logError('Failed to search for all missing movies', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchAllMissing', 'Failed to search for all missing movies', error, stack); + logError('Failed to search for all missing movies', error, stack); return Future.error(error); } } @@ -528,10 +518,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('toggleMovieMonitored', 'Failed to toggle movie monitored status ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to toggle movie monitored status ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('toggleMovieMonitored', 'Failed to toggle movie monitored status ($movieID)', error, stack); + logError('Failed to toggle movie monitored status ($movieID)', error, stack); return Future.error(error); } } @@ -548,10 +538,10 @@ class RadarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack, uploadToSentry: false); + logError('Failed to fetch quality profiles', error, stack); return Future.error(error); } catch (error, stack) { - logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack); + logError('Failed to fetch quality profiles', error, stack); return Future.error(error); } } @@ -586,10 +576,10 @@ class RadarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getReleases', 'Failed to get releases ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to get releases ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('getReleases', 'Failed to get releases ($movieID)', error, stack); + logError('Failed to get releases ($movieID)', error, stack); return Future.error(error); } } @@ -605,10 +595,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('downloadRelease', 'Failed to download release ($guid)', error, stack, uploadToSentry: false); + logError('Failed to download release ($guid)', error, stack); return Future.error(error); } catch (error, stack) { - logError('downloadRelease', 'Failed to download release ($guid)', error, stack); + logError('Failed to download release ($guid)', error, stack); return Future.error(error); } } @@ -624,10 +614,10 @@ class RadarrAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('automaticSearchMovie', 'Failed to automatically search for movie releases ($movieID)', error, stack, uploadToSentry: false); + logError('Failed to automatically search for movie releases ($movieID)', error, stack); return Future.error(error); } catch (error, stack) { - logError('automaticSearchMovie', 'Failed to automatically search for movie releases ($movieID)', error, stack); + logError('Failed to automatically search for movie releases ($movieID)', error, stack); return Future.error(error); } } @@ -645,7 +635,7 @@ class RadarrAPI extends API { for(var entry in response.data) { entries.add(RadarrSearchData( title: entry['title'] ?? 'Unknown Title', - titleSlug: entry['titleSlug'] ?? 'unknown-title', + titleSlug: entry['titleSlug'] ?? ((entry['title'] as String) ?? 'Unknown Title').lunaConvertToSlug(), overview: entry['overview'] == null || entry['overview'] == '' ? 'No summary is available.' : entry['overview'], year: entry['year'] ?? 0, tmdbId: entry['tmdbId'] ?? 0, @@ -655,10 +645,10 @@ class RadarrAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('searchMovies', 'Failed to search ($search)', error, stack, uploadToSentry: false); + logError('Failed to search ($search)', error, stack); return Future.error(error); } catch (error, stack) { - logError('searchMovies', 'Failed to search ($search)', error, stack); + logError('Failed to search ($search)', error, stack); return Future.error(error); } } @@ -676,10 +666,10 @@ class RadarrAPI extends API { } return _entries; } on DioError catch (error, stack) { - logError('getRootFolders', 'Failed to fetch root folders', error, stack, uploadToSentry: false); + logError('Failed to fetch root folders', error, stack); return Future.error(error); } catch (error, stack) { - logError('getRootFolders', 'Failed to fetch root folders', error, stack); + logError('Failed to fetch root folders', error, stack); return Future.error(error); } } diff --git a/lib/modules/radarr/core/api/data/catalogue.dart b/lib/modules/radarr/core/api/data/catalogue.dart index e5bb2667..fdcd3e26 100644 --- a/lib/modules/radarr/core/api/data/catalogue.dart +++ b/lib/modules/radarr/core/api/data/catalogue.dart @@ -134,10 +134,7 @@ class RadarrCatalogueData { return '$year'; } - String subtitle(RadarrCatalogueSorting sorting) { - String _base = '$yearString${runtime.lsTime_runtimeString(dot: true)}'; - return '$_base\t•\t${_sortSubtitle(sorting)}'; - } + String subtitle(RadarrCatalogueSorting sorting) => '$yearString\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}\t•\t${_sortSubtitle(sorting)}'; String _sortSubtitle(RadarrCatalogueSorting sorting) { switch(sorting) { @@ -155,25 +152,24 @@ class RadarrCatalogueData { TextSpan get releaseSubtitle { String text = ''; Color color = Colors.white; - DateTime now = DateTime.now(); if (downloaded) { - text = sizeOnDisk?.lsBytes_BytesToString(); + text = sizeOnDisk?.lunaBytesToString(); color = monitored ? Color(LunaColours.ACCENT_COLOR) : Color(LunaColours.ACCENT_COLOR).withOpacity(0.30); } else if(isPhysicallyReleased) { text = ''; } else if(isInCinemas) { if(physicalReleaseObject != null) { - text = now.lsDateTime_upcomingString(physicalReleaseObject)?.toUpperCase(); + text = physicalReleaseObject.lunaUpcomingDays.toUpperCase(); color = monitored ? Colors.blue : Colors.blue.withOpacity(0.30); } } else if(isAnnounced) { if(inCinemasObject != null) { - text = now.lsDateTime_upcomingString(inCinemasObject)?.toUpperCase(); + text = inCinemasObject.lunaUpcomingDays.toUpperCase(); color = monitored ? Colors.orange : Colors.orange.withOpacity(0.30); } } else if(isTBA) { text = 'TO BE ANNOUNCED'; - color = monitored ? Color(LunaColours.ACCENT_COLOR) : Color(LunaColours.ACCENT_COLOR).withOpacity(0.30); + color = monitored ? LunaColours.blueGrey : LunaColours.blueGrey.withOpacity(0.30); } return TextSpan( text: text, diff --git a/lib/modules/radarr/core/api/data/missing.dart b/lib/modules/radarr/core/api/data/missing.dart index fe5f2238..78cfb0ae 100644 --- a/lib/modules/radarr/core/api/data/missing.dart +++ b/lib/modules/radarr/core/api/data/missing.dart @@ -49,9 +49,7 @@ class RadarrMissingData { return '\t•\t$profile'; } - String get runtimeString { - return runtime.lsTime_runtimeString(dot: true); - } + String get runtimeString => '\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}${runtime.lunaRuntime()}'; List get subtitle { DateTime now = DateTime.now(); diff --git a/lib/modules/radarr/core/api/data/upcoming.dart b/lib/modules/radarr/core/api/data/upcoming.dart index c39c025f..eb31e26d 100644 --- a/lib/modules/radarr/core/api/data/upcoming.dart +++ b/lib/modules/radarr/core/api/data/upcoming.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/database.dart'; +import 'package:lunasea/core.dart'; class RadarrUpcomingData { final Map api = Database.currentProfileObject.getRadarr(); diff --git a/lib/modules/radarr/core/constants.dart b/lib/modules/radarr/core/constants.dart index 24741730..162dcfbc 100644 --- a/lib/modules/radarr/core/constants.dart +++ b/lib/modules/radarr/core/constants.dart @@ -7,19 +7,22 @@ class RadarrConstants { static const String MODULE_KEY = 'radarr'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Radarr', description: 'Manage Movies', settingsDescription: 'Configure Radarr', + helpMessage: 'Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. It can also be configured to automatically upgrade the quality of existing files in the library when a better quality format becomes available.', icon: CustomIcons.radarr, route: '/radarr', color: Color(0xFFFEC333), + website: 'https://radarr.video', + github: 'https://github.com/Radarr/Radarr', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); static const Map EVENT_TYPE_MESSAGES = { diff --git a/lib/modules/radarr/core/database.dart b/lib/modules/radarr/core/database.dart index 9598a01b..f812866b 100644 --- a/lib/modules/radarr/core/database.dart +++ b/lib/modules/radarr/core/database.dart @@ -1,15 +1,60 @@ +import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart' hide RadarrDatabaseValueExtension; -class RadarrDatabase { - RadarrDatabase._(); - - static void registerAdapters() { +class RadarrDatabase extends LunaModuleDatabase { + @override + void registerAdapters() { Hive.registerAdapter(RadarrQualityProfileAdapter()); Hive.registerAdapter(RadarrRootFolderAdapter()); Hive.registerAdapter(RadarrAvailabilityAdapter()); } + + @override + Map export() { + Map data = {}; + for(RadarrDatabaseValue value in RadarrDatabaseValue.values) { + switch(value) { + // Primitive values + case RadarrDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + // Non-exported values + case RadarrDatabaseValue.ADD_MONITORED: + case RadarrDatabaseValue.ADD_QUALITY_PROFILE: + case RadarrDatabaseValue.ADD_ROOT_FOLDER: + case RadarrDatabaseValue.ADD_AVAILABILITY: break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + RadarrDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case RadarrDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + // Non-imported values + case RadarrDatabaseValue.ADD_MONITORED: + case RadarrDatabaseValue.ADD_QUALITY_PROFILE: + case RadarrDatabaseValue.ADD_AVAILABILITY: + case RadarrDatabaseValue.ADD_ROOT_FOLDER: break; + } + } + } + + @override + RadarrDatabaseValue valueFromKey(String key) { + switch(key) { + case 'RADARR_NAVIGATION_INDEX': return RadarrDatabaseValue.NAVIGATION_INDEX; + case 'RADARR_ADD_MONITORED': return RadarrDatabaseValue.ADD_MONITORED; + case 'RADARR_ADD_QUALITY_PROFILE': return RadarrDatabaseValue.ADD_QUALITY_PROFILE; + case 'RADARR_ADD_ROOT_FOLDER': return RadarrDatabaseValue.ADD_ROOT_FOLDER; + case 'RADARR_ADD_AVAILABILITY': return RadarrDatabaseValue. ADD_AVAILABILITY; + default: return null; + } + } } enum RadarrDatabaseValue { @@ -44,5 +89,20 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case RadarrDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MONITORED: if(value.runtimeType == bool) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_QUALITY_PROFILE: if(value.runtimeType == RadarrQualityProfile) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_ROOT_FOLDER: if(value.runtimeType == RadarrRootFolder) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_AVAILABILITY: if(value.runtimeType == RadarrAvailability) box.put(this.key, value); return; + } + LunaLogger().warning('RadarrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid RadarrDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/radarr/core/dialogs.dart b/lib/modules/radarr/core/dialogs.dart index 2f35475d..a06a1f20 100644 --- a/lib/modules/radarr/core/dialogs.dart +++ b/lib/modules/radarr/core/dialogs.dart @@ -192,7 +192,7 @@ class RadarrDialogs { text: folders[index].path, subtitle: LSDialog.richText( children: [ - LSDialog.bolded(text: folders[index].freeSpace.lsBytes_BytesToString()), + LSDialog.bolded(text: folders[index].freeSpace.lunaBytesToString()), ], ), icon: Icons.folder, diff --git a/lib/modules/radarr/core/sorting/catalogue.dart b/lib/modules/radarr/core/sorting/catalogue.dart index b678bc45..47064c9e 100644 --- a/lib/modules/radarr/core/sorting/catalogue.dart +++ b/lib/modules/radarr/core/sorting/catalogue.dart @@ -12,8 +12,6 @@ enum RadarrCatalogueSorting { } extension RadarrCatalogueSortingExtension on RadarrCatalogueSorting { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case RadarrCatalogueSorting.alphabetical: return 'abc'; @@ -40,10 +38,7 @@ extension RadarrCatalogueSortingExtension on RadarrCatalogueSorting { throw Exception('readable not found'); } - List sort( - List data, - bool ascending - ) => _sorter.byType(data, this, ascending); + List sort(List data, bool ascending) => _Sorter().byType(data, this, ascending); } class _Sorter extends LunaSorter { diff --git a/lib/modules/radarr/core/sorting/releases.dart b/lib/modules/radarr/core/sorting/releases.dart index 6b9d0c29..e80a9650 100644 --- a/lib/modules/radarr/core/sorting/releases.dart +++ b/lib/modules/radarr/core/sorting/releases.dart @@ -11,8 +11,6 @@ enum RadarrReleasesSorting { } extension RadarrReleasesSortingExtension on RadarrReleasesSorting { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case RadarrReleasesSorting.age: return 'age'; @@ -37,10 +35,7 @@ extension RadarrReleasesSortingExtension on RadarrReleasesSorting { throw Exception('readable not found'); } - List sort( - List data, - bool ascending - ) => _sorter.byType(data, this, ascending); + List sort(List data, bool ascending) => _Sorter().byType(data, this, ascending); } class _Sorter extends LunaSorter { diff --git a/lib/modules/radarr/core/state/global.dart b/lib/modules/radarr/core/state/global.dart index 1e15ddf0..9718277b 100644 --- a/lib/modules/radarr/core/state/global.dart +++ b/lib/modules/radarr/core/state/global.dart @@ -1,7 +1,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; -class RadarrState extends LunaGlobalState { +class RadarrState extends LunaModuleState { @override void reset() {} diff --git a/lib/modules/radarr/routes/add_details.dart b/lib/modules/radarr/routes/add_details.dart index 90daaeb3..163af27a 100644 --- a/lib/modules/radarr/routes/add_details.dart +++ b/lib/modules/radarr/routes/add_details.dart @@ -104,7 +104,7 @@ class _State extends State { title: 'No TMDB Page Available', message: 'No TMDB URL is available', ) - : _arguments.data.tmdbId.toString().lsLinks_OpenMovieDB(), + : _arguments.data.tmdbId.toString().lunaOpenTheMovieDB(), ) ], ); @@ -229,6 +229,6 @@ class _State extends State { search: search, ) .then((id) => Navigator.of(context).pop(['movie_added', _arguments.data.title, id])) - .catchError((_) => LSSnackBar(context: context, title: search ? 'Failed to Add Movie (With Search)' : 'Failed to Add Movie', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: search ? 'Failed to Add Movie (With Search)' : 'Failed to Add Movie', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } diff --git a/lib/modules/radarr/routes/edit_movie.dart b/lib/modules/radarr/routes/edit_movie.dart index ed52d288..671a4474 100644 --- a/lib/modules/radarr/routes/edit_movie.dart +++ b/lib/modules/radarr/routes/edit_movie.dart @@ -141,7 +141,7 @@ class _State extends State { ); Future _changePath() async { - List _values = await LunaDialogs.editText(context, 'Movie Path', prefill: _path); + List _values = await LunaDialogs().editText(context, 'Movie Path', prefill: _path); if(_values[0] && mounted) setState(() => _path = _values[1]); } @@ -174,6 +174,6 @@ class _State extends State { _arguments.data.monitored = _monitored; Navigator.of(context).pop([true]); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } diff --git a/lib/modules/radarr/routes/radarr.dart b/lib/modules/radarr/routes/radarr.dart index b7c24871..8f5585e1 100644 --- a/lib/modules/radarr/routes/radarr.dart +++ b/lib/modules/radarr/routes/radarr.dart @@ -41,7 +41,7 @@ class _State extends State { } }, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, box, widget) { if(_profileState != Database.currentProfileObject.toString()) _refreshProfile(); return Scaffold( @@ -129,34 +129,34 @@ class _State extends State { _refreshAllPages(); break; } - default: LunaLogger.warning('Radarr', '_enterAddMovie', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('Radarr', '_enterAddMovie', 'Unknown Case: ${result[0]}'); } } Future _handlePopup() async { List values = await RadarrDialogs.globalSettings(context); if(values[0]) switch(values[1]) { - case 'web_gui': await _api.host?.toString()?.lsLinks_OpenLink(); break; + case 'web_gui': await _api.host?.toString()?.lunaOpenGenericLink(); break; case 'update_library': await _api.updateLibrary() .then((_) => LSSnackBar(context: context, title: 'Updating Library...', message: 'Updating your library in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update Library', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Update Library', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'rss_sync': await _api.triggerRssSync() .then((_) => LSSnackBar(context: context, title: 'Running RSS Sync...', message: 'Running RSS sync in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Run RSS Sync', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Run RSS Sync', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'backup': await _api.triggerBackup() .then((_) => LSSnackBar(context: context, title: 'Backing Up Database...', message: 'Backing up database in the background')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Backup Database', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Backup Database', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; case 'missing_search': { List values = await RadarrDialogs.searchAllMissing(context); if(values[0]) await _api.searchAllMissing() .then((_) => LSSnackBar(context: context, title: 'Searching...', message: 'Search for all missing movies')) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); break; } - default: LunaLogger.warning('Radarr', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('Radarr', '_handlePopup', 'Unknown Case: ${values[1]}'); } } diff --git a/lib/modules/radarr/widgets/add_search_result_tile.dart b/lib/modules/radarr/widgets/add_search_result_tile.dart index c0df3aa9..64a05986 100644 --- a/lib/modules/radarr/widgets/add_search_result_tile.dart +++ b/lib/modules/radarr/widgets/add_search_result_tile.dart @@ -41,7 +41,7 @@ class RadarrAddSearchResultTile extends StatelessWidget { ); if(result != null) switch(result[0]) { case 'movie_added': Navigator.of(context).pop(result); break; - default: LunaLogger.warning('RadarrAddSearchResultTile', '_enterDetails', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('RadarrAddSearchResultTile', '_enterDetails', 'Unknown Case: ${result[0]}'); } } } diff --git a/lib/modules/radarr/widgets/catalogue_sorting_button.dart b/lib/modules/radarr/widgets/catalogue_sorting_button.dart index 73df9332..bce60826 100644 --- a/lib/modules/radarr/widgets/catalogue_sorting_button.dart +++ b/lib/modules/radarr/widgets/catalogue_sorting_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, model, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/radarr/widgets/catalogue_tile.dart b/lib/modules/radarr/widgets/catalogue_tile.dart index cc7b8b16..9aeae3da 100644 --- a/lib/modules/radarr/widgets/catalogue_tile.dart +++ b/lib/modules/radarr/widgets/catalogue_tile.dart @@ -111,7 +111,7 @@ class _State extends State { LSSnackBar( context: context, title: widget.data.monitored ? 'Failed to Stop Monitoring' : 'Failed to Monitor', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); }); @@ -136,7 +136,7 @@ class _State extends State { widget.refresh(); break; } - default: LunaLogger.warning('RadarrCatalogueTile', '_enterMovie', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('RadarrCatalogueTile', '_enterMovie', 'Unknown Case: ${result[0]}'); } } @@ -146,7 +146,7 @@ class _State extends State { case 'refresh_movie': _refreshMovie(); break; case 'edit_movie': _editMovie(); break; case 'remove_movie': _removeMovie(); break; - default: LunaLogger.warning('RadarrCatalogueTile', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); + default: LunaLogger().warning('RadarrCatalogueTile', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); } } @@ -154,7 +154,7 @@ class _State extends State { final _api = RadarrAPI.from(Database.currentProfileObject); await _api.refreshMovie(widget.data.movieID) .then((_) => LSSnackBar(context: context, title: 'Refreshing...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _editMovie() async { @@ -186,7 +186,7 @@ class _State extends State { LSSnackBar(context: context, title: 'Removed', message: widget.data.title, type: SNACKBAR_TYPE.success); widget.refresh(); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } } \ No newline at end of file diff --git a/lib/modules/radarr/widgets/details_edit_button.dart b/lib/modules/radarr/widgets/details_edit_button.dart index f7d85206..44613c82 100644 --- a/lib/modules/radarr/widgets/details_edit_button.dart +++ b/lib/modules/radarr/widgets/details_edit_button.dart @@ -30,7 +30,7 @@ class _State extends State { case 'refresh_movie': _refreshMovie(context); break; case 'edit_movie': _editMovie(context); break; case 'remove_movie': _removeMovie(context); break; - default: LunaLogger.warning('RadarrDetailsEditButton', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); + default: LunaLogger().warning('RadarrDetailsEditButton', '_handlePopup', 'Invalid method passed through popup. (${values[1]})'); } } @@ -51,7 +51,7 @@ class _State extends State { final _api = RadarrAPI.from(Database.currentProfileObject); await _api.refreshMovie(widget.data.movieID) .then((_) => LSSnackBar(context: context, title: 'Refreshing...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Refresh', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _removeMovie(BuildContext context) async { @@ -68,7 +68,7 @@ class _State extends State { LSSnackBar(context: context, title: 'Removed', message: widget.data.title, type: SNACKBAR_TYPE.success); widget.remove(); }) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Remove', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } } } diff --git a/lib/modules/radarr/widgets/details_file_tile.dart b/lib/modules/radarr/widgets/details_file_tile.dart index 7156bb5e..d2fdb2b6 100644 --- a/lib/modules/radarr/widgets/details_file_tile.dart +++ b/lib/modules/radarr/widgets/details_file_tile.dart @@ -27,13 +27,13 @@ class _State extends State { ), children: widget.data.movieFile['mediaInfo'] != null ? [ - TextSpan(text: '${int.tryParse(widget.data.movieFile['size'].toString())?.lsBytes_BytesToString() ?? 'Unknown'}\t•\t'), + TextSpan(text: '${int.tryParse(widget.data.movieFile['size'].toString())?.lunaBytesToString() ?? 'Unknown'}\t•\t'), TextSpan(text: '${widget.data.movieFile['quality']['quality']['name'] ?? 'Unknown'}\n'), TextSpan(text: '${widget.data.movieFile['mediaInfo']['videoFormat'] ?? 'Unknown'}/'), TextSpan(text: '${widget.data.movieFile['mediaInfo']['audioFormat'] ?? 'Unknown'}'), ] : [ - TextSpan(text: '${int.tryParse(widget.data.movieFile['size'].toString())?.lsBytes_BytesToString() ?? 'Unknown'}\t•\t'), + TextSpan(text: '${int.tryParse(widget.data.movieFile['size'].toString())?.lunaBytesToString() ?? 'Unknown'}\t•\t'), TextSpan(text: '${widget.data.movieFile['quality']['quality']['name'] ?? 'Unknown'}\n'), ], ), @@ -69,7 +69,7 @@ class _State extends State { LSSnackBar( context: context, title: 'Failed to Delete Movie File', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); return Future.error(error); diff --git a/lib/modules/radarr/widgets/details_overview.dart b/lib/modules/radarr/widgets/details_overview.dart index 8cdedee5..d221bd39 100644 --- a/lib/modules/radarr/widgets/details_overview.dart +++ b/lib/modules/radarr/widgets/details_overview.dart @@ -35,7 +35,7 @@ class _State extends State with AutomaticKeepAliveClientM LSCardTile( title: LSTitle(text: 'Movie Path', centerText: true), subtitle: LSSubtitle(text: widget?.data?.path ?? 'Unknown', centerText: true), - onTap: () => LunaDialogs.textPreview(context, widget?.data?.title, widget?.data?.path ?? 'Unknown'), + onTap: () => LunaDialogs().textPreview(context, widget?.data?.title, widget?.data?.path ?? 'Unknown'), ), LSContainerRow( children: [ @@ -92,7 +92,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(18.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.imdbId?.lsLinks_OpenIMDB(), + onTap: () async => await widget.data?.imdbId?.lunaOpenIMDB(), ), reducedMargin: true, ), @@ -108,7 +108,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(19.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.tmdbId?.toString()?.lsLinks_OpenMovieDB(), + onTap: () async => await widget.data?.tmdbId?.toString()?.lunaOpenTheMovieDB(), ), reducedMargin: true, ), @@ -124,7 +124,7 @@ class _State extends State with AutomaticKeepAliveClientM padding: EdgeInsets.all(20.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await widget.data?.youtubeId?.lsLinks_OpenYoutube(), + onTap: () async => await widget.data?.youtubeId?.lunaOpenYouTube(), ), reducedMargin: true, ), diff --git a/lib/modules/radarr/widgets/details_search.dart b/lib/modules/radarr/widgets/details_search.dart index 527ab0b8..06b3c294 100644 --- a/lib/modules/radarr/widgets/details_search.dart +++ b/lib/modules/radarr/widgets/details_search.dart @@ -71,7 +71,7 @@ class _State extends State with AutomaticKeepAliveClientMix .catchError((_) => LSSnackBar( context: context, title: 'Failed to Search', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/radarr/widgets/details_search_tile.dart b/lib/modules/radarr/widgets/details_search_tile.dart index 8afdc640..a0c694a9 100644 --- a/lib/modules/radarr/widgets/details_search_tile.dart +++ b/lib/modules/radarr/widgets/details_search_tile.dart @@ -35,7 +35,7 @@ class RadarrSearchResultTile extends StatelessWidget { runSpacing: 10.0, children: [ LSTextHighlighted( - text: data.protocol.lsLanguage_Capitalize(), + text: data.protocol.lunaCapitalizeFirstLetters(), bgColor: data.isTorrent ? LunaColours.purple : LunaColours.blue, @@ -67,9 +67,9 @@ class RadarrSearchResultTile extends StatelessWidget { ), ), TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'), - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}\t•\t'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}\t•\t'), TextSpan(text: '${data.indexer}\n'), - TextSpan(text: '${data.ageHours.lsTime_releaseAgeString() ?? 'Unknown'}'), + TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}'), ], ), ), @@ -128,7 +128,7 @@ class RadarrSearchResultTile extends StatelessWidget { : LunaColours.blue, fontWeight: FontWeight.bold, ), - text: data.protocol.lsLanguage_Capitalize(), + text: data.protocol.lunaCapitalizeFirstLetters(), ), if(data.isTorrent) TextSpan( text: ' (${data.seeders}/${data.leechers})', @@ -138,9 +138,9 @@ class RadarrSearchResultTile extends StatelessWidget { ), ), TextSpan(text: '\t•\t${data.indexer}\t•\t'), - TextSpan(text: '${data.ageHours.lsTime_releaseAgeString() ?? 'Unknown'}\n'), + TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}\n'), TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'), - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}'), ], ), ), @@ -178,7 +178,7 @@ class RadarrSearchResultTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Start Downloading', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -188,6 +188,6 @@ class RadarrSearchResultTile extends StatelessWidget { for(var i=0; i { final _api = RadarrAPI.from(Database.currentProfileObject); await _api.searchMissingMovies([widget.data.movieID]) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _interactiveSearch() async => Navigator.of(context).pushNamed( diff --git a/lib/modules/radarr/widgets/releases_sorting_button.dart b/lib/modules/radarr/widgets/releases_sorting_button.dart index 435bc684..7d3621ad 100644 --- a/lib/modules/radarr/widgets/releases_sorting_button.dart +++ b/lib/modules/radarr/widgets/releases_sorting_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, model, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/radarr/widgets/upcoming_tile.dart b/lib/modules/radarr/widgets/upcoming_tile.dart index 6e4080c7..e2d1873f 100644 --- a/lib/modules/radarr/widgets/upcoming_tile.dart +++ b/lib/modules/radarr/widgets/upcoming_tile.dart @@ -47,7 +47,7 @@ class _State extends State { final _api = RadarrAPI.from(Database.currentProfileObject); await _api.searchMissingMovies([widget.data.movieID]) .then((_) => LSSnackBar(context: context, title: 'Searching...', message: widget.data.title)) - .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); + .catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure)); } Future _interactiveSearch() async => Navigator.of(context).pushNamed( diff --git a/lib/modules/sabnzbd/core/api/api.dart b/lib/modules/sabnzbd/core/api/api.dart index 2bff962e..7bea7eee 100644 --- a/lib/modules/sabnzbd/core/api/api.dart +++ b/lib/modules/sabnzbd/core/api/api.dart @@ -2,7 +2,7 @@ import 'package:dio/dio.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sabnzbd.dart'; -class SABnzbdAPI extends API { +class SABnzbdAPI { final Map _values; final Dio _dio; @@ -27,17 +27,7 @@ class SABnzbdAPI extends API { ); } - void logWarning(String methodName, String text) => LunaLogger.warning('SABnzbdAPI', methodName, 'SABnzbd: $text'); - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'SABnzbdAPI', - methodName, - 'SABnzbd: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('SABnzbd: $text', error, trace); bool get enabled => _values['enabled']; String get host => _values['host']; @@ -88,10 +78,10 @@ class SABnzbdAPI extends API { totalUsage: statistics.data['total'] ?? 0, ); } on DioError catch (error, stack) { - logError('getStatistics', 'Failed to fetch statistics', error, stack, uploadToSentry: false); + logError('Failed to fetch statistics', error, stack); return Future.error(error); } catch (error, stack) { - logError('getStatistics', 'Failed to fetch statistics', error, stack); + logError('Failed to fetch statistics', error, stack); return Future.error(error); } } @@ -108,10 +98,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('pauseQueue', 'Failed to pause queue', error, stack, uploadToSentry: false); + logError('Failed to pause queue', error, stack); return Future.error(error); } catch (error, stack) { - logError('pauseQueue', 'Failed to pause queue', error, stack); + logError('Failed to pause queue', error, stack); return Future.error(error); } } @@ -130,10 +120,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('pauseQueueFor', 'Failed to pause queue for $minutes minutes', error, stack, uploadToSentry: false); + logError('Failed to pause queue for $minutes minutes', error, stack); return Future.error(error); } catch (error, stack) { - logError('pauseQueueFor', 'Failed to pause queue for $minutes minutes', error, stack); + logError('Failed to pause queue for $minutes minutes', error, stack); return Future.error(error); } } @@ -150,10 +140,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('resumeQueue', 'Failed to resume queue', error, stack, uploadToSentry: false); + logError('Failed to resume queue', error, stack); return Future.error(error); } catch (error, stack) { - logError('resumeQueue', 'Failed to resume queue', error, stack); + logError('Failed to resume queue', error, stack); return Future.error(error); } } @@ -172,10 +162,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('pauseSingleJob', 'Failed to pause job ($nzoId)', error, stack, uploadToSentry: false); + logError('Failed to pause job ($nzoId)', error, stack); return Future.error(error); } catch (error, stack) { - logError('pauseSingleJob', 'Failed to pause job ($nzoId)', error, stack); + logError('Failed to pause job ($nzoId)', error, stack); return Future.error(error); } } @@ -194,10 +184,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('resumeSingleJob', 'Failed to resume job ($nzoId)', error, stack, uploadToSentry: false); + logError('Failed to resume job ($nzoId)', error, stack); return Future.error(error); } catch (error, stack) { - logError('resumeSingleJob', 'Failed to resume job ($nzoId)', error, stack); + logError('Failed to resume job ($nzoId)', error, stack); return Future.error(error); } } @@ -217,10 +207,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('deleteJob', 'Failed to delete job ($nzoId)', error, stack, uploadToSentry: false); + logError('Failed to delete job ($nzoId)', error, stack); return Future.error(error); } catch (error, stack) { - logError('deleteJob', 'Failed to delete job ($nzoId)', error, stack); + logError('Failed to delete job ($nzoId)', error, stack); return Future.error(error); } } @@ -240,10 +230,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('renameJob', 'Failed to rename job ($nzoId, $name)', error, stack, uploadToSentry: false); + logError('Failed to rename job ($nzoId, $name)', error, stack); return Future.error(error); } catch (error, stack) { - logError('renameJob', 'Failed to rename job ($nzoId, $name)', error, stack); + logError('Failed to rename job ($nzoId, $name)', error, stack); return Future.error(error); } } @@ -264,10 +254,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('setJobPassword', 'Failed to set job password ($nzoId, $password)', error, stack, uploadToSentry: false); + logError('Failed to set job password ($nzoId, $password)', error, stack); return Future.error(error); } catch (error, stack) { - logError('setJobPassword', 'Failed to set job password ($nzoId, $password)', error, stack); + logError('Failed to set job password ($nzoId, $password)', error, stack); return Future.error(error); } } @@ -285,10 +275,10 @@ class SABnzbdAPI extends API { ); return true; } on DioError catch (error, stack) { - logError('setJobPriority', 'Failed to set job priority ($nzoId, $priority)', error, stack, uploadToSentry: false); + logError('Failed to set job priority ($nzoId, $priority)', error, stack); return Future.error(error); } catch (error, stack) { - logError('setJobPriority', 'Failed to set job priority ($nzoId, $priority)', error, stack); + logError('Failed to set job priority ($nzoId, $priority)', error, stack); return Future.error(error); } } @@ -308,10 +298,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('deleteHistory', 'Failed to delete history entry ($nzoId)', error, stack, uploadToSentry: false); + logError('Failed to delete history entry ($nzoId)', error, stack); return Future.error(error); } catch (error, stack) { - logError('deleteHistory', 'Failed to delete history entry ($nzoId)', error, stack); + logError('Failed to delete history entry ($nzoId)', error, stack); return Future.error(error); } } @@ -349,10 +339,10 @@ class SABnzbdAPI extends API { queue, ]; } on DioError catch (error, stack) { - logError('getStatusAndQueue', 'Failed to fetch status and queue', error, stack, uploadToSentry: false); + logError('Failed to fetch status and queue', error, stack); return Future.error(error); } catch (error, stack) { - logError('getStatusAndQueue', 'Failed to fetch status and queue', error, stack); + logError('Failed to fetch status and queue', error, stack); return Future.error(error); } } @@ -384,10 +374,10 @@ class SABnzbdAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack, uploadToSentry: false); + logError('Failed to fetch history', error, stack); return Future.error(error); } catch (error, stack) { - logError('getHistory', 'Failed to fetch history', error, stack); + logError('Failed to fetch history', error, stack); return Future.error(error); } } @@ -406,10 +396,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('moveQueue', 'Failed to move queue entry ($nzoId, $index)', error, stack, uploadToSentry: false); + logError('Failed to move queue entry ($nzoId, $index)', error, stack); return Future.error(error); } catch (error, stack) { - logError('moveQueue', 'Failed to move queue entry ($nzoId, $index)', error, stack); + logError('Failed to move queue entry ($nzoId, $index)', error, stack); return Future.error(error); } } @@ -429,10 +419,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('sortQueue', 'Failed to sort queue ($sort, $dir)', error, stack, uploadToSentry: false); + logError('Failed to sort queue ($sort, $dir)', error, stack); return Future.error(error); } catch (error, stack) { - logError('sortQueue', 'Failed to sort queue ($sort, $dir)', error, stack); + logError('Failed to sort queue ($sort, $dir)', error, stack); return Future.error(error); } } @@ -451,10 +441,10 @@ class SABnzbdAPI extends API { } return entries; } on DioError catch (error, stack) { - logError('getCategories', 'Failed to fetch categories', error, stack, uploadToSentry: false); + logError('Failed to fetch categories', error, stack); return Future.error(error); } catch (error, stack) { - logError('getCategories', 'Failed to fetch categories', error, stack); + logError('Failed to fetch categories', error, stack); return Future.error(error); } } @@ -473,10 +463,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('setCategory', 'Failed to set category ($nzoId, $category)', error, stack, uploadToSentry: false); + logError('Failed to set category ($nzoId, $category)', error, stack); return Future.error(error); } catch (error, stack) { - logError('setCategory', 'Failed to set category ($nzoId, $category)', error, stack); + logError('Failed to set category ($nzoId, $category)', error, stack); return Future.error(error); } } @@ -495,10 +485,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('setSpeedLimit', 'Failed to set speed limit ($limit)', error, stack, uploadToSentry: false); + logError('Failed to set speed limit ($limit)', error, stack); return Future.error(error); } catch (error, stack) { - logError('setSpeedLimit', 'Failed to set speed limit ($limit)', error, stack); + logError('Failed to set speed limit ($limit)', error, stack); return Future.error(error); } } @@ -516,10 +506,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('uploadURL', 'Failed to upload NZB by URL ($url)', error, stack, uploadToSentry: false); + logError('Failed to upload NZB by URL ($url)', error, stack); return Future.error(error); } catch (error, stack) { - logError('uploadURL', 'Failed to upload NZB by URL ($url)', error, stack); + logError('Failed to upload NZB by URL ($url)', error, stack); return Future.error(error); } } @@ -539,10 +529,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('uploadFile', 'Failed to upload nzb file ($name)', error, stack, uploadToSentry: false); + logError('Failed to upload nzb file ($name)', error, stack); return Future.error(error); } catch (error, stack) { - logError('uploadFile', 'Failed to upload nzb file ($name)', error, stack); + logError('Failed to upload nzb file ($name)', error, stack); return Future.error(error); } } @@ -561,10 +551,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('setOnCompleteAction', 'Failed to set on-complete action ($action)', error, stack, uploadToSentry: false); + logError('Failed to set on-complete action ($action)', error, stack); return Future.error(error); } catch (error, stack) { - logError('setOnCompleteAction', 'Failed to set on-complete action ($action)', error, stack); + logError('Failed to set on-complete action ($action)', error, stack); return Future.error(error); } } @@ -584,10 +574,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('clearHistory', 'Failed to clear history ($action, $delete)', error, stack, uploadToSentry: false); + logError('Failed to clear history ($action, $delete)', error, stack); return Future.error(error); } catch (error, stack) { - logError('clearHistory', 'Failed to clear history ($action, $delete)', error, stack); + logError('Failed to clear history ($action, $delete)', error, stack); return Future.error(error); } } @@ -605,10 +595,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('retryFailedJob', 'Failed to retry job ($nzoId)', error, stack, uploadToSentry: false); + logError('Failed to retry job ($nzoId)', error, stack); return Future.error(error); } catch (error, stack) { - logError('retryFailedJob', 'Failed to retry job ($nzoId)', error, stack); + logError('Failed to retry job ($nzoId)', error, stack); return Future.error(error); } } @@ -627,10 +617,10 @@ class SABnzbdAPI extends API { ? true : Future.error(null); } on DioError catch (error, stack) { - logError('retryFailedJobPassword', 'Failed to retry job with new password ($nzoId, $password)', error, stack, uploadToSentry: false); + logError('Failed to retry job with new password ($nzoId, $password)', error, stack); return Future.error(error); } catch (error, stack) { - logError('retryFailedJobPassword', 'Failed to retry job with new password ($nzoId, $password)', error, stack); + logError('Failed to retry job with new password ($nzoId, $password)', error, stack); return Future.error(error); } } diff --git a/lib/modules/sabnzbd/core/api/data/history.dart b/lib/modules/sabnzbd/core/api/data/history.dart index 1fdde6c8..9dde24cc 100644 --- a/lib/modules/sabnzbd/core/api/data/history.dart +++ b/lib/modules/sabnzbd/core/api/data/history.dart @@ -34,11 +34,11 @@ class SABnzbdHistoryData { } String get completeTimeString { - return now?.lsDateTime_ageString(completeTimeObject) ?? 'Unknown Time'; + return completeTimeObject?.lunaAge ?? 'Unknown Time'; } String get sizeReadable { - return size?.lsBytes_BytesToString() ?? 'Unknown Size'; + return size?.lunaBytesToString() ?? 'Unknown Size'; } bool get failed { diff --git a/lib/modules/sabnzbd/core/api/data/status.dart b/lib/modules/sabnzbd/core/api/data/status.dart index 94bd4813..dd5626da 100644 --- a/lib/modules/sabnzbd/core/api/data/status.dart +++ b/lib/modules/sabnzbd/core/api/data/status.dart @@ -17,10 +17,10 @@ class SABnzbdStatusData { }); String get currentSpeed { - return '${speed?.floor()?.lsBytes_KilobytesToString(decimals: 1)}/s'; + return '${speed?.floor()?.lunaKilobytesToString(decimals: 1)}/s'; } String get remainingSize { - return sizeLeft?.floor()?.lsBytes_MegabytesToString(decimals: 1); + return sizeLeft?.floor()?.lunaMegabytesToString(decimals: 1); } } diff --git a/lib/modules/sabnzbd/core/constants.dart b/lib/modules/sabnzbd/core/constants.dart index 0c51740d..9b932785 100644 --- a/lib/modules/sabnzbd/core/constants.dart +++ b/lib/modules/sabnzbd/core/constants.dart @@ -6,18 +6,21 @@ class SABnzbdConstants { static const String MODULE_KEY = 'sabnzbd'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'SABnzbd', description: 'Manage Usenet Downloads', settingsDescription: 'Configure SABnzbd', + helpMessage: 'SABnzbd is a multi-platform binary newsgroup downloader. The program works in the background and simplifies the downloading verifying and extracting of files from Usenet.', icon: CustomIcons.sabnzbd, route: '/sabnzbd', color: Color(0xFFFECC2B), + website: 'https://sabnzbd.org', + github: 'https://github.com/sabnzbd/sabnzbd', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/sabnzbd/core/database.dart b/lib/modules/sabnzbd/core/database.dart index 7dfeaf65..7179eb54 100644 --- a/lib/modules/sabnzbd/core/database.dart +++ b/lib/modules/sabnzbd/core/database.dart @@ -1,9 +1,40 @@ -import 'package:lunasea/core/database.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; -class SABnzbdDatabase { - SABnzbdDatabase._(); +class SABnzbdDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} - static void registerAdapters() {} + @override + Map export() { + Map data = {}; + for(SABnzbdDatabaseValue value in SABnzbdDatabaseValue.values) { + switch(value) { + // Primitive values + case SABnzbdDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + SABnzbdDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case SABnzbdDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + } + } + } + + @override + SABnzbdDatabaseValue valueFromKey(String key) { + switch(key) { + case 'SABNZBD_NAVIGATION_INDEX': return SABnzbdDatabaseValue.NAVIGATION_INDEX; + default: return null; + } + } } enum SABnzbdDatabaseValue { @@ -26,5 +57,16 @@ extension SABnzbdDatabaseValueExtension on SABnzbdDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case SABnzbdDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('SABnzbdDatabaseValueExtension', 'put', 'Attempted to enter data for invalid SABnzbdDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/sabnzbd/core/state/global.dart b/lib/modules/sabnzbd/core/state/global.dart index ba05b07c..85e5bb0e 100644 --- a/lib/modules/sabnzbd/core/state/global.dart +++ b/lib/modules/sabnzbd/core/state/global.dart @@ -1,6 +1,6 @@ import 'package:lunasea/core.dart'; -class SABnzbdState extends LunaGlobalState { +class SABnzbdState extends LunaModuleState { SABnzbdState() { reset(); } diff --git a/lib/modules/sabnzbd/routes/history_stages.dart b/lib/modules/sabnzbd/routes/history_stages.dart index c46012e3..375aa373 100644 --- a/lib/modules/sabnzbd/routes/history_stages.dart +++ b/lib/modules/sabnzbd/routes/history_stages.dart @@ -53,7 +53,7 @@ class _State extends State { trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { String _data = _arguments.data.stageLog[index]['actions'].join(',\n').replaceAll('
', '.\n'); - LunaDialogs.textPreview(context, _arguments.data.stageLog[index]['name'], _data); + LunaDialogs().textPreview(context, _arguments.data.stageLog[index]['name'], _data); } ), ), diff --git a/lib/modules/sabnzbd/routes/queue.dart b/lib/modules/sabnzbd/routes/queue.dart index d5a81ab2..f61eb9c1 100644 --- a/lib/modules/sabnzbd/routes/queue.dart +++ b/lib/modules/sabnzbd/routes/queue.dart @@ -143,7 +143,7 @@ class _State extends State with TickerProviderStateMixin, Automati .catchError((_) => LSSnackBar( context: context, title: 'Failed to Move Job', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); }, diff --git a/lib/modules/sabnzbd/routes/sabnzbd.dart b/lib/modules/sabnzbd/routes/sabnzbd.dart index 6d78d904..695d2480 100644 --- a/lib/modules/sabnzbd/routes/sabnzbd.dart +++ b/lib/modules/sabnzbd/routes/sabnzbd.dart @@ -40,7 +40,7 @@ class _State extends State { } }, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, box, widget) { if(_profileState != Database.currentProfileObject.toString()) _refreshProfile(); return Scaffold( @@ -96,13 +96,13 @@ class _State extends State { Future _handlePopup() async { List values = await SABnzbdDialogs.globalSettings(context); if(values[0]) switch(values[1]) { - case 'web_gui': _api.host.lsLinks_OpenLink(); break; + case 'web_gui': _api.host.lunaOpenGenericLink(); break; case 'add_nzb': _addNZB(); break; case 'sort': _sort(); break; case 'clear_history': _clearHistory(); break; case 'complete_action': _completeAction(); break; case 'server_details': _serverDetails(); break; - default: LunaLogger.warning('SABnzbd', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('SABnzbd', '_handlePopup', 'Unknown Case: ${values[1]}'); } } @@ -120,7 +120,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Complete Action', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -140,7 +140,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Upload NZB', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -160,7 +160,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Sort Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -170,7 +170,7 @@ class _State extends State { if(values[0]) switch(values[1]) { case 'link': _addByURL(); break; case 'file': _addByFile(); break; - default: LunaLogger.warning('SABnzbd', '_addNZB', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('SABnzbd', '_addNZB', 'Unknown Case: ${values[1]}'); } } @@ -200,22 +200,12 @@ class _State extends State { message: _name, ); }) - .catchError((error, stack) { - LunaLogger.error( - 'SABnzbd', - '_addByFile', - 'Failed to add NZB file', - error, - stack, - uploadToSentry: !(error is DioError), - ); - showLunaErrorSnackBar( - context: context, - title: 'Failed to Upload NZB', - message: Constants.CHECK_LOGS_MESSAGE, - error: error, - ); - }); + .catchError((error, stack) => showLunaErrorSnackBar( + context: context, + title: 'Failed to Upload NZB', + message: LunaLogger.CHECK_LOGS_MESSAGE, + error: error, + )); } else { showLunaErrorSnackBar( context: context, @@ -224,7 +214,7 @@ class _State extends State { ); } } catch (error, stack) { - LunaLogger.error('SABnzbd', '_addByFile', 'Failed to add NZB by file', error, stack); + LunaLogger().error('Failed to add NZB by file', error, stack); showLunaErrorSnackBar( context: context, title: 'Failed to Upload NZB', @@ -245,7 +235,7 @@ class _State extends State { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Upload NZB', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/sabnzbd/routes/statistics.dart b/lib/modules/sabnzbd/routes/statistics.dart index b6d26fe6..50cee6ec 100644 --- a/lib/modules/sabnzbd/routes/statistics.dart +++ b/lib/modules/sabnzbd/routes/statistics.dart @@ -139,14 +139,14 @@ class _State extends State { Expanded( child: LSCardTile( title: LSTitle(text: 'Daily', centerText: true), - subtitle: LSSubtitle(text: daily.lsBytes_BytesToString(), centerText: true), + subtitle: LSSubtitle(text: daily.lunaBytesToString(), centerText: true), reducedMargin: true, ), ), Expanded( child: LSCardTile( title: LSTitle(text: 'Weekly', centerText: true), - subtitle: LSSubtitle(text: weekly.lsBytes_BytesToString(), centerText: true), + subtitle: LSSubtitle(text: weekly.lunaBytesToString(), centerText: true), reducedMargin: true, ), ), @@ -157,14 +157,14 @@ class _State extends State { Expanded( child: LSCardTile( title: LSTitle(text: 'Monthly', centerText: true), - subtitle: LSSubtitle(text: monthly.lsBytes_BytesToString(), centerText: true), + subtitle: LSSubtitle(text: monthly.lunaBytesToString(), centerText: true), reducedMargin: true, ), ), Expanded( child: LSCardTile( title: LSTitle(text: 'Total', centerText: true), - subtitle: LSSubtitle(text: total.lsBytes_BytesToString(), centerText: true), + subtitle: LSSubtitle(text: total.lunaBytesToString(), centerText: true), reducedMargin: true, ), ), diff --git a/lib/modules/sabnzbd/widgets/app_bar_stats.dart b/lib/modules/sabnzbd/widgets/app_bar_stats.dart index f1894a4f..fbe91992 100644 --- a/lib/modules/sabnzbd/widgets/app_bar_stats.dart +++ b/lib/modules/sabnzbd/widgets/app_bar_stats.dart @@ -67,7 +67,7 @@ class SABnzbdAppBarStats extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Speed Limit', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); break; @@ -82,7 +82,7 @@ class SABnzbdAppBarStats extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Speed Limit', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/sabnzbd/widgets/history_tile.dart b/lib/modules/sabnzbd/widgets/history_tile.dart index 4da693c3..c6af64d5 100644 --- a/lib/modules/sabnzbd/widgets/history_tile.dart +++ b/lib/modules/sabnzbd/widgets/history_tile.dart @@ -148,7 +148,7 @@ class SABnzbdHistoryTile extends StatelessWidget { ); if(result != null) switch(result[0]) { case 'delete': _handleRefresh(context, 'History Deleted'); break; - default: LunaLogger.warning('SABnzbdHistoryTile', '_enterDetails', 'Unknown Case: ${result[0]}'); + default: LunaLogger().warning('SABnzbdHistoryTile', '_enterDetails', 'Unknown Case: ${result[0]}'); } } @@ -158,7 +158,7 @@ class SABnzbdHistoryTile extends StatelessWidget { case 'retry': _retry(context); break; case 'password': _password(context); break; case 'delete': _delete(context); break; - default: LunaLogger.warning('SABnzbdHistoryTile', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('SABnzbdHistoryTile', '_handlePopup', 'Unknown Case: ${values[1]}'); } } @@ -170,7 +170,7 @@ class SABnzbdHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Delete History', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -183,7 +183,7 @@ class SABnzbdHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Set Password / Retry Job', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -194,7 +194,7 @@ class SABnzbdHistoryTile extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Retry Job', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/sabnzbd/widgets/queue_fab.dart b/lib/modules/sabnzbd/widgets/queue_fab.dart index 94a434e4..63571616 100644 --- a/lib/modules/sabnzbd/widgets/queue_fab.dart +++ b/lib/modules/sabnzbd/widgets/queue_fab.dart @@ -103,13 +103,13 @@ class _State extends State with TickerProviderStateMixin { .then((_) => LSSnackBar( context: context, title: 'Pausing Queue', - message: 'For ${(values[1] as int).lsTime_durationString(multiplier: 60)}', + message: 'For ${(values[1] as int).lunaDuration(multiplier: 60)}', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } else { @@ -117,13 +117,13 @@ class _State extends State with TickerProviderStateMixin { .then((_) => LSSnackBar( context: context, title: 'Pausing Queue', - message: 'For ${(values[1] as int).lsTime_durationString(multiplier: 60)}', + message: 'For ${(values[1] as int).lunaDuration(multiplier: 60)}', type: SNACKBAR_TYPE.success, )) .catchError((_) => LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -140,7 +140,7 @@ class _State extends State with TickerProviderStateMixin { LSSnackBar( context: context, title: 'Failed to Pause Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); _iconController.reverse(); @@ -157,7 +157,7 @@ class _State extends State with TickerProviderStateMixin { LSSnackBar( context: context, title: 'Failed to Resume Queue', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, ); _iconController.forward(); diff --git a/lib/modules/sabnzbd/widgets/queue_tile.dart b/lib/modules/sabnzbd/widgets/queue_tile.dart index 9d1795ad..4f27dbc4 100644 --- a/lib/modules/sabnzbd/widgets/queue_tile.dart +++ b/lib/modules/sabnzbd/widgets/queue_tile.dart @@ -69,7 +69,7 @@ class _State extends State { case 'password': _helper._password(); break; case 'rename': _helper._rename(); break; case 'delete': _helper._delete(); break; - default: LunaLogger.warning('SABnzbdQueueTile', '_handlePopup', 'Unknown Case: ${values[1]}'); + default: LunaLogger().warning('SABnzbdQueueTile', '_handlePopup', 'Unknown Case: ${values[1]}'); } } } @@ -99,7 +99,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Pause Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -116,7 +116,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Resume Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -135,7 +135,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Set Category', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -153,7 +153,7 @@ class _Helper { }) .catchError((error) => snackbar( 'Failed to Set Priority', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -171,7 +171,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Rename Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -189,7 +189,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Delete Job', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } @@ -207,7 +207,7 @@ class _Helper { }) .catchError((_) => snackbar( 'Failed to Set Job Password', - Constants.CHECK_LOGS_MESSAGE, + LunaLogger.CHECK_LOGS_MESSAGE, SNACKBAR_TYPE.failure, )); } diff --git a/lib/modules/search/core/api/api.dart b/lib/modules/search/core/api/api.dart index ff76f72e..63de8105 100644 --- a/lib/modules/search/core/api/api.dart +++ b/lib/modules/search/core/api/api.dart @@ -4,7 +4,7 @@ import 'package:xml_parser/xml_parser.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/search.dart'; -class NewznabAPI extends API { +class NewznabAPI { final Map _values; final Dio _dio; @@ -35,29 +35,14 @@ class NewznabAPI extends API { String get host => _values['host']; String get key => _values['key']; - void logWarning(String methodName, String text) => LunaLogger.warning( - 'package:lunasea/core/api/newznab/api.dart', - methodName, - 'Newznab: $text', - ); - - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'package:lunasea/core/api/newznab/api.dart', - methodName, - 'Newznab: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Newznab: $text', error, trace); Future testConnection() async { try { Response response = await _dio.get(''); if(response.statusCode == 200) return true; } catch (error, stack) { - logError('testConnection', 'Connection test failed', error, stack, uploadToSentry: false); + logError('Connection test failed', error, stack); } return false; } @@ -87,10 +72,10 @@ class NewznabAPI extends API { } return _results; } on DioError catch (error, stack) { - logError('getCategories', 'Unable to fetch categories', error, stack, uploadToSentry: false); + logError('Unable to fetch categories', error, stack); return Future.error(error); } catch (error, stack) { - logError('getCategories', 'Unable to fetch categories', error, stack); + logError('Unable to fetch categories', error, stack); return Future.error(error); } } @@ -121,11 +106,11 @@ class NewznabAPI extends API { } return _results; } on DioError catch (error, stack) { - logError('getResults', 'Unable to fetch results', error, stack, uploadToSentry: false); + logError('Unable to fetch results', error, stack); return Future.error(error); } catch (error, stack) { - logError('getResults', 'Unable to fetch results', error, stack); + logError('Unable to fetch results', error, stack); return Future.error(error); } } -} \ No newline at end of file +} diff --git a/lib/modules/search/core/api/data/result.dart b/lib/modules/search/core/api/data/result.dart index 7565b530..b0774114 100644 --- a/lib/modules/search/core/api/data/result.dart +++ b/lib/modules/search/core/api/data/result.dart @@ -9,7 +9,6 @@ class NewznabResultData { String linkDownload; String linkComments; String date; - DateTime _now = DateTime.now(); NewznabResultData({ @required this.title, @@ -31,12 +30,7 @@ class NewznabResultData { return null; } - String get age { - DateTime _date = dateObject; - return _date == null - ? 'Unknown Age' - : _now.lsDateTime_ageString(_date); - } + String get age => dateObject?.lunaAge ?? 'Unknown Age'; int get posix => dateObject?.millisecondsSinceEpoch ?? 0; } \ No newline at end of file diff --git a/lib/modules/search/core/constants.dart b/lib/modules/search/core/constants.dart index a97607e9..19191c01 100644 --- a/lib/modules/search/core/constants.dart +++ b/lib/modules/search/core/constants.dart @@ -6,19 +6,22 @@ class SearchConstants { static const String MODULE_KEY = 'search'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Search', description: 'Search Newznab Indexers', settingsDescription: 'Configure Search', + helpMessage: 'LunaSea currently supports all indexers that support the newznab protocol, including NZBHydra2.', icon: Icons.search, route: '/search', color: Color(LunaColours.ACCENT_COLOR), + website: '', + github: 'https://github.com/theotherp/nzbhydra2', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); static const List ADULT_CATEGORIES = ['xxx', 'adult', 'porn']; diff --git a/lib/modules/search/core/database.dart b/lib/modules/search/core/database.dart index c321c0d8..83931def 100644 --- a/lib/modules/search/core/database.dart +++ b/lib/modules/search/core/database.dart @@ -1,9 +1,43 @@ -import 'package:lunasea/core/database.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; -class SearchDatabase { - SearchDatabase._(); +class SearchDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} - static void registerAdapters() {} + @override + Map export() { + Map data = {}; + for(SearchDatabaseValue value in SearchDatabaseValue.values) { + switch(value) { + // Primitive values + case SearchDatabaseValue.NAVIGATION_INDEX: + case SearchDatabaseValue.HIDE_XXX: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + SearchDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case SearchDatabaseValue.NAVIGATION_INDEX: + case SearchDatabaseValue.HIDE_XXX: value.put(config[key]); break; + } + } + } + + @override + SearchDatabaseValue valueFromKey(String key) { + switch(key) { + case 'SEARCH_NAVIGATION_INDEX': return SearchDatabaseValue.NAVIGATION_INDEX; + case 'SEARCH_HIDE_XXX': return SearchDatabaseValue.HIDE_XXX; + default: return null; + } + } } enum SearchDatabaseValue { @@ -29,5 +63,17 @@ extension SearchDatabaseValueExtension on SearchDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case SearchDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + case SearchDatabaseValue.HIDE_XXX: if(value.runtimeType == bool) box.put(this.key, value); return; + } + LunaLogger().warning('SearchDatabaseValueExtension', 'put', 'Attempted to enter data for invalid SearchDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/search/core/dialogs.dart b/lib/modules/search/core/dialogs.dart index 658f5119..72127572 100644 --- a/lib/modules/search/core/dialogs.dart +++ b/lib/modules/search/core/dialogs.dart @@ -22,14 +22,14 @@ class SearchDialogs { LSDialog.cancel(context, textColor: LunaColours.accent), ], content: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), builder: (context, lunaBox, widget) => ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), builder: (context, profilesBox, widget) => LSDialog.content( children: [ Padding( child: PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), child: Container( @@ -38,7 +38,7 @@ class SearchDialogs { children: [ Expanded( child: Text( - LunaSeaDatabaseValue.ENABLED_PROFILE.data, + LunaDatabaseValue.ENABLED_PROFILE.data, style: TextStyle( fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), @@ -60,7 +60,7 @@ class SearchDialogs { ), ), ), - onSelected: (result) => LunaProfile.changeProfile(context, result), + onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result), itemBuilder: (context) { return >[for(String profile in (profilesBox as Box).keys) PopupMenuItem( value: profile, @@ -98,7 +98,7 @@ class SearchDialogs { ), ), contentPadding: EdgeInsets.fromLTRB(0.0, 26.0, 24.0, 0.0), - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), diff --git a/lib/modules/search/core/sorting.dart b/lib/modules/search/core/sorting.dart index e8493b11..d9196ad9 100644 --- a/lib/modules/search/core/sorting.dart +++ b/lib/modules/search/core/sorting.dart @@ -8,8 +8,6 @@ enum SearchResultsSorting { } extension SearchResultsSortingExtension on SearchResultsSorting { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case SearchResultsSorting.alphabetical: return 'abc'; @@ -28,10 +26,7 @@ extension SearchResultsSortingExtension on SearchResultsSorting { throw Exception('readable not found'); } - List sort( - List data, - bool ascending - ) => _sorter.byType(data, this, ascending); + List sort(List data, bool ascending) => _Sorter().byType(data, this, ascending); } class _Sorter extends LunaSorter { diff --git a/lib/modules/search/core/state/global.dart b/lib/modules/search/core/state/global.dart index fcde3fee..0cbd368a 100644 --- a/lib/modules/search/core/state/global.dart +++ b/lib/modules/search/core/state/global.dart @@ -1,7 +1,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/search.dart'; -class SearchState extends LunaGlobalState { +class SearchState extends LunaModuleState { SearchState() { reset(); } diff --git a/lib/modules/search/widgets/download_button.dart b/lib/modules/search/widgets/download_button.dart index 3a556d00..0d5e1185 100644 --- a/lib/modules/search/widgets/download_button.dart +++ b/lib/modules/search/widgets/download_button.dart @@ -33,7 +33,7 @@ class SearchDetailsDownloadButton extends StatelessWidget { case 'sabnzbd': _sendToSABnzbd(context, data); break; case 'nzbget': _sendToNZBGet(context, data); break; case 'filesystem': _downloadToFilesystem(context); break; - default: LunaLogger.warning('SearchDetailsDownloadButton', '_sendToClient', 'Unknown case: ${_values[1]}'); break; + default: LunaLogger().warning('SearchDetailsDownloadButton', '_sendToClient', 'Unknown case: ${_values[1]}'); break; } } } @@ -53,7 +53,7 @@ class SearchDetailsDownloadButton extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Send', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -72,7 +72,7 @@ class SearchDetailsDownloadButton extends StatelessWidget { .catchError((_) => LSSnackBar( context: context, title: 'Failed to Send', - message: Constants.CHECK_LOGS_MESSAGE, + message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure, )); } @@ -89,10 +89,10 @@ class SearchDetailsDownloadButton extends StatelessWidget { maxRedirects: 5, ), ).get(data.linkDownload); - await LunaFileSystem.exportFileToTemporaryStorage('${data.title.replaceAll(RegExp(r'[^0-9a-zA-Z. -]+'), '')}.nzb', response.data); + await LunaFileSystem().exportStringToShareSheet('${data.title.replaceAll(RegExp(r'[^0-9a-zA-Z. -]+'), '')}.nzb', response.data); } catch (error) { - LunaLogger.error('SearchDetailsDownloadButton', '_downloadToFilesystem', 'Error downloading NZB', error, StackTrace.current); - LSSnackBar(context: context, title: 'Failed to Download NZB', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure); + LunaLogger().error('Error downloading NZB', error, StackTrace.current); + LSSnackBar(context: context, title: 'Failed to Download NZB', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure); } } } diff --git a/lib/modules/search/widgets/result_sorting_button.dart b/lib/modules/search/widgets/result_sorting_button.dart index 64fc8558..798a1e8e 100644 --- a/lib/modules/search/widgets/result_sorting_button.dart +++ b/lib/modules/search/widgets/result_sorting_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, model, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/search/widgets/result_tile.dart b/lib/modules/search/widgets/result_tile.dart index 97c126f3..5bd831f5 100644 --- a/lib/modules/search/widgets/result_tile.dart +++ b/lib/modules/search/widgets/result_tile.dart @@ -27,7 +27,7 @@ class SearchResultTile extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), children: [ - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}\n'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}\n'), TextSpan(text: '${data.age ?? 'Unknown'}'), ] ), @@ -58,7 +58,7 @@ class SearchResultTile extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), children: [ - TextSpan(text: '${data.size.lsBytes_BytesToString() ?? 'Unknown'}\n'), + TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}\n'), TextSpan(text: '${data.age ?? 'Unknown'}'), ], ), diff --git a/lib/modules/settings/core/constants.dart b/lib/modules/settings/core/constants.dart index 761e1ce6..8b810084 100644 --- a/lib/modules/settings/core/constants.dart +++ b/lib/modules/settings/core/constants.dart @@ -6,12 +6,15 @@ class SettingsConstants { static const String MODULE_KEY = 'settings'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Settings', description: 'Update Configuration', settingsDescription: '', + helpMessage: '', icon: CustomIcons.settings, route: '/settings', color: Color(LunaColours.ACCENT_COLOR), + website: '', + github: '', ); } diff --git a/lib/modules/settings/core/database.dart b/lib/modules/settings/core/database.dart index 887009f1..d523f96b 100644 --- a/lib/modules/settings/core/database.dart +++ b/lib/modules/settings/core/database.dart @@ -1,9 +1,40 @@ -import 'package:lunasea/core/database.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; -class SettingsDatabase { - SettingsDatabase._(); +class SettingsDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} - static void registerAdapters() {} + @override + Map export() { + Map data = {}; + for(SettingsDatabaseValue value in SettingsDatabaseValue.values) { + switch(value) { + // Primitive values + case SettingsDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + SettingsDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case SettingsDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break; + } + } + } + + @override + SettingsDatabaseValue valueFromKey(String key) { + switch(key) { + case 'SETTINGS_NAVIGATION_INDEX': return SettingsDatabaseValue.NAVIGATION_INDEX; + default: return null; + } + } } enum SettingsDatabaseValue { @@ -25,4 +56,17 @@ extension SettingsDatabaseValueExtension on SettingsDatabaseValue { } throw Exception('data not found'); } + + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case SettingsDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('SettingsDatabaseValueExtension', 'put', 'Attempted to enter data for invalid SettingsDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/settings/core/dialogs.dart b/lib/modules/settings/core/dialogs.dart index 522cf146..3021408f 100644 --- a/lib/modules/settings/core/dialogs.dart +++ b/lib/modules/settings/core/dialogs.dart @@ -262,32 +262,6 @@ class SettingsDialogs { return [_flag, _username.text, _password.text]; } - static Future> exportLogs(BuildContext context) async { - bool _flag = false; - - void _setValues(bool flag) { - _flag = flag; - Navigator.of(context, rootNavigator: true).pop(); - } - - await LSDialog.dialog( - context: context, - title: 'Export Logs', - buttons: [ - LSDialog.button( - text: 'Export', - onPressed: () => _setValues(true), - textColor: LunaColours.accent, - ), - ], - content: [ - LSDialog.textContent(text: 'Are you sure you want to export all recorded logs to the filesystem?'), - ], - contentPadding: LSDialog.textDialogContentPadding(), - ); - return [_flag]; - } - static Future> clearLogs(BuildContext context) async { bool _flag = false; @@ -469,9 +443,9 @@ class SettingsDialogs { static Future> changeBrowser(BuildContext context) async { bool _flag = false; - LSBrowsers _browser; + LunaBrowser _browser; - void _setValues(bool flag, LSBrowsers browser) { + void _setValues(bool flag, LunaBrowser browser) { _flag = flag; _browser = browser; Navigator.of(context, rootNavigator: true).pop(); @@ -481,12 +455,12 @@ class SettingsDialogs { context: context, title: 'Open Links In...', content: List.generate( - LSBrowsers.values.length, + LunaBrowser.values.length, (index) => LSDialog.tile( - icon: LSBrowsers.values[index].icon, + icon: LunaBrowser.values[index].icon, iconColor: LunaColours.list(index), - text: LSBrowsers.values[index].name, - onTap: () => _setValues(true, LSBrowsers.values[index]), + text: LunaBrowser.values[index].name, + onTap: () => _setValues(true, LunaBrowser.values[index]), ), ), contentPadding: LSDialog.listDialogContentPadding(), @@ -765,7 +739,6 @@ class SettingsDialogs { LSDialog.richText( children: [ LSDialog.textSpanContent(text: '•\tAll backups are encrypted before being exported to the filesystem\n'), - LSDialog.textSpanContent(text: '•\tThe backups do not contain customization options, only your configuration details\n'), LSDialog.textSpanContent(text: '•\tThe encryption key must be at least 8 characters'), ], ), @@ -800,11 +773,11 @@ class SettingsDialogs { await LSDialog.dialog( context: context, - title: 'Disable Sentry Logging', + title: 'Sentry', buttons: [ LSDialog.button( - text: 'Sentry Website', - onPressed: () => Constants.URL_SENTRY.lsLinks_OpenLink(), + text: 'Website', + onPressed: () => Constants.URL_SENTRY.lunaOpenGenericLink(), textColor: LunaColours.accent, ), LSDialog.button( @@ -817,12 +790,12 @@ class SettingsDialogs { LSDialog.richText( children: [ LSDialog.bolded( - text: 'Error logs and stacktraces contain absolutely no identifying information on any users.\n\n', + text: 'Errors and stacktraces contain absolutely no identifying information on any users.\n\n', color: LunaColours.red, fontSize: LSDialog.SUBBODY_SIZE, ), LSDialog.textSpanContent(text: 'Sentry is an open-source platform used for capturing crashes and errors.\n\n'), - LSDialog.textSpanContent(text: 'To reserve your right to privacy, I have added the option to disable Sentry logging, but please know that these error logs and stacktraces are incredibly useful for catching and pinpointing bugs!\n\n'), + LSDialog.textSpanContent(text: 'To reserve your right to privacy, I have added the option to disable Sentry error tracking, but please know that these errors and stacktraces are incredibly useful for catching and pinpointing bugs!\n\n'), LSDialog.bolded( text: 'A link to their website is available below for more information to help make an informed decision.', color: LunaColours.accent, @@ -841,7 +814,7 @@ class SettingsDialogs { bool _flag = false; int _opacity = 0; final _formKey = GlobalKey(); - final _textController = TextEditingController()..text = LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data.toString(); + final _textController = TextEditingController()..text = LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data.toString(); void _setValues(bool flag) { if(_formKey.currentState.validate()) { @@ -850,7 +823,7 @@ class SettingsDialogs { _flag = flag; Navigator.of(context, rootNavigator: true).pop(); } else { - LunaLogger.warning( + LunaLogger().warning( 'SettingsDialogs', 'changeBackgroundImageOpacity', 'Opacity passed validation but failed int.tryParse: ${_textController.text}', @@ -890,4 +863,18 @@ class SettingsDialogs { ); return [_flag, _opacity]; } + + static Future helpMessage(BuildContext context, { @required String title, @required String message, String website, String github }) async { + await LSDialog.dialog( + context: context, + title: title ?? Constants.TEXT_EMDASH, + buttons: ((github?.isNotEmpty ?? false) || (website?.isNotEmpty ?? false)) + ? [ + if(github?.isNotEmpty ?? false) LSDialog.button(text: 'GitHub', onPressed: () async => github.lunaOpenGenericLink()), + if(website?.isNotEmpty ?? false) LSDialog.button(text: 'Website', textColor: LunaColours.orange, onPressed: () async => website.lunaOpenGenericLink()), + ] : null, + content: [LSDialog.textContent(text: message)], + contentPadding: LSDialog.textDialogContentPadding(), + ); + } } diff --git a/lib/modules/settings/core/router.dart b/lib/modules/settings/core/router.dart index 874752fd..573a05ed 100644 --- a/lib/modules/settings/core/router.dart +++ b/lib/modules/settings/core/router.dart @@ -1,51 +1,40 @@ import 'package:fluro/fluro.dart'; +import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsRouter { - SettingsRouter._(); - - static void initialize(FluroRouter router) { - SettingsHomeRouter.defineRoutes(router); - // Customization - SettingsCustomizationRouter.defineRoutes(router); - SettingsCustomizationAppearanceRouter.defineRoutes(router); - SettingsCustomizationCalendarRouter.defineRoutes(router); - SettingsCustomizationDrawerRouter.defineRoutes(router); - SettingsCustomizationHomeRouter.defineRoutes(router); - SettingsCustomizationLidarrRouter.defineRoutes(router); - SettingsCustomizationNZBGetRouter.defineRoutes(router); - SettingsCustomizationQuickActionsRouter.defineRoutes(router); - SettingsCustomizationRadarrRouter.defineRoutes(router); - SettingsCustomizationSABnzbdRouter.defineRoutes(router); - SettingsCustomizationSearchRouter.defineRoutes(router); - SettingsCustomizationSonarrRouter.defineRoutes(router); - SettingsCustomizationTautulliRouter.defineRoutes(router); - // Modules - SettingsModulesRouter.defineRoutes(router); - SettingsModulesWakeOnLANRouter.defineRoutes(router); - SettingsModulesSearchRouter.defineRoutes(router); - SettingsModulesSearchAddRouter.defineRoutes(router); - SettingsModulesSearchEditRouter.defineRoutes(router); - SettingsModulesLidarrRouter.defineRoutes(router); - SettingsModulesLidarrHeadersRouter.defineRoutes(router); - SettingsModulesRadarrRouter.defineRoutes(router); - SettingsModulesRadarrHeadersRouter.defineRoutes(router); - SettingsModulesSonarrRouter.defineRoutes(router); - SettingsModulesSonarrHeadersRouter.defineRoutes(router); - SettingsModulesNZBGetRouter.defineRoutes(router); - SettingsModulesNZBGetHeadersRouter.defineRoutes(router); - SettingsModulesSABnzbdRouter.defineRoutes(router); - SettingsModulesSABnzbdHeadersRouter.defineRoutes(router); - SettingsModulesTautulliRouter.defineRoutes(router); - SettingsModulesTautulliHeadersRouter.defineRoutes(router); +class SettingsRouter extends LunaModuleRouter { + @override + void defineAllRoutes(FluroRouter router) { + SettingsHomeRouter().defineRoute(router); + // Configuration + SettingsConfigurationRouter().defineRoute(router); + SettingsConfigurationAppearanceRouter().defineRoute(router); + SettingsConfigurationDrawerRouter().defineRoute(router); + SettingsConfigurationHomeRouter().defineRoute(router); + SettingsConfigurationLidarrRouter().defineRoute(router); + SettingsConfigurationLidarrHeadersRouter().defineRoute(router); + SettingsConfigurationNZBGetRouter().defineRoute(router); + SettingsConfigurationNZBGetHeadersRouter().defineRoute(router); + SettingsConfigurationQuickActionsRouter().defineRoute(router); + SettingsConfigurationRadarrRouter().defineRoute(router); + SettingsConfigurationRadarrHeadersRouter().defineRoute(router); + SettingsConfigurationSABnzbdRouter().defineRoute(router); + SettingsConfigurationSABnzbdHeadersRouter().defineRoute(router); + SettingsConfigurationSearchRouter().defineRoute(router); + SettingsConfigurationSearchAddRouter().defineRoute(router); + SettingsConfigurationSearchEditRouter().defineRoute(router); + SettingsConfigurationSonarrRouter().defineRoute(router); + SettingsConfigurationSonarrHeadersRouter().defineRoute(router); + SettingsConfigurationTautulliRouter().defineRoute(router); + SettingsConfigurationTautulliHeadersRouter().defineRoute(router); + SettingsConfigurationWakeOnLANRouter().defineRoute(router); // Other - SettingsProfilesRouter.defineRoutes(router); - SettingsBackupRestoreRouter.defineRoutes(router); - SettingsDonationsRouter.defineRoutes(router); - SettingsDonationsThankYouRouter.defineRoutes(router); - SettingsLogsRouter.defineRoutes(router); - SettingsLogsDetailsRouter.defineRoutes(router); - SettingsResourcesRouter.defineRoutes(router); - SettingsSystemRouter.defineRoutes(router); + SettingsProfilesRouter().defineRoute(router); + SettingsDonationsRouter().defineRoute(router); + SettingsDonationsThankYouRouter().defineRoute(router); + SettingsResourcesRouter().defineRoute(router); + SettingsSystemRouter().defineRoute(router); + SettingsSystemLogsRouter().defineRoute(router); + SettingsSystemLogsDetailsRouter().defineRoute(router); } } diff --git a/lib/modules/settings/core/state.dart b/lib/modules/settings/core/state.dart index 234eca2f..b8babd12 100644 --- a/lib/modules/settings/core/state.dart +++ b/lib/modules/settings/core/state.dart @@ -1,6 +1,6 @@ import 'package:lunasea/core.dart'; -class SettingsState extends LunaGlobalState { +class SettingsState extends LunaModuleState { @override void reset() {} } diff --git a/lib/modules/settings/modules.dart b/lib/modules/settings/modules.dart index 5cc914e8..7e5377a0 100644 --- a/lib/modules/settings/modules.dart +++ b/lib/modules/settings/modules.dart @@ -1,41 +1,31 @@ -export 'modules/backuprestore.dart'; -export 'modules/customization.dart'; -export 'modules/customization_appearance.dart'; -export 'modules/customization_calendar.dart'; -export 'modules/customization_drawer.dart'; -export 'modules/customization_home.dart'; -export 'modules/customization_lidarr.dart'; -export 'modules/customization_nzbget.dart'; -export 'modules/customization_quickactions.dart'; -export 'modules/customization_radarr.dart'; -export 'modules/customization_sabnzbd.dart'; -export 'modules/customization_search.dart'; -export 'modules/customization_sonarr.dart'; -export 'modules/customization_tautulli.dart'; export 'modules/donations.dart'; export 'modules/donations_thankyou.dart'; -export 'modules/logs.dart'; -export 'modules/logs_details.dart'; -export 'modules/modules.dart'; -export 'modules/modules_lidarr.dart'; -export 'modules/modules_lidarr_headers.dart'; -export 'modules/modules_nzbget.dart'; -export 'modules/modules_nzbget_headers.dart'; -export 'modules/modules_radarr.dart'; -export 'modules/modules_radarr_headers.dart'; -export 'modules/modules_sabnzbd.dart'; -export 'modules/modules_sabnzbd_headers.dart'; -export 'modules/modules_search.dart'; -export 'modules/modules_search_add.dart'; -export 'modules/modules_search_add_headers.dart'; -export 'modules/modules_search_edit.dart'; -export 'modules/modules_search_edit_headers.dart'; -export 'modules/modules_sonarr.dart'; -export 'modules/modules_sonarr_headers.dart'; -export 'modules/modules_tautulli.dart'; -export 'modules/modules_tautulli_headers.dart'; -export 'modules/modules_wakeonlan.dart'; +export 'modules/configuration.dart'; +export 'modules/configuration_appearance.dart'; +export 'modules/configuration_drawer.dart'; +export 'modules/configuration_home.dart'; +export 'modules/configuration_lidarr.dart'; +export 'modules/configuration_lidarr_headers.dart'; +export 'modules/configuration_nzbget.dart'; +export 'modules/configuration_nzbget_headers.dart'; +export 'modules/configuration_quickactions.dart'; +export 'modules/configuration_radarr.dart'; +export 'modules/configuration_radarr_headers.dart'; +export 'modules/configuration_sabnzbd.dart'; +export 'modules/configuration_sabnzbd_headers.dart'; +export 'modules/configuration_search.dart'; +export 'modules/configuration_search_add.dart'; +export 'modules/configuration_search_add_headers.dart'; +export 'modules/configuration_search_edit.dart'; +export 'modules/configuration_search_edit_headers.dart'; +export 'modules/configuration_sonarr.dart'; +export 'modules/configuration_sonarr_headers.dart'; +export 'modules/configuration_tautulli.dart'; +export 'modules/configuration_tautulli_headers.dart'; +export 'modules/configuration_wakeonlan.dart'; export 'modules/profiles.dart'; export 'modules/resources.dart'; export 'modules/settings.dart'; export 'modules/system.dart'; +export 'modules/system_logs.dart'; +export 'modules/system_logs_details.dart'; diff --git a/lib/modules/settings/modules/backuprestore.dart b/lib/modules/settings/modules/backuprestore.dart deleted file mode 100644 index 258d8e82..00000000 --- a/lib/modules/settings/modules/backuprestore.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'backuprestore/route.dart'; -export 'backuprestore/widgets.dart'; diff --git a/lib/modules/settings/modules/backuprestore/route.dart b/lib/modules/settings/modules/backuprestore/route.dart deleted file mode 100644 index 579aeeb4..00000000 --- a/lib/modules/settings/modules/backuprestore/route.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:fluro/fluro.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsBackupRestoreRouter { - static const ROUTE_NAME = '/settings/backuprestore'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsBackupRestoreRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsBackupRestoreRouter._(); -} - -class _SettingsBackupRestoreRoute extends StatefulWidget { - @override - State<_SettingsBackupRestoreRoute> createState() => _State(); -} - -class _State extends State<_SettingsBackupRestoreRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Backup & Restore', - ); - - Widget get _body => LSListView( - children: [ - SettingsBackupRestoreBackupTile(), - SettingsBackupRestoreRestoreTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/backuprestore/widgets.dart b/lib/modules/settings/modules/backuprestore/widgets.dart deleted file mode 100644 index 0b5a672b..00000000 --- a/lib/modules/settings/modules/backuprestore/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/backup_tile.dart'; -export 'widgets/restore_tile.dart'; diff --git a/lib/modules/settings/modules/configuration.dart b/lib/modules/settings/modules/configuration.dart new file mode 100644 index 00000000..99a8180f --- /dev/null +++ b/lib/modules/settings/modules/configuration.dart @@ -0,0 +1,138 @@ +import 'dart:io'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules.dart'; + +class SettingsConfigurationRouter extends LunaPageRouter { + SettingsConfigurationRouter() : super('/settings/configuration'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationRoute()); +} + +class _SettingsConfigurationRoute extends StatefulWidget { + @override + State<_SettingsConfigurationRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Configuration', + actions: [ + _profileButton, + ], + ); + + Widget get _profileButton { + Future _execute() async { + List values = await SettingsDialogs.enabledProfile( + context, + Database.profilesBox.keys.map((x) => x as String).toList()..sort((a,b) => a.toLowerCase().compareTo(b.toLowerCase())), + ); + if(values[0] && values[1] != LunaDatabaseValue.ENABLED_PROFILE.data) + LunaProfile().safelyChangeProfiles(context, values[1]); + } + return LSIconButton( + icon: Icons.person, + onPressed: _execute, + ); + } + + Widget get _body => LSListView( + children: [ + ..._customization, + LSDivider(), + ..._general, + LSDivider(), + ..._automation, + LSDivider(), + ..._clients, + LSDivider(), + ..._monitoring, + ], + ); + + List get _customization => [ + LSCardTile( + title: LSTitle(text: 'Appearance'), + subtitle: LSSubtitle(text: 'Customize the Look & Feel'), + trailing: LSIconButton(icon: Icons.brush), + onTap: () async => SettingsConfigurationAppearanceRouter().navigateTo(context), + ), + LSCardTile( + title: LSTitle(text: 'Drawer'), + subtitle: LSSubtitle(text: 'Drawer Customizations'), + trailing: LSIconButton(icon: Icons.dehaze), + onTap: () async => SettingsConfigurationDrawerRouter().navigateTo(context), + ), + if(Platform.isIOS) _openLinksInTile, + LSCardTile( + title: LSTitle(text: 'Quick Actions'), + subtitle: LSSubtitle(text: 'Quick Actions on the Home Screen'), + trailing: LSIconButton(icon: Icons.rounded_corner), + onTap: () async => SettingsConfigurationQuickActionsRouter().navigateTo(context), + ), + ]; + + List get _general => [ + LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: 'Configure the Home Screen'), + trailing: LSIconButton(icon: CustomIcons.home), + onTap: () async => SettingsConfigurationHomeRouter().navigateTo(context), + ), + _tileFromModuleMap(SearchConstants.MODULE_METADATA, () async => SettingsConfigurationSearchRouter().navigateTo(context)), + _tileFromModuleMap(WakeOnLANConstants.MODULE_METADATA, () async => SettingsConfigurationWakeOnLANRouter().navigateTo(context)), + ]; + + List get _automation => [ + _tileFromModuleMap(LidarrConstants.MODULE_METADATA, () async => SettingsConfigurationLidarrRouter().navigateTo(context)), + _tileFromModuleMap(RadarrConstants.MODULE_METADATA, () async => SettingsConfigurationRadarrRouter().navigateTo(context)), + _tileFromModuleMap(SonarrConstants.MODULE_METADATA, () async => SettingsConfigurationSonarrRouter().navigateTo(context)), + ]; + + List get _clients => [ + _tileFromModuleMap(NZBGetConstants.MODULE_METADATA, () async => SettingsConfigurationNZBGetRouter().navigateTo(context)), + _tileFromModuleMap(SABnzbdConstants.MODULE_METADATA, () async => SettingsConfigurationSABnzbdRouter().navigateTo(context)), + ]; + + List get _monitoring => [ + _tileFromModuleMap(TautulliConstants.MODULE_METADATA, () async => SettingsConfigurationTautulliRouter().navigateTo(context)), + ]; + + Widget _tileFromModuleMap(LunaModuleMetadata map, Function onTap) => LSCardTile( + title: LSTitle(text: map.name), + subtitle: LSSubtitle(text: map.settingsDescription), + trailing: LSIconButton(icon: map.icon), + onTap: onTap, + ); + + Widget get _openLinksInTile { + Future _execute() async { + List _values = await SettingsDialogs.changeBrowser(context); + if(_values[0]) LunaDatabaseValue.SELECTED_BROWSER.put(_values[1]); + } + return ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.SELECTED_BROWSER.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Open Links In...'), + subtitle: LSSubtitle(text: (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).name), + trailing: LSIconButton( + icon: (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).icon, + ), + onTap: _execute, + ), + ); + } +} diff --git a/lib/modules/settings/modules/configuration_appearance.dart b/lib/modules/settings/modules/configuration_appearance.dart new file mode 100644 index 00000000..3a64b710 --- /dev/null +++ b/lib/modules/settings/modules/configuration_appearance.dart @@ -0,0 +1,100 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationAppearanceRouter extends LunaPageRouter { + SettingsConfigurationAppearanceRouter() : super('/settings/configuration/appearance'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationAppearanceRoute()); +} + +class _SettingsConfigurationAppearanceRoute extends StatefulWidget { + @override + State<_SettingsConfigurationAppearanceRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationAppearanceRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Appearance', + ); + + Widget get _body => LSListView( + children: [ + _amoledThemeTile, + _amoledThemeBordersTile, + _imageBackgroundOpacityTile, + _use24HourTime, + ], + ); + + Widget get _amoledThemeTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.THEME_AMOLED.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'AMOLED Dark Theme'), + subtitle: LSSubtitle(text: 'Pure Black Dark Theme'), + trailing: Switch( + value: LunaDatabaseValue.THEME_AMOLED.data, + onChanged: (value) => LunaDatabaseValue.THEME_AMOLED.put(value), + ), + ), + ); + + Widget get _amoledThemeBordersTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [ + LunaDatabaseValue.THEME_AMOLED_BORDER.key, + LunaDatabaseValue.THEME_AMOLED.key, + ]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'AMOLED Borders'), + subtitle: LSSubtitle(text: 'Add Subtle Borders Across the UI'), + trailing: Switch( + value: LunaDatabaseValue.THEME_AMOLED_BORDER.data, + onChanged: LunaDatabaseValue.THEME_AMOLED.data + ? (value) => LunaDatabaseValue.THEME_AMOLED_BORDER.put(value) + : null, + ), + ), + ); + + Widget get _imageBackgroundOpacityTile { + Future _execute() async { + List _values = await SettingsDialogs.changeBackgroundImageOpacity(context); + if(_values[0]) LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.put(_values[1]); + } + return ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Background Image Opacity'), + subtitle: LSSubtitle(text: LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0 + ? 'Disabled' + : '${LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data}%' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ), + ); + } + + Widget get _use24HourTime => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.USE_24_HOUR_TIME.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Use 24 Hour Time'), + subtitle: LSSubtitle(text: 'Show Timestamps in 24 Hour Style'), + trailing: Switch( + value: LunaDatabaseValue.USE_24_HOUR_TIME.data, + onChanged: (value) => LunaDatabaseValue.USE_24_HOUR_TIME.put(value), + ), + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_drawer.dart b/lib/modules/settings/modules/configuration_drawer.dart new file mode 100644 index 00000000..4d2c5c37 --- /dev/null +++ b/lib/modules/settings/modules/configuration_drawer.dart @@ -0,0 +1,106 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class SettingsConfigurationDrawerRouter extends LunaPageRouter { + SettingsConfigurationDrawerRouter() : super('/settings/configuration/drawer'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationDrawerRoute()); +} + +class _SettingsConfigurationDrawerRoute extends StatefulWidget { + @override + State<_SettingsConfigurationDrawerRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationDrawerRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Drawer', + ); + + Widget get _body => LSListView( + children: [ + ..._folders, + ], + ); + + List get _folders => [ + _useCategoriesTile, + _expandAutomationTile, + _expandClientsTile, + _expandMonitoringTile, + ]; + + Widget get _useCategoriesTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.DRAWER_GROUP_MODULES.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Use Folders'), + subtitle: LSSubtitle(text: 'Group Modules into Categories'), + trailing: Switch( + value: LunaDatabaseValue.DRAWER_GROUP_MODULES.data, + onChanged: (value) => LunaDatabaseValue.DRAWER_GROUP_MODULES.put(value), + ), + ), + ); + + Widget get _expandAutomationTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [ + LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION.key, + LunaDatabaseValue.DRAWER_GROUP_MODULES.key, + ]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Expand Automation'), + subtitle: LSSubtitle(text: 'Expand Automation Folder Initially'), + trailing: Switch( + value: LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data, + onChanged: LunaDatabaseValue.DRAWER_GROUP_MODULES.data + ? (value) => LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION.put(value) + : null, + ), + ), + ); + + Widget get _expandClientsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [ + LunaDatabaseValue.DRAWER_EXPAND_CLIENTS.key, + LunaDatabaseValue.DRAWER_GROUP_MODULES.key, + ]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Expand Clients'), + subtitle: LSSubtitle(text: 'Expand Clients Folder Initially'), + trailing: Switch( + value: LunaDatabaseValue.DRAWER_EXPAND_CLIENTS.data, + onChanged: LunaDatabaseValue.DRAWER_GROUP_MODULES.data + ? (value) => LunaDatabaseValue.DRAWER_EXPAND_CLIENTS.put(value) + : null, + ), + ), + ); + + Widget get _expandMonitoringTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [ + LunaDatabaseValue.DRAWER_EXPAND_MONITORING.key, + LunaDatabaseValue.DRAWER_GROUP_MODULES.key, + ]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Expand Monitoring'), + subtitle: LSSubtitle(text: 'Expand Monitoring Folder Initially'), + trailing: Switch( + value: LunaDatabaseValue.DRAWER_EXPAND_MONITORING.data, + onChanged: LunaDatabaseValue.DRAWER_GROUP_MODULES.data + ? (value) => LunaDatabaseValue.DRAWER_EXPAND_MONITORING.put(value) + : null, + ), + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_home.dart b/lib/modules/settings/modules/configuration_home.dart new file mode 100644 index 00000000..01f4b543 --- /dev/null +++ b/lib/modules/settings/modules/configuration_home.dart @@ -0,0 +1,184 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/home.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationHomeRouter extends LunaPageRouter { + SettingsConfigurationHomeRouter() : super('/settings/configuration/home'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationHomeRoute()); +} + +class _SettingsConfigurationHomeRoute extends StatefulWidget { + @override + State<_SettingsConfigurationHomeRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationHomeRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Home', + ); + + Widget get _body => LSListView( + children: [ + LSHeader(text: 'Default Pages'), + _defaultPageTile, + LSHeader(text: 'Appearance'), + _brandColoursTile, + LSHeader(text: 'Calendar Date Range'), + _pastDaysTile, + _futureDaysTile, + LSHeader(text: 'Calendar Options'), + _startingDayTile, + _startingSizeTile, + _startingTypeTile, + LSHeader(text: 'Calendar Modules'), + _modulesEnableLidarrTile, + _modulesEnableRadarrTile, + _modulesEnableSonarrTile, + ], + ); + + Widget get _brandColoursTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.MODULES_BRAND_COLOURS.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Use Brand Colours'), + subtitle: LSSubtitle(text: 'For Module Icons'), + trailing: Switch( + value: HomeDatabaseValue.MODULES_BRAND_COLOURS.data, + onChanged: (value) => HomeDatabaseValue.MODULES_BRAND_COLOURS.put(value), + ), + ), + ); + + Widget get _defaultPageTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: HomeNavigationBar.titles[HomeDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: HomeNavigationBar.icons[HomeDatabaseValue.NAVIGATION_INDEX.data]), + onTap: () async { + List _values = await HomeDialogs.defaultPage(context); + if(_values[0]) HomeDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + }, + ), + ); + + Widget get _pastDaysTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_DAYS_PAST.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Past Days'), + subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_DAYS_PAST.data == 1 + ? '1 Day' + : '${HomeDatabaseValue.CALENDAR_DAYS_PAST.data} Days', + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await HomeDialogs.setPastDays(context); + if(_values[0]) HomeDatabaseValue.CALENDAR_DAYS_PAST.put(_values[1]); + }, + ), + ); + + Widget get _futureDaysTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_DAYS_FUTURE.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Future Days'), + subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_DAYS_FUTURE.data == 1 + ? '1 Day' + : '${HomeDatabaseValue.CALENDAR_DAYS_FUTURE.data} Days', + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await HomeDialogs.setFutureDays(context); + if(_values[0]) HomeDatabaseValue.CALENDAR_DAYS_FUTURE.put(_values[1]); + }, + ), + ); + + Widget get _modulesEnableLidarrTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Lidarr'), + subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.data ? 'Showing Lidarr Entries' : 'Hiding Lidarr Entries'), + trailing: Switch( + value: HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.data, + onChanged: (value) => HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.put(value), + ), + ), + ); + + Widget get _modulesEnableRadarrTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_RADARR.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Radarr'), + subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_ENABLE_RADARR.data ? 'Showing Radarr Entries' : 'Hiding Radarr Entries'), + trailing: Switch( + value: HomeDatabaseValue.CALENDAR_ENABLE_RADARR.data, + onChanged: (value) => HomeDatabaseValue.CALENDAR_ENABLE_RADARR.put(value), + ), + ), + ); + + Widget get _modulesEnableSonarrTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_SONARR.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Sonarr'), + subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_ENABLE_SONARR.data ? 'Showing Sonarr Entries' : 'Hiding Sonarr Entries'), + trailing: Switch( + value: HomeDatabaseValue.CALENDAR_ENABLE_SONARR.data, + onChanged: (value) => HomeDatabaseValue.CALENDAR_ENABLE_SONARR.put(value), + ), + ), + ); + + Widget get _startingTypeTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_TYPE.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Starting Type'), + subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_TYPE.data as CalendarStartingType).name), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await SettingsDialogs.editCalendarStartingType(context); + if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_TYPE.put(_values[1]); + }, + ), + ); + + Widget get _startingDayTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_DAY.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Starting Day'), + subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_DAY.data as CalendarStartingDay).name), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await SettingsDialogs.editCalendarStartingDay(context); + if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_DAY.put(_values[1]); + }, + ), + ); + + Widget get _startingSizeTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_SIZE.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Starting Size'), + subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_SIZE.data as CalendarStartingSize).name), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await SettingsDialogs.editCalendarStartingSize(context); + if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_SIZE.put(_values[1]); + }, + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_lidarr.dart b/lib/modules/settings/modules/configuration_lidarr.dart new file mode 100644 index 00000000..853b4d3f --- /dev/null +++ b/lib/modules/settings/modules/configuration_lidarr.dart @@ -0,0 +1,171 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/lidarr.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationLidarrRouter extends LunaPageRouter { + SettingsConfigurationLidarrRouter() : super('/settings/configuration/lidarr'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationLidarrRoute()); +} + +class _SettingsConfigurationLidarrRoute extends StatefulWidget { + @override + State<_SettingsConfigurationLidarrRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationLidarrRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Lidarr', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: LidarrConstants.MODULE_METADATA.name, + message: LidarrConstants.MODULE_METADATA.helpMessage, + github: LidarrConstants.MODULE_METADATA.github, + website: LidarrConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _apiKeyTile, + _customHeadersTile, + _testConnectionTile, + ]; + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _configDefaultPageTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable Lidarr'), + trailing: Switch( + value: Database.currentProfileObject.lidarrEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.lidarrEnabled = value; + Database.currentProfileObject.save(); + }, + ), + ); + + Widget get _hostTile { + Future _execute() async { + List _values = await SettingsDialogs.editHost( + context, + 'Lidarr Host', + prefill: Database.currentProfileObject.lidarrHost ?? '', + ); + if(_values[0]) { + Database.currentProfileObject.lidarrHost = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.lidarrHost == null || Database.currentProfileObject.lidarrHost == '' + ? 'Not Set' + : Database.currentProfileObject.lidarrHost + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _apiKeyTile { + Future _execute() async { + List _values = await LunaDialogs().editText( + context, + 'Lidarr API Key', + prefill: Database.currentProfileObject.lidarrKey ?? '', + ); + if(_values[0]) { + Database.currentProfileObject.lidarrKey = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'API Key'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.lidarrKey == null || Database.currentProfileObject.lidarrKey == '' + ? 'Not Set' + : '••••••••••••' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _testConnectionTile { + Future _testConnection(BuildContext context) async => await LidarrAPI.from(Database.currentProfileObject).testConnection() + .then((_) => showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'Lidarr is ready to use with LunaSea', + )) + .catchError((error, stack) { + LunaLogger().error('Connection Test Failed', error, stack); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + return LSButton( + text: 'Test Connection', + onTap: () async => _testConnection(context), + ); + } + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationLidarrHeadersRouter().navigateTo(context), + ); + + Widget get _configDefaultPageTile { + Future _execute() async { + List _values = await LidarrDialogs.defaultPage(context); + if(_values[0]) LidarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + } + return ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LidarrDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Default Page'), + subtitle: LSSubtitle(text: LidarrNavigationBar.titles[LidarrDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: LidarrNavigationBar.icons[LidarrDatabaseValue.NAVIGATION_INDEX.data]), + onTap: _execute, + ), + ); + } +} diff --git a/lib/modules/settings/modules/configuration_lidarr_headers.dart b/lib/modules/settings/modules/configuration_lidarr_headers.dart new file mode 100644 index 00000000..39abcc05 --- /dev/null +++ b/lib/modules/settings/modules/configuration_lidarr_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationLidarrHeadersRouter extends LunaPageRouter { + SettingsConfigurationLidarrHeadersRouter() : super('/settings/configuration/lidarr/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationLidarrHeadersRoute()); +} + +class _SettingsConfigurationLidarrHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationLidarrHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationLidarrHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.lidarrHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.lidarrHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.lidarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.lidarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationLidarrHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_nzbget.dart b/lib/modules/settings/modules/configuration_nzbget.dart new file mode 100644 index 00000000..c64b4e7b --- /dev/null +++ b/lib/modules/settings/modules/configuration_nzbget.dart @@ -0,0 +1,190 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/nzbget.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationNZBGetRouter extends LunaPageRouter { + SettingsConfigurationNZBGetRouter() : super('/settings/configuration/nzbget'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationNZBGetRoute()); +} + +class _SettingsConfigurationNZBGetRoute extends StatefulWidget { + @override + State<_SettingsConfigurationNZBGetRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationNZBGetRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'NZBGet', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: NZBGetConstants.MODULE_METADATA.name, + message: NZBGetConstants.MODULE_METADATA.helpMessage, + github: NZBGetConstants.MODULE_METADATA.github, + website: NZBGetConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _usernameTile, + _passwordTile, + _customHeadersTile, + _testConnectionTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable NZBGet'), + trailing: Switch( + value: Database.currentProfileObject.nzbgetEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.nzbgetEnabled = value; + Database.currentProfileObject.save(); + }, + ), + ); + + Widget get _hostTile { + Future _execute() async { + List _values = await SettingsDialogs.editHost(context, 'NZBGet Host', prefill: Database.currentProfileObject.nzbgetHost ?? ''); + if(_values[0]) { + Database.currentProfileObject.nzbgetHost = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.nzbgetHost == null || Database.currentProfileObject.nzbgetHost == '' + ? 'Not Set' + : Database.currentProfileObject.nzbgetHost + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _usernameTile { + Future _execute() async { + List _values = await LunaDialogs().editText(context, 'NZBGet Username', prefill: Database.currentProfileObject.nzbgetUser ?? ''); + if(_values[0]) { + Database.currentProfileObject.nzbgetUser = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Username'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.nzbgetUser == null || Database.currentProfileObject.nzbgetUser == '' + ? 'Not Set' + : Database.currentProfileObject.nzbgetUser + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _passwordTile { + Future _execute() async { + List _values = await LunaDialogs().editText( + context, + 'NZBGet Password', + prefill: Database.currentProfileObject.nzbgetPass ?? '', + extraText: [ + LSDialog.textSpanContent(text: '${Constants.TEXT_BULLET}\tIf your password includes special characters, considering adding a '), + LSDialog.bolded(text: 'basic authentication'), + LSDialog.textSpanContent(text: ' header with your username and password instead for better support'), + ], + ); + if(_values[0]) { + Database.currentProfileObject.nzbgetPass = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Password'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.nzbgetPass == null || Database.currentProfileObject.nzbgetPass == '' + ? 'Not Set' + : '••••••••••••' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationNZBGetHeadersRouter().navigateTo(context), + ); + + Widget get _testConnectionTile { + Future _testConnection(BuildContext context) async => await NZBGetAPI.from(Database.currentProfileObject).testConnection() + .then((_) => showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'NZBGet is ready to use with LunaSea', + )) + .catchError((error, stack) { + LunaLogger().error('Connection Test Failed', error, stack); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + return LSButton( + text: 'Test Connection', + onTap: () async => _testConnection(context), + ); + } + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _defaultPageTile, + ]; + + Widget get _defaultPageTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [NZBGetDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: NZBGetNavigationBar.titles[NZBGetDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: NZBGetNavigationBar.icons[NZBGetDatabaseValue.NAVIGATION_INDEX.data]), + onTap: () async { + List _values = await NZBGetDialogs.defaultPage(context); + if(_values[0]) NZBGetDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + }, + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_nzbget_headers.dart b/lib/modules/settings/modules/configuration_nzbget_headers.dart new file mode 100644 index 00000000..60e7117c --- /dev/null +++ b/lib/modules/settings/modules/configuration_nzbget_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationNZBGetHeadersRouter extends LunaPageRouter { + SettingsConfigurationNZBGetHeadersRouter() : super('/settings/configuration/nzbget/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationNZBGetHeadersRoute()); +} + +class _SettingsConfigurationNZBGetHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationNZBGetHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationNZBGetHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.nzbgetHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.nzbgetHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.nzbgetHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.nzbgetHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationNZBGetHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_quickactions.dart b/lib/modules/settings/modules/configuration_quickactions.dart new file mode 100644 index 00000000..98d5067c --- /dev/null +++ b/lib/modules/settings/modules/configuration_quickactions.dart @@ -0,0 +1,69 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class SettingsConfigurationQuickActionsRouter extends LunaPageRouter { + SettingsConfigurationQuickActionsRouter() : super('/settings/configuration/quickactions'); + + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationQuickActionsRoute()); +} + +class _SettingsConfigurationQuickActionsRoute extends StatefulWidget { + @override + State<_SettingsConfigurationQuickActionsRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationQuickActionsRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + final _helpMessage = [ + 'Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing ${Constants.APPLICATION_NAME}\'s icon.', + 'A limited number of quick actions can be set at a time, and enabling more than your launcher can support will have no effect.' + ].join('\n\n'); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Quick Actions', + actions: [ + LSIconButton( + icon: Icons.help_outline, + onPressed: () async => LunaDialogs().textPreview(context, 'Help', _helpMessage), + ) + ], + ); + + Widget get _body => LSListView( + children: [ + _actionTile('Search', LunaDatabaseValue.QUICK_ACTIONS_SEARCH), + LSDivider(), + _actionTile('Lidarr', LunaDatabaseValue.QUICK_ACTIONS_LIDARR), + _actionTile('Radarr', LunaDatabaseValue.QUICK_ACTIONS_RADARR), + _actionTile('Sonarr', LunaDatabaseValue.QUICK_ACTIONS_SONARR), + LSDivider(), + _actionTile('NZBGet', LunaDatabaseValue.QUICK_ACTIONS_NZBGET), + _actionTile('SABnzbd', LunaDatabaseValue.QUICK_ACTIONS_SABNZBD), + LSDivider(), + _actionTile('Tautulli', LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI), + ], + ); + + Widget _actionTile(String title, LunaDatabaseValue action) => LSCardTile( + title: LSTitle(text: title), + trailing: ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [action.key]), + builder: (context, box, _) => Switch( + value: action.data, + onChanged: (value) { + action.put(value); + LunaQuickActions.setShortcutItems(); + } + ), + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_radarr.dart b/lib/modules/settings/modules/configuration_radarr.dart new file mode 100644 index 00000000..0f860b0c --- /dev/null +++ b/lib/modules/settings/modules/configuration_radarr.dart @@ -0,0 +1,171 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/radarr.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationRadarrRouter extends LunaPageRouter { + SettingsConfigurationRadarrRouter() : super('/settings/configuration/radarr'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationRadarrRoute()); +} + +class _SettingsConfigurationRadarrRoute extends StatefulWidget { + @override + State<_SettingsConfigurationRadarrRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationRadarrRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Radarr', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: RadarrConstants.MODULE_METADATA.name, + message: RadarrConstants.MODULE_METADATA.helpMessage, + github: RadarrConstants.MODULE_METADATA.github, + website: RadarrConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _apiKeyTile, + _customHeadersTile, + _testConnectionTile, + ]; + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _configDefaultPageTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable Radarr'), + trailing: Switch( + value: Database.currentProfileObject.radarrEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.radarrEnabled = value; + Database.currentProfileObject.save(); + }, + ), + ); + + Widget get _hostTile { + Future _execute() async { + List _values = await SettingsDialogs.editHost( + context, + 'Radarr Host', + prefill: Database.currentProfileObject.radarrHost ?? '', + ); + if(_values[0]) { + Database.currentProfileObject.radarrHost = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.radarrHost == null || Database.currentProfileObject.radarrHost == '' + ? 'Not Set' + : Database.currentProfileObject.radarrHost + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _apiKeyTile { + Future _execute() async { + List _values = await LunaDialogs().editText( + context, + 'Radarr API Key', + prefill: Database.currentProfileObject.radarrKey ?? '', + ); + if(_values[0]) { + Database.currentProfileObject.radarrKey = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'API Key'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.radarrKey == null || Database.currentProfileObject.radarrKey == '' + ? 'Not Set' + : '••••••••••••' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _testConnectionTile { + Future _testConnection(BuildContext context) async => await RadarrAPI.from(Database.currentProfileObject).testConnection() + .then((_) => showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'Radarr is ready to use with LunaSea', + )) + .catchError((error, stack) { + LunaLogger().error('Connection Test Failed', error, stack); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + return LSButton( + text: 'Test Connection', + onTap: () async => _testConnection(context), + ); + } + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationRadarrHeadersRouter().navigateTo(context), + ); + + Widget get _configDefaultPageTile { + Future _execute() async { + List _values = await RadarrDialogs.defaultPage(context); + if(_values[0]) RadarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + } + return ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [RadarrDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Default Page'), + subtitle: LSSubtitle(text: RadarrNavigationBar.titles[RadarrDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: RadarrNavigationBar.icons[RadarrDatabaseValue.NAVIGATION_INDEX.data]), + onTap: _execute, + ), + ); + } +} diff --git a/lib/modules/settings/modules/configuration_radarr_headers.dart b/lib/modules/settings/modules/configuration_radarr_headers.dart new file mode 100644 index 00000000..c882e95b --- /dev/null +++ b/lib/modules/settings/modules/configuration_radarr_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationRadarrHeadersRouter extends LunaPageRouter { + SettingsConfigurationRadarrHeadersRouter() : super('/settings/configuration/radarr/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationRadarrHeadersRoute()); +} + +class _SettingsConfigurationRadarrHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationRadarrHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationRadarrHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.radarrHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.radarrHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.radarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.radarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationRadarrHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_sabnzbd.dart b/lib/modules/settings/modules/configuration_sabnzbd.dart new file mode 100644 index 00000000..2650802c --- /dev/null +++ b/lib/modules/settings/modules/configuration_sabnzbd.dart @@ -0,0 +1,171 @@ +import 'package:dio/dio.dart'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/sabnzbd.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationSABnzbdRouter extends LunaPageRouter { + SettingsConfigurationSABnzbdRouter() : super('/settings/configuration/sabnzbd'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationSABnzbdRoute()); +} + +class _SettingsConfigurationSABnzbdRoute extends StatefulWidget { + @override + State<_SettingsConfigurationSABnzbdRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationSABnzbdRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'SABnzbd', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: SABnzbdConstants.MODULE_METADATA.name, + message: SABnzbdConstants.MODULE_METADATA.helpMessage, + github: SABnzbdConstants.MODULE_METADATA.github, + website: SABnzbdConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _apiKeyTile, + _customHeadersTile, + _testConnectionTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable SABnzbd'), + trailing: Switch( + value: Database.currentProfileObject.sabnzbdEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.sabnzbdEnabled = value; + Database.currentProfileObject.save(); + }, + ), + ); + + Widget get _hostTile { + Future _execute() async { + List _values = await SettingsDialogs.editHost(context, 'SABnzbd Host', prefill: Database.currentProfileObject.sabnzbdHost ?? ''); + if(_values[0]) { + Database.currentProfileObject.sabnzbdHost = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.sabnzbdHost == null || Database.currentProfileObject.sabnzbdHost == '' + ? 'Not Set' + : Database.currentProfileObject.sabnzbdHost + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _apiKeyTile { + Future _execute() async { + List _values = await LunaDialogs().editText(context, 'SABnzbd API Key', prefill: Database.currentProfileObject.sabnzbdKey ?? ''); + if(_values[0]) { + Database.currentProfileObject.sabnzbdKey = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'API Key'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.sabnzbdKey == null || Database.currentProfileObject.sabnzbdKey == '' + ? 'Not Set' + : '••••••••••••' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationSABnzbdHeadersRouter().navigateTo(context), + ); + + Widget get _testConnectionTile { + Future _testConnection(BuildContext context) async => await SABnzbdAPI.from(Database.currentProfileObject).testConnection() + .then((response) { + if((response as Response).data['status'] != false) { + showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'SABnzbd is ready to use with LunaSea', + ); + } else { + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + message: 'SABnzbd: ${(response as Response).data['error'] ?? Constants.TEXT_EMDASH}', + ); + } + }) + .catchError((error, stack) { + LunaLogger().error('Connection Test Failed', error, stack); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + return LSButton( + text: 'Test Connection', + onTap: () async => _testConnection(context), + ); + } + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _defaultPageTile, + ]; + + Widget get _defaultPageTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SABnzbdDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: SABnzbdNavigationBar.titles[SABnzbdDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: SABnzbdNavigationBar.icons[SABnzbdDatabaseValue.NAVIGATION_INDEX.data]), + onTap: () async { + List _values = await SABnzbdDialogs.defaultPage(context); + if(_values[0]) SABnzbdDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + }, + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_sabnzbd_headers.dart b/lib/modules/settings/modules/configuration_sabnzbd_headers.dart new file mode 100644 index 00000000..3a68486a --- /dev/null +++ b/lib/modules/settings/modules/configuration_sabnzbd_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationSABnzbdHeadersRouter extends LunaPageRouter { + SettingsConfigurationSABnzbdHeadersRouter() : super('/settings/configuration/sabnzbd/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationSABnzbdHeadersRoute()); +} + +class _SettingsConfigurationSABnzbdHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationSABnzbdHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationSABnzbdHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.sabnzbdHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.sabnzbdHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.sabnzbdHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.sabnzbdHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationSABnzbdHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_search.dart b/lib/modules/settings/modules/configuration_search.dart new file mode 100644 index 00000000..c57a69aa --- /dev/null +++ b/lib/modules/settings/modules/configuration_search.dart @@ -0,0 +1,103 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/search/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationSearchRouter extends LunaPageRouter { + SettingsConfigurationSearchRouter() : super('/settings/configuration/search'); + + void defineRoute(FluroRouter router) => router.define( + fullRoute, + handler: Handler(handlerFunc: (context, params) => _SettingsConfigurationSearchRoute()), + transitionType: LunaRouter.transitionType, + ); +} + +class _SettingsConfigurationSearchRoute extends StatefulWidget { + @override + State<_SettingsConfigurationSearchRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationSearchRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Search', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: SearchConstants.MODULE_METADATA.name, + message: SearchConstants.MODULE_METADATA.helpMessage, + github: SearchConstants.MODULE_METADATA.github, + website: SearchConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.indexersBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._indexerSection, + ..._filteringSection, + ], + ), + ); + + List get _indexerSection => [ + if(Database.indexersBox.isEmpty) LSGenericMessage(text: 'No Indexers Added'), + ..._indexers, + LSButton( + text: 'Add New Indexer', + onTap: () async => SettingsConfigurationSearchAddRouter().navigateTo(context), + ), + ]; + + List get _indexers { + List list = List.generate( + Database.indexersBox.length, + (index) => _indexerTile(Database.indexersBox.getAt(index), index), + ); + list.sort((a,b) => (a.subtitle as LSSubtitle).text.toLowerCase().trim().compareTo((b.subtitle as LSSubtitle).text.toLowerCase().trim())); + return list; + } + + List get _filteringSection => [ + LSHeader(text: 'Filtering'), + _hideAdultCategoriesTile, + ]; + + Widget get _hideAdultCategoriesTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SearchDatabaseValue.HIDE_XXX.key]), + builder: (context, box, widget) => LSCardTile( + title: LSTitle(text: 'Hide Adult Categories'), + subtitle: LSSubtitle(text: 'Hide Adult Content'), + trailing: Switch( + value: SearchDatabaseValue.HIDE_XXX.data, + onChanged: (value) => SearchDatabaseValue.HIDE_XXX.put(value), + ), + ), + ); + + Widget _indexerTile(IndexerHiveObject indexer, int index) => LSCardTile( + title: LSTitle(text: indexer.displayName), + subtitle: LSSubtitle(text: indexer.host), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationSearchEditRouter().navigateTo( + context, + index: index, + ), + ); +} diff --git a/lib/modules/settings/modules/modules_search_add/route.dart b/lib/modules/settings/modules/configuration_search_add.dart similarity index 73% rename from lib/modules/settings/modules/modules_search_add/route.dart rename to lib/modules/settings/modules/configuration_search_add.dart index 75f412da..5469f9b2 100644 --- a/lib/modules/settings/modules/modules_search_add/route.dart +++ b/lib/modules/settings/modules/configuration_search_add.dart @@ -3,31 +3,18 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsModulesSearchAddRouter { - static const ROUTE_NAME = '/settings/modules/search/add'; +class SettingsConfigurationSearchAddRouter extends LunaPageRouter { + SettingsConfigurationSearchAddRouter() : super('/settings/configuration/search/add'); - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSearchAddRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSearchAddRouter._(); + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationSearchAddRoute()); } -class _SettingsModulesSearchAddRoute extends StatefulWidget { +class _SettingsConfigurationSearchAddRoute extends StatefulWidget { @override - State<_SettingsModulesSearchAddRoute> createState() => _State(); + State<_SettingsConfigurationSearchAddRoute> createState() => _State(); } -class _State extends State<_SettingsModulesSearchAddRoute> { +class _State extends State<_SettingsConfigurationSearchAddRoute> { final GlobalKey _scaffoldKey = GlobalKey(); IndexerHiveObject indexer = IndexerHiveObject.empty(); @@ -62,7 +49,7 @@ class _State extends State<_SettingsModulesSearchAddRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Display Name', prefill: indexer.displayName); + List _values = await LunaDialogs().editText(context, 'Display Name', prefill: indexer.displayName); setState(() => indexer.displayName = _values[0] ? _values[1] : indexer.displayName @@ -79,7 +66,7 @@ class _State extends State<_SettingsModulesSearchAddRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Indexer API Host', prefill: indexer.host); + List _values = await LunaDialogs().editText(context, 'Indexer API Host', prefill: indexer.host); setState(() => indexer.host = _values[0] ? _values[1] : indexer.host @@ -96,7 +83,7 @@ class _State extends State<_SettingsModulesSearchAddRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Indexer API Key', prefill: indexer.key); + List _values = await LunaDialogs().editText(context, 'Indexer API Key', prefill: indexer.key); setState(() => indexer.key = _values[0] ? _values[1] : indexer.key @@ -109,7 +96,7 @@ class _State extends State<_SettingsModulesSearchAddRoute> { subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => SettingsModulesSearchAddHeadersRoute(indexer: indexer), + builder: (context) => SettingsConfigurationSearchAddHeadersRoute(indexer: indexer), )), ); diff --git a/lib/modules/settings/modules/modules_search_add_headers/route.dart b/lib/modules/settings/modules/configuration_search_add_headers.dart similarity index 92% rename from lib/modules/settings/modules/modules_search_add_headers/route.dart rename to lib/modules/settings/modules/configuration_search_add_headers.dart index 91aec3df..9237f81f 100644 --- a/lib/modules/settings/modules/modules_search_add_headers/route.dart +++ b/lib/modules/settings/modules/configuration_search_add_headers.dart @@ -3,19 +3,19 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsModulesSearchAddHeadersRoute extends StatefulWidget { +class SettingsConfigurationSearchAddHeadersRoute extends StatefulWidget { final IndexerHiveObject indexer; - SettingsModulesSearchAddHeadersRoute({ + SettingsConfigurationSearchAddHeadersRoute({ Key key, @required this.indexer, }) : super(key: key); @override - State createState() => _State(); + State createState() => _State(); } -class _State extends State { +class _State extends State { final GlobalKey _scaffoldKey = GlobalKey(); @override @@ -73,9 +73,9 @@ class _State extends State { _showCustomPrompt(context); break; default: - LunaLogger.warning( + LunaLogger().warning( 'SettingsModulesLidarrHeadersAddHeaderTile', - '_addPrompt', + '_add', 'Unknown case: ${results[1]}', ); break; diff --git a/lib/modules/settings/modules/modules_search_edit/route.dart b/lib/modules/settings/modules/configuration_search_edit.dart similarity index 74% rename from lib/modules/settings/modules/modules_search_edit/route.dart rename to lib/modules/settings/modules/configuration_search_edit.dart index 52195a34..d82127cc 100644 --- a/lib/modules/settings/modules/modules_search_edit/route.dart +++ b/lib/modules/settings/modules/configuration_search_edit.dart @@ -3,44 +3,38 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsModulesSearchEditRouter { - static const ROUTE_NAME = '/settings/modules/search/edit/:index'; +class SettingsConfigurationSearchEditRouter extends LunaPageRouter { + SettingsConfigurationSearchEditRouter() : super('/settings/configuration/search/edit/:index'); - static Future navigateTo(BuildContext context, { - @required int index, - }) async => LunaRouter.router.navigateTo( - context, - route(index: index), - ); + @override + Future navigateTo(BuildContext context, { @required int index }) async => LunaRouter.router.navigateTo(context, route(index: index)); - static String route({ - @required int index, - }) => ROUTE_NAME.replaceFirst(':index', index?.toString() ?? 0); + @override + String route({ @required int index }) => super.fullRoute.replaceFirst(':index', index?.toString() ?? 0); - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSearchEditRoute( + @override + void defineRoute(FluroRouter router) => router.define( + super.fullRoute, + handler: Handler(handlerFunc: (context, params) => _SettingsConfigurationSearchEditRoute( index: params['index'] == null ? 0 : int.tryParse(params['index'][0] ?? 0), )), transitionType: LunaRouter.transitionType, ); - - SettingsModulesSearchEditRouter._(); } -class _SettingsModulesSearchEditRoute extends StatefulWidget { +class _SettingsConfigurationSearchEditRoute extends StatefulWidget { final int index; - _SettingsModulesSearchEditRoute({ + _SettingsConfigurationSearchEditRoute({ Key key, @required this.index, }) : super(key: key); @override - State<_SettingsModulesSearchEditRoute> createState() => _State(); + State<_SettingsConfigurationSearchEditRoute> createState() => _State(); } -class _State extends State<_SettingsModulesSearchEditRoute> { +class _State extends State<_SettingsConfigurationSearchEditRoute> { final GlobalKey _scaffoldKey = GlobalKey(); IndexerHiveObject _indexer; @@ -54,8 +48,8 @@ class _State extends State<_SettingsModulesSearchEditRoute> { try { _indexer = Database.indexersBox.getAt(widget.index); } catch (_) { - LunaLogger.warning( - '_SettingsModulesSearchEditRoute', + LunaLogger().warning( + '_SettingsConfigurationSearchEditRoute', '_fetchIndexer', 'Unable to fetch indexer', ); @@ -95,7 +89,7 @@ class _State extends State<_SettingsModulesSearchEditRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Display Name', prefill: _indexer.displayName); + List _values = await LunaDialogs().editText(context, 'Display Name', prefill: _indexer.displayName); setState(() => _indexer.displayName = _values[0] ? _values[1] : _indexer.displayName @@ -113,7 +107,7 @@ class _State extends State<_SettingsModulesSearchEditRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Indexer API Host', prefill: _indexer.host); + List _values = await LunaDialogs().editText(context, 'Indexer API Host', prefill: _indexer.host); setState(() => _indexer.host = _values[0] ? _values[1] : _indexer.host @@ -131,7 +125,7 @@ class _State extends State<_SettingsModulesSearchEditRoute> { ), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async { - List _values = await LunaDialogs.editText(context, 'Indexer API Key', prefill: _indexer.key); + List _values = await LunaDialogs().editText(context, 'Indexer API Key', prefill: _indexer.key); setState(() => _indexer.key = _values[0] ? _values[1] : _indexer.key @@ -145,7 +139,7 @@ class _State extends State<_SettingsModulesSearchEditRoute> { subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => SettingsModulesSearchEditHeadersRoute(indexer: _indexer), + builder: (context) => SettingsConfigurationSearchEditHeadersRoute(indexer: _indexer), )), ); diff --git a/lib/modules/settings/modules/modules_search_edit_headers/route.dart b/lib/modules/settings/modules/configuration_search_edit_headers.dart similarity index 90% rename from lib/modules/settings/modules/modules_search_edit_headers/route.dart rename to lib/modules/settings/modules/configuration_search_edit_headers.dart index 20cf0f33..691210f4 100644 --- a/lib/modules/settings/modules/modules_search_edit_headers/route.dart +++ b/lib/modules/settings/modules/configuration_search_edit_headers.dart @@ -3,19 +3,19 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsModulesSearchEditHeadersRoute extends StatefulWidget { +class SettingsConfigurationSearchEditHeadersRoute extends StatefulWidget { final IndexerHiveObject indexer; - SettingsModulesSearchEditHeadersRoute({ + SettingsConfigurationSearchEditHeadersRoute({ Key key, @required this.indexer, }) : super(key: key); @override - State createState() => _State(); + State createState() => _State(); } -class _State extends State { +class _State extends State { final GlobalKey _scaffoldKey = GlobalKey(); @override @@ -73,9 +73,9 @@ class _State extends State { _showCustomPrompt(context); break; default: - LunaLogger.warning( - 'SettingsModulesLidarrHeadersAddHeaderTile', - '_addPrompt', + LunaLogger().warning( + 'SettingsConfigurationSearchEditHeadersRoute', + '_add', 'Unknown case: ${results[1]}', ); break; diff --git a/lib/modules/settings/modules/configuration_sonarr.dart b/lib/modules/settings/modules/configuration_sonarr.dart new file mode 100644 index 00000000..5b437e38 --- /dev/null +++ b/lib/modules/settings/modules/configuration_sonarr.dart @@ -0,0 +1,264 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; +import 'package:lunasea/modules/sonarr.dart'; + +class SettingsConfigurationSonarrRouter extends LunaPageRouter { + SettingsConfigurationSonarrRouter() : super('/settings/configuration/sonarr'); + + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationSonarrRoute()); +} + +class _SettingsConfigurationSonarrRoute extends StatefulWidget { + @override + State<_SettingsConfigurationSonarrRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationSonarrRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Sonarr', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: SonarrConstants.MODULE_METADATA.name, + message: SonarrConstants.MODULE_METADATA.helpMessage, + github: SonarrConstants.MODULE_METADATA.github, + website: SonarrConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _apiKeyTile, + _customHeadersTile, + _enableVersion3Tile, + _testConnectionTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable Sonarr'), + trailing: Switch( + value: Database.currentProfileObject.sonarrEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.sonarrEnabled = value; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + }, + ), + ); + + Widget get _hostTile => LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle(text: Database.currentProfileObject.sonarrHost == null || Database.currentProfileObject.sonarrHost == '' ? 'Not Set' : Database.currentProfileObject.sonarrHost), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await SettingsDialogs.editHost(context, 'Sonarr Host', prefill: Database.currentProfileObject.sonarrHost ?? ''); + if(_values[0]) { + Database.currentProfileObject.sonarrHost = _values[1]; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + } + }, + ); + + Widget get _apiKeyTile => LSCardTile( + title: LSTitle(text: 'API Key'), + subtitle: LSSubtitle(text: Database.currentProfileObject.sonarrKey == null || Database.currentProfileObject.sonarrKey == '' ? 'Not Set' : '••••••••••••'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _values = await LunaDialogs().editText(context, 'Sonarr API Key', prefill: Database.currentProfileObject.sonarrKey ?? ''); + if(_values[0]) { + Database.currentProfileObject.sonarrKey = _values[1]; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + } + }, + ); + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationSonarrHeadersRouter().navigateTo(context), + ); + + Widget get _enableVersion3Tile => LSCardTile( + title: LSTitle(text: 'Sonarr v3 Features'), + subtitle: LSSubtitle(text: 'Enable Version 3 Specific Features'), + trailing: Switch( + value: Database.currentProfileObject.sonarrVersion3 ?? false, + onChanged: (value) { + Database.currentProfileObject.sonarrVersion3 = value; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + }, + ), + ); + + Widget get _testConnectionTile => LSButton( + text: 'Test Connection', + onTap: () async { + SonarrState state = Provider.of(context, listen: false); + if(state.host == null || state.host.isEmpty) { + showLunaErrorSnackBar( + context: context, + title: 'Host Required', + message: 'Host is required to connect to Sonarr', + ); + return; + } + if(state.apiKey == null || state.apiKey.isEmpty) { + showLunaErrorSnackBar( + context: context, + title: 'API Key Required', + message: 'API key is required to connect to Sonarr', + ); + return; + } + Sonarr(host: state.host, apiKey: state.apiKey, headers: Map.from(state.headers)).system.getStatus() + .then((_) => showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'Sonarr is ready to use with LunaSea', + )).catchError((error, trace) { + LunaLogger().error('Connection Test Failed', error, trace); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + }, + ); + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _defaultPageHomeTile, + _defaultPageSeriesDetailsTile, + LSHeader(text: 'Default Sorting'), + _defaultSortingReleasesTile, + _defaultSortingReleasesDirectionTile, + _defaultSortingSeriesTile, + _defaultSortingSeriesDirectionTile, + ]; + + Widget get _defaultPageHomeTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: SonarrNavigationBar.titles[SonarrDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: SonarrNavigationBar.icons[SonarrDatabaseValue.NAVIGATION_INDEX.data]), + onTap: () async { + List _values = await SonarrDialogs.setDefaultPage(context, titles: SonarrNavigationBar.titles, icons: SonarrNavigationBar.icons); + if(_values[0]) SonarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + }, + ), + ); + + Widget get _defaultPageSeriesDetailsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Series Details'), + subtitle: LSSubtitle(text: SonarrSeriesDetailsNavigationBar.titles[SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), + trailing: LSIconButton(icon: SonarrSeriesDetailsNavigationBar.icons[SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), + onTap: () async { + List _values = await SonarrDialogs.setDefaultPage(context, titles: SonarrSeriesDetailsNavigationBar.titles, icons: SonarrSeriesDetailsNavigationBar.icons); + if(_values[0]) SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.put(_values[1]); + }, + ), + ); + + Widget get _defaultSortingReleasesTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Releases Category'), + subtitle: LSSubtitle(text: (SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data as SonarrReleasesSorting).readable), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _titles = SonarrReleasesSorting.values.map((e) => e.readable).toList(); + List _values = await SonarrDialogs.setDefaultSorting(context, titles: _titles); + if(_values[0]) { + SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.put(SonarrReleasesSorting.values[_values[1]]); + context.read().releasesSortType = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data; + context.read().releasesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data; + } + }, + ), + ); + + Widget get _defaultSortingReleasesDirectionTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Releases Sort Direction'), + subtitle: LSSubtitle(text: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data ? 'Ascending' : 'Descending'), + trailing: Switch( + value: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data, + onChanged: (value) { + SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.put(value); + context.read().releasesSortType = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data; + context.read().releasesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data; + }, + ), + ), + ); + + Widget get _defaultSortingSeriesTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_SERIES.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Series Category'), + subtitle: LSSubtitle(text: (SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data as SonarrSeriesSorting).readable), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async { + List _titles = SonarrSeriesSorting.values.map((e) => e.readable).toList(); + List _values = await SonarrDialogs.setDefaultSorting(context, titles: _titles); + if(_values[0]) { + SonarrDatabaseValue.DEFAULT_SORTING_SERIES.put(SonarrSeriesSorting.values[_values[1]]); + context.read().seriesSortType = SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; + context.read().seriesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; + } + }, + ), + ); + + Widget get _defaultSortingSeriesDirectionTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Series Sort Direction'), + subtitle: LSSubtitle(text: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data ? 'Ascending' : 'Descending'), + trailing: Switch( + value: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data, + onChanged: (value) { + SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.put(value); + context.read().seriesSortType = SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; + context.read().seriesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; + }, + ), + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_sonarr_headers.dart b/lib/modules/settings/modules/configuration_sonarr_headers.dart new file mode 100644 index 00000000..d18757dc --- /dev/null +++ b/lib/modules/settings/modules/configuration_sonarr_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationSonarrHeadersRouter extends LunaPageRouter { + SettingsConfigurationSonarrHeadersRouter() : super('/settings/configuration/sonarr/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationSonarrHeadersRoute()); +} + +class _SettingsConfigurationSonarrHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationSonarrHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationSonarrHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.sonarrHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.sonarrHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.sonarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.sonarrHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationSonarrHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_tautulli.dart b/lib/modules/settings/modules/configuration_tautulli.dart new file mode 100644 index 00000000..8b9fe1ea --- /dev/null +++ b/lib/modules/settings/modules/configuration_tautulli.dart @@ -0,0 +1,294 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; +import 'package:lunasea/modules/tautulli.dart'; + +class SettingsConfigurationTautulliRouter extends LunaPageRouter { + SettingsConfigurationTautulliRouter() : super('/settings/configuration/tautulli'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationTautulliRoute()); +} + +class _SettingsConfigurationTautulliRoute extends StatefulWidget { + @override + State<_SettingsConfigurationTautulliRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationTautulliRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Tautulli', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: TautulliConstants.MODULE_METADATA.name, + message: TautulliConstants.MODULE_METADATA.helpMessage, + github: TautulliConstants.MODULE_METADATA.github, + website: TautulliConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ..._customization, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _hostTile, + _apiKeyTile, + _customHeadersTile, + _testConnectionTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable Tautulli'), + trailing: Switch( + value: Database.currentProfileObject.tautulliEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.tautulliEnabled = value; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + }, + ), + ); + + Widget get _hostTile { + Future _execute() async { + List _values = await SettingsDialogs.editHost(context, 'Tautulli Host', prefill: Database.currentProfileObject.tautulliHost ?? ''); + if(_values[0]) { + Database.currentProfileObject.tautulliHost = _values[1]; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + } + } + return LSCardTile( + title: LSTitle(text: 'Host'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.tautulliHost == null || Database.currentProfileObject.tautulliHost == '' + ? 'Not Set' + : Database.currentProfileObject.tautulliHost + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _apiKeyTile { + Future _execute() async { + List _values = await LunaDialogs().editText(context, 'Tautulli API Key', prefill: Database.currentProfileObject.tautulliKey ?? ''); + if(_values[0]) { + Database.currentProfileObject.tautulliKey = _values[1]; + Database.currentProfileObject.save(); + Provider.of(context, listen: false).reset(); + } + } + return LSCardTile( + title: LSTitle(text: 'API Key'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.tautulliKey == null || Database.currentProfileObject.tautulliKey == '' + ? 'Not Set' + : '••••••••••••' + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _customHeadersTile => LSCardTile( + title: LSTitle(text: 'Custom Headers'), + subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: () async => SettingsConfigurationTautulliHeadersRouter().navigateTo(context), + ); + + Widget get _testConnectionTile { + Future _testConnection(BuildContext context) async { + TautulliState state = Provider.of(context, listen: false); + if(state.host == null || state.host.isEmpty) { + showLunaErrorSnackBar( + context: context, + title: 'Host Required', + message: 'Host is required to connect to Tautulli', + ); + return; + } + if(state.apiKey == null || state.apiKey.isEmpty) { + showLunaErrorSnackBar( + context: context, + title: 'API Key Required', + message: 'API key is required to connect to Tautulli', + ); + return; + } + Tautulli(host: state.host, apiKey: state.apiKey, headers: Map.from(state.headers)).miscellaneous.arnold() + .then((_) { + showLunaSuccessSnackBar( + context: context, + title: 'Connected Successfully', + message: 'Tautulli is ready to use with LunaSea', + ); + }).catchError((error, trace) { + LunaLogger().error('Connection Test Failed', error, trace); + showLunaErrorSnackBar( + context: context, + title: 'Connection Test Failed', + error: error, + ); + }); + } + return LSButton( + text: 'Test Connection', + onTap: () async => _testConnection(context), + ); + } + + List get _customization => [ + LSHeader(text: 'Default Pages'), + _defaultPageHomeTile, + _defaultPageGraphsTile, + _defaultPageLibrariesDetailsTile, + _defaultPageMediaDetailsTile, + _defaultPageUserDetailsTile, + LSHeader(text: 'Activity'), + _defaultTerminationMessageTile, + _activityRefreshRateTile, + LSHeader(text: 'Statistics'), + _statisticsItemCountTile, + ]; + + Widget get _defaultPageHomeTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Home'), + subtitle: LSSubtitle(text: TautulliNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX.data]), + trailing: LSIconButton(icon: TautulliNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX.data]), + onTap: () async { + List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliNavigationBar.titles, icons: TautulliNavigationBar.icons); + if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX.put(_values[1]); + } + ), + ); + + Widget get _defaultPageGraphsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Graphs'), + subtitle: LSSubtitle(text: TautulliGraphsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), + trailing: LSIconButton(icon: TautulliGraphsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), + onTap: () async { + List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliGraphsNavigationBar.titles, icons: TautulliGraphsNavigationBar.icons); + if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.put(_values[1]); + }, + ), + ); + + Widget get _defaultPageLibrariesDetailsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Library Details'), + subtitle: LSSubtitle(text: TautulliLibrariesDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), + trailing: LSIconButton(icon: TautulliLibrariesDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), + onTap: () async { + List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliLibrariesDetailsNavigationBar.titles, icons: TautulliLibrariesDetailsNavigationBar.icons); + if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.put(_values[1]); + }, + ), + ); + + Widget get _defaultPageMediaDetailsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Media Details'), + subtitle: LSSubtitle(text: TautulliMediaDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), + trailing: LSIconButton(icon: TautulliMediaDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), + onTap: () async { + List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliMediaDetailsNavigationBar.titles, icons: TautulliMediaDetailsNavigationBar.icons); + if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.put(_values[1]); + }, + ), + ); + + Widget get _defaultPageUserDetailsTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'User Details'), + subtitle: LSSubtitle(text: TautulliUserDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), + trailing: LSIconButton(icon: TautulliUserDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), + onTap: () async { + List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliUserDetailsNavigationBar.titles, icons: TautulliUserDetailsNavigationBar.icons); + if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.put(_values[1]); + }, + ), + ); + + Widget get _defaultTerminationMessageTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.TERMINATION_MESSAGE.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Default Termination Message'), + subtitle: LSSubtitle(text: (TautulliDatabaseValue.TERMINATION_MESSAGE.data as String).isEmpty ? 'Not Set' : TautulliDatabaseValue.TERMINATION_MESSAGE.data), + trailing: LSIconButton(icon: Icons.videocam_off), + onTap: () async { + List _values = await TautulliDialogs.setTerminationMessage(context); + if(_values[0]) TautulliDatabaseValue.TERMINATION_MESSAGE.put(_values[1]); + }, + ), + ); + + Widget get _activityRefreshRateTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.REFRESH_RATE.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Refresh Rate'), + subtitle: LSSubtitle( + text: [ + 'Every', + TautulliDatabaseValue.REFRESH_RATE.data == 1 ? ' ' : ' ${TautulliDatabaseValue.REFRESH_RATE.data.toString()} ', + TautulliDatabaseValue.REFRESH_RATE.data == 1 ? 'Second' : 'Seconds' + ].join(), + ), + trailing: LSIconButton(icon: Icons.refresh), + onTap: () async { + List _values = await TautulliDialogs.setRefreshRate(context); + if(_values[0]) TautulliDatabaseValue.REFRESH_RATE.put(_values[1]); + }, + ), + ); + + Widget get _statisticsItemCountTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.STATISTICS_STATS_COUNT.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Item Count'), + subtitle: LSSubtitle( + text: [ + TautulliDatabaseValue.STATISTICS_STATS_COUNT.data.toString(), + TautulliDatabaseValue.STATISTICS_STATS_COUNT.data == 1 ? ' Item' : ' Items' + ].join(), + ), + trailing: LSIconButton(icon: Icons.format_list_numbered), + onTap: () async { + List _values = await TautulliDialogs.setStatisticsItemCount(context); + if(_values[0]) TautulliDatabaseValue.STATISTICS_STATS_COUNT.put(_values[1]); + }, + ), + ); +} diff --git a/lib/modules/settings/modules/configuration_tautulli_headers.dart b/lib/modules/settings/modules/configuration_tautulli_headers.dart new file mode 100644 index 00000000..33e942dd --- /dev/null +++ b/lib/modules/settings/modules/configuration_tautulli_headers.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationTautulliHeadersRouter extends LunaPageRouter { + SettingsConfigurationTautulliHeadersRouter() : super('/settings/configuration/tautulli/headers'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationTautulliHeadersRoute()); +} + +class _SettingsConfigurationTautulliHeadersRoute extends StatefulWidget { + @override + State<_SettingsConfigurationTautulliHeadersRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationTautulliHeadersRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Custom Headers', + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profile, _) => LSListView( + children: _headers, + ), + ); + + List get _headers => [ + if((Database.currentProfileObject.tautulliHeaders ?? {}).isEmpty) _noHeaders, + ..._list, + _addHeaderTile, + ]; + + Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); + + List get _list { + Map headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); + List list = []; + headers.forEach((key, value) => list.add(_headerTile(key.toString(),value.toString()))); + list.sort((a,b) => (a.title as LSTitle).text.toLowerCase().compareTo((b.title as LSTitle).text.toLowerCase())); + return list; + } + + Widget _headerTile(String key, String value) => LSCardTile( + title: LSTitle(text: key), + subtitle: LSSubtitle(text: value), + trailing: LSIconButton( + icon: Icons.delete, + color: LunaColours.red, + onPressed: () async { + List results = await SettingsDialogs.deleteHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); + _headers.remove(key); + Database.currentProfileObject.tautulliHeaders = _headers; + Database.currentProfileObject.save(); + showLunaSuccessSnackBar( + context: context, + message: key, + title: 'Header Deleted', + ); + } + }, + ), + ); + + Widget get _addHeaderTile => LSButton( + text: 'Add Header', + onTap: () async { + List results = await SettingsDialogs.addHeader(context); + if(results[0]) switch(results[1]) { + case 1: + List results = await SettingsDialogs.addAuthenticationHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); + String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); + _headers.addAll({'Authorization': 'Basic $_auth'}); + Database.currentProfileObject.tautulliHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + case 100: + List results = await SettingsDialogs.addCustomHeader(context); + if(results[0]) { + Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); + _headers.addAll({results[1]: results[2]}); + Database.currentProfileObject.tautulliHeaders = _headers; + Database.currentProfileObject.save(); + } + break; + default: + LunaLogger().warning( + '_SettingsConfigurationTautulliHeadersRoute', + '_addHeaderTile', + 'Unknown case: ${results[1]}', + ); + break; + } + }, + ); +} diff --git a/lib/modules/settings/modules/configuration_wakeonlan.dart b/lib/modules/settings/modules/configuration_wakeonlan.dart new file mode 100644 index 00000000..dff63d19 --- /dev/null +++ b/lib/modules/settings/modules/configuration_wakeonlan.dart @@ -0,0 +1,110 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; +import 'package:lunasea/modules/wake_on_lan.dart'; + +class SettingsConfigurationWakeOnLANRouter extends LunaPageRouter { + SettingsConfigurationWakeOnLANRouter() : super('/settings/configuration/wakeonlan'); + + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationWakeOnLANRoute()); +} + +class _SettingsConfigurationWakeOnLANRoute extends StatefulWidget { + @override + State<_SettingsConfigurationWakeOnLANRoute> createState() => _State(); +} + +class _State extends State<_SettingsConfigurationWakeOnLANRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Wake on LAN', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: WakeOnLANConstants.MODULE_METADATA.name, + message: WakeOnLANConstants.MODULE_METADATA.helpMessage, + github: WakeOnLANConstants.MODULE_METADATA.github, + website: WakeOnLANConstants.MODULE_METADATA.website, + ), + ); + + Widget get _body => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, box, _) => LSListView( + children: [ + ..._configuration, + ], + ), + ); + + List get _configuration => [ + _enabledTile, + _broadcastAddressTile, + _macAddressTile, + ]; + + Widget get _enabledTile => LSCardTile( + title: LSTitle(text: 'Enable Wake on LAN'), + trailing: Switch( + value: Database.currentProfileObject.wakeOnLANEnabled ?? false, + onChanged: (value) { + Database.currentProfileObject.wakeOnLANEnabled = value; + Database.currentProfileObject.save(); + }, + ), + ); + + Widget get _broadcastAddressTile { + Future _execute() async { + List _values = await SettingsDialogs.editBroadcastAddress(context, Database.currentProfileObject.wakeOnLANBroadcastAddress ?? ''); + if(_values[0]) { + Database.currentProfileObject.wakeOnLANBroadcastAddress = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Broadcast Address'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.wakeOnLANBroadcastAddress == null || Database.currentProfileObject.wakeOnLANBroadcastAddress == '' + ? 'Not Set' + : Database.currentProfileObject.wakeOnLANBroadcastAddress, + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } + + Widget get _macAddressTile { + Future _execute() async { + List _values = await SettingsDialogs.editMACAddress(context, Database.currentProfileObject.wakeOnLANMACAddress ?? ''); + if(_values[0]) { + Database.currentProfileObject.wakeOnLANMACAddress = _values[1]; + Database.currentProfileObject.save(); + } + } + return LSCardTile( + title: LSTitle(text: 'Device MAC Address'), + subtitle: LSSubtitle( + text: Database.currentProfileObject.wakeOnLANMACAddress == null || Database.currentProfileObject.wakeOnLANMACAddress == '' + ? 'Not Set' + : Database.currentProfileObject.wakeOnLANMACAddress, + ), + trailing: LSIconButton(icon: Icons.arrow_forward_ios), + onTap: _execute, + ); + } +} diff --git a/lib/modules/settings/modules/customization.dart b/lib/modules/settings/modules/customization.dart deleted file mode 100644 index c03149d7..00000000 --- a/lib/modules/settings/modules/customization.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization/route.dart'; -export 'customization/widgets.dart'; diff --git a/lib/modules/settings/modules/customization/route.dart b/lib/modules/settings/modules/customization/route.dart deleted file mode 100644 index 5c0016f1..00000000 --- a/lib/modules/settings/modules/customization/route.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'dart:io'; -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules.dart'; - -class SettingsCustomizationRouter { - static const ROUTE_NAME = '/settings/customization'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationRouter._(); -} - -class _SettingsCustomizationRoute extends StatefulWidget { - @override - State<_SettingsCustomizationRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationRoute> with AutomaticKeepAliveClientMixin { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - bool get wantKeepAlive => true; - - @override - Widget build(BuildContext context) { - super.build(context); - return Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - } - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Customization', - ); - - Widget get _body => LSListView( - children: [ - ..._general, - LSDivider(), - ..._modules, - ], - ); - - List get _general => [ - LSCardTile( - title: LSTitle(text: 'Appearance'), - subtitle: LSSubtitle(text: 'Appearance Customizations'), - trailing: LSIconButton(icon: Icons.brush), - onTap: () async => SettingsCustomizationAppearanceRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Drawer'), - subtitle: LSSubtitle(text: 'Drawer Customizations'), - trailing: LSIconButton(icon: Icons.dehaze), - onTap: () async => SettingsCustomizationDrawerRouter.navigateTo(context), - ), - if(Platform.isIOS) SettingsCustomizationBrowserTile(), - LSCardTile( - title: LSTitle(text: 'Quick Actions'), - subtitle: LSSubtitle(text: 'Quick Actions on the Home Screen'), - trailing: LSIconButton(icon: Icons.rounded_corner), - onTap: () async => SettingsCustomizationQuickActionsRouter.navigateTo(context), - ), - ]; - - List get _modules => [ - LSCardTile( - title: LSTitle(text: 'Calendar'), - subtitle: LSSubtitle(text: 'Calendar Customizations'), - trailing: LSIconButton(icon: CustomIcons.calendar), - onTap: () async => SettingsCustomizationCalendarRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: 'Home Customizations'), - trailing: LSIconButton(icon: CustomIcons.home), - onTap: () async => SettingsCustomizationHomeRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Search'), - subtitle: LSSubtitle(text: 'Search Customizations'), - trailing: LSIconButton(icon: Icons.search), - onTap: () async => SettingsCustomizationSearchRouter.navigateTo(context), - ), - LSDivider(), - LSCardTile( - title: LSTitle(text: 'Lidarr'), - subtitle: LSSubtitle(text: 'Lidarr Customizations'), - trailing: LSIconButton(icon: CustomIcons.music), - onTap: () async => SettingsCustomizationLidarrRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: RadarrConstants.MODULE_MAP.name), - subtitle: LSSubtitle(text: 'Radarr Customizations'), - trailing: LSIconButton(icon: RadarrConstants.MODULE_MAP.icon), - onTap: () async => SettingsCustomizationRadarrRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Sonarr'), - subtitle: LSSubtitle(text: 'Sonarr Customizations'), - trailing: LSIconButton(icon: CustomIcons.television), - onTap: () async => SettingsCustomizationSonarrRouter.navigateTo(context), - ), - LSDivider(), - LSCardTile( - title: LSTitle(text: 'NZBGet'), - subtitle: LSSubtitle(text: 'NZBGet Customizations'), - trailing: LSIconButton(icon: CustomIcons.nzbget), - onTap: () async => SettingsCustomizationNZBGetRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'SABnzbd'), - subtitle: LSSubtitle(text: 'SABnzbd Customizations'), - trailing: LSIconButton(icon: CustomIcons.sabnzbd), - onTap: () async => SettingsCustomizationSABnzbdRouter.navigateTo(context), - ), - LSDivider(), - LSCardTile( - title: LSTitle(text: 'Tautulli'), - subtitle: LSSubtitle(text: 'Tautulli Customizations'), - trailing: LSIconButton(icon: CustomIcons.tautulli), - onTap: () async => SettingsCustomizationTautulliRouter.navigateTo(context), - ), - ]; -} diff --git a/lib/modules/settings/modules/customization/widgets.dart b/lib/modules/settings/modules/customization/widgets.dart deleted file mode 100644 index 8ee110f1..00000000 --- a/lib/modules/settings/modules/customization/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/browser_tile.dart'; diff --git a/lib/modules/settings/modules/customization/widgets/browser_tile.dart b/lib/modules/settings/modules/customization/widgets/browser_tile.dart deleted file mode 100644 index fbb2523d..00000000 --- a/lib/modules/settings/modules/customization/widgets/browser_tile.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationBrowserTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.SELECTED_BROWSER.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Open Links In...'), - subtitle: LSSubtitle(text: (LunaSeaDatabaseValue.SELECTED_BROWSER.data as LSBrowsers).name), - trailing: LSIconButton( - icon: (LunaSeaDatabaseValue.SELECTED_BROWSER.data as LSBrowsers).icon, - ), - onTap: () async => _changeBrowser(context), - ), - ); - - Future _changeBrowser(BuildContext context) async { - List _values = await SettingsDialogs.changeBrowser(context); - if(_values[0]) LunaSeaDatabaseValue.SELECTED_BROWSER.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_appearance.dart b/lib/modules/settings/modules/customization_appearance.dart deleted file mode 100644 index 29db39fe..00000000 --- a/lib/modules/settings/modules/customization_appearance.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_appearance/route.dart'; -export 'customization_appearance/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_appearance/route.dart b/lib/modules/settings/modules/customization_appearance/route.dart deleted file mode 100644 index 5530b820..00000000 --- a/lib/modules/settings/modules/customization_appearance/route.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationAppearanceRouter { - static const ROUTE_NAME = '/settings/customization/appearance'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationAppearanceRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationAppearanceRouter._(); -} - -class _SettingsCustomizationAppearanceRoute extends StatefulWidget { - @override - State<_SettingsCustomizationAppearanceRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationAppearanceRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Appearance', - ); - - Widget get _body => LSListView( - children: [ - ..._theme, - ..._localization, - ], - ); - - List get _theme => [ - LSHeader( - text: 'Theme', - subtitle: 'Customize the theme of LunaSea', - ), - SettingsCustomizationAppearanceAMOLEDTile(), - SettingsCustomizationAppearanceAMOLEDBorderTile(), - SettingsCustomizationAppearanceBackgroundImageOpacityTile(), - - ]; - - List get _localization => [ - LSHeader( - text: 'Localization', - subtitle: 'Customizable options for localization', - ), - SettingsCustomizationAppearance24HourTimeTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_appearance/widgets.dart b/lib/modules/settings/modules/customization_appearance/widgets.dart deleted file mode 100644 index 0367a95c..00000000 --- a/lib/modules/settings/modules/customization_appearance/widgets.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'widgets/amoled_border_tile.dart'; -export 'widgets/amoled_tile.dart'; -export 'widgets/image_background_opacity.dart'; -export 'widgets/use_24h_time.dart'; diff --git a/lib/modules/settings/modules/customization_appearance/widgets/amoled_border_tile.dart b/lib/modules/settings/modules/customization_appearance/widgets/amoled_border_tile.dart deleted file mode 100644 index 7643522b..00000000 --- a/lib/modules/settings/modules/customization_appearance/widgets/amoled_border_tile.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationAppearanceAMOLEDBorderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key, - LunaSeaDatabaseValue.THEME_AMOLED.key, - ]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'AMOLED Borders'), - subtitle: LSSubtitle(text: 'Add Subtle Borders Across UI'), - trailing: Switch( - value: LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data, - onChanged: LunaSeaDatabaseValue.THEME_AMOLED.data - ? _changeState - : null, - ), - ), - ); - - void _changeState(bool value) => LunaSeaDatabaseValue.THEME_AMOLED_BORDER.put(value); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/customization_appearance/widgets/amoled_tile.dart b/lib/modules/settings/modules/customization_appearance/widgets/amoled_tile.dart deleted file mode 100644 index 13cfbfbb..00000000 --- a/lib/modules/settings/modules/customization_appearance/widgets/amoled_tile.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationAppearanceAMOLEDTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.THEME_AMOLED.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'AMOLED Dark Theme'), - subtitle: LSSubtitle(text: 'Pure Black Dark Theme'), - trailing: Switch( - value: LunaSeaDatabaseValue.THEME_AMOLED.data, - onChanged: _changeTheme, - ), - ), - ); - - void _changeTheme(bool value) => LunaSeaDatabaseValue.THEME_AMOLED.put(value); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/customization_appearance/widgets/image_background_opacity.dart b/lib/modules/settings/modules/customization_appearance/widgets/image_background_opacity.dart deleted file mode 100644 index 105816a7..00000000 --- a/lib/modules/settings/modules/customization_appearance/widgets/image_background_opacity.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationAppearanceBackgroundImageOpacityTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Background Image Opacity'), - subtitle: LSSubtitle(text: LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0 - ? 'Disabled' - : '${LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data}%' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeOpacity(context), - ), - ); - - Future _changeOpacity(BuildContext context) async { - List _values = await SettingsDialogs.changeBackgroundImageOpacity(context); - if(_values[0]) LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_appearance/widgets/use_24h_time.dart b/lib/modules/settings/modules/customization_appearance/widgets/use_24h_time.dart deleted file mode 100644 index 8fd04ec8..00000000 --- a/lib/modules/settings/modules/customization_appearance/widgets/use_24h_time.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationAppearance24HourTimeTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.USE_24_HOUR_TIME.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Use 24 Hour Time'), - subtitle: LSSubtitle(text: 'Show Timestamps in 24 Hour Style'), - trailing: Switch( - value: LunaSeaDatabaseValue.USE_24_HOUR_TIME.data, - onChanged: _changeState, - ), - ), - ); - - void _changeState(bool value) => LunaSeaDatabaseValue.USE_24_HOUR_TIME.put(value); -} diff --git a/lib/modules/settings/modules/customization_calendar.dart b/lib/modules/settings/modules/customization_calendar.dart deleted file mode 100644 index 85832a48..00000000 --- a/lib/modules/settings/modules/customization_calendar.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_calendar/route.dart'; -export 'customization_calendar/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_calendar/route.dart b/lib/modules/settings/modules/customization_calendar/route.dart deleted file mode 100644 index b70e3ba1..00000000 --- a/lib/modules/settings/modules/customization_calendar/route.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationCalendarRouter { - static const ROUTE_NAME = '/settings/customization/calendar'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationCalendarRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationCalendarRouter._(); -} - -class _SettingsCustomizationCalendarRoute extends StatefulWidget { - @override - State<_SettingsCustomizationCalendarRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationCalendarRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Calendar', - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Date Range', - subtitle: 'Choose how far in the past and future the calendar will fetch data', - ), - SettingsCustomizationCalendarPastDaysTile(), - SettingsCustomizationCalendarFutureDaysTile(), - LSHeader( - text: 'Starting Options', - subtitle: 'Customizable options for the default starting date, size, and type of the calendar', - ), - SettingsCustomizationCalendarStartingDateTile(), - SettingsCustomizationCalendarStartingSizeTile(), - SettingsCustomizationCalendarStartingTypeTile(), - LSHeader( - text: 'Modules', - subtitle: 'Choose which modules will appear in the calendar', - ), - SettingsCustomizationCalendarEnableLidarrTile(), - SettingsCustomizationCalendarEnableRadarrTile(), - SettingsCustomizationCalendarEnableSonarrTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets.dart b/lib/modules/settings/modules/customization_calendar/widgets.dart deleted file mode 100644 index 5f479b93..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'widgets/date_range_future_days.dart'; -export 'widgets/date_range_past_days.dart'; -export 'widgets/enable_lidarr_tile.dart'; -export 'widgets/enable_radarr_tile.dart'; -export 'widgets/enable_sonarr_tile.dart'; -export 'widgets/start_date_tile.dart'; -export 'widgets/start_size_tile.dart'; -export 'widgets/start_type_tile.dart'; diff --git a/lib/modules/settings/modules/customization_calendar/widgets/date_range_future_days.dart b/lib/modules/settings/modules/customization_calendar/widgets/date_range_future_days.dart deleted file mode 100644 index 981c180c..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/date_range_future_days.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationCalendarFutureDaysTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_DAYS_FUTURE.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Future Days'), - subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_DAYS_FUTURE.data == 1 - ? '1 Day' - : '${HomeDatabaseValue.CALENDAR_DAYS_FUTURE.data} Days', - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _onTap(context), - ), - ); - - Future _onTap(BuildContext context) async { - List _values = await HomeDialogs.setFutureDays(context); - if(_values[0]) HomeDatabaseValue.CALENDAR_DAYS_FUTURE.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets/date_range_past_days.dart b/lib/modules/settings/modules/customization_calendar/widgets/date_range_past_days.dart deleted file mode 100644 index d68fdfa4..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/date_range_past_days.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationCalendarPastDaysTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_DAYS_PAST.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Past Days'), - subtitle: LSSubtitle(text: HomeDatabaseValue.CALENDAR_DAYS_PAST.data == 1 - ? '1 Day' - : '${HomeDatabaseValue.CALENDAR_DAYS_PAST.data} Days', - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _onTap(context), - ), - ); - - Future _onTap(BuildContext context) async { - List _values = await HomeDialogs.setPastDays(context); - if(_values[0]) HomeDatabaseValue.CALENDAR_DAYS_PAST.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets/enable_lidarr_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/enable_lidarr_tile.dart deleted file mode 100644 index d114b233..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/enable_lidarr_tile.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationCalendarEnableLidarrTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Show Lidarr'), - subtitle: LSSubtitle(text: 'Show Lidarr Calendar Entries'), - trailing: Switch( - value: HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.data, - onChanged: _changeState, - ), - ), - ); - - void _changeState(bool value) => HomeDatabaseValue.CALENDAR_ENABLE_LIDARR.put(value); -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets/enable_radarr_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/enable_radarr_tile.dart deleted file mode 100644 index ed37ed8f..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/enable_radarr_tile.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationCalendarEnableRadarrTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_RADARR.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Show Radarr'), - subtitle: LSSubtitle(text: 'Show Radarr Calendar Entries'), - trailing: Switch( - value: HomeDatabaseValue.CALENDAR_ENABLE_RADARR.data, - onChanged: _changeState, - ), - ), - ); - - void _changeState(bool value) => HomeDatabaseValue.CALENDAR_ENABLE_RADARR.put(value); -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets/enable_sonarr_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/enable_sonarr_tile.dart deleted file mode 100644 index 2404ae32..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/enable_sonarr_tile.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationCalendarEnableSonarrTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_ENABLE_SONARR.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Show Sonarr'), - subtitle: LSSubtitle(text: 'Show Sonarr Calendar Entries'), - trailing: Switch( - value: HomeDatabaseValue.CALENDAR_ENABLE_SONARR.data, - onChanged: _changeState, - ), - ), - ); - - void _changeState(bool value) => HomeDatabaseValue.CALENDAR_ENABLE_SONARR.put(value); -} diff --git a/lib/modules/settings/modules/customization_calendar/widgets/start_date_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/start_date_tile.dart deleted file mode 100644 index 73933a71..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/start_date_tile.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationCalendarStartingDateTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_DAY.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Starting Day'), - subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_DAY.data as CalendarStartingDay).name), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeDate(context), - ), - ); - - Future _changeDate(BuildContext context) async { - List _values = await SettingsDialogs.editCalendarStartingDay(context); - if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_DAY.put(_values[1]); - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/customization_calendar/widgets/start_size_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/start_size_tile.dart deleted file mode 100644 index 5fb3009c..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/start_size_tile.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationCalendarStartingSizeTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_SIZE.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Starting Size'), - subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_SIZE.data as CalendarStartingSize).name), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeSize(context), - ), - ); - - Future _changeSize(BuildContext context) async { - List _values = await SettingsDialogs.editCalendarStartingSize(context); - if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_SIZE.put(_values[1]); - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/customization_calendar/widgets/start_type_tile.dart b/lib/modules/settings/modules/customization_calendar/widgets/start_type_tile.dart deleted file mode 100644 index e095cdf6..00000000 --- a/lib/modules/settings/modules/customization_calendar/widgets/start_type_tile.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationCalendarStartingTypeTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.CALENDAR_STARTING_TYPE.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Starting Type'), - subtitle: LSSubtitle(text: (HomeDatabaseValue.CALENDAR_STARTING_TYPE.data as CalendarStartingType).name), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeType(context), - ), - ); - - Future _changeType(BuildContext context) async { - List _values = await SettingsDialogs.editCalendarStartingType(context); - if(_values[0]) HomeDatabaseValue.CALENDAR_STARTING_TYPE.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_drawer.dart b/lib/modules/settings/modules/customization_drawer.dart deleted file mode 100644 index 4a38ab7e..00000000 --- a/lib/modules/settings/modules/customization_drawer.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_drawer/route.dart'; -export 'customization_drawer/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_drawer/route.dart b/lib/modules/settings/modules/customization_drawer/route.dart deleted file mode 100644 index af00b90f..00000000 --- a/lib/modules/settings/modules/customization_drawer/route.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationDrawerRouter { - static const ROUTE_NAME = '/settings/customization/drawer'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationDrawerRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationDrawerRouter._(); -} - -class _SettingsCustomizationDrawerRoute extends StatefulWidget { - @override - State<_SettingsCustomizationDrawerRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationDrawerRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Drawer', - ); - - Widget get _body => LSListView( - children: [ - ..._folders, - ], - ); - - List get _folders => [ - LSHeader( - text: 'Folders', - subtitle: 'Customizable options related to grouping modules into folders', - ), - SettingsCustomizationDrawerUseCategoriesTile(), - SettingsCustomizationDrawerExpandAutomationTile(), - SettingsCustomizationDrawerExpandClientsTile(), - SettingsCustomizationDrawerExpandMonitoringTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_drawer/widgets.dart b/lib/modules/settings/modules/customization_drawer/widgets.dart deleted file mode 100644 index d8d0521c..00000000 --- a/lib/modules/settings/modules/customization_drawer/widgets.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'widgets/expand_automation_tile.dart'; -export 'widgets/expand_clients_tile.dart'; -export 'widgets/expand_monitoring_tile.dart'; -export 'widgets/use_categories.dart'; diff --git a/lib/modules/settings/modules/customization_drawer/widgets/expand_automation_tile.dart b/lib/modules/settings/modules/customization_drawer/widgets/expand_automation_tile.dart deleted file mode 100644 index 5d498875..00000000 --- a/lib/modules/settings/modules/customization_drawer/widgets/expand_automation_tile.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationDrawerExpandAutomationTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION.key, - LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.key, - ]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Expand Automation'), - subtitle: LSSubtitle(text: 'Expand Automation Folder Initially'), - trailing: Switch( - value: LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data, - onChanged: LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data - ? _onChange - : null, - ), - ), - ); - - void _onChange(bool value) => LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION.put(value); -} diff --git a/lib/modules/settings/modules/customization_drawer/widgets/expand_clients_tile.dart b/lib/modules/settings/modules/customization_drawer/widgets/expand_clients_tile.dart deleted file mode 100644 index 0543f583..00000000 --- a/lib/modules/settings/modules/customization_drawer/widgets/expand_clients_tile.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationDrawerExpandClientsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS.key, - LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.key, - ]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Expand Clients'), - subtitle: LSSubtitle(text: 'Expand Clients Folder Initially'), - trailing: Switch( - value: LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS.data, - onChanged: LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data - ? _onChange - : null, - ), - ), - ); - - void _onChange(bool value) => LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS.put(value); -} diff --git a/lib/modules/settings/modules/customization_drawer/widgets/expand_monitoring_tile.dart b/lib/modules/settings/modules/customization_drawer/widgets/expand_monitoring_tile.dart deleted file mode 100644 index 3882545f..00000000 --- a/lib/modules/settings/modules/customization_drawer/widgets/expand_monitoring_tile.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationDrawerExpandMonitoringTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING.key, - LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.key, - ]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Expand Monitoring'), - subtitle: LSSubtitle(text: 'Expand Monitoring Folder Initially'), - trailing: Switch( - value: LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING.data, - onChanged: LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data - ? _onChange - : null, - ), - ), - ); - - void _onChange(bool value) => LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING.put(value); -} diff --git a/lib/modules/settings/modules/customization_drawer/widgets/use_categories.dart b/lib/modules/settings/modules/customization_drawer/widgets/use_categories.dart deleted file mode 100644 index 85a90aac..00000000 --- a/lib/modules/settings/modules/customization_drawer/widgets/use_categories.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationDrawerUseCategoriesTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Use Folders'), - subtitle: LSSubtitle(text: 'Group Modules into Categories'), - trailing: Switch( - value: LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data, - onChanged: _onChange, - ), - ), - ); - - void _onChange(bool value) => LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.put(value); -} diff --git a/lib/modules/settings/modules/customization_home.dart b/lib/modules/settings/modules/customization_home.dart deleted file mode 100644 index 7c284359..00000000 --- a/lib/modules/settings/modules/customization_home.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_home/route.dart'; -export 'customization_home/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_home/route.dart b/lib/modules/settings/modules/customization_home/route.dart deleted file mode 100644 index bfaf0cb7..00000000 --- a/lib/modules/settings/modules/customization_home/route.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationHomeRouter { - static const ROUTE_NAME = '/settings/customization/home'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationHomeRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationHomeRouter._(); -} - -class _SettingsCustomizationHomeRoute extends StatefulWidget { - @override - State<_SettingsCustomizationHomeRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationHomeRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Home', - ); - - Widget get _body => LSListView( - children: [ - ..._defaultPages, - ..._modules, - ], - ); - - List get _defaultPages => [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationHomeDefaultPageTile(), - ]; - - List get _modules => [ - LSHeader( - text: 'Modules', - subtitle: 'Customizable options related to the module list', - ), - SettingsCustomizationHomeBrandColoursTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_home/widgets.dart b/lib/modules/settings/modules/customization_home/widgets.dart deleted file mode 100644 index 5420a93f..00000000 --- a/lib/modules/settings/modules/customization_home/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/brand_colours_tile.dart'; -export 'widgets/default_page_tile.dart'; diff --git a/lib/modules/settings/modules/customization_home/widgets/brand_colours_tile.dart b/lib/modules/settings/modules/customization_home/widgets/brand_colours_tile.dart deleted file mode 100644 index 4e862a1b..00000000 --- a/lib/modules/settings/modules/customization_home/widgets/brand_colours_tile.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationHomeBrandColoursTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.MODULES_BRAND_COLOURS.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Use Brand Colours'), - subtitle: LSSubtitle(text: 'For Module Icons'), - trailing: Switch( - value: HomeDatabaseValue.MODULES_BRAND_COLOURS.data, - onChanged: (value) => HomeDatabaseValue.MODULES_BRAND_COLOURS.put(value), - ), - ), - ); -} diff --git a/lib/modules/settings/modules/customization_home/widgets/default_page_tile.dart b/lib/modules/settings/modules/customization_home/widgets/default_page_tile.dart deleted file mode 100644 index e78e397b..00000000 --- a/lib/modules/settings/modules/customization_home/widgets/default_page_tile.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/home.dart'; - -class SettingsCustomizationHomeDefaultPageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [HomeDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: HomeNavigationBar.titles[HomeDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: HomeNavigationBar.icons[HomeDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await HomeDialogs.defaultPage(context); - if(_values[0]) HomeDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_lidarr.dart b/lib/modules/settings/modules/customization_lidarr.dart deleted file mode 100644 index bdc577dc..00000000 --- a/lib/modules/settings/modules/customization_lidarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_lidarr/route.dart'; -export 'customization_lidarr/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_lidarr/route.dart b/lib/modules/settings/modules/customization_lidarr/route.dart deleted file mode 100644 index d707da17..00000000 --- a/lib/modules/settings/modules/customization_lidarr/route.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationLidarrRouter { - static const ROUTE_NAME = '/settings/customization/lidarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationLidarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationLidarrRouter._(); -} - -class _SettingsCustomizationLidarrRoute extends StatefulWidget { - @override - State<_SettingsCustomizationLidarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationLidarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Lidarr', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesLidarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationLidarrDefaultPageTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_lidarr/widgets.dart b/lib/modules/settings/modules/customization_lidarr/widgets.dart deleted file mode 100644 index cf719210..00000000 --- a/lib/modules/settings/modules/customization_lidarr/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/default_page_tile.dart'; diff --git a/lib/modules/settings/modules/customization_lidarr/widgets/default_page_tile.dart b/lib/modules/settings/modules/customization_lidarr/widgets/default_page_tile.dart deleted file mode 100644 index 63758a69..00000000 --- a/lib/modules/settings/modules/customization_lidarr/widgets/default_page_tile.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/lidarr.dart'; - -class SettingsCustomizationLidarrDefaultPageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LidarrDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Default Page'), - subtitle: LSSubtitle(text: LidarrNavigationBar.titles[LidarrDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: LidarrNavigationBar.icons[LidarrDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await LidarrDialogs.defaultPage(context); - if(_values[0]) LidarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_nzbget.dart b/lib/modules/settings/modules/customization_nzbget.dart deleted file mode 100644 index c8252eec..00000000 --- a/lib/modules/settings/modules/customization_nzbget.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_nzbget/route.dart'; -export 'customization_nzbget/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_nzbget/route.dart b/lib/modules/settings/modules/customization_nzbget/route.dart deleted file mode 100644 index 29472699..00000000 --- a/lib/modules/settings/modules/customization_nzbget/route.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationNZBGetRouter { - static const ROUTE_NAME = '/settings/customization/nzbget'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationNZBGetRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationNZBGetRouter._(); -} - -class _SettingsCustomizationNZBGetRoute extends StatefulWidget { - @override - State<_SettingsCustomizationNZBGetRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationNZBGetRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'NZBGet', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesNZBGetRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationNZBGetDefaultPageTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_nzbget/widgets.dart b/lib/modules/settings/modules/customization_nzbget/widgets.dart deleted file mode 100644 index cf719210..00000000 --- a/lib/modules/settings/modules/customization_nzbget/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/default_page_tile.dart'; diff --git a/lib/modules/settings/modules/customization_nzbget/widgets/default_page_tile.dart b/lib/modules/settings/modules/customization_nzbget/widgets/default_page_tile.dart deleted file mode 100644 index 8243b577..00000000 --- a/lib/modules/settings/modules/customization_nzbget/widgets/default_page_tile.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/nzbget.dart'; - -class SettingsCustomizationNZBGetDefaultPageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [NZBGetDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: NZBGetNavigationBar.titles[NZBGetDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: NZBGetNavigationBar.icons[NZBGetDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await NZBGetDialogs.defaultPage(context); - if(_values[0]) NZBGetDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_quickactions.dart b/lib/modules/settings/modules/customization_quickactions.dart deleted file mode 100644 index e8d6cae1..00000000 --- a/lib/modules/settings/modules/customization_quickactions.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_quickactions/route.dart'; -export 'customization_quickactions/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_quickactions/route.dart b/lib/modules/settings/modules/customization_quickactions/route.dart deleted file mode 100644 index f8e262e1..00000000 --- a/lib/modules/settings/modules/customization_quickactions/route.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationQuickActionsRouter { - static const ROUTE_NAME = '/settings/customization/quickactions'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationQuickActionsRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationQuickActionsRouter._(); -} - -class _SettingsCustomizationQuickActionsRoute extends StatefulWidget { - @override - State<_SettingsCustomizationQuickActionsRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationQuickActionsRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Quick Actions', - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Modules', - subtitle: 'Only four quick actions will appear, and are added in the order below. Enabling more than four modules will have no effect.', - ), - SettingsCustomizationQuickActionTile( - title: 'Search', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH, - ), - LSDivider(), - SettingsCustomizationQuickActionTile( - title: 'Lidarr', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR, - ), - SettingsCustomizationQuickActionTile( - title: 'Radarr', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR, - ), - SettingsCustomizationQuickActionTile( - title: 'Sonarr', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR, - ), - LSDivider(), - SettingsCustomizationQuickActionTile( - title: 'NZBGet', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET, - ), - SettingsCustomizationQuickActionTile( - title: 'SABnzbd', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD, - ), - LSDivider(), - SettingsCustomizationQuickActionTile( - title: 'Tautulli', - action: LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI, - ), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_quickactions/widgets.dart b/lib/modules/settings/modules/customization_quickactions/widgets.dart deleted file mode 100644 index 5bb78e70..00000000 --- a/lib/modules/settings/modules/customization_quickactions/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/action_tile.dart'; diff --git a/lib/modules/settings/modules/customization_quickactions/widgets/action_tile.dart b/lib/modules/settings/modules/customization_quickactions/widgets/action_tile.dart deleted file mode 100644 index 8989ba6f..00000000 --- a/lib/modules/settings/modules/customization_quickactions/widgets/action_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsCustomizationQuickActionTile extends StatelessWidget { - final String title; - final LunaSeaDatabaseValue action; - - SettingsCustomizationQuickActionTile({ - Key key, - @required this.title, - @required this.action, - }): super(key: key); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: title), - trailing: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [action.key]), - builder: (context, box, _) => Switch( - value: action.data, - onChanged: (value) { - action.put(value); - LunaQuickActions.setShortcutItems(); - } - ), - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/customization_radarr.dart b/lib/modules/settings/modules/customization_radarr.dart deleted file mode 100644 index d21958e6..00000000 --- a/lib/modules/settings/modules/customization_radarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_radarr/route.dart'; -export 'customization_radarr/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_radarr/route.dart b/lib/modules/settings/modules/customization_radarr/route.dart deleted file mode 100644 index e3fb7c38..00000000 --- a/lib/modules/settings/modules/customization_radarr/route.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationRadarrRouter { - static const ROUTE_NAME = '/settings/customization/radarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationRadarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationRadarrRouter._(); -} - -class _SettingsCustomizationRadarrRoute extends StatefulWidget { - @override - State<_SettingsCustomizationRadarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationRadarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Radarr', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesRadarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationRadarrDefaultPageTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_radarr/widgets.dart b/lib/modules/settings/modules/customization_radarr/widgets.dart deleted file mode 100644 index cf719210..00000000 --- a/lib/modules/settings/modules/customization_radarr/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/default_page_tile.dart'; diff --git a/lib/modules/settings/modules/customization_radarr/widgets/default_page_tile.dart b/lib/modules/settings/modules/customization_radarr/widgets/default_page_tile.dart deleted file mode 100644 index 423149a2..00000000 --- a/lib/modules/settings/modules/customization_radarr/widgets/default_page_tile.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/radarr.dart'; - -class SettingsCustomizationRadarrDefaultPageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [RadarrDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: RadarrNavigationBar.titles[RadarrDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: RadarrNavigationBar.icons[RadarrDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await RadarrDialogs.defaultPage(context); - if(_values[0]) RadarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_sabnzbd.dart b/lib/modules/settings/modules/customization_sabnzbd.dart deleted file mode 100644 index 980112c5..00000000 --- a/lib/modules/settings/modules/customization_sabnzbd.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_sabnzbd/route.dart'; -export 'customization_sabnzbd/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_sabnzbd/route.dart b/lib/modules/settings/modules/customization_sabnzbd/route.dart deleted file mode 100644 index 5a4b5ea8..00000000 --- a/lib/modules/settings/modules/customization_sabnzbd/route.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationSABnzbdRouter { - static const ROUTE_NAME = '/settings/customization/sabnzbd'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationSABnzbdRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationSABnzbdRouter._(); -} - -class _SettingsCustomizationSABnzbdRoute extends StatefulWidget { - @override - State<_SettingsCustomizationSABnzbdRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationSABnzbdRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'SABnzbd', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesSABnzbdRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationSABnzbdDefaultPageTile(), - ], - ); -} diff --git a/lib/modules/settings/modules/customization_sabnzbd/widgets.dart b/lib/modules/settings/modules/customization_sabnzbd/widgets.dart deleted file mode 100644 index cf719210..00000000 --- a/lib/modules/settings/modules/customization_sabnzbd/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/default_page_tile.dart'; diff --git a/lib/modules/settings/modules/customization_sabnzbd/widgets/default_page_tile.dart b/lib/modules/settings/modules/customization_sabnzbd/widgets/default_page_tile.dart deleted file mode 100644 index c22f1658..00000000 --- a/lib/modules/settings/modules/customization_sabnzbd/widgets/default_page_tile.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sabnzbd.dart'; - -class SettingsCustomizationSABnzbdDefaultPageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SABnzbdDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: SABnzbdNavigationBar.titles[SABnzbdDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: SABnzbdNavigationBar.icons[SABnzbdDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await SABnzbdDialogs.defaultPage(context); - if(_values[0]) SABnzbdDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_search.dart b/lib/modules/settings/modules/customization_search.dart deleted file mode 100644 index bf3ed65d..00000000 --- a/lib/modules/settings/modules/customization_search.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_search/route.dart'; -export 'customization_search/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_search/route.dart b/lib/modules/settings/modules/customization_search/route.dart deleted file mode 100644 index adc552d3..00000000 --- a/lib/modules/settings/modules/customization_search/route.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationSearchRouter { - static const ROUTE_NAME = '/settings/customization/search'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationSearchRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationSearchRouter._(); -} - -class _SettingsCustomizationSearchRoute extends StatefulWidget { - @override - State<_SettingsCustomizationSearchRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationSearchRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Search', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesSearchRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - ..._categories, - ], - ); - - List get _categories => [ - LSHeader( - text: 'Filtering', - subtitle: 'Customizable options related to the filtering categories', - ), - SettingsCustomizationSearchHideAdultCategoriesTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_search/widgets.dart b/lib/modules/settings/modules/customization_search/widgets.dart deleted file mode 100644 index b2863f55..00000000 --- a/lib/modules/settings/modules/customization_search/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/hide_adult_categories_tile.dart'; diff --git a/lib/modules/settings/modules/customization_search/widgets/hide_adult_categories_tile.dart b/lib/modules/settings/modules/customization_search/widgets/hide_adult_categories_tile.dart deleted file mode 100644 index 62873ee6..00000000 --- a/lib/modules/settings/modules/customization_search/widgets/hide_adult_categories_tile.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/search.dart'; - -class SettingsCustomizationSearchHideAdultCategoriesTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SearchDatabaseValue.HIDE_XXX.key]), - builder: (context, box, widget) => LSCardTile( - title: LSTitle(text: 'Hide Adult Categories'), - subtitle: LSSubtitle(text: 'Hide Adult Content'), - trailing: Switch( - value: SearchDatabaseValue.HIDE_XXX.data, - onChanged: (value) => SearchDatabaseValue.HIDE_XXX.put(value), - ), - ), - ); -} diff --git a/lib/modules/settings/modules/customization_sonarr.dart b/lib/modules/settings/modules/customization_sonarr.dart deleted file mode 100644 index e49b262b..00000000 --- a/lib/modules/settings/modules/customization_sonarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_sonarr/route.dart'; -export 'customization_sonarr/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_sonarr/route.dart b/lib/modules/settings/modules/customization_sonarr/route.dart deleted file mode 100644 index 2d1de670..00000000 --- a/lib/modules/settings/modules/customization_sonarr/route.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationSonarrRouter { - static const ROUTE_NAME = '/settings/customization/sonarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationSonarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationSonarrRouter._(); -} - -class _SettingsCustomizationSonarrRoute extends StatefulWidget { - @override - State<_SettingsCustomizationSonarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationSonarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Sonarr', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesSonarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - ..._defaultPages, - ..._defaultSorting, - ], - ); - - List get _defaultPages => [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationSonarrDefaultPageHomeTile(), - SettingsCustomizationSonarrDefaultPageSeriesDetailsTile(), - ]; - - List get _defaultSorting => [ - LSHeader( - text: 'Default Sorting', - subtitle: 'Choose default sorting options for sortable lists', - ), - SettingsCustomizationSonarrDefaultSortingReleasesTile(), - SettingsCustomizationSonarrDefaultSortingReleasesAscendingTile(), - SettingsCustomizationSonarrDefaultSortingSeriesTile(), - SettingsCustomizationSonarrDefaultSortingSeriesAscendingTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets.dart b/lib/modules/settings/modules/customization_sonarr/widgets.dart deleted file mode 100644 index a2f9c32e..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'widgets/default_pages_home.dart'; -export 'widgets/default_pages_series_details.dart'; -export 'widgets/default_sorting_releases_ascending.dart'; -export 'widgets/default_sorting_releases.dart'; -export 'widgets/default_sorting_series_ascending.dart'; -export 'widgets/default_sorting_series.dart'; diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_home.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_home.dart deleted file mode 100644 index b9a83133..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_home.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultPageHomeTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: SonarrNavigationBar.titles[SonarrDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: SonarrNavigationBar.icons[SonarrDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await SonarrDialogs.setDefaultPage(context, titles: SonarrNavigationBar.titles, icons: SonarrNavigationBar.icons); - if(_values[0]) SonarrDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_series_details.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_series_details.dart deleted file mode 100644 index 213a921a..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_pages_series_details.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultPageSeriesDetailsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Series Details'), - subtitle: LSSubtitle(text: SonarrSeriesDetailsNavigationBar.titles[SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), - trailing: LSIconButton(icon: SonarrSeriesDetailsNavigationBar.icons[SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await SonarrDialogs.setDefaultPage(context, titles: SonarrSeriesDetailsNavigationBar.titles, icons: SonarrSeriesDetailsNavigationBar.icons); - if(_values[0]) SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases.dart deleted file mode 100644 index 02057ef6..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultSortingReleasesTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Releases Category'), - subtitle: LSSubtitle(text: (SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data as SonarrReleasesSorting).readable), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _defaultSorting(context), - ), - ); - - Future _defaultSorting(BuildContext context) async { - List _titles = SonarrReleasesSorting.values.map((e) => e.readable).toList(); - List _values = await SonarrDialogs.setDefaultSorting(context, titles: _titles); - if(_values[0]) { - SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.put(SonarrReleasesSorting.values[_values[1]]); - // Reset the state of the sorting - context.read().releasesSortType = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data; - context.read().releasesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data; - } - } -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases_ascending.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases_ascending.dart deleted file mode 100644 index f505f36a..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_releases_ascending.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultSortingReleasesAscendingTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Releases Sort Direction'), - subtitle: LSSubtitle(text: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data - ? 'Ascending' - : 'Descending' - ), - trailing: Switch( - value: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data, - onChanged: (value) { - SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.put(value); - // Reset the state of the sorting - context.read().releasesSortType = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data; - context.read().releasesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data; - }, - ), - ), - ); -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series.dart deleted file mode 100644 index 6b71c43e..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultSortingSeriesTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_SERIES.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Series Category'), - subtitle: LSSubtitle(text: (SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data as SonarrSeriesSorting).readable), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _defaultSorting(context), - ), - ); - - Future _defaultSorting(BuildContext context) async { - List _titles = SonarrSeriesSorting.values.map((e) => e.readable).toList(); - List _values = await SonarrDialogs.setDefaultSorting(context, titles: _titles); - if(_values[0]) { - SonarrDatabaseValue.DEFAULT_SORTING_SERIES.put(SonarrSeriesSorting.values[_values[1]]); - // Reset the state of the sorting - context.read().seriesSortType = SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; - context.read().seriesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; - } - } -} diff --git a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series_ascending.dart b/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series_ascending.dart deleted file mode 100644 index 4c2be79d..00000000 --- a/lib/modules/settings/modules/customization_sonarr/widgets/default_sorting_series_ascending.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsCustomizationSonarrDefaultSortingSeriesAscendingTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Series Sort Direction'), - subtitle: LSSubtitle(text: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data - ? 'Ascending' - : 'Descending' - ), - trailing: Switch( - value: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data, - onChanged: (value) { - SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.put(value); - // Reset the state of the sorting - context.read().seriesSortType = SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; - context.read().seriesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; - }, - ), - ), - ); -} diff --git a/lib/modules/settings/modules/customization_tautulli.dart b/lib/modules/settings/modules/customization_tautulli.dart deleted file mode 100644 index 4f22fc32..00000000 --- a/lib/modules/settings/modules/customization_tautulli.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'customization_tautulli/route.dart'; -export 'customization_tautulli/widgets.dart'; diff --git a/lib/modules/settings/modules/customization_tautulli/route.dart b/lib/modules/settings/modules/customization_tautulli/route.dart deleted file mode 100644 index 39fb2db6..00000000 --- a/lib/modules/settings/modules/customization_tautulli/route.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsCustomizationTautulliRouter { - static const ROUTE_NAME = '/settings/customization/tautulli'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsCustomizationTautulliRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsCustomizationTautulliRouter._(); -} - -class _SettingsCustomizationTautulliRoute extends StatefulWidget { - @override - State<_SettingsCustomizationTautulliRoute> createState() => _State(); -} - -class _State extends State<_SettingsCustomizationTautulliRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Tautulli', - actions: [ - LSIconButton( - icon: Icons.settings, - onPressed: () async => SettingsModulesTautulliRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => LSListView( - children: [ - ..._defaultPages, - ..._activity, - ..._statistics, - ], - ); - - List get _defaultPages => [ - LSHeader( - text: 'Default Pages', - subtitle: 'Choose the default page when opening routes with navigation bars', - ), - SettingsCustomizationTautulliDefaultPageHomeTile(), - SettingsCustomizationTautulliDefaultPageGraphsTile(), - SettingsCustomizationTautulliDefaultPageLibrariesDetailsTile(), - SettingsCustomizationTautulliDefaultPageMediaDetailsTile(), - SettingsCustomizationTautulliDefaultPageUserDetailsTile(), - ]; - - List get _activity => [ - LSHeader( - text: 'Activity', - subtitle: 'Customizable options related to Tautulli activity', - ), - SettingsCustomizationTautulliDefaultTerminationMessageTile(), - SettingsCustomizationTautulliRefreshRateTile(), - ]; - - List get _statistics => [ - LSHeader( - text: 'Statistics', - subtitle: 'Customizable options related to Tautulli statistics', - ), - SettingsCustomizationTautulliStatisticsItemCountTile(), - ]; -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets.dart b/lib/modules/settings/modules/customization_tautulli/widgets.dart deleted file mode 100644 index 17e07379..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'widgets/activity_refresh_rate_tile.dart'; -export 'widgets/activity_termination_message_tile.dart'; -export 'widgets/default_pages_graphs.dart'; -export 'widgets/default_pages_home.dart'; -export 'widgets/default_pages_libraries_details.dart'; -export 'widgets/default_pages_media_details.dart'; -export 'widgets/default_pages_user_details.dart'; -export 'widgets/statistics_item_count_tile.dart'; diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/activity_refresh_rate_tile.dart b/lib/modules/settings/modules/customization_tautulli/widgets/activity_refresh_rate_tile.dart deleted file mode 100644 index 05fd1e3e..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/activity_refresh_rate_tile.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliRefreshRateTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.REFRESH_RATE.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Refresh Rate'), - subtitle: LSSubtitle( - text: [ - 'Every', - TautulliDatabaseValue.REFRESH_RATE.data == 1 - ? ' ' - : ' ${TautulliDatabaseValue.REFRESH_RATE.data.toString()} ', - TautulliDatabaseValue.REFRESH_RATE.data == 1 - ? 'Second' - : 'Seconds' - ].join(), - ), - trailing: LSIconButton(icon: Icons.refresh), - onTap: () async => _onTap(context), - ), - ); - - Future _onTap(BuildContext context) async { - List _values = await TautulliDialogs.setRefreshRate(context); - if(_values[0]) TautulliDatabaseValue.REFRESH_RATE.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/activity_termination_message_tile.dart b/lib/modules/settings/modules/customization_tautulli/widgets/activity_termination_message_tile.dart deleted file mode 100644 index 21b94b36..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/activity_termination_message_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultTerminationMessageTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.TERMINATION_MESSAGE.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Default Termination Message'), - subtitle: LSSubtitle( - text: (TautulliDatabaseValue.TERMINATION_MESSAGE.data as String).isEmpty - ? 'Not Set' - : TautulliDatabaseValue.TERMINATION_MESSAGE.data, - ), - trailing: LSIconButton(icon: Icons.videocam_off), - onTap: () async => _onTap(context), - ), - ); - - Future _onTap(BuildContext context) async { - List _values = await TautulliDialogs.setTerminationMessage(context); - if(_values[0]) TautulliDatabaseValue.TERMINATION_MESSAGE.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_graphs.dart b/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_graphs.dart deleted file mode 100644 index 1537d6ea..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_graphs.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultPageGraphsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Graphs'), - subtitle: LSSubtitle(text: TautulliGraphsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), - trailing: LSIconButton(icon: TautulliGraphsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliGraphsNavigationBar.titles, icons: TautulliGraphsNavigationBar.icons); - if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_home.dart b/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_home.dart deleted file mode 100644 index 4cd9cef0..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_home.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultPageHomeTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Home'), - subtitle: LSSubtitle(text: TautulliNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LSIconButton(icon: TautulliNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliNavigationBar.titles, icons: TautulliNavigationBar.icons); - if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_libraries_details.dart b/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_libraries_details.dart deleted file mode 100644 index c2f96be9..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_libraries_details.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultPageLibrariesDetailsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Library Details'), - subtitle: LSSubtitle(text: TautulliLibrariesDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), - trailing: LSIconButton(icon: TautulliLibrariesDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliLibrariesDetailsNavigationBar.titles, icons: TautulliLibrariesDetailsNavigationBar.icons); - if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_media_details.dart b/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_media_details.dart deleted file mode 100644 index 807924f1..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_media_details.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultPageMediaDetailsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Media Details'), - subtitle: LSSubtitle(text: TautulliMediaDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), - trailing: LSIconButton(icon: TautulliMediaDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliMediaDetailsNavigationBar.titles, icons: TautulliMediaDetailsNavigationBar.icons); - if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_user_details.dart b/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_user_details.dart deleted file mode 100644 index da6b038f..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/default_pages_user_details.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliDefaultPageUserDetailsTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'User Details'), - subtitle: LSSubtitle(text: TautulliUserDetailsNavigationBar.titles[TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), - trailing: LSIconButton(icon: TautulliUserDetailsNavigationBar.icons[TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), - onTap: () async => _defaultPage(context), - ), - ); - - Future _defaultPage(BuildContext context) async { - List _values = await TautulliDialogs.setDefaultPage(context, titles: TautulliUserDetailsNavigationBar.titles, icons: TautulliUserDetailsNavigationBar.icons); - if(_values[0]) TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/customization_tautulli/widgets/statistics_item_count_tile.dart b/lib/modules/settings/modules/customization_tautulli/widgets/statistics_item_count_tile.dart deleted file mode 100644 index c9772905..00000000 --- a/lib/modules/settings/modules/customization_tautulli/widgets/statistics_item_count_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsCustomizationTautulliStatisticsItemCountTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [TautulliDatabaseValue.STATISTICS_STATS_COUNT.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Item Count'), - subtitle: LSSubtitle( - text: [ - TautulliDatabaseValue.STATISTICS_STATS_COUNT.data.toString(), - TautulliDatabaseValue.STATISTICS_STATS_COUNT.data == 1 - ? ' Item' - : ' Items' - ].join(), - ), - trailing: LSIconButton(icon: Icons.format_list_numbered), - onTap: () async => _onTap(context), - ), - ); - - Future _onTap(BuildContext context) async { - List _values = await TautulliDialogs.setStatisticsItemCount(context); - if(_values[0]) TautulliDatabaseValue.STATISTICS_STATS_COUNT.put(_values[1]); - } -} diff --git a/lib/modules/settings/modules/donations.dart b/lib/modules/settings/modules/donations.dart index e71fbae3..25d6dd43 100644 --- a/lib/modules/settings/modules/donations.dart +++ b/lib/modules/settings/modules/donations.dart @@ -1,2 +1,87 @@ -export 'donations/route.dart'; -export 'donations/widgets.dart'; +import 'dart:async'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsDonationsRouter extends LunaPageRouter { + SettingsDonationsRouter() : super('/settings/donations'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsDonationsRoute()); +} + +class _SettingsDonationsRoute extends StatefulWidget { + @override + State<_SettingsDonationsRoute> createState() => _State(); +} + +class _State extends State<_SettingsDonationsRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + static StreamSubscription> purchaseStream; + + @override + void initState() { + super.initState(); + if(LunaInAppPurchases.isAvailable) purchaseStream = LunaInAppPurchases.connection.purchaseUpdatedStream.listen(_purchasedCallback); + } + + @override + void dispose() { + purchaseStream?.cancel(); + super.dispose(); + } + + Future _purchasedCallback(List purchases) async { + for(var purchase in purchases) { + if(purchase.pendingCompletePurchase) { + switch(purchase.status) { + case PurchaseStatus.error: _purchaseFailed(); break; + case PurchaseStatus.purchased: _purchasedSuccess(); break; + default: break; + } + } + } + } + + void _purchasedSuccess() => SettingsDonationsThankYouRouter().navigateTo(context); + + void _purchaseFailed() => showLunaErrorSnackBar( + context: context, + title: 'Transaction Failure', + message: 'The transaction has failed, please try again', + ); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Donations', + ); + + Widget get _body => LunaInAppPurchases.isAvailable && LunaInAppPurchases.donationIAPs.length != 0 + ? LSListViewBuilder( + itemCount: LunaInAppPurchases.donationIAPs.length, + itemBuilder: (context, index) => _iapTile(LunaInAppPurchases.donationIAPs[index]), + ) + : LSGenericMessage(text: 'In-App Purchases Unavailable'); + + Widget _iapTile(ProductDetails product) { + Future _execute() async { + final PurchaseParam _parameters = PurchaseParam(productDetails: product, sandboxTesting: false); + await LunaInAppPurchases.connection.buyConsumable(purchaseParam: _parameters, autoConsume: true); + } + return LSCardTile( + title: LSTitle(text: product.lunaName), + subtitle: LSSubtitle(text: product.price), + trailing: LSIconButton(icon: product.lunaIcon), + onTap: _execute, + ); + } +} diff --git a/lib/modules/settings/modules/donations/route.dart b/lib/modules/settings/modules/donations/route.dart deleted file mode 100644 index 5cd09527..00000000 --- a/lib/modules/settings/modules/donations/route.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'dart:async'; -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:in_app_purchase/in_app_purchase.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsDonationsRouter { - static const ROUTE_NAME = '/settings/donations'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsDonationsRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsDonationsRouter._(); -} - -class _SettingsDonationsRoute extends StatefulWidget { - @override - State<_SettingsDonationsRoute> createState() => _State(); -} - -class _State extends State<_SettingsDonationsRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - static StreamSubscription> purchaseStream; - - @override - void initState() { - super.initState(); - purchaseStream = LunaInAppPurchases.connection.purchaseUpdatedStream.listen(_purchasedCallback); - } - - @override - void dispose() { - purchaseStream?.cancel(); - super.dispose(); - } - - Future _purchasedCallback(List purchases) async { - for(var purchase in purchases) { - if(purchase.pendingCompletePurchase) { - switch(purchase.status) { - case PurchaseStatus.error: _purchaseFailed(); break; - case PurchaseStatus.purchased: _purchasedSuccess(); break; - default: break; - } - } - } - } - - void _purchasedSuccess() => SettingsDonationsThankYouRouter.navigateTo(context); - - void _purchaseFailed() => showLunaErrorSnackBar( - context: context, - title: 'Transaction Failure', - message: 'The transaction has failed, please try again', - ); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Donations', - ); - - Widget get _body => LunaInAppPurchases.available && LunaInAppPurchases.products.length != 0 - ? LSListViewBuilder( - itemCount: LunaInAppPurchases.products.length, - itemBuilder: (context, index) => SettingsDonationsIAPTile(product: LunaInAppPurchases.products[index]), - ) - : LSGenericMessage(text: 'In-App Purchases Unavailable'); -} diff --git a/lib/modules/settings/modules/donations/widgets.dart b/lib/modules/settings/modules/donations/widgets.dart deleted file mode 100644 index f25fa296..00000000 --- a/lib/modules/settings/modules/donations/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/iap_tile.dart'; diff --git a/lib/modules/settings/modules/donations/widgets/iap_tile.dart b/lib/modules/settings/modules/donations/widgets/iap_tile.dart deleted file mode 100644 index 3c35a79b..00000000 --- a/lib/modules/settings/modules/donations/widgets/iap_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:in_app_purchase/in_app_purchase.dart'; -import 'package:lunasea/core.dart'; - -class SettingsDonationsIAPTile extends StatelessWidget { - final ProductDetails product; - - SettingsDonationsIAPTile({ - Key key, - @required this.product, - }) : super(key: key); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: product.ls_Name), - subtitle: LSSubtitle(text: product.ls_Description), - trailing: LSIconButton(icon: product.ls_Icon), - onTap: () async => await _purchase(), - ); - - Future _purchase() async { - final PurchaseParam _parameters = PurchaseParam(productDetails: product, sandboxTesting: false); - await LunaInAppPurchases.connection.buyConsumable(purchaseParam: _parameters, autoConsume: true); - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/donations_thankyou.dart b/lib/modules/settings/modules/donations_thankyou.dart index ea4d52fb..f1ee380f 100644 --- a/lib/modules/settings/modules/donations_thankyou.dart +++ b/lib/modules/settings/modules/donations_thankyou.dart @@ -1 +1,47 @@ -export 'donations_thankyou/route.dart'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class SettingsDonationsThankYouRouter extends LunaPageRouter { + SettingsDonationsThankYouRouter() : super('/settings/donations/thankyou'); + + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsDonationsThankYouRoute()); +} + +class _SettingsDonationsThankYouRoute extends StatefulWidget { + @override + State<_SettingsDonationsThankYouRoute> createState() => _State(); +} + +class _State extends State<_SettingsDonationsThankYouRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Donations', + ); + + Widget get _body => Padding( + child: Column( + children: [ + LSHeader( + text: 'Thank You', + subtitle: [ + "Thank you for supporting the open-source community!", + "Donations are never expected or assumed, so thank you for giving back and helping LunaSea remain free, forever!", + ].join("\n\n"), + ), + ], + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + ), + padding: EdgeInsets.only(top: 6.0), + ); +} diff --git a/lib/modules/settings/modules/donations_thankyou/route.dart b/lib/modules/settings/modules/donations_thankyou/route.dart deleted file mode 100644 index 75bc4f8f..00000000 --- a/lib/modules/settings/modules/donations_thankyou/route.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:flare_flutter/flare_actor.dart'; - -class SettingsDonationsThankYouRouter { - static const ROUTE_NAME = '/settings/donations/thankyou'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsDonationsThankYouRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsDonationsThankYouRouter._(); -} - -class _SettingsDonationsThankYouRoute extends StatefulWidget { - @override - State<_SettingsDonationsThankYouRoute> createState() => _State(); -} - -class _State extends State<_SettingsDonationsThankYouRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Donations', - ); - - Widget get _body => Padding( - child: Column( - children: [ - LSHeader( - text: 'Thank You', - subtitle: [ - "Thank you for supporting the open-source community! ", - "LunaSea was and still is a passion project built for the community, and will remain entirely open-source.", - "\n\n", - "Donations are never expected or assumed, so thank you for giving back and helping LunaSea remain free, forever!", - ].join(), - ), - Container( - height: MediaQuery.of(context).size.height/3, - width: MediaQuery.of(context).size.width, - child: FlareActor( - RiveAnimations.CODER['path'], - shouldClip: false, - alignment: Alignment.center, - fit: BoxFit.contain, - animation: RiveAnimations.CODER['animation'], - ), - ), - ], - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - ), - padding: EdgeInsets.only(top: 6.0), - ); -} diff --git a/lib/modules/settings/modules/logs.dart b/lib/modules/settings/modules/logs.dart deleted file mode 100644 index 1de871a3..00000000 --- a/lib/modules/settings/modules/logs.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'logs/route.dart'; -export 'logs/widgets.dart'; diff --git a/lib/modules/settings/modules/logs/route.dart b/lib/modules/settings/modules/logs/route.dart deleted file mode 100644 index 18500cf5..00000000 --- a/lib/modules/settings/modules/logs/route.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsLogsRouter { - static const ROUTE_NAME = '/settings/logs'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsLogsRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsLogsRouter._(); -} - -class _SettingsLogsRoute extends StatefulWidget { - @override - State<_SettingsLogsRoute> createState() => _State(); -} - -class _State extends State<_SettingsLogsRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Logs', - ); - - Widget get _body => LSListView( - children: [ - ..._logs, - LSDivider(), - ..._other, - ], - ); - - List get _logs => [ - LSCardTile( - title: LSTitle(text: 'All Logs'), - subtitle: LSSubtitle(text: 'View Logs of All Types'), - trailing: LSIconButton(icon: Icons.developer_mode), - onTap: () async => _viewLogs('All'), - ), - LSCardTile( - title: LSTitle(text: 'Warning'), - subtitle: LSSubtitle(text: 'View Warning Logs'), - trailing: LSIconButton(icon: Icons.warning), - onTap: () async => _viewLogs('Warning'), - ), - LSCardTile( - title: LSTitle(text: 'Error'), - subtitle: LSSubtitle(text: 'View Error Logs'), - trailing: LSIconButton(icon: Icons.report), - onTap: () async => _viewLogs('Error'), - ), - LSCardTile( - title: LSTitle(text: 'Fatal'), - subtitle: LSSubtitle(text: 'View Fatal Logs'), - trailing: LSIconButton(icon: Icons.new_releases), - onTap: () async => _viewLogs('Fatal'), - ), - ]; - - List get _other => [ - SettingsLogsExportTile(), - SettingsLogsClearTile(), - ]; - - Future _viewLogs(String type) async => SettingsLogsDetailsRouter.navigateTo( - context, - type: type, - ); -} diff --git a/lib/modules/settings/modules/logs/widgets.dart b/lib/modules/settings/modules/logs/widgets.dart deleted file mode 100644 index a8e43e8f..00000000 --- a/lib/modules/settings/modules/logs/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/clear_tile.dart'; -export 'widgets/export_tile.dart'; diff --git a/lib/modules/settings/modules/logs/widgets/clear_tile.dart b/lib/modules/settings/modules/logs/widgets/clear_tile.dart deleted file mode 100644 index 67128e8f..00000000 --- a/lib/modules/settings/modules/logs/widgets/clear_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsLogsClearTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Clear'), - subtitle: LSSubtitle(text: 'Clear All Recorded Logs'), - trailing: LSIconButton(icon: Icons.delete), - onTap: () async => _clearLogs(context), - ); - - Future _clearLogs(BuildContext context) async { - List _values = await SettingsDialogs.clearLogs(context); - if(_values[0]) { - LunaLogger.clearLogs(); - showLunaSuccessSnackBar( - context: context, - title: 'Logs Cleared', - message: 'All recorded logs have been cleared', - ); - } - } -} diff --git a/lib/modules/settings/modules/logs/widgets/export_tile.dart b/lib/modules/settings/modules/logs/widgets/export_tile.dart deleted file mode 100644 index ff7e7871..00000000 --- a/lib/modules/settings/modules/logs/widgets/export_tile.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:share/share.dart'; - -class SettingsLogsExportTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Export'), - subtitle: LSSubtitle(text: 'Export All Recorded Logs'), - trailing: LSIconButton(icon: Icons.file_download), - onTap: () async => _exportLogs(context), - ); - - Future _exportLogs(BuildContext context) async { - List _values = await SettingsDialogs.exportLogs(context); - if(_values[0]) { - File _logs = await LunaLogger.exportLogs(); - Share.shareFiles([_logs.path]); - } - } -} diff --git a/lib/modules/settings/modules/logs_details.dart b/lib/modules/settings/modules/logs_details.dart deleted file mode 100644 index dcbcb333..00000000 --- a/lib/modules/settings/modules/logs_details.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'logs_details/route.dart'; -export 'logs_details/widgets.dart'; diff --git a/lib/modules/settings/modules/modules.dart b/lib/modules/settings/modules/modules.dart deleted file mode 100644 index 36b205e5..00000000 --- a/lib/modules/settings/modules/modules.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules/route.dart'; -export 'modules/widgets.dart'; diff --git a/lib/modules/settings/modules/modules/route.dart b/lib/modules/settings/modules/modules/route.dart deleted file mode 100644 index 022883c6..00000000 --- a/lib/modules/settings/modules/modules/route.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules.dart'; - -class SettingsModulesRouter { - static const ROUTE_NAME = '/settings/modules'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesRouter._(); -} - -class _SettingsModulesRoute extends StatefulWidget { - @override - State<_SettingsModulesRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesRoute> with AutomaticKeepAliveClientMixin { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - bool get wantKeepAlive => true; - - @override - Widget build(BuildContext context) { - super.build(context); - return Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - } - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Modules', - actions: [ - SettingsModulesEnabledProfileButton(), - ], - ); - - Widget get _body => LSListView( - children: [ - ..._general, - LSDivider(), - ..._automation, - LSDivider(), - ..._clients, - LSDivider(), - ..._monitoring, - ], - ); - - List get _general => [ - _tileFromModuleMap(SearchConstants.MODULE_MAP, () async => SettingsModulesSearchRouter.navigateTo(context)), - _tileFromModuleMap(WakeOnLANConstants.MODULE_MAP, () async => SettingsModulesWakeOnLANRouter.navigateTo(context)), - ]; - - List get _automation => [ - _tileFromModuleMap(LidarrConstants.MODULE_MAP, () async => SettingsModulesLidarrRouter.navigateTo(context)), - _tileFromModuleMap(RadarrConstants.MODULE_MAP, () async => SettingsModulesRadarrRouter.navigateTo(context)), - _tileFromModuleMap(SonarrConstants.MODULE_MAP, () async => SettingsModulesSonarrRouter.navigateTo(context)), - ]; - - List get _clients => [ - _tileFromModuleMap(NZBGetConstants.MODULE_MAP, () async => SettingsModulesNZBGetRouter.navigateTo(context)), - _tileFromModuleMap(SABnzbdConstants.MODULE_MAP, () async => SettingsModulesSABnzbdRouter.navigateTo(context)), - ]; - - List get _monitoring => [ - _tileFromModuleMap(TautulliConstants.MODULE_MAP, () async => SettingsModulesTautulliRouter.navigateTo(context)), - ]; - - Widget _tileFromModuleMap(LunaModuleMap map, Function onTap) => LSCardTile( - title: LSTitle(text: map.name), - subtitle: LSSubtitle(text: map.settingsDescription), - trailing: LSIconButton(icon: map.icon), - onTap: onTap, - ); -} diff --git a/lib/modules/settings/modules/modules/widgets.dart b/lib/modules/settings/modules/modules/widgets.dart deleted file mode 100644 index 30e51b8f..00000000 --- a/lib/modules/settings/modules/modules/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/enabled_profile.dart'; diff --git a/lib/modules/settings/modules/modules/widgets/enabled_profile.dart b/lib/modules/settings/modules/modules/widgets/enabled_profile.dart deleted file mode 100644 index 1a73fb68..00000000 --- a/lib/modules/settings/modules/modules/widgets/enabled_profile.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesEnabledProfileButton extends StatelessWidget { - @override - Widget build(BuildContext context) => LSIconButton( - icon: Icons.person, - onPressed: () async => _changeProfile(context), - ); - - Future _changeProfile(BuildContext context) async { - List values = await SettingsDialogs.enabledProfile( - context, - Database.profilesBox.keys.map((x) => x as String).toList()..sort((a,b) => a.toLowerCase().compareTo(b.toLowerCase())), - ); - if(values[0] && values[1] != LunaSeaDatabaseValue.ENABLED_PROFILE.data) - LunaProfile.changeProfile(context, values[1]); - } -} diff --git a/lib/modules/settings/modules/modules_lidarr.dart b/lib/modules/settings/modules/modules_lidarr.dart deleted file mode 100644 index c2115b91..00000000 --- a/lib/modules/settings/modules/modules_lidarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_lidarr/route.dart'; -export 'modules_lidarr/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_lidarr/route.dart b/lib/modules/settings/modules/modules_lidarr/route.dart deleted file mode 100644 index eb33f116..00000000 --- a/lib/modules/settings/modules/modules_lidarr/route.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrRouter { - static const ROUTE_NAME = '/settings/modules/lidarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesLidarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesLidarrRouter._(); -} - -class _SettingsModulesLidarrRoute extends StatefulWidget { - @override - State<_SettingsModulesLidarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesLidarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Lidarr', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationLidarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesLidarrTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesLidarrEnabledTile(), - SettingsModulesLidarrHostTile(), - SettingsModulesLidarrAPIKeyTile(), - SettingsModulesLidarrCustomHeadersTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_lidarr/widgets.dart b/lib/modules/settings/modules/modules_lidarr/widgets.dart deleted file mode 100644 index 9d21dbdd..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'widgets/apikey_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_lidarr/widgets/apikey_tile.dart b/lib/modules/settings/modules/modules_lidarr/widgets/apikey_tile.dart deleted file mode 100644 index fbe6707c..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets/apikey_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesLidarrAPIKeyTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'API Key'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.lidarrKey == null || Database.currentProfileObject.lidarrKey == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeKey(context), - ); - - Future _changeKey(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'Lidarr API Key', - prefill: Database.currentProfileObject.lidarrKey ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.lidarrKey = _values[1]; - Database.currentProfileObject.save(); - } - } -} diff --git a/lib/modules/settings/modules/modules_lidarr/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_lidarr/widgets/custom_headers_tile.dart deleted file mode 100644 index db18f5cf..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesLidarrHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_lidarr/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_lidarr/widgets/enabled_tile.dart deleted file mode 100644 index b01bd0ec..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets/enabled_tile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesLidarrEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable Lidarr'), - trailing: Switch( - value: Database.currentProfileObject.lidarrEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.lidarrEnabled = value; - Database.currentProfileObject.save(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_lidarr/widgets/host_tile.dart b/lib/modules/settings/modules/modules_lidarr/widgets/host_tile.dart deleted file mode 100644 index 6590652a..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets/host_tile.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.lidarrHost == null || Database.currentProfileObject.lidarrHost == '' - ? 'Not Set' - : Database.currentProfileObject.lidarrHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'Lidarr Host', - prefill: Database.currentProfileObject.lidarrHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.lidarrHost = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_lidarr/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_lidarr/widgets/test_connection_tile.dart deleted file mode 100644 index ed89b185..00000000 --- a/lib/modules/settings/modules/modules_lidarr/widgets/test_connection_tile.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/lidarr.dart'; - -class SettingsModulesLidarrTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async => await LidarrAPI.from(Database.currentProfileObject).testConnection() - .then((_) => showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'Lidarr is ready to use with LunaSea', - )) - .catchError((error, stack) { - LunaLogger.error( - 'SettingsModulesLidarrTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - stack, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); -} diff --git a/lib/modules/settings/modules/modules_lidarr_headers.dart b/lib/modules/settings/modules/modules_lidarr_headers.dart deleted file mode 100644 index c5cacbd7..00000000 --- a/lib/modules/settings/modules/modules_lidarr_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_lidarr_headers/route.dart'; -export 'modules_lidarr_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_lidarr_headers/route.dart b/lib/modules/settings/modules/modules_lidarr_headers/route.dart deleted file mode 100644 index 3f9843a5..00000000 --- a/lib/modules/settings/modules/modules_lidarr_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrHeadersRouter { - static const ROUTE_NAME = '/settings/modules/lidarr/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesLidarrHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesLidarrHeadersRouter._(); -} - -class _SettingsModulesLidarrHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesLidarrHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesLidarrHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.lidarrHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesLidarrHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesLidarrHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_lidarr_headers/widgets.dart b/lib/modules/settings/modules/modules_lidarr_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_lidarr_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_lidarr_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_lidarr_headers/widgets/add_header_tile.dart deleted file mode 100644 index 2609f213..00000000 --- a/lib/modules/settings/modules/modules_lidarr_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesLidarrHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.lidarrHeaders = _headers; - Database.currentProfileObject.save(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.lidarrHeaders = _headers; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_lidarr_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_lidarr_headers/widgets/header_tile.dart deleted file mode 100644 index 59aadcad..00000000 --- a/lib/modules/settings/modules/modules_lidarr_headers/widgets/header_tile.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesLidarrHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesLidarrHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.lidarrHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.lidarrHeaders = _headers; - Database.currentProfileObject.save(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_nzbget.dart b/lib/modules/settings/modules/modules_nzbget.dart deleted file mode 100644 index f642a359..00000000 --- a/lib/modules/settings/modules/modules_nzbget.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_nzbget/route.dart'; -export 'modules_nzbget/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_nzbget/route.dart b/lib/modules/settings/modules/modules_nzbget/route.dart deleted file mode 100644 index 88eda3b9..00000000 --- a/lib/modules/settings/modules/modules_nzbget/route.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetRouter { - static const ROUTE_NAME = '/settings/modules/nzbget'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesNZBGetRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesNZBGetRouter._(); -} - -class _SettingsModulesNZBGetRoute extends StatefulWidget { - @override - State<_SettingsModulesNZBGetRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesNZBGetRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'NZBGet', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationNZBGetRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesNZBGetTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesNZBGetEnabledTile(), - SettingsModulesNZBGetHostTile(), - SettingsModulesNZBGetUsernameTile(), - SettingsModulesNZBGetPasswordTile(), - SettingsModulesNZBGetCustomHeadersTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_nzbget/widgets.dart b/lib/modules/settings/modules/modules_nzbget/widgets.dart deleted file mode 100644 index 8446093a..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'widgets/username_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/password_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/custom_headers_tile.dart deleted file mode 100644 index 32da96ba..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesNZBGetHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/enabled_tile.dart deleted file mode 100644 index 1092332a..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/enabled_tile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesNZBGetEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable NZBGet'), - trailing: Switch( - value: Database.currentProfileObject.nzbgetEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.nzbgetEnabled = value; - Database.currentProfileObject.save(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/host_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/host_tile.dart deleted file mode 100644 index d79ec8ac..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/host_tile.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.nzbgetHost == null || Database.currentProfileObject.nzbgetHost == '' - ? 'Not Set' - : Database.currentProfileObject.nzbgetHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'NZBGet Host', - prefill: Database.currentProfileObject.nzbgetHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.nzbgetHost = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/password_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/password_tile.dart deleted file mode 100644 index 723532f3..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/password_tile.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesNZBGetPasswordTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Password'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.nzbgetPass == null || Database.currentProfileObject.nzbgetPass == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changePassword(context), - ); - - Future _changePassword(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'NZBGet Password', - prefill: Database.currentProfileObject.nzbgetPass ?? '', - extraText: [ - LSDialog.textSpanContent(text: '${Constants.TEXT_BULLET}\tIf your password includes special characters, considering adding a '), - LSDialog.bolded(text: 'basic authentication'), - LSDialog.textSpanContent(text: ' header with your username and password instead for better support'), - ], - ); - if(_values[0]) { - Database.currentProfileObject.nzbgetPass = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/test_connection_tile.dart deleted file mode 100644 index 165102f7..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/test_connection_tile.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/nzbget.dart'; - -class SettingsModulesNZBGetTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async => await NZBGetAPI.from(Database.currentProfileObject).testConnection() - .then((_) => showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'NZBGet is ready to use with LunaSea', - )) - .catchError((error, stack) { - LunaLogger.error( - 'SettingsModulesNZBGetTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - stack, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); -} diff --git a/lib/modules/settings/modules/modules_nzbget/widgets/username_tile.dart b/lib/modules/settings/modules/modules_nzbget/widgets/username_tile.dart deleted file mode 100644 index d924baee..00000000 --- a/lib/modules/settings/modules/modules_nzbget/widgets/username_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesNZBGetUsernameTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Username'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.nzbgetUser == null || Database.currentProfileObject.nzbgetUser == '' - ? 'Not Set' - : Database.currentProfileObject.nzbgetUser - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeUsername(context), - ); - - Future _changeUsername(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'NZBGet Username', - prefill: Database.currentProfileObject.nzbgetUser ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.nzbgetUser = _values[1]; - Database.currentProfileObject.save(); - } - } -} diff --git a/lib/modules/settings/modules/modules_nzbget_headers.dart b/lib/modules/settings/modules/modules_nzbget_headers.dart deleted file mode 100644 index 959e5aaf..00000000 --- a/lib/modules/settings/modules/modules_nzbget_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_nzbget_headers/route.dart'; -export 'modules_nzbget_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_nzbget_headers/route.dart b/lib/modules/settings/modules/modules_nzbget_headers/route.dart deleted file mode 100644 index 73bf94f9..00000000 --- a/lib/modules/settings/modules/modules_nzbget_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetHeadersRouter { - static const ROUTE_NAME = '/settings/modules/nzbget/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesNZBGetHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesNZBGetHeadersRouter._(); -} - -class _SettingsModulesNZBGetHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesNZBGetHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesNZBGetHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.nzbgetHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesNZBGetHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesNZBGetHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_nzbget_headers/widgets.dart b/lib/modules/settings/modules/modules_nzbget_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_nzbget_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_nzbget_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_nzbget_headers/widgets/add_header_tile.dart deleted file mode 100644 index f791a52c..00000000 --- a/lib/modules/settings/modules/modules_nzbget_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesNZBGetHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.nzbgetHeaders = _headers; - Database.currentProfileObject.save(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.nzbgetHeaders = _headers; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_nzbget_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_nzbget_headers/widgets/header_tile.dart deleted file mode 100644 index 457c8f18..00000000 --- a/lib/modules/settings/modules/modules_nzbget_headers/widgets/header_tile.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesNZBGetHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesNZBGetHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.nzbgetHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.nzbgetHeaders = _headers; - Database.currentProfileObject.save(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_radarr.dart b/lib/modules/settings/modules/modules_radarr.dart deleted file mode 100644 index eff04074..00000000 --- a/lib/modules/settings/modules/modules_radarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_radarr/route.dart'; -export 'modules_radarr/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_radarr/route.dart b/lib/modules/settings/modules/modules_radarr/route.dart deleted file mode 100644 index d7a6d21f..00000000 --- a/lib/modules/settings/modules/modules_radarr/route.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrRouter { - static const ROUTE_NAME = '/settings/modules/radarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesRadarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesRadarrRouter._(); -} - -class _SettingsModulesRadarrRoute extends StatefulWidget { - @override - State<_SettingsModulesRadarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesRadarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Radarr', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationRadarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesRadarrTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesRadarrEnabledTile(), - SettingsModulesRadarrHostTile(), - SettingsModulesRadarrAPIKeyTile(), - SettingsModulesRadarrCustomHeadersTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_radarr/widgets.dart b/lib/modules/settings/modules/modules_radarr/widgets.dart deleted file mode 100644 index 9d21dbdd..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'widgets/apikey_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_radarr/widgets/apikey_tile.dart b/lib/modules/settings/modules/modules_radarr/widgets/apikey_tile.dart deleted file mode 100644 index fdc297b6..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets/apikey_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesRadarrAPIKeyTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'API Key'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.radarrKey == null || Database.currentProfileObject.radarrKey == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeKey(context), - ); - - Future _changeKey(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'Radarr API Key', - prefill: Database.currentProfileObject.radarrKey ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.radarrKey = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_radarr/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_radarr/widgets/custom_headers_tile.dart deleted file mode 100644 index cf46af3f..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesRadarrHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_radarr/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_radarr/widgets/enabled_tile.dart deleted file mode 100644 index 0a3ca460..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets/enabled_tile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesRadarrEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable Radarr'), - trailing: Switch( - value: Database.currentProfileObject.radarrEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.radarrEnabled = value; - Database.currentProfileObject.save(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_radarr/widgets/host_tile.dart b/lib/modules/settings/modules/modules_radarr/widgets/host_tile.dart deleted file mode 100644 index 79c8b7c6..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets/host_tile.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.radarrHost == null || Database.currentProfileObject.radarrHost == '' - ? 'Not Set' - : Database.currentProfileObject.radarrHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'Radarr Host', - prefill: Database.currentProfileObject.radarrHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.radarrHost = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_radarr/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_radarr/widgets/test_connection_tile.dart deleted file mode 100644 index 0ab68f37..00000000 --- a/lib/modules/settings/modules/modules_radarr/widgets/test_connection_tile.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/radarr.dart'; - -class SettingsModulesRadarrTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async => await RadarrAPI.from(Database.currentProfileObject).testConnection() - .then((_) => showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'Radarr is ready to use with LunaSea', - )) - .catchError((error, stack) { - LunaLogger.error( - 'SettingsModulesRadarrTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - stack, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); -} diff --git a/lib/modules/settings/modules/modules_radarr_headers.dart b/lib/modules/settings/modules/modules_radarr_headers.dart deleted file mode 100644 index 60d88556..00000000 --- a/lib/modules/settings/modules/modules_radarr_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_radarr_headers/route.dart'; -export 'modules_radarr_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_radarr_headers/route.dart b/lib/modules/settings/modules/modules_radarr_headers/route.dart deleted file mode 100644 index b756b106..00000000 --- a/lib/modules/settings/modules/modules_radarr_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrHeadersRouter { - static const ROUTE_NAME = '/settings/modules/radarr/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesRadarrHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesRadarrHeadersRouter._(); -} - -class _SettingsModulesRadarrHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesRadarrHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesRadarrHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.radarrHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesRadarrHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesRadarrHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_radarr_headers/widgets.dart b/lib/modules/settings/modules/modules_radarr_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_radarr_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_radarr_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_radarr_headers/widgets/add_header_tile.dart deleted file mode 100644 index 53e0d57b..00000000 --- a/lib/modules/settings/modules/modules_radarr_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesRadarrHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.radarrHeaders = _headers; - Database.currentProfileObject.save(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.radarrHeaders = _headers; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_radarr_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_radarr_headers/widgets/header_tile.dart deleted file mode 100644 index d8ea17ed..00000000 --- a/lib/modules/settings/modules/modules_radarr_headers/widgets/header_tile.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesRadarrHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesRadarrHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.radarrHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.radarrHeaders = _headers; - Database.currentProfileObject.save(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_sabnzbd.dart b/lib/modules/settings/modules/modules_sabnzbd.dart deleted file mode 100644 index 62c56b90..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_sabnzbd/route.dart'; -export 'modules_sabnzbd/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_sabnzbd/route.dart b/lib/modules/settings/modules/modules_sabnzbd/route.dart deleted file mode 100644 index 48902991..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/route.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdRouter { - static const ROUTE_NAME = '/settings/modules/sabnzbd'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSABnzbdRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSABnzbdRouter._(); -} - -class _SettingsModulesSABnzbdRoute extends StatefulWidget { - @override - State<_SettingsModulesSABnzbdRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesSABnzbdRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'SABnzbd', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationSABnzbdRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesSABnzbdTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesSABnzbdEnabledTile(), - SettingsModulesSABnzbdHostTile(), - SettingsModulesSABnzbdAPIKeyTile(), - SettingsModulesSABnzbdCustomHeadersTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets.dart deleted file mode 100644 index 9d21dbdd..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'widgets/apikey_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets/apikey_tile.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets/apikey_tile.dart deleted file mode 100644 index 55889ac8..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets/apikey_tile.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesSABnzbdAPIKeyTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'API Key'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.sabnzbdKey == null || Database.currentProfileObject.sabnzbdKey == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeKey(context), - ); - - Future _changeKey(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'SABnzbd API Key', - prefill: Database.currentProfileObject.sabnzbdKey ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.sabnzbdKey = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets/custom_headers_tile.dart deleted file mode 100644 index 646f51ac..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesSABnzbdHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets/enabled_tile.dart deleted file mode 100644 index 966449a4..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets/enabled_tile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesSABnzbdEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable SABnzbd'), - trailing: Switch( - value: Database.currentProfileObject.sabnzbdEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.sabnzbdEnabled = value; - Database.currentProfileObject.save(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets/host_tile.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets/host_tile.dart deleted file mode 100644 index ac528d89..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets/host_tile.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.sabnzbdHost == null || Database.currentProfileObject.sabnzbdHost == '' - ? 'Not Set' - : Database.currentProfileObject.sabnzbdHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'SABnzbd Host', - prefill: Database.currentProfileObject.sabnzbdHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.sabnzbdHost = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sabnzbd/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_sabnzbd/widgets/test_connection_tile.dart deleted file mode 100644 index d2f78f53..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd/widgets/test_connection_tile.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sabnzbd.dart'; - -class SettingsModulesSABnzbdTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async => await SABnzbdAPI.from(Database.currentProfileObject).testConnection() - .then((response) { - if((response as Response).data['status'] != false) { - showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'SABnzbd is ready to use with LunaSea', - ); - } else { - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - message: 'SABnzbd: ${(response as Response).data['error'] ?? Constants.TEXT_EMDASH}', - ); - } - }) - .catchError((error, stack) { - LunaLogger.error( - 'SettingsModulesSABnzbdTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - stack, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); -} diff --git a/lib/modules/settings/modules/modules_sabnzbd_headers.dart b/lib/modules/settings/modules/modules_sabnzbd_headers.dart deleted file mode 100644 index 690a7421..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_sabnzbd_headers/route.dart'; -export 'modules_sabnzbd_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_sabnzbd_headers/route.dart b/lib/modules/settings/modules/modules_sabnzbd_headers/route.dart deleted file mode 100644 index bc53f685..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdHeadersRouter { - static const ROUTE_NAME = '/settings/modules/sabnzbd/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSABnzbdHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSABnzbdHeadersRouter._(); -} - -class _SettingsModulesSABnzbdHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesSABnzbdHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesSABnzbdHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.sabnzbdHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesSABnzbdHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesSABnzbdHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets.dart b/lib/modules/settings/modules/modules_sabnzbd_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/add_header_tile.dart deleted file mode 100644 index 6798c45c..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesSABnzbdHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.sabnzbdHeaders = _headers; - Database.currentProfileObject.save(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.sabnzbdHeaders = _headers; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/header_tile.dart deleted file mode 100644 index 9666a34c..00000000 --- a/lib/modules/settings/modules/modules_sabnzbd_headers/widgets/header_tile.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSABnzbdHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesSABnzbdHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sabnzbdHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.sabnzbdHeaders = _headers; - Database.currentProfileObject.save(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_search.dart b/lib/modules/settings/modules/modules_search.dart deleted file mode 100644 index 787771a5..00000000 --- a/lib/modules/settings/modules/modules_search.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_search/route.dart'; -export 'modules_search/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_search/route.dart b/lib/modules/settings/modules/modules_search/route.dart deleted file mode 100644 index 39940e0c..00000000 --- a/lib/modules/settings/modules/modules_search/route.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSearchRouter { - static const ROUTE_NAME = '/settings/modules/search'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSearchRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSearchRouter._(); -} - -class _SettingsModulesSearchRoute extends StatefulWidget { - @override - State<_SettingsModulesSearchRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesSearchRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Search', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationSearchRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.indexersBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - LSHeader( - text: 'Newznab Indexers', - subtitle: 'Supports all standard newznab-based indexers, including NZBHydra2', - ), - if(Database.indexersBox.isEmpty) _noIndexers, - ..._indexerList, - _addIndexer, - ], - ), - ); - - List get _indexerList { - List list = List.generate(Database.indexersBox.length, (index) => SettingsModulesSearchIndexerTile( - indexer: Database.indexersBox.getAt(index), - index: index, - )); - list.sort((a,b) => a.indexer.displayName.toLowerCase().trim().compareTo(b.indexer.displayName.toLowerCase().trim())); - return list; - } - - Widget get _noIndexers => LSGenericMessage(text: 'No Indexers Added'); - - Widget get _addIndexer => LSButton( - text: 'Add New Indexer', - onTap: () async => SettingsModulesSearchAddRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_search/widgets.dart b/lib/modules/settings/modules/modules_search/widgets.dart deleted file mode 100644 index 5a8f0042..00000000 --- a/lib/modules/settings/modules/modules_search/widgets.dart +++ /dev/null @@ -1 +0,0 @@ -export 'widgets/indexer_tile.dart'; diff --git a/lib/modules/settings/modules/modules_search/widgets/indexer_tile.dart b/lib/modules/settings/modules/modules_search/widgets/indexer_tile.dart deleted file mode 100644 index b44e5adf..00000000 --- a/lib/modules/settings/modules/modules_search/widgets/indexer_tile.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSearchIndexerTile extends StatelessWidget { - final IndexerHiveObject indexer; - final int index; - - SettingsModulesSearchIndexerTile({ - Key key, - @required this.indexer, - @required this.index, - }) : super(key: key); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: indexer.displayName), - subtitle: LSSubtitle(text: indexer.host), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _enterIndexer(context), - ); - - Future _enterIndexer(BuildContext context) async => SettingsModulesSearchEditRouter.navigateTo( - context, - index: index, - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_search_add.dart b/lib/modules/settings/modules/modules_search_add.dart deleted file mode 100644 index ceee7b53..00000000 --- a/lib/modules/settings/modules/modules_search_add.dart +++ /dev/null @@ -1 +0,0 @@ -export 'modules_search_add/route.dart'; diff --git a/lib/modules/settings/modules/modules_search_add_headers.dart b/lib/modules/settings/modules/modules_search_add_headers.dart deleted file mode 100644 index 846dcdc8..00000000 --- a/lib/modules/settings/modules/modules_search_add_headers.dart +++ /dev/null @@ -1 +0,0 @@ -export 'modules_search_add_headers/route.dart'; diff --git a/lib/modules/settings/modules/modules_search_edit.dart b/lib/modules/settings/modules/modules_search_edit.dart deleted file mode 100644 index 31a99c23..00000000 --- a/lib/modules/settings/modules/modules_search_edit.dart +++ /dev/null @@ -1 +0,0 @@ -export 'modules_search_edit/route.dart'; diff --git a/lib/modules/settings/modules/modules_search_edit_headers.dart b/lib/modules/settings/modules/modules_search_edit_headers.dart deleted file mode 100644 index f82ce099..00000000 --- a/lib/modules/settings/modules/modules_search_edit_headers.dart +++ /dev/null @@ -1 +0,0 @@ -export 'modules_search_edit_headers/route.dart'; diff --git a/lib/modules/settings/modules/modules_sonarr.dart b/lib/modules/settings/modules/modules_sonarr.dart deleted file mode 100644 index a5f2873c..00000000 --- a/lib/modules/settings/modules/modules_sonarr.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_sonarr/route.dart'; -export 'modules_sonarr/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_sonarr/route.dart b/lib/modules/settings/modules/modules_sonarr/route.dart deleted file mode 100644 index a08a1c32..00000000 --- a/lib/modules/settings/modules/modules_sonarr/route.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSonarrRouter { - static const ROUTE_NAME = '/settings/modules/sonarr'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSonarrRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSonarrRouter._(); -} - -class _SettingsModulesSonarrRoute extends StatefulWidget { - @override - State<_SettingsModulesSonarrRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesSonarrRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Sonarr', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationSonarrRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesSonarrTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesSonarrEnabledTile(), - SettingsModulesSonarrHostTile(), - SettingsModulesSonarrAPIKeyTile(), - SettingsModulesSonarrCustomHeadersTile(), - SettingsModulesSonarrEnableVersion3Tile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_sonarr/widgets.dart b/lib/modules/settings/modules/modules_sonarr/widgets.dart deleted file mode 100644 index b1b01476..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'widgets/apikey_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enable_version_3_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/apikey_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/apikey_tile.dart deleted file mode 100644 index df6390a3..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/apikey_tile.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrAPIKeyTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'API Key'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.sonarrKey == null || Database.currentProfileObject.sonarrKey == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeKey(context), - ); - - Future _changeKey(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'Sonarr API Key', - prefill: Database.currentProfileObject.sonarrKey ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.sonarrKey = _values[1]; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/custom_headers_tile.dart deleted file mode 100644 index b17b5f94..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSonarrCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesSonarrHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/enable_version_3_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/enable_version_3_tile.dart deleted file mode 100644 index 37ddd92e..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/enable_version_3_tile.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrEnableVersion3Tile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Sonarr v3 Features'), - subtitle: LSSubtitle(text: 'Enable Version 3 Specific Features',), - trailing: Switch( - value: Database.currentProfileObject.sonarrVersion3 ?? false, - onChanged: (value) { - Database.currentProfileObject.sonarrVersion3 = value; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - }, - ), - ); -} diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/enabled_tile.dart deleted file mode 100644 index 4f0ea9a1..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/enabled_tile.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable Sonarr'), - trailing: Switch( - value: Database.currentProfileObject.sonarrEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.sonarrEnabled = value; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/host_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/host_tile.dart deleted file mode 100644 index 21901715..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/host_tile.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.sonarrHost == null || Database.currentProfileObject.sonarrHost == '' - ? 'Not Set' - : Database.currentProfileObject.sonarrHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'Sonarr Host', - prefill: Database.currentProfileObject.sonarrHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.sonarrHost = _values[1]; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} diff --git a/lib/modules/settings/modules/modules_sonarr/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_sonarr/widgets/test_connection_tile.dart deleted file mode 100644 index b666f4f7..00000000 --- a/lib/modules/settings/modules/modules_sonarr/widgets/test_connection_tile.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async { - SonarrState state = Provider.of(context, listen: false); - if(!state.enabled) { - showLunaErrorSnackBar( - context: context, - title: 'Sonarr Not Enabled', - message: 'Sonarr needs to be enabled', - ); - return; - } - if(state.host == null || state.host.isEmpty) { - showLunaErrorSnackBar( - context: context, - title: 'Host Required', - message: 'Host is required to connect to Sonarr', - ); - return; - } - if(state.apiKey == null || state.apiKey.isEmpty) { - showLunaErrorSnackBar( - context: context, - title: 'API Key Required', - message: 'API key is required to connect to Sonarr', - ); - return; - } - state.api.system.getStatus() - .then((_) => showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'Sonarr is ready to use with LunaSea', - )).catchError((error, trace) { - LunaLogger.error( - 'SettingsModulesSonarrTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - trace, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); - } -} diff --git a/lib/modules/settings/modules/modules_sonarr_headers.dart b/lib/modules/settings/modules/modules_sonarr_headers.dart deleted file mode 100644 index 28c85722..00000000 --- a/lib/modules/settings/modules/modules_sonarr_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_sonarr_headers/route.dart'; -export 'modules_sonarr_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_sonarr_headers/route.dart b/lib/modules/settings/modules/modules_sonarr_headers/route.dart deleted file mode 100644 index 88a17ca9..00000000 --- a/lib/modules/settings/modules/modules_sonarr_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesSonarrHeadersRouter { - static const ROUTE_NAME = '/settings/modules/sonarr/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesSonarrHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesSonarrHeadersRouter._(); -} - -class _SettingsModulesSonarrHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesSonarrHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesSonarrHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.sonarrHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesSonarrHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesSonarrHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_sonarr_headers/widgets.dart b/lib/modules/settings/modules/modules_sonarr_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_sonarr_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_sonarr_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_sonarr_headers/widgets/add_header_tile.dart deleted file mode 100644 index 28cd64c5..00000000 --- a/lib/modules/settings/modules/modules_sonarr_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesSonarrHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.sonarrHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.sonarrHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} diff --git a/lib/modules/settings/modules/modules_sonarr_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_sonarr_headers/widgets/header_tile.dart deleted file mode 100644 index df2ec980..00000000 --- a/lib/modules/settings/modules/modules_sonarr_headers/widgets/header_tile.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/sonarr.dart'; - -class SettingsModulesSonarrHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesSonarrHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.sonarrHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.sonarrHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_tautulli.dart b/lib/modules/settings/modules/modules_tautulli.dart deleted file mode 100644 index c33cec9c..00000000 --- a/lib/modules/settings/modules/modules_tautulli.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_tautulli/route.dart'; -export 'modules_tautulli/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_tautulli/route.dart b/lib/modules/settings/modules/modules_tautulli/route.dart deleted file mode 100644 index 21fc3ad5..00000000 --- a/lib/modules/settings/modules/modules_tautulli/route.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesTautulliRouter { - static const ROUTE_NAME = '/settings/modules/tautulli'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesTautulliRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesTautulliRouter._(); -} - -class _SettingsModulesTautulliRoute extends StatefulWidget { - @override - State<_SettingsModulesTautulliRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesTautulliRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Tautulli', - actions: [ - LSIconButton( - icon: Icons.brush, - onPressed: () async => SettingsCustomizationTautulliRouter.navigateTo(context), - ), - ] - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - SettingsModulesTautulliTestConnectionTile(), - ], - ), - ); - - List get _configuration => [ - SettingsModulesTautulliEnabledTile(), - SettingsModulesTautulliHostTile(), - SettingsModulesTautulliAPIKeyTile(), - SettingsModulesTautulliCustomHeadersTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_tautulli/widgets.dart b/lib/modules/settings/modules/modules_tautulli/widgets.dart deleted file mode 100644 index 9d21dbdd..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'widgets/apikey_tile.dart'; -export 'widgets/custom_headers_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/host_tile.dart'; -export 'widgets/test_connection_tile.dart'; diff --git a/lib/modules/settings/modules/modules_tautulli/widgets/apikey_tile.dart b/lib/modules/settings/modules/modules_tautulli/widgets/apikey_tile.dart deleted file mode 100644 index e38bde82..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets/apikey_tile.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliAPIKeyTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'API Key'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.tautulliKey == null || Database.currentProfileObject.tautulliKey == '' - ? 'Not Set' - : '••••••••••••' - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeKey(context), - ); - - Future _changeKey(BuildContext context) async { - List _values = await LunaDialogs.editText( - context, - 'Tautulli API Key', - prefill: Database.currentProfileObject.tautulliKey ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.tautulliKey = _values[1]; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_tautulli/widgets/custom_headers_tile.dart b/lib/modules/settings/modules/modules_tautulli/widgets/custom_headers_tile.dart deleted file mode 100644 index 781540a2..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets/custom_headers_tile.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesTautulliCustomHeadersTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Custom Headers'), - subtitle: LSSubtitle(text: 'Add Custom Headers to Requests'), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => SettingsModulesTautulliHeadersRouter.navigateTo(context), - ); -} diff --git a/lib/modules/settings/modules/modules_tautulli/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_tautulli/widgets/enabled_tile.dart deleted file mode 100644 index 8208894f..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets/enabled_tile.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable Tautulli'), - trailing: Switch( - value: Database.currentProfileObject.tautulliEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.tautulliEnabled = value; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_tautulli/widgets/host_tile.dart b/lib/modules/settings/modules/modules_tautulli/widgets/host_tile.dart deleted file mode 100644 index 05e04748..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets/host_tile.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliHostTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Host'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.tautulliHost == null || Database.currentProfileObject.tautulliHost == '' - ? 'Not Set' - : Database.currentProfileObject.tautulliHost - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeHost(context), - ); - - Future _changeHost(BuildContext context) async { - List _values = await SettingsDialogs.editHost( - context, - 'Tautulli Host', - prefill: Database.currentProfileObject.tautulliHost ?? '', - ); - if(_values[0]) { - Database.currentProfileObject.tautulliHost = _values[1]; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} diff --git a/lib/modules/settings/modules/modules_tautulli/widgets/test_connection_tile.dart b/lib/modules/settings/modules/modules_tautulli/widgets/test_connection_tile.dart deleted file mode 100644 index d6bc6e03..00000000 --- a/lib/modules/settings/modules/modules_tautulli/widgets/test_connection_tile.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliTestConnectionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Test Connection', - onTap: () async => _testConnection(context), - ); - - Future _testConnection(BuildContext context) async { - TautulliState state = Provider.of(context, listen: false); - if(!state.enabled) { - showLunaErrorSnackBar( - context: context, - title: 'Tautulli Not Enabled', - message: 'Tautulli needs to be enabled', - ); - return; - } - if(state.host == null || state.host.isEmpty) { - showLunaErrorSnackBar( - context: context, - title: 'Host Required', - message: 'Host is required to connect to Tautulli', - ); - return; - } - if(state.apiKey == null || state.apiKey.isEmpty) { - showLunaErrorSnackBar( - context: context, - title: 'API Key Required', - message: 'API key is required to connect to Tautulli', - ); - return; - } - state.api.miscellaneous.arnold() - .then((_) { - showLunaSuccessSnackBar( - context: context, - title: 'Connected Successfully', - message: 'Tautulli is ready to use with LunaSea', - ); - }).catchError((error, trace) { - LunaLogger.error( - 'SettingsModulesTautulliTestConnectionTile', - '_testConnection', - 'Connection Test Failed', - error, - trace, - uploadToSentry: false, - ); - showLunaErrorSnackBar( - context: context, - title: 'Connection Test Failed', - error: error, - ); - }); - } -} diff --git a/lib/modules/settings/modules/modules_tautulli_headers.dart b/lib/modules/settings/modules/modules_tautulli_headers.dart deleted file mode 100644 index 4696e4f1..00000000 --- a/lib/modules/settings/modules/modules_tautulli_headers.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_tautulli_headers/route.dart'; -export 'modules_tautulli_headers/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_tautulli_headers/route.dart b/lib/modules/settings/modules/modules_tautulli_headers/route.dart deleted file mode 100644 index 5b9057ed..00000000 --- a/lib/modules/settings/modules/modules_tautulli_headers/route.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesTautulliHeadersRouter { - static const ROUTE_NAME = '/settings/modules/tautulli/headers'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesTautulliHeadersRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesTautulliHeadersRouter._(); -} - -class _SettingsModulesTautulliHeadersRoute extends StatefulWidget { - @override - State<_SettingsModulesTautulliHeadersRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesTautulliHeadersRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Custom Headers', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profile, _) => LSListView( - children: _headers, - ), - ); - - List get _headers => [ - if((Database.currentProfileObject.tautulliHeaders ?? {}).isEmpty) _noHeaders, - ..._list, - SettingsModulesTautulliHeadersAddHeaderTile(), - ]; - - Widget get _noHeaders => LSGenericMessage(text: 'No Custom Headers Added'); - - List get _list { - Map headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); - List list = []; - headers.forEach((key, value) => list.add(SettingsModulesTautulliHeadersHeaderTile( - headerKey: key.toString(), - headerValue: value.toString(), - ))); - list.sort((a,b) => a.headerKey.toLowerCase().compareTo(b.headerKey.toLowerCase())); - return list; - } -} diff --git a/lib/modules/settings/modules/modules_tautulli_headers/widgets.dart b/lib/modules/settings/modules/modules_tautulli_headers/widgets.dart deleted file mode 100644 index fc17a9b4..00000000 --- a/lib/modules/settings/modules/modules_tautulli_headers/widgets.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'widgets/add_header_tile.dart'; -export 'widgets/header_tile.dart'; diff --git a/lib/modules/settings/modules/modules_tautulli_headers/widgets/add_header_tile.dart b/lib/modules/settings/modules/modules_tautulli_headers/widgets/add_header_tile.dart deleted file mode 100644 index 7c7ca0d3..00000000 --- a/lib/modules/settings/modules/modules_tautulli_headers/widgets/add_header_tile.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliHeadersAddHeaderTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSButton( - text: 'Add Header', - onTap: () async => _addPrompt(context), - ); - - Future _addPrompt(BuildContext context) async { - List results = await SettingsDialogs.addHeader(context); - if(results[0]) switch(results[1]) { - case 1: - _showAuthenticationPrompt(context); - break; - case 100: - _showCustomPrompt(context); - break; - default: - LunaLogger.warning( - 'SettingsModulesTautulliHeadersAddHeaderTile', - '_addPrompt', - 'Unknown case: ${results[1]}', - ); - break; - } - } - - Future _showAuthenticationPrompt(BuildContext context) async { - List results = await SettingsDialogs.addAuthenticationHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); - String _auth = base64.encode(utf8.encode('${results[1]}:${results[2]}')); - _headers.addAll({'Authorization': 'Basic $_auth'}); - Database.currentProfileObject.tautulliHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } - - Future _showCustomPrompt(BuildContext context) async { - List results = await SettingsDialogs.addCustomHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); - _headers.addAll({results[1]: results[2]}); - Database.currentProfileObject.tautulliHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_tautulli_headers/widgets/header_tile.dart b/lib/modules/settings/modules/modules_tautulli_headers/widgets/header_tile.dart deleted file mode 100644 index c96208f7..00000000 --- a/lib/modules/settings/modules/modules_tautulli_headers/widgets/header_tile.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; -import 'package:lunasea/modules/tautulli.dart'; - -class SettingsModulesTautulliHeadersHeaderTile extends StatelessWidget { - final String headerKey; - final String headerValue; - - SettingsModulesTautulliHeadersHeaderTile({ - @required this.headerKey, - @required this.headerValue, - }); - - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: headerKey), - subtitle: LSSubtitle(text: headerValue), - trailing: LSIconButton( - icon: Icons.delete, - color: LunaColours.red, - onPressed: () async => _deleteHeader(context), - ), - ); - - Future _deleteHeader(BuildContext context) async { - List results = await SettingsDialogs.deleteHeader(context); - if(results[0]) { - Map _headers = (Database.currentProfileObject.tautulliHeaders ?? {}).cast(); - _headers.remove(headerKey); - Database.currentProfileObject.tautulliHeaders = _headers; - Database.currentProfileObject.save(); - Provider.of(context, listen: false).reset(); - showLunaSuccessSnackBar( - context: context, - message: headerKey, - title: 'Header Deleted', - ); - } - } -} diff --git a/lib/modules/settings/modules/modules_wakeonlan.dart b/lib/modules/settings/modules/modules_wakeonlan.dart deleted file mode 100644 index 98f9e13f..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'modules_wakeonlan/route.dart'; -export 'modules_wakeonlan/widgets.dart'; diff --git a/lib/modules/settings/modules/modules_wakeonlan/route.dart b/lib/modules/settings/modules/modules_wakeonlan/route.dart deleted file mode 100644 index 4e1a3a0a..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan/route.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesWakeOnLANRouter { - static const ROUTE_NAME = '/settings/modules/wakeonlan'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsModulesWakeOnLANRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsModulesWakeOnLANRouter._(); -} - -class _SettingsModulesWakeOnLANRoute extends StatefulWidget { - @override - State<_SettingsModulesWakeOnLANRoute> createState() => _State(); -} - -class _State extends State<_SettingsModulesWakeOnLANRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Wake on LAN', - ); - - Widget get _body => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, box, _) => LSListView( - children: [ - ..._configuration, - ], - ), - ); - - List get _configuration => [ - SettingsModulesWakeOnLANEnabledTile(), - SettingsModulesWakeOnLANBroadcastAddressTile(), - SettingsModulesWakeOnLANMACAddressTile(), - ]; -} diff --git a/lib/modules/settings/modules/modules_wakeonlan/widgets.dart b/lib/modules/settings/modules/modules_wakeonlan/widgets.dart deleted file mode 100644 index 58015265..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan/widgets.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'widgets/broadcast_address_tile.dart'; -export 'widgets/enabled_tile.dart'; -export 'widgets/mac_address_tile.dart'; diff --git a/lib/modules/settings/modules/modules_wakeonlan/widgets/broadcast_address_tile.dart b/lib/modules/settings/modules/modules_wakeonlan/widgets/broadcast_address_tile.dart deleted file mode 100644 index ce87841f..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan/widgets/broadcast_address_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesWakeOnLANBroadcastAddressTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Broadcast Address'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.wakeOnLANBroadcastAddress == null || Database.currentProfileObject.wakeOnLANBroadcastAddress == '' - ? 'Not Set' - : Database.currentProfileObject.wakeOnLANBroadcastAddress, - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeAddress(context), - ); - - Future _changeAddress(BuildContext context) async { - List _values = await SettingsDialogs.editBroadcastAddress(context, Database.currentProfileObject.wakeOnLANBroadcastAddress ?? ''); - if(_values[0]) { - Database.currentProfileObject.wakeOnLANBroadcastAddress = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_wakeonlan/widgets/enabled_tile.dart b/lib/modules/settings/modules/modules_wakeonlan/widgets/enabled_tile.dart deleted file mode 100644 index a7f299e8..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan/widgets/enabled_tile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsModulesWakeOnLANEnabledTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Enable Wake on LAN'), - trailing: Switch( - value: Database.currentProfileObject.wakeOnLANEnabled ?? false, - onChanged: (value) { - Database.currentProfileObject.wakeOnLANEnabled = value; - Database.currentProfileObject.save(); - }, - ), - ); -} \ No newline at end of file diff --git a/lib/modules/settings/modules/modules_wakeonlan/widgets/mac_address_tile.dart b/lib/modules/settings/modules/modules_wakeonlan/widgets/mac_address_tile.dart deleted file mode 100644 index 8868af83..00000000 --- a/lib/modules/settings/modules/modules_wakeonlan/widgets/mac_address_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsModulesWakeOnLANMACAddressTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Device MAC Address'), - subtitle: LSSubtitle( - text: Database.currentProfileObject.wakeOnLANMACAddress == null || Database.currentProfileObject.wakeOnLANMACAddress == '' - ? 'Not Set' - : Database.currentProfileObject.wakeOnLANMACAddress, - ), - trailing: LSIconButton(icon: Icons.arrow_forward_ios), - onTap: () async => _changeAddress(context), - ); - - Future _changeAddress(BuildContext context) async { - List _values = await SettingsDialogs.editMACAddress(context, Database.currentProfileObject.wakeOnLANMACAddress ?? ''); - if(_values[0]) { - Database.currentProfileObject.wakeOnLANMACAddress = _values[1]; - Database.currentProfileObject.save(); - } - } -} \ No newline at end of file diff --git a/lib/modules/settings/modules/profiles/route.dart b/lib/modules/settings/modules/profiles/route.dart index 3c741cac..d07f792c 100644 --- a/lib/modules/settings/modules/profiles/route.dart +++ b/lib/modules/settings/modules/profiles/route.dart @@ -3,23 +3,11 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsProfilesRouter { - static const ROUTE_NAME = '/settings/profiles'; +class SettingsProfilesRouter extends LunaPageRouter { + SettingsProfilesRouter() : super('/settings/profiles'); - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsProfilesRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsProfilesRouter._(); + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsProfilesRoute()); } class _SettingsProfilesRoute extends StatefulWidget { @@ -43,6 +31,20 @@ class _State extends State<_SettingsProfilesRoute> with AutomaticKeepAliveClient Widget get _appBar => LunaAppBar( context: context, title: 'Profiles', + actions: [_helpMessageButton], + ); + + Widget get _helpMessageButton => LSIconButton( + icon: Icons.help_outline, + onPressed: () async => SettingsDialogs.helpMessage( + context, + title: 'Profiles', + message: [ + 'Profiles allow you to add multiple instances of modules into ${Constants.APPLICATION_NAME}.', + 'Newznab indexer searching is enabled and shared across all profiles.', + 'You can switch between profiles in the main navigation drawer of ${Constants.APPLICATION_NAME}.', + ].join('\n\n'), + ), ); Widget get _body => LSListView( diff --git a/lib/modules/settings/modules/profiles/widgets/add_tile.dart b/lib/modules/settings/modules/profiles/widgets/add_tile.dart index c93a07a5..45db36f8 100644 --- a/lib/modules/settings/modules/profiles/widgets/add_tile.dart +++ b/lib/modules/settings/modules/profiles/widgets/add_tile.dart @@ -29,7 +29,7 @@ class SettingsProfileAddTile extends StatelessWidget { ); } else { Database.profilesBox.put(_values[1], ProfileHiveObject.empty()); - LunaProfile.changeProfile(context, _values[1]); + LunaProfile().safelyChangeProfiles(context, _values[1]); showLunaSuccessSnackBar( context: context, title: 'Profile Added', diff --git a/lib/modules/settings/modules/profiles/widgets/delete_tile.dart b/lib/modules/settings/modules/profiles/widgets/delete_tile.dart index 0a2b43d5..b1712d62 100644 --- a/lib/modules/settings/modules/profiles/widgets/delete_tile.dart +++ b/lib/modules/settings/modules/profiles/widgets/delete_tile.dart @@ -17,7 +17,7 @@ class SettingsProfileDeleteTile extends StatelessWidget { Database.profilesBox.keys.map((x) => x as String).toList()..sort((a,b) => a.toLowerCase().compareTo(b.toLowerCase())), ); if(_values[0]) { - if(_values[1] == LunaSeaDatabaseValue.ENABLED_PROFILE.data) { + if(_values[1] == LunaDatabaseValue.ENABLED_PROFILE.data) { showLunaErrorSnackBar( context: context, title: 'Unable to Delete Profile', diff --git a/lib/modules/settings/modules/profiles/widgets/enabled_tile.dart b/lib/modules/settings/modules/profiles/widgets/enabled_tile.dart index a7735b39..01a050a7 100644 --- a/lib/modules/settings/modules/profiles/widgets/enabled_tile.dart +++ b/lib/modules/settings/modules/profiles/widgets/enabled_tile.dart @@ -7,8 +7,8 @@ class SettingsProfileEnabledTile extends StatelessWidget { Widget build(BuildContext context) => LSCardTile( title: LSTitle(text: 'Enabled Profile'), subtitle: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]), - builder: (context, box, widget) => LSSubtitle(text: box.get(LunaSeaDatabaseValue.ENABLED_PROFILE.key)), + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), + builder: (context, box, widget) => LSSubtitle(text: box.get(LunaDatabaseValue.ENABLED_PROFILE.key)), ), trailing: LSIconButton(icon: Icons.person), onTap: () => _changeProfile(context), @@ -19,7 +19,7 @@ class SettingsProfileEnabledTile extends StatelessWidget { context, Database.profilesBox.keys.map((x) => x as String).toList()..sort((a,b) => a.toLowerCase().compareTo(b.toLowerCase())), ); - if(values[0] && values[1] != LunaSeaDatabaseValue.ENABLED_PROFILE.data) - LunaProfile.changeProfile(context, values[1]); + if(values[0] && values[1] != LunaDatabaseValue.ENABLED_PROFILE.data) + LunaProfile().safelyChangeProfiles(context, values[1]); } } diff --git a/lib/modules/settings/modules/profiles/widgets/rename_tile.dart b/lib/modules/settings/modules/profiles/widgets/rename_tile.dart index 5d0463b9..76e70dd7 100644 --- a/lib/modules/settings/modules/profiles/widgets/rename_tile.dart +++ b/lib/modules/settings/modules/profiles/widgets/rename_tile.dart @@ -34,8 +34,8 @@ class SettingsProfileRenameTile extends StatelessWidget { ); } else { ProfileHiveObject obj = Database.profilesBox.get(old); - Database.profilesBox.put(_values[1], ProfileHiveObject.from(obj)); - if(LunaSeaDatabaseValue.ENABLED_PROFILE.data == old) LunaProfile.changeProfile(context, _values[1]); + Database.profilesBox.put(_values[1], ProfileHiveObject.fromProfileHiveObject(obj)); + if(LunaDatabaseValue.ENABLED_PROFILE.data == old) LunaProfile().safelyChangeProfiles(context, _values[1]); obj.delete(); showLunaSuccessSnackBar( context: context, diff --git a/lib/modules/settings/modules/resources.dart b/lib/modules/settings/modules/resources.dart index 7cb48dc9..31d24b85 100644 --- a/lib/modules/settings/modules/resources.dart +++ b/lib/modules/settings/modules/resources.dart @@ -1 +1,79 @@ -export 'resources/route.dart'; +import 'dart:io'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class SettingsResourcesRouter extends LunaPageRouter { + SettingsResourcesRouter() : super('/settings/resources'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsResourcesRoute()); +} + +class _SettingsResourcesRoute extends StatefulWidget { + @override + State<_SettingsResourcesRoute> createState() => _State(); +} + +class _State extends State<_SettingsResourcesRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Resources', + ); + + Widget get _body => LSListView( + children: [ + LSCardTile( + title: LSTitle(text: 'Discord'), + subtitle: LSSubtitle(text: 'Chat & Discussions'), + trailing: LSIconButton(icon: CustomIcons.discord), + onTap: () async => await Constants.URL_DISCORD.lunaOpenGenericLink(), + ), + LSCardTile( + title: LSTitle(text: 'Documentation'), + subtitle: LSSubtitle(text: 'View the Documentation'), + trailing: LSIconButton(icon: CustomIcons.documentation), + onTap: () async => await Constants.URL_DOCUMENTATION.lunaOpenGenericLink(), + ), + LSCardTile( + title: LSTitle(text: 'Feedback Board'), + subtitle: LSSubtitle(text: 'Request New Features'), + trailing: LSIconButton(icon: Icons.speaker_notes), + onTap: () async => await Constants.URL_FEEDBACK.lunaOpenGenericLink(), + ), + LSCardTile( + title: LSTitle(text: 'GitHub'), + subtitle: LSSubtitle(text: 'View the Source Code'), + trailing: LSIconButton(icon: CustomIcons.github), + onTap: () async => await Constants.URL_GITHUB.lunaOpenGenericLink(), + ), + LSCardTile( + title: LSTitle(text: 'Reddit'), + subtitle: LSSubtitle(text: 'Ask Questions & Get Support'), + trailing: LSIconButton(icon: CustomIcons.reddit), + onTap: () async => await Constants.URL_REDDIT.lunaOpenGenericLink(), + ), + if(Platform.isIOS) LSCardTile( + title: LSTitle(text: 'TestFlight'), + subtitle: LSSubtitle(text: 'Join the TestFlight Beta'), + trailing: LSIconButton(icon: Icons.developer_board), + onTap: () async => await Constants.URL_TESTFLIGHT.lunaOpenGenericLink() + ), + LSCardTile( + title: LSTitle(text: 'Website'), + subtitle: LSSubtitle(text: 'Visit LunaSea\'s Website'), + trailing: LSIconButton(icon: CustomIcons.home), + onTap: () async => await Constants.URL_WEBSITE.lunaOpenGenericLink(), + ), + ], + ); +} diff --git a/lib/modules/settings/modules/resources/route.dart b/lib/modules/settings/modules/resources/route.dart deleted file mode 100644 index de994d52..00000000 --- a/lib/modules/settings/modules/resources/route.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:io'; -import 'package:fluro/fluro.dart'; -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsResourcesRouter { - static const ROUTE_NAME = '/settings/resources'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsResourcesRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsResourcesRouter._(); -} - -class _SettingsResourcesRoute extends StatefulWidget { - @override - State<_SettingsResourcesRoute> createState() => _State(); -} - -class _State extends State<_SettingsResourcesRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => Scaffold( - key: _scaffoldKey, - appBar: _appBar, - body: _body, - ); - - Widget get _appBar => LunaAppBar( - context: context, - title: 'Resources', - ); - - Widget get _body => LSListView( - children: [ - LSCardTile( - title: LSTitle(text: 'Discord'), - subtitle: LSSubtitle(text: 'Chat & Discussions'), - trailing: LSIconButton(icon: CustomIcons.discord), - onTap: () async => await Constants.URL_DISCORD.lsLinks_OpenLink(), - ), - // LSCardTile( - // title: LSTitle(text: 'Documentation'), - // subtitle: LSSubtitle(text: 'View the Documentation'), - // trailing: LSIconButton(icon: CustomIcons.documentation), - // onTap: () async => await Constants.URL_DOCUMENTATION.lsLinks_OpenLink(), - // ), - LSCardTile( - title: LSTitle(text: 'Feedback Board'), - subtitle: LSSubtitle(text: 'Request New Features'), - trailing: LSIconButton(icon: Icons.speaker_notes), - onTap: () async => await Constants.URL_FEEDBACK.lsLinks_OpenLink(), - ), - LSCardTile( - title: LSTitle(text: 'GitHub'), - subtitle: LSSubtitle(text: 'View the Source Code'), - trailing: LSIconButton(icon: CustomIcons.github), - onTap: () async => await Constants.URL_GITHUB.lsLinks_OpenLink(), - ), - LSCardTile( - title: LSTitle(text: 'Reddit'), - subtitle: LSSubtitle(text: 'Ask Questions & Get Support'), - trailing: LSIconButton(icon: CustomIcons.reddit), - onTap: () async => await Constants.URL_REDDIT.lsLinks_OpenLink(), - ), - if(Platform.isIOS) LSCardTile( - title: LSTitle(text: 'TestFlight'), - subtitle: LSSubtitle(text: 'Join the TestFlight Beta'), - trailing: LSIconButton(icon: Icons.developer_board), - onTap: () async => await Constants.URL_TESTFLIGHT.lsLinks_OpenLink() - ), - LSCardTile( - title: LSTitle(text: 'Website'), - subtitle: LSSubtitle(text: 'Visit LunaSea\'s Website'), - trailing: LSIconButton(icon: CustomIcons.home), - onTap: () async => await Constants.URL_WEBSITE.lsLinks_OpenLink(), - ), - ], - ); -} diff --git a/lib/modules/settings/modules/settings.dart b/lib/modules/settings/modules/settings.dart index 82b784ec..e6d30899 100644 --- a/lib/modules/settings/modules/settings.dart +++ b/lib/modules/settings/modules/settings.dart @@ -1 +1,90 @@ -export 'settings/route.dart'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsHomeRouter extends LunaPageRouter { + SettingsHomeRouter() : super('/settings'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsHomeRoute()); +} + +class _SettingsHomeRoute extends StatefulWidget { + @override + State<_SettingsHomeRoute> createState() => _State(); +} + +class _State extends State<_SettingsHomeRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => WillPopScope( + onWillPop: _onWillPop, + child: Scaffold( + key: _scaffoldKey, + appBar: _appBar, + drawer: _drawer, + body: _body, + ), + ); + + Future _onWillPop() async { + if(_scaffoldKey.currentState.isDrawerOpen) return true; + _scaffoldKey.currentState.openDrawer(); + return false; + } + + Widget get _drawer => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.DRAWER_GROUP_MODULES.key]), + builder: (context, box, _) => LSDrawer(page: SettingsConstants.MODULE_KEY), + ); + + Widget get _appBar => LunaAppBar( + context: context, + hideLeading: true, + title: SettingsConstants.MODULE_METADATA.name, + ); + + Widget get _body => LSListView( + children: [ + // LSCardTile( + // title: LSTitle(text: 'Account'), + // subtitle: LSSubtitle(text: 'Your LunaSea Account'), + // trailing: LSIconButton(icon: Icons.account_circle), + // //onTap: () async => SettingsConfigurationRouter().navigateTo(context), + // ), + LSCardTile( + title: LSTitle(text: 'Configuration'), + subtitle: LSSubtitle(text: 'Configure & Setup LunaSea'), + trailing: LSIconButton(icon: Icons.device_hub), + onTap: () async => SettingsConfigurationRouter().navigateTo(context), + ), + LSCardTile( + title: LSTitle(text: 'Profiles'), + subtitle: LSSubtitle(text: 'Manage Your Profiles'), + trailing: LSIconButton(icon: Icons.person), + onTap: () async => SettingsProfilesRouter().navigateTo(context), + ), + LSDivider(), + LSCardTile( + title: LSTitle(text: 'Donations'), + subtitle: LSSubtitle(text: 'Donate to the Developer'), + trailing: LSIconButton(icon: Icons.attach_money), + onTap: () async => SettingsDonationsRouter().navigateTo(context), + ), + LSCardTile( + title: LSTitle(text: 'Resources'), + subtitle: LSSubtitle(text: 'Useful Resources & Links'), + trailing: LSIconButton(icon: Icons.help_outline), + onTap: () async => SettingsResourcesRouter().navigateTo(context), + ), + LSCardTile( + title: LSTitle(text: 'System'), + subtitle: LSSubtitle(text: 'System Utilities & Information'), + trailing: LSIconButton(icon: Icons.settings), + onTap: () async => SettingsSystemRouter().navigateTo(context), + ), + ], + ); +} diff --git a/lib/modules/settings/modules/settings/route.dart b/lib/modules/settings/modules/settings/route.dart deleted file mode 100644 index af3872da..00000000 --- a/lib/modules/settings/modules/settings/route.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:fluro/fluro.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsHomeRouter { - static const ROUTE_NAME = '/settings'; - - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsHomeRouter._(); -} - -class _SettingsRoute extends StatefulWidget { - @override - State<_SettingsRoute> createState() => _State(); -} - -class _State extends State<_SettingsRoute> { - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - Widget build(BuildContext context) => WillPopScope( - onWillPop: _onWillPop, - child: Scaffold( - key: _scaffoldKey, - appBar: _appBar, - drawer: _drawer, - body: _body, - ), - ); - - Future _onWillPop() async { - if(_scaffoldKey.currentState.isDrawerOpen) return true; - _scaffoldKey.currentState.openDrawer(); - return false; - } - - Widget get _drawer => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.key]), - builder: (context, box, _) => LSDrawer(page: SettingsConstants.MODULE_KEY), - ); - - Widget get _appBar => LunaAppBar( - context: context, - hideLeading: true, - title: SettingsConstants.MODULE_MAP.name, - ); - - Widget get _body => LSListView( - children: [ - ..._leading, - LSDivider(), - ..._trailing, - ], - ); - - List get _leading => [ - LSCardTile( - title: LSTitle(text: 'Customization'), - subtitle: LSSubtitle(text: 'Customize LunaSea & Modules'), - trailing: LSIconButton(icon: Icons.brush), - onTap: () async => SettingsCustomizationRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Modules'), - subtitle: LSSubtitle(text: 'Configure & Setup Modules'), - trailing: LSIconButton(icon: Icons.device_hub), - onTap: () async => SettingsModulesRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Profiles'), - subtitle: LSSubtitle(text: 'Manage Your Profiles'), - trailing: LSIconButton(icon: Icons.person), - onTap: () async => SettingsProfilesRouter.navigateTo(context), - ), - ]; - - List get _trailing => [ - LSCardTile( - title: LSTitle(text: 'Backup & Restore'), - subtitle: LSSubtitle(text: 'Backup & Restore Your Configuration'), - trailing: LSIconButton(icon: Icons.settings_backup_restore), - onTap: () async => SettingsBackupRestoreRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Donations'), - subtitle: LSSubtitle(text: 'Donate to the Developer'), - trailing: LSIconButton(icon: Icons.attach_money), - onTap: () async => SettingsDonationsRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Logs'), - subtitle: LSSubtitle(text: 'View, Export, & Clear Logs'), - trailing: LSIconButton(icon: Icons.developer_mode), - onTap: () async => SettingsLogsRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'Resources'), - subtitle: LSSubtitle(text: 'Useful Resources & Links'), - trailing: LSIconButton(icon: Icons.help_outline), - onTap: () async => SettingsResourcesRouter.navigateTo(context), - ), - LSCardTile( - title: LSTitle(text: 'System'), - subtitle: LSSubtitle(text: 'System Utilities & Information'), - trailing: LSIconButton(icon: Icons.settings), - onTap: () async => SettingsSystemRouter.navigateTo(context), - ), - ]; -} diff --git a/lib/modules/settings/modules/system/route.dart b/lib/modules/settings/modules/system/route.dart index 62471d1e..1a872168 100644 --- a/lib/modules/settings/modules/system/route.dart +++ b/lib/modules/settings/modules/system/route.dart @@ -2,24 +2,13 @@ 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'; -class SettingsSystemRouter { - static const ROUTE_NAME = '/settings/system'; +class SettingsSystemRouter extends LunaPageRouter { + SettingsSystemRouter() : super('/settings/system'); - static Future navigateTo(BuildContext context) async => LunaRouter.router.navigateTo( - context, - route(), - ); - - static String route() => ROUTE_NAME; - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsSystemRoute()), - transitionType: LunaRouter.transitionType, - ); - - SettingsSystemRouter._(); + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsSystemRoute()); } class _SettingsSystemRoute extends StatefulWidget { @@ -50,11 +39,73 @@ class _State extends State<_SettingsSystemRoute> with AutomaticKeepAliveClientMi Widget get _body => LSListView( children: [ - SettingsSystemLicensesTile(), - SettingsSystemVersionTile(), + _versionTile, + _logsTile, LSDivider(), - SettingsSystemEnableSentryTile(), - SettingsSystemClearConfigurationTile(), + SettingsSystemBackupRestoreBackupTile(), + SettingsSystemBackupRestoreRestoreTile(), + LSDivider(), + _enableSentryTile, + _clearConfigurationTile, ], ); + + Widget get _logsTile => LSCardTile( + title: LSTitle(text: 'Logs'), + subtitle: LSSubtitle(text: 'View, Export, & Clear Logs'), + trailing: LSIconButton(icon: Icons.developer_mode), + onTap: () async => SettingsSystemLogsRouter().navigateTo(context), + ); + + Widget get _versionTile => FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, AsyncSnapshot snapshot) => LSCardTile( + title: LSTitle( + text: snapshot.hasData + ? 'Version: ${snapshot.data.version} (${snapshot.data.buildNumber})' + : 'Version: Loading...', + ), + subtitle: LSSubtitle(text: 'View Recent Changes'), + trailing: LSIconButton(icon: Icons.system_update), + onTap: () async => await Constants.URL_CHANGELOG.lunaOpenGenericLink(), + ), + ); + + Widget get _enableSentryTile => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_SENTRY.key]), + builder: (context, box, _) => LSCardTile( + title: LSTitle(text: 'Sentry'), + subtitle: LSSubtitle(text: 'Crash and Error Tracking'), + trailing: Switch( + value: LunaDatabaseValue.ENABLED_SENTRY.data, + onChanged: (value) async { + List _values = value + ? [true] + : await SettingsDialogs.disableSentryWarning(context); + if(_values[0]) LunaDatabaseValue.ENABLED_SENTRY.put(value); + } + ), + ), + ); + + Widget get _clearConfigurationTile { + Future _execute() async { + List values = await SettingsDialogs.clearConfiguration(context); + if(values[0]) { + Database.setDefaults(); + LunaState.reset(context); + showLunaSuccessSnackBar( + context: context, + title: 'Configuration Cleared', + message: 'Your configuration has been cleared', + ); + } + } + return LSCardTile( + title: LSTitle(text: 'Clear Configuration'), + subtitle: LSSubtitle(text: 'Clean Slate'), + trailing: LSIconButton(icon: Icons.delete_sweep), + onTap: _execute, + ); + } } diff --git a/lib/modules/settings/modules/system/widgets.dart b/lib/modules/settings/modules/system/widgets.dart index 9b7661f3..0b5a672b 100644 --- a/lib/modules/settings/modules/system/widgets.dart +++ b/lib/modules/settings/modules/system/widgets.dart @@ -1,4 +1,2 @@ -export 'widgets/clear_configuration_tile.dart'; -export 'widgets/licenses_tile.dart'; -export 'widgets/sentry_tile.dart'; -export 'widgets/version_tile.dart'; +export 'widgets/backup_tile.dart'; +export 'widgets/restore_tile.dart'; diff --git a/lib/modules/settings/modules/backuprestore/widgets/backup_tile.dart b/lib/modules/settings/modules/system/widgets/backup_tile.dart similarity index 64% rename from lib/modules/settings/modules/backuprestore/widgets/backup_tile.dart rename to lib/modules/settings/modules/system/widgets/backup_tile.dart index 6a48b7e3..96a280d7 100644 --- a/lib/modules/settings/modules/backuprestore/widgets/backup_tile.dart +++ b/lib/modules/settings/modules/system/widgets/backup_tile.dart @@ -3,12 +3,12 @@ import 'package:intl/intl.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsBackupRestoreBackupTile extends StatelessWidget { +class SettingsSystemBackupRestoreBackupTile extends StatelessWidget { @override Widget build(BuildContext context) => LSCardTile( title: LSTitle(text: 'Backup'), subtitle: LSSubtitle(text: 'Backup Configuration Data'), - trailing: LSIconButton(icon: Icons.cloud_upload), + trailing: LSIconButton(icon: Icons.upload_rounded), onTap: () async => _backup(context), ); @@ -16,13 +16,13 @@ class SettingsBackupRestoreBackupTile extends StatelessWidget { try { List _values = await SettingsDialogs.backupConfiguration(context); if(_values[0]) { - String data = Export.export(); - String encrypted = LunaEncryption.encrypt(_values[1], data); + String data = LunaConfiguration().export(); + String encrypted = LunaEncryption().encrypt(_values[1], data); String name = DateFormat('y-MM-dd kk-mm-ss').format(DateTime.now()); - if(encrypted != Constants.ENCRYPTION_FAILURE) await LunaFileSystem.exportFileToTemporaryStorage('$name.lunasea', encrypted); + if(encrypted != LunaEncryption.ENCRYPTION_FAILURE) await LunaFileSystem().exportStringToShareSheet('$name.lunasea', encrypted); } } catch (error, stack) { - LunaLogger.error('SettingsGeneralConfiguration', '_backup', 'Backup Failed', error, stack); + LunaLogger().error('Backup Failed', error, stack); showLunaErrorSnackBar( context: context, title: 'Back Up Failed', diff --git a/lib/modules/settings/modules/system/widgets/clear_configuration_tile.dart b/lib/modules/settings/modules/system/widgets/clear_configuration_tile.dart deleted file mode 100644 index d4cafb8d..00000000 --- a/lib/modules/settings/modules/system/widgets/clear_configuration_tile.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsSystemClearConfigurationTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Clear Configuration'), - subtitle: LSSubtitle(text: 'Clean Slate'), - trailing: LSIconButton(icon: Icons.delete_sweep), - onTap: () => _clear(context), - ); - - Future _clear(BuildContext context) async { - List values = await SettingsDialogs.clearConfiguration(context); - if(values[0]) { - Database.setDefaults(); - showLunaSuccessSnackBar( - context: context, - title: 'Configuration Cleared', - message: 'Your configuration has been cleared', - ); - } - } -} diff --git a/lib/modules/settings/modules/system/widgets/licenses_tile.dart b/lib/modules/settings/modules/system/widgets/licenses_tile.dart deleted file mode 100644 index d26874b2..00000000 --- a/lib/modules/settings/modules/system/widgets/licenses_tile.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class SettingsSystemLicensesTile extends StatelessWidget { - @override - Widget build(BuildContext context) => LSCardTile( - title: LSTitle(text: 'Licenses'), - subtitle: LSSubtitle(text: 'License Attributions'), - trailing: LSIconButton(icon: Icons.description), - onTap: () async => Constants.URL_LICENSES.lsLinks_OpenLink(), - ); -} diff --git a/lib/modules/settings/modules/backuprestore/widgets/restore_tile.dart b/lib/modules/settings/modules/system/widgets/restore_tile.dart similarity index 63% rename from lib/modules/settings/modules/backuprestore/widgets/restore_tile.dart rename to lib/modules/settings/modules/system/widgets/restore_tile.dart index ce3f5a85..86e69c89 100644 --- a/lib/modules/settings/modules/backuprestore/widgets/restore_tile.dart +++ b/lib/modules/settings/modules/system/widgets/restore_tile.dart @@ -3,13 +3,13 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsBackupRestoreRestoreTile extends StatelessWidget { +class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget { @override Widget build(BuildContext context) => LSCardTile( title: LSTitle(text: 'Restore'), subtitle: LSSubtitle(text: 'Restore Configuration Data'), - trailing: LSIconButton(icon: Icons.cloud_download), - onTap: () => _restore(context), + trailing: LSIconButton(icon: Icons.download_rounded), + onTap: () async => _restore(context), ); Future _restore(BuildContext context) async { @@ -28,19 +28,13 @@ class SettingsBackupRestoreRestoreTile extends StatelessWidget { String _data = String.fromCharCodes(_file.files[0].bytes); List _key = await SettingsDialogs.enterEncryptionKey(context); if(_key[0]) { - String _decrypted = LunaEncryption.decrypt(_key[1], _data); - if(_decrypted != Constants.ENCRYPTION_FAILURE) { - await Import.import(context, _decrypted) - ? showLunaSuccessSnackBar( - context: context, - title: 'Restored', - message: 'Your configuration has been restored', - ) - : showLunaErrorSnackBar( - context: context, - title: 'Failed to Restore', - message: 'This is not a valid LunaSea configuration backup', - ); + String _decrypted = LunaEncryption().decrypt(_key[1], _data); + if(_decrypted != LunaEncryption.ENCRYPTION_FAILURE) { + LunaConfiguration().import(context, _decrypted).then((_) => showLunaSuccessSnackBar( + context: context, + title: 'Restored', + message: 'Your configuration has been restored', + )); } else { showLunaErrorSnackBar( context: context, @@ -57,7 +51,7 @@ class SettingsBackupRestoreRestoreTile extends StatelessWidget { ); } } catch (error, stack) { - LunaLogger.error('SettingsBackupRestoreRestoreTile', '_restore', 'Restore Failed', error, stack); + LunaLogger().error('Restore Failed', error, stack); showLunaErrorSnackBar( context: context, title: 'Failed to Restore', diff --git a/lib/modules/settings/modules/system/widgets/sentry_tile.dart b/lib/modules/settings/modules/system/widgets/sentry_tile.dart deleted file mode 100644 index 08e043e2..00000000 --- a/lib/modules/settings/modules/system/widgets/sentry_tile.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class SettingsSystemEnableSentryTile extends StatelessWidget { - @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_SENTRY.key]), - builder: (context, box, _) => LSCardTile( - title: LSTitle(text: 'Sentry Logging'), - subtitle: LSSubtitle(text: 'Crash and Error Tracking'), - trailing: Switch( - value: LunaSeaDatabaseValue.ENABLED_SENTRY.data, - onChanged: (value) async { - List _values = value - ? [true] - : await SettingsDialogs.disableSentryWarning(context); - if(_values[0]) LunaSeaDatabaseValue.ENABLED_SENTRY.put(value); - } - ), - ), - ); -} diff --git a/lib/modules/settings/modules/system/widgets/version_tile.dart b/lib/modules/settings/modules/system/widgets/version_tile.dart deleted file mode 100644 index 7cf0ece3..00000000 --- a/lib/modules/settings/modules/system/widgets/version_tile.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -import 'package:package_info/package_info.dart'; - -class SettingsSystemVersionTile extends StatelessWidget { - @override - Widget build(BuildContext context) => FutureBuilder( - future: PackageInfo.fromPlatform(), - builder: (context, AsyncSnapshot snapshot) => LSCardTile( - title: LSTitle( - text: snapshot.hasData - ? 'Version: ${snapshot.data.version} (${snapshot.data.buildNumber})' - : 'Version: Loading...', - ), - subtitle: LSSubtitle(text: 'View Recent Changes'), - trailing: LSIconButton(icon: Icons.system_update), - onTap: () async => await Constants.URL_CHANGELOG.lsLinks_OpenLink(), - ), - ); -} diff --git a/lib/modules/settings/modules/system_logs.dart b/lib/modules/settings/modules/system_logs.dart new file mode 100644 index 00000000..29a7b046 --- /dev/null +++ b/lib/modules/settings/modules/system_logs.dart @@ -0,0 +1,113 @@ +import 'dart:io'; +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/settings.dart'; +import 'package:share/share.dart'; + +class SettingsSystemLogsRouter extends LunaPageRouter { + SettingsSystemLogsRouter() : super('/settings/logs'); + + @override + void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsSystemLogsRoute()); +} + +class _SettingsSystemLogsRoute extends StatefulWidget { + @override + State<_SettingsSystemLogsRoute> createState() => _State(); +} + +class _State extends State<_SettingsSystemLogsRoute> { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) => Scaffold( + key: _scaffoldKey, + appBar: _appBar, + body: _body, + ); + + Widget get _appBar => LunaAppBar( + context: context, + title: 'Logs', + ); + + Widget get _body => LSListView( + children: [ + LSCardTile( + title: LSTitle(text: 'All Logs'), + subtitle: LSSubtitle(text: 'View Logs of All Types'), + trailing: LSIconButton(icon: Icons.developer_mode), + onTap: () async => _viewLogs('All'), + ), + LSCardTile( + title: LSTitle(text: 'Warning'), + subtitle: LSSubtitle(text: 'View Warning Logs'), + trailing: LSIconButton(icon: Icons.warning), + onTap: () async => _viewLogs('Warning'), + ), + LSCardTile( + title: LSTitle(text: 'Error'), + subtitle: LSSubtitle(text: 'View Error Logs'), + trailing: LSIconButton(icon: Icons.report), + onTap: () async => _viewLogs('Error'), + ), + LSCardTile( + title: LSTitle(text: 'Fatal'), + subtitle: LSSubtitle(text: 'View Fatal Logs'), + trailing: LSIconButton(icon: Icons.new_releases), + onTap: () async => _viewLogs('Fatal'), + ), + LSDivider(), + _exportTile, + _clearTile, + ] + ); + + Future _viewLogs(String type) async => SettingsSystemLogsDetailsRouter().navigateTo( + context, + type: type, + ); + + Widget get _clearTile { + // Execute action + Future _execute() async { + List _values = await SettingsDialogs.clearLogs(context); + if(_values[0]) { + LunaLogger().clearLogs(); + showLunaSuccessSnackBar( + context: context, + title: 'Logs Cleared', + message: 'All recorded logs have been cleared', + ); + } + } + // Tile + return LSCardTile( + title: LSTitle(text: 'Clear'), + subtitle: LSSubtitle(text: 'Clear All Recorded Logs'), + trailing: LSIconButton(icon: Icons.delete), + onTap: _execute, + ); + } + + Widget get _exportTile { + // Execute action + Future _execute() async { + showLunaInfoSnackBar( + context: context, + title: 'Exporting Logs', + message: 'Please wait...', + ); + File _logs = await LunaLogger().exportLogs(); + Share.shareFiles([_logs.path]); + } + // Tile + return LSCardTile( + title: LSTitle(text: 'Export'), + subtitle: LSSubtitle(text: 'Export All Recorded Logs'), + trailing: LSIconButton(icon: Icons.file_download), + onTap: _execute, + ); + } +} diff --git a/lib/modules/settings/modules/system_logs_details.dart b/lib/modules/settings/modules/system_logs_details.dart new file mode 100644 index 00000000..d21138c2 --- /dev/null +++ b/lib/modules/settings/modules/system_logs_details.dart @@ -0,0 +1,2 @@ +export 'system_logs_details/route.dart'; +export 'system_logs_details/widgets.dart'; diff --git a/lib/modules/settings/modules/logs_details/route.dart b/lib/modules/settings/modules/system_logs_details/route.dart similarity index 68% rename from lib/modules/settings/modules/logs_details/route.dart rename to lib/modules/settings/modules/system_logs_details/route.dart index 6396cddd..c071b0ec 100644 --- a/lib/modules/settings/modules/logs_details/route.dart +++ b/lib/modules/settings/modules/system_logs_details/route.dart @@ -4,44 +4,36 @@ import 'package:f_logs/f_logs.dart' as FLog; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/settings.dart'; -class SettingsLogsDetailsRouter { - static const ROUTE_NAME = '/settings/logs/details/:type'; +class SettingsSystemLogsDetailsRouter extends LunaPageRouter { + SettingsSystemLogsDetailsRouter() : super('/settings/logs/details/:type'); - static Future navigateTo(BuildContext context, { - @required String type, - }) async => LunaRouter.router.navigateTo( - context, - route(type: type), - ); + @override + Future navigateTo(BuildContext context, { @required String type }) async => LunaRouter.router.navigateTo(context, route(type: type)); - static String route({ - @required String type, - }) => ROUTE_NAME.replaceFirst(':type', type); - - static void defineRoutes(FluroRouter router) => router.define( - ROUTE_NAME, - handler: Handler(handlerFunc: (context, params) => _SettingsLogsDetailsRoute( - type: params['type'] == null ? 'All' : params['type'][0], - )), + @override + String route({ @required String type }) => fullRoute.replaceFirst(':type', type); + + @override + void defineRoute(FluroRouter router) => router.define( + fullRoute, + handler: Handler(handlerFunc: (context, params) => _SettingsSystemLogsDetailsRoute(type: params['type'] == null ? 'All' : params['type'][0])), transitionType: LunaRouter.transitionType, ); - - SettingsLogsDetailsRouter._(); } -class _SettingsLogsDetailsRoute extends StatefulWidget { +class _SettingsSystemLogsDetailsRoute extends StatefulWidget { final String type; - _SettingsLogsDetailsRoute({ + _SettingsSystemLogsDetailsRoute({ Key key, @required this.type, }) : super(key: key); @override - State<_SettingsLogsDetailsRoute> createState() => _State(); + State<_SettingsSystemLogsDetailsRoute> createState() => _State(); } -class _State extends State<_SettingsLogsDetailsRoute> { +class _State extends State<_SettingsSystemLogsDetailsRoute> { final GlobalKey _scaffoldKey = GlobalKey(); List levels = []; @@ -79,13 +71,7 @@ class _State extends State<_SettingsLogsDetailsRoute> { switch(snapshot.connectionState) { case ConnectionState.done: if(snapshot.hasError) { - LunaLogger.error( - '_SettingsLogsDetailsRoute', - '_body', - 'Unable to fetch logs', - snapshot.error, - StackTrace.current, - ); + LunaLogger().error('Unable to fetch logs', snapshot.error, StackTrace.current); return LSErrorMessage(onTapHandler: () => {}); } if(snapshot.hasData) return snapshot.data.length == 0 diff --git a/lib/modules/settings/modules/logs_details/widgets.dart b/lib/modules/settings/modules/system_logs_details/widgets.dart similarity index 100% rename from lib/modules/settings/modules/logs_details/widgets.dart rename to lib/modules/settings/modules/system_logs_details/widgets.dart diff --git a/lib/modules/settings/modules/logs_details/widgets/log_tile.dart b/lib/modules/settings/modules/system_logs_details/widgets/log_tile.dart similarity index 86% rename from lib/modules/settings/modules/logs_details/widgets/log_tile.dart rename to lib/modules/settings/modules/system_logs_details/widgets/log_tile.dart index d84e8af8..1057f548 100644 --- a/lib/modules/settings/modules/logs_details/widgets/log_tile.dart +++ b/lib/modules/settings/modules/system_logs_details/widgets/log_tile.dart @@ -31,10 +31,10 @@ class SettingsLogsDetailsLogTile extends StatelessWidget { TextSpan(text: '\n'), TextSpan( style: TextStyle( - color: log.logLevel.lsFLogLogLevel_color(), + color: log.logLevel.lunaColour, fontWeight: FontWeight.w600, ), - text: log.logLevel.lsFLogLogLevel_name(), + text: log.logLevel.lunaName, ) ], ), @@ -60,8 +60,8 @@ class SettingsLogsDetailsLogTile extends StatelessWidget { runSpacing: 10.0, children: [ LSTextHighlighted( - text: log.logLevel.lsFLogLogLevel_name(), - bgColor: log.logLevel.lsFLogLogLevel_color(), + text: log.logLevel.lunaName, + bgColor: log.logLevel.lunaColour, ), ], ), @@ -76,16 +76,14 @@ class SettingsLogsDetailsLogTile extends StatelessWidget { ), children: [ TextSpan(text: log.timestamp), - TextSpan(text: '\n'), - TextSpan(text: log.className), - TextSpan(text: '\n'), - TextSpan(text: log.methodName), + if(log?.className != null) TextSpan(text: '\n${log.className}'), + if(log?.methodName != null) TextSpan(text: '\n${log.methodName}'), ], ), ), - padding: EdgeInsets.only(top: 6.0, bottom: 10.0), + padding: EdgeInsets.only(top: 6.0, bottom: (log?.exception != null && (log?.exception?.toLowerCase() ?? 'null') != 'null') ? 10.0 : 0.0), ), - Padding( + if(log?.exception != null && (log?.exception?.toLowerCase() ?? 'null') != 'null') Padding( child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -93,7 +91,7 @@ class SettingsLogsDetailsLogTile extends StatelessWidget { child: LSButtonSlim( text: 'Exception', backgroundColor: LunaColours.red, - onTap: () async => LunaDialogs.textPreview(context, 'Exception', log?.exception ?? 'Unavailable', alignLeft: true), + onTap: () async => LunaDialogs().textPreview(context, 'Exception', log?.exception ?? 'Unavailable', alignLeft: true), margin: EdgeInsets.only(right: 6.0), ), ), @@ -101,7 +99,7 @@ class SettingsLogsDetailsLogTile extends StatelessWidget { child: LSButtonSlim( text: 'Stack Trace', backgroundColor: LunaColours.blue, - onTap: () async => LunaDialogs.textPreview(context, 'Stack Trace', log?.stacktrace ?? 'Unavailable', alignLeft: true), + onTap: () async => LunaDialogs().textPreview(context, 'Stack Trace', log?.stacktrace ?? 'Unavailable', alignLeft: true), margin: EdgeInsets.only(left: 6.0), ), ), diff --git a/lib/modules/sonarr/core/constants.dart b/lib/modules/sonarr/core/constants.dart index 957b13af..0294503e 100644 --- a/lib/modules/sonarr/core/constants.dart +++ b/lib/modules/sonarr/core/constants.dart @@ -6,18 +6,21 @@ class SonarrConstants { static const String MODULE_KEY = 'sonarr'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Sonarr', description: 'Manage Television Series', settingsDescription: 'Configure Sonarr', + helpMessage: 'Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.', icon: CustomIcons.television, route: '/sonarr', color: Color(0xFF3FC6F4), + website: 'https://sonarr.tv', + github: 'https://github.com/Sonarr/Sonarr', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/sonarr/core/database.dart b/lib/modules/sonarr/core/database.dart index 03233073..a3709a04 100644 --- a/lib/modules/sonarr/core/database.dart +++ b/lib/modules/sonarr/core/database.dart @@ -1,10 +1,10 @@ +import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; -class SonarrDatabase { - SonarrDatabase._(); - - static void registerAdapters() { +class SonarrDatabase extends LunaModuleDatabase { + @override + void registerAdapters() { // Deprecated, not in use but necessary to avoid Hive read errors Hive.registerAdapter(DeprecatedSonarrQualityProfileAdapter()); Hive.registerAdapter(DeprecatedSonarrRootFolderAdapter()); @@ -14,6 +14,84 @@ class SonarrDatabase { Hive.registerAdapter(SonarrSeriesSortingAdapter()); Hive.registerAdapter(SonarrReleasesSortingAdapter()); } + + @override + Map export() { + Map data = {}; + for(SonarrDatabaseValue value in SonarrDatabaseValue.values) { + switch(value) { + // Non-primitive values + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS: data[value.key] = (SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS.data as SonarrMonitorStatus).key; break; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: data[value.key] = (SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data as SonarrSeriesSorting).key; break; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: data[value.key] = (SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data as SonarrReleasesSorting).key; break; + // Primitive values + case SonarrDatabaseValue.NAVIGATION_INDEX: + case SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: + case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: + case SonarrDatabaseValue.QUEUE_REFRESH_RATE: + case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + SonarrDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Non-primitive values + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS: value.put(SonarrMonitorStatus.ALL.fromKey(config[key])); break; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: value.put(SonarrSeriesSorting.ALPHABETICAL.fromKey(config[key])); break; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: value.put(SonarrReleasesSorting.ALPHABETICAL.fromKey(config[key])); break; + // Primitive values + case SonarrDatabaseValue.NAVIGATION_INDEX: + case SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: + case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: + case SonarrDatabaseValue.QUEUE_REFRESH_RATE: + case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: value.put(config[key]); break; + } + } + } + + @override + SonarrDatabaseValue valueFromKey(String key) { + switch(key) { + case 'SONARR_NAVIGATION_INDEX': return SonarrDatabaseValue.NAVIGATION_INDEX; + case 'SONARR_NAVIGATION_INDEX_SERIES_DETAILS': return SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS; + case 'SONARR_UPCOMING_FUTURE_DAYS': return SonarrDatabaseValue.UPCOMING_FUTURE_DAYS; + case 'SONARR_QUEUE_REFRESH_RATE': return SonarrDatabaseValue.QUEUE_REFRESH_RATE; + case 'SONARR_CONTENT_LOAD_LENGTH': return SonarrDatabaseValue.CONTENT_LOAD_LENGTH; + case 'SONARR_ADD_SERIES_DEFAULT_MONITORED': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED; + case 'SONARR_ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS; + case 'SONARR_ADD_SERIES_DEFAULT_SERIES_TYPE': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE; + case 'SONARR_ADD_SERIES_DEFAULT_MONITOR_STATUS': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS; + case 'SONARR_ADD_SERIES_DEFAULT_LANGUAGE_PROFILE': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE; + case 'SONARR_ADD_SERIES_DEFAULT_QUALITY_PROFILE': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE; + case 'SONARR_ADD_SERIES_DEFAULT_ROOT_FOLDER': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER; + case 'SONARR_DEFAULT_SORTING_SERIES': return SonarrDatabaseValue.DEFAULT_SORTING_SERIES; + case 'SONARR_DEFAULT_SORTING_RELEASES': return SonarrDatabaseValue.DEFAULT_SORTING_RELEASES; + case 'SONARR_DEFAULT_SORTING_SERIES_ASCENDING': return SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING; + case 'SONARR_DEFAULT_SORTING_RELEASES_ASCENDING': return SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING; + default: return null; + } + } } enum SonarrDatabaseValue { @@ -81,5 +159,31 @@ extension SonarrDatabaseValueExtension on SonarrDatabaseValue { throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case SonarrDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED: if(value.runtimeType == bool) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS: if(value.runtimeType == bool) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE: if(value.runtimeType == String) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS: if(value.runtimeType == SonarrMonitorStatus) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: if(value.runtimeType == SonarrSeriesSorting) box.put(this.key, value); break; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: if(value.runtimeType == SonarrReleasesSorting) box.put(this.key, value); break; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); break; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); break; + case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.QUEUE_REFRESH_RATE: if(value.runtimeType == int) box.put(this.key, value); break; + case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: if(value.runtimeType == int) box.put(this.key, value); break; + } + LunaLogger().warning('SonarrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid SonarrDatabaseValue: ${this?.toString() ?? 'null'}'); + }//=> Database.lunaSeaBox.put(this.key, value); + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/sonarr/core/dialogs.dart b/lib/modules/sonarr/core/dialogs.dart index 362df8cb..ecfd6c9b 100644 --- a/lib/modules/sonarr/core/dialogs.dart +++ b/lib/modules/sonarr/core/dialogs.dart @@ -193,7 +193,7 @@ class SonarrDialogs { ), ), contentPadding: LSDialog.textDialogContentPadding(), - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), @@ -246,7 +246,7 @@ class SonarrDialogs { ), ), contentPadding: LSDialog.textDialogContentPadding(), - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), ), @@ -448,7 +448,7 @@ class SonarrDialogs { text: folders[index].path, subtitle: LSDialog.richText( children: [ - LSDialog.bolded(text: folders[index].freeSpace.lsBytes_BytesToString()) + LSDialog.bolded(text: folders[index].freeSpace.lunaBytesToString()) ], ), icon: Icons.folder, @@ -504,7 +504,7 @@ class SonarrDialogs { content: List.generate( SonarrSeriesType.values.length, (index) => LSDialog.tile( - text: SonarrSeriesType.values[index].value.lsLanguage_Capitalize(), + text: SonarrSeriesType.values[index].value.lunaCapitalizeFirstLetters(), icon: Icons.folder_open, iconColor: LunaColours.list(index), onTap: () => _setValues(true, SonarrSeriesType.values[index]), diff --git a/lib/modules/sonarr/core/extensions/sonarr_calendar.dart b/lib/modules/sonarr/core/extensions/sonarr_calendar.dart index 89c645cd..ff326d00 100644 --- a/lib/modules/sonarr/core/extensions/sonarr_calendar.dart +++ b/lib/modules/sonarr/core/extensions/sonarr_calendar.dart @@ -4,7 +4,7 @@ import 'package:lunasea/modules/sonarr.dart'; extension SonarrCalendarExtension on SonarrCalendar { String get lunaAirTime { - if(this.airDateUtc != null) return LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + if(this.airDateUtc != null) return LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat.Hm().format(this.airDateUtc.toLocal()) : DateFormat('hh:mm a').format(this.airDateUtc.toLocal()); return Constants.TEXT_EMDASH; diff --git a/lib/modules/sonarr/core/extensions/sonarr_series.dart b/lib/modules/sonarr/core/extensions/sonarr_series.dart index b4a19d46..171c4973 100644 --- a/lib/modules/sonarr/core/extensions/sonarr_series.dart +++ b/lib/modules/sonarr/core/extensions/sonarr_series.dart @@ -25,7 +25,7 @@ extension SonarrSeriesExtension on SonarrSeries { } String get lunaAirTime { - if(this.previousAiring != null) return LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + if(this.previousAiring != null) return LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat.Hm().format(this.previousAiring.toLocal()) : DateFormat('hh:mm a').format(this.previousAiring.toLocal()); if(this.airTime == null) return 'Unknown'; @@ -34,7 +34,7 @@ extension SonarrSeriesExtension on SonarrSeries { String get lunaSeriesType { if(this.seriesType == null) return 'Unknown'; - return this.seriesType.value.lsLanguage_Capitalize(); + return this.seriesType.value.lunaCapitalizeFirstLetters(); } String get lunaSeasonCount { @@ -46,7 +46,7 @@ extension SonarrSeriesExtension on SonarrSeries { String get lunaSizeOnDisk { if(this.sizeOnDisk == null) return '0.0 B'; - return this.sizeOnDisk.lsBytes_BytesToString(decimals: 1); + return this.sizeOnDisk.lunaBytesToString(decimals: 1); } String get lunaAirsOn { diff --git a/lib/modules/sonarr/core/router.dart b/lib/modules/sonarr/core/router.dart index 5f792866..189a8175 100644 --- a/lib/modules/sonarr/core/router.dart +++ b/lib/modules/sonarr/core/router.dart @@ -1,10 +1,10 @@ import 'package:fluro/fluro.dart'; +import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; -class SonarrRouter { - SonarrRouter._(); - - static void initialize(FluroRouter router) { +class SonarrRouter extends LunaModuleRouter { + @override + void defineAllRoutes(FluroRouter router) { SonarrHomeRouter.defineRoutes(router); // Series SonarrSeriesAddRouter.defineRoutes(router); diff --git a/lib/modules/sonarr/core/state.dart b/lib/modules/sonarr/core/state.dart index f3cfd0ba..3e64cc24 100644 --- a/lib/modules/sonarr/core/state.dart +++ b/lib/modules/sonarr/core/state.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; -class SonarrState extends LunaGlobalState { +class SonarrState extends LunaModuleState { SonarrState() { reset(); } diff --git a/lib/modules/sonarr/core/types/hiding_releases.dart b/lib/modules/sonarr/core/types/hiding_releases.dart index 9cd9c013..b2c786ff 100644 --- a/lib/modules/sonarr/core/types/hiding_releases.dart +++ b/lib/modules/sonarr/core/types/hiding_releases.dart @@ -7,8 +7,6 @@ enum SonarrReleasesHiding { } extension SonarrReleasesHidingExtension on SonarrReleasesHiding { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case SonarrReleasesHiding.ALL: return 'all'; @@ -27,7 +25,7 @@ extension SonarrReleasesHidingExtension on SonarrReleasesHiding { throw Exception('readable not found'); } - List filter(List releases) => _sorter.byType(releases, this); + List filter(List releases) => _Sorter().byType(releases, this); } class _Sorter { diff --git a/lib/modules/sonarr/core/types/hiding_series.dart b/lib/modules/sonarr/core/types/hiding_series.dart index 2d505009..7a5cd500 100644 --- a/lib/modules/sonarr/core/types/hiding_series.dart +++ b/lib/modules/sonarr/core/types/hiding_series.dart @@ -7,8 +7,6 @@ enum SonarrSeriesHiding { } extension SonarrSeriesHidingExtension on SonarrSeriesHiding { - static _Sorter _sorter = _Sorter(); - String get value { switch(this) { case SonarrSeriesHiding.ALL: return 'all'; @@ -27,7 +25,7 @@ extension SonarrSeriesHidingExtension on SonarrSeriesHiding { throw Exception('readable not found'); } - List filter(List series) => _sorter.byType(series, this); + List filter(List series) => _Sorter().byType(series, this); } class _Sorter { diff --git a/lib/modules/sonarr/core/types/monitor_status.dart b/lib/modules/sonarr/core/types/monitor_status.dart index 92c955e3..1fb1f36e 100644 --- a/lib/modules/sonarr/core/types/monitor_status.dart +++ b/lib/modules/sonarr/core/types/monitor_status.dart @@ -33,6 +33,32 @@ extension SonarrMonitorStatusExtension on SonarrMonitorStatus { } throw Exception('unknown name'); } + + String get key { + switch(this) { + case SonarrMonitorStatus.ALL: return 'all'; + case SonarrMonitorStatus.FUTURE: return 'future'; + case SonarrMonitorStatus.MISSING: return 'missing'; + case SonarrMonitorStatus.EXISTING: return 'existing'; + case SonarrMonitorStatus.FIRST_SEASON: return 'firstseason'; + case SonarrMonitorStatus.LAST_SEASON: return 'lastseason'; + case SonarrMonitorStatus.NONE: return 'none'; + default: return null; + } + } + + SonarrMonitorStatus fromKey(String key) { + switch(key) { + case 'all': return SonarrMonitorStatus.ALL; + case 'future': return SonarrMonitorStatus.FUTURE; + case 'missing': return SonarrMonitorStatus.MISSING; + case 'existing': return SonarrMonitorStatus.EXISTING; + case 'firstseason': return SonarrMonitorStatus.FIRST_SEASON; + case 'lastseason': return SonarrMonitorStatus.LAST_SEASON; + case 'none': return SonarrMonitorStatus.NONE; + default: return null; + } + } void process(List season) { if(season == null || season.length == 0) return; diff --git a/lib/modules/sonarr/core/types/sorting_releases.dart b/lib/modules/sonarr/core/types/sorting_releases.dart index edb3bba4..37e06ea3 100644 --- a/lib/modules/sonarr/core/types/sorting_releases.dart +++ b/lib/modules/sonarr/core/types/sorting_releases.dart @@ -20,9 +20,7 @@ enum SonarrReleasesSorting { } extension SonarrReleasesSortingExtension on SonarrReleasesSorting { - static _Sorter _sorter = _Sorter(); - - String get value { + String get key { switch(this) { case SonarrReleasesSorting.AGE: return 'age'; case SonarrReleasesSorting.ALPHABETICAL: return 'abc'; @@ -30,8 +28,8 @@ extension SonarrReleasesSortingExtension on SonarrReleasesSorting { case SonarrReleasesSorting.WEIGHT: return 'weight'; case SonarrReleasesSorting.TYPE: return 'type'; case SonarrReleasesSorting.SIZE: return 'size'; + default: return null; } - throw Exception('value not found'); } String get readable { @@ -42,14 +40,23 @@ extension SonarrReleasesSortingExtension on SonarrReleasesSorting { case SonarrReleasesSorting.WEIGHT: return 'Weight'; case SonarrReleasesSorting.TYPE: return 'Type'; case SonarrReleasesSorting.SIZE: return 'Size'; + default: return null; } - throw Exception('readable not found'); } - List sort( - List releases, - bool ascending - ) => _sorter.byType(releases, this, ascending); + SonarrReleasesSorting fromKey(String key) { + switch(key) { + case 'age': return SonarrReleasesSorting.AGE; + case 'abc': return SonarrReleasesSorting.ALPHABETICAL; + case 'seeders': return SonarrReleasesSorting.SEEDERS; + case 'size': return SonarrReleasesSorting.SIZE; + case 'type': return SonarrReleasesSorting.TYPE; + case 'weight': return SonarrReleasesSorting.WEIGHT; + default: return null; + } + } + + List sort(List releases, bool ascending) => _Sorter().byType(releases, this, ascending); } class _Sorter { diff --git a/lib/modules/sonarr/core/types/sorting_series.dart b/lib/modules/sonarr/core/types/sorting_series.dart index dd1945e6..42d29768 100644 --- a/lib/modules/sonarr/core/types/sorting_series.dart +++ b/lib/modules/sonarr/core/types/sorting_series.dart @@ -24,9 +24,7 @@ enum SonarrSeriesSorting { } extension SonarrSeriesSortingExtension on SonarrSeriesSorting { - static _Sorter _sorter = _Sorter(); - - String get value { + String get key { switch(this) { case SonarrSeriesSorting.ALPHABETICAL: return 'abc'; case SonarrSeriesSorting.DATE_ADDED: return 'date_added'; @@ -36,8 +34,8 @@ extension SonarrSeriesSortingExtension on SonarrSeriesSorting { case SonarrSeriesSorting.NETWORK: return 'network'; case SonarrSeriesSorting.QUALITY: return 'quality'; case SonarrSeriesSorting.NEXT_AIRING: return 'next_airing'; + default: return null; } - throw Exception('value not found'); } String get readable { @@ -50,11 +48,25 @@ extension SonarrSeriesSortingExtension on SonarrSeriesSorting { case SonarrSeriesSorting.TYPE: return 'Type'; case SonarrSeriesSorting.QUALITY: return 'Quality Profile'; case SonarrSeriesSorting.NEXT_AIRING: return 'Next Airing'; + default: return null; } - throw Exception('readable not found'); } - List sort(List data, bool ascending) => _sorter.byType(data, this, ascending); + SonarrSeriesSorting fromKey(String key) { + switch(key) { + case 'abc': return SonarrSeriesSorting.ALPHABETICAL; + case 'date_added': return SonarrSeriesSorting.DATE_ADDED; + case 'episodes': return SonarrSeriesSorting.EPISODES; + case 'size': return SonarrSeriesSorting.SIZE; + case 'type': return SonarrSeriesSorting.TYPE; + case 'network': return SonarrSeriesSorting.NETWORK; + case 'quality': return SonarrSeriesSorting.QUALITY; + case 'next_airing': return SonarrSeriesSorting.NEXT_AIRING; + default: return null; + } + } + + List sort(List data, bool ascending) => _Sorter().byType(data, this, ascending); } class _Sorter { diff --git a/lib/modules/sonarr/modules/history/route.dart b/lib/modules/sonarr/modules/history/route.dart index 6da75a77..93c0e828 100644 --- a/lib/modules/sonarr/modules/history/route.dart +++ b/lib/modules/sonarr/modules/history/route.dart @@ -46,14 +46,7 @@ class _State extends State with AutomaticKeepAliveClientMixi builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrHistoryRoute', - '_body', - 'Unable to fetch Sonarr history', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr history', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/history/widgets/history_tile.dart b/lib/modules/sonarr/modules/history/widgets/history_tile.dart index 729f2d57..d9188227 100644 --- a/lib/modules/sonarr/modules/history/widgets/history_tile.dart +++ b/lib/modules/sonarr/modules/history/widgets/history_tile.dart @@ -74,9 +74,7 @@ class _State extends State { color: Colors.white70, ), children: [ - TextSpan( - text: DateTime.now().toLocal().lsDateTime_ageString(widget.record.date), - ), + TextSpan(text: widget.record.date?.lunaAge ?? 'Unknown'), ], ), overflow: TextOverflow.fade, diff --git a/lib/modules/sonarr/modules/missing/route.dart b/lib/modules/sonarr/modules/missing/route.dart index 8d96b773..1557d6a1 100644 --- a/lib/modules/sonarr/modules/missing/route.dart +++ b/lib/modules/sonarr/modules/missing/route.dart @@ -46,14 +46,7 @@ class _State extends State with AutomaticKeepAliveClientMixi builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrMissingRoute', - '_body', - 'Unable to fetch Sonarr missing episodes', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr missing episodes', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/missing/widgets/missing_tile.dart b/lib/modules/sonarr/modules/missing/widgets/missing_tile.dart index 45ca32df..f8778709 100644 --- a/lib/modules/sonarr/modules/missing/widgets/missing_tile.dart +++ b/lib/modules/sonarr/modules/missing/widgets/missing_tile.dart @@ -129,7 +129,7 @@ class _State extends State { children: [ TextSpan(text: widget.record.airDateUtc == null ? 'Aired' - : 'Aired ${DateTime.now().lsDateTime_ageString(widget.record.airDateUtc?.toLocal())}'), + : 'Aired ${widget.record.airDateUtc?.toLocal()?.lunaAge}'), ], ), overflow: TextOverflow.fade, @@ -157,14 +157,7 @@ class _State extends State { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'SonarrMissingTile', - '_trailingOnPressed', - 'Failed to search for episode: ${widget.record.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to search for episode: ${widget.record.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Search', diff --git a/lib/modules/sonarr/modules/queue/route.dart b/lib/modules/sonarr/modules/queue/route.dart index ca2f6a9f..d5710b37 100644 --- a/lib/modules/sonarr/modules/queue/route.dart +++ b/lib/modules/sonarr/modules/queue/route.dart @@ -63,14 +63,7 @@ class _State extends State<_SonarrQueueRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrQueueRoute', - '_body', - 'Unable to fetch Sonarr queue', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr queue', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/queue/widgets/queue_tile.dart b/lib/modules/sonarr/modules/queue/widgets/queue_tile.dart index ce2f255c..65453783 100644 --- a/lib/modules/sonarr/modules/queue/widgets/queue_tile.dart +++ b/lib/modules/sonarr/modules/queue/widgets/queue_tile.dart @@ -74,7 +74,7 @@ class _State extends State { runSpacing: 10.0, children: [ LSTextHighlighted( - text: widget.record?.protocol?.lsLanguage_Capitalize() ?? Constants.TEXT_EMDASH, + text: widget.record?.protocol?.lunaCapitalizeFirstLetters() ?? Constants.TEXT_EMDASH, bgColor: widget.record.protocol == 'torrent' ? LunaColours.purple : LunaColours.blue, @@ -148,7 +148,7 @@ class _State extends State { child: LSButtonSlim( text: 'Messages', backgroundColor: LunaColours.accent, - onTap: () async => LunaDialogs.textPreview(context, 'Warnings', widget.record.statusMessages.fold('', (warnings, status) { + onTap: () async => LunaDialogs().textPreview(context, 'Warnings', widget.record.statusMessages.fold('', (warnings, status) { warnings += status.messages.fold('', (message, element) => message += '\n${Constants.TEXT_BULLET} $element'); return warnings.trim(); })), @@ -191,14 +191,7 @@ class _State extends State { showLunaSuccessSnackBar(context: context, title: 'Removed From Queue', message: widget.record.title); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrQueueQueueTile', - '_deleteQueueRecord', - 'Failed to remove item from queue: ${widget.record.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to remove item from queue: ${widget.record.id}', error, stack); showLunaErrorSnackBar(context: context, title: 'Failed to Delete From Queue'); }); } diff --git a/lib/modules/sonarr/modules/releases/route.dart b/lib/modules/sonarr/modules/releases/route.dart index 1ef12542..f94afb38 100644 --- a/lib/modules/sonarr/modules/releases/route.dart +++ b/lib/modules/sonarr/modules/releases/route.dart @@ -84,7 +84,7 @@ class _State extends State<_SonarrReleasesRoute> { _future = context.read().api.release.getSeasonReleases(seriesId: widget.seriesId, seasonNumber: widget.seasonNumber) .then((data) => data = data.where((release) => release.fullSeason).toList()); } else { - LunaLogger.warning( + LunaLogger().warning( '_SonarrReleasesRoute', '_refresh', 'No valid episodeId or (seriesId & seasonNumber) found', @@ -110,14 +110,7 @@ class _State extends State<_SonarrReleasesRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrReleasesRoute', - '_body', - 'Unable to fetch Sonarr releases: ${widget.episodeId}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr releases: ${widget.episodeId}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/releases/widgets/appbar_hide_button.dart b/lib/modules/sonarr/modules/releases/widgets/appbar_hide_button.dart index 8ecaab70..eae96114 100644 --- a/lib/modules/sonarr/modules/releases/widgets/appbar_hide_button.dart +++ b/lib/modules/sonarr/modules/releases/widgets/appbar_hide_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, state, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.visibility), diff --git a/lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart b/lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart index 0fa28abc..db16044c 100644 --- a/lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart +++ b/lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, state, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/sonarr/modules/releases/widgets/tile_release.dart b/lib/modules/sonarr/modules/releases/widgets/tile_release.dart index 3fea83ce..9d8f91b8 100644 --- a/lib/modules/sonarr/modules/releases/widgets/tile_release.dart +++ b/lib/modules/sonarr/modules/releases/widgets/tile_release.dart @@ -37,7 +37,7 @@ class SonarrReleasesReleaseTile extends StatelessWidget { : LunaColours.blue, fontWeight: FontWeight.bold, ), - text: release.protocol.lsLanguage_Capitalize(), + text: release.protocol.lunaCapitalizeFirstLetters(), ), if(release.protocol == 'torrent') TextSpan( text: ' (${release.seeders}/${release.leechers})', @@ -47,9 +47,9 @@ class SonarrReleasesReleaseTile extends StatelessWidget { ), ), TextSpan(text: '\t•\t${release.indexer}\t•\t'), - TextSpan(text: '${release?.ageHours?.lsTime_releaseAgeString() ?? 'Unknown'}\n'), + TextSpan(text: '${release?.ageHours?.lunaHoursToAge() ?? 'Unknown'}\n'), TextSpan(text: '${release?.quality?.quality?.name ?? 'Unknown'}\t•\t'), - TextSpan(text: '${release?.size?.lsBytes_BytesToString() ?? 'Unknown'}'), + TextSpan(text: '${release?.size?.lunaBytesToString() ?? 'Unknown'}'), ] ), ), @@ -84,7 +84,7 @@ class SonarrReleasesReleaseTile extends StatelessWidget { runSpacing: 10.0, children: [ LSTextHighlighted( - text: release.protocol.lsLanguage_Capitalize(), + text: release.protocol.lunaCapitalizeFirstLetters(), bgColor: release.protocol == 'torrent' ? LunaColours.purple : LunaColours.blue, @@ -100,9 +100,9 @@ class SonarrReleasesReleaseTile extends StatelessWidget { Padding( child: Column( children: [ - _tableContent('age', release?.ageHours?.lsTime_releaseAgeString() ?? 'Unknown'), + _tableContent('age', release?.ageHours?.lunaHoursToAge() ?? 'Unknown'), _tableContent('quality', release?.quality?.quality?.name ?? 'Unknown'), - _tableContent('size', release?.size?.lsBytes_BytesToString() ?? 'Unknown'), + _tableContent('size', release?.size?.lunaBytesToString() ?? 'Unknown'), if(release.protocol == 'torrent') _tableContent( 'statistics', [ @@ -170,14 +170,7 @@ class SonarrReleasesReleaseTile extends StatelessWidget { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'SonarrReleasesReleaseTile', - '_startDownload', - 'Unable to download release: ${release.guid}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to download release: ${release.guid}', error, stack); LSSnackBar( context: context, title: 'Failed to Download Release', @@ -191,6 +184,6 @@ class SonarrReleasesReleaseTile extends StatelessWidget { for(var i=0; i with AutomaticKeepAliveClientMixin builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'SonarrSeriesRoute', - '_body', - 'Unable to fetch Sonarr series', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr series', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/series/widgets/search_bar_hide_button.dart b/lib/modules/sonarr/modules/series/widgets/search_bar_hide_button.dart index 11eec127..c8e88bc6 100644 --- a/lib/modules/sonarr/modules/series/widgets/search_bar_hide_button.dart +++ b/lib/modules/sonarr/modules/series/widgets/search_bar_hide_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, state, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.visibility), diff --git a/lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart b/lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart index a7c9185a..f1c04e15 100644 --- a/lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart +++ b/lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart @@ -19,7 +19,7 @@ class _State extends State { Widget build(BuildContext context) => LSCard( child: Consumer( builder: (context, state, widget) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.sort), diff --git a/lib/modules/sonarr/modules/series_add/widgets/search_results.dart b/lib/modules/sonarr/modules/series_add/widgets/search_results.dart index 20cd3c6f..e75793c0 100644 --- a/lib/modules/sonarr/modules/series_add/widgets/search_results.dart +++ b/lib/modules/sonarr/modules/series_add/widgets/search_results.dart @@ -36,14 +36,7 @@ class _State extends State { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'SonarrSeriesAddSearchResults', - '_futureBuilder', - 'Unable to fetch Sonarr series lookup', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr series lookup', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart b/lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart index 27b83c42..08bbf251 100644 --- a/lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart +++ b/lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart @@ -111,7 +111,7 @@ class _State extends State { Future _onTap(BuildContext context) async { if(widget.onTapShowOverview) { - LunaDialogs.textPreview(context, widget.series.title, widget.series.overview ?? 'No summary is available.'); + LunaDialogs().textPreview(context, widget.series.title, widget.series.overview ?? 'No summary is available.'); } else if(widget.exists) { Provider.of(context, listen: false).enableVersion3 ? SonarrSeriesDetailsRouter.navigateTo(context, seriesId: widget.series.id ?? -1) diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart b/lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart index 2d425544..0239754f 100644 --- a/lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart +++ b/lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart @@ -11,6 +11,6 @@ class SonarrSeriesAddDetailsAppbarLinkAction extends StatelessWidget { @override Widget build(BuildContext context) => LSIconButton( icon: Icons.link, - onPressed: () async => tvdbId?.toString()?.lsLinks_OpenTVDB(), + onPressed: () async => tvdbId?.toString()?.lunaOpenTheTVDB(), ); } \ No newline at end of file diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart b/lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart index 4cca55ed..0909446a 100644 --- a/lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart +++ b/lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart @@ -116,14 +116,7 @@ class _State extends State { Navigator.of(context).popAndPushNamed(SonarrSeriesDetailsRouter.route(seriesId: addedSeries.id)); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrSeriesAddDetailsAddSeriesButton', - '_onTap', - 'Failed to add series: ${context.read().series.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to add series: ${context.read().series.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Add Series', diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart b/lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart index 3601d92d..dc6f7708 100644 --- a/lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart +++ b/lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart @@ -6,7 +6,7 @@ class SonarrSeriesAddDetailsSeriesTypeTile extends StatelessWidget { @override Widget build(BuildContext context) => LSCardTile( title: LSTitle(text: 'Series Type'), - subtitle: LSSubtitle(text: context.watch().seriesType?.value?.lsLanguage_Capitalize() ?? Constants.TEXT_EMDASH), + subtitle: LSSubtitle(text: context.watch().seriesType?.value?.lunaCapitalizeFirstLetters() ?? Constants.TEXT_EMDASH), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async => _onTap(context), ); diff --git a/lib/modules/sonarr/modules/series_details/route.dart b/lib/modules/sonarr/modules/series_details/route.dart index 36f33905..806a6ab7 100644 --- a/lib/modules/sonarr/modules/series_details/route.dart +++ b/lib/modules/sonarr/modules/series_details/route.dart @@ -130,14 +130,7 @@ class _State extends State<_SonarrSeriesDetailsRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrSeriesDetailsRoute', - '_body', - 'Unable to pull Sonarr series details', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to pull Sonarr series details', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () => _refresh()); } diff --git a/lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart b/lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart index 938b4f5d..c6791ed0 100644 --- a/lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart +++ b/lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart @@ -39,7 +39,7 @@ class SonarrAppBarSeriesSettingsAction extends StatelessWidget { case SonarrSeriesSettingsType.DELETE: _delete(context, series); break; case SonarrSeriesSettingsType.REFRESH: _refresh(context, series); break; case SonarrSeriesSettingsType.MONITORED: _monitored(context, series); break; - default: LunaLogger.warning('SonarrAppBarSeriesSettingsAction', '_handler', 'Unknown case: ${(values[1] as SonarrSeriesSettingsType)}'); + default: LunaLogger().warning('SonarrAppBarSeriesSettingsAction', '_handler', 'Unknown case: ${(values[1] as SonarrSeriesSettingsType)}'); } } @@ -70,14 +70,7 @@ class SonarrAppBarSeriesSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarSeriesSettingsAction', - '_monitored', - 'Failed to toggle monitored state for series: ${series.id} / ${series.monitored}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to toggle monitored state for series: ${series.id} / ${series.monitored}', error, stack); LSSnackBar( context: context, title: series.monitored @@ -103,14 +96,7 @@ class SonarrAppBarSeriesSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarSeriesSettingsAction', - '_refresh', - 'Unable to refresh series: ${series.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to refresh series: ${series.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Refresh', @@ -142,14 +128,7 @@ class SonarrAppBarSeriesSettingsAction extends StatelessWidget { if(Navigator.of(context).canPop()) Navigator.of(context).pop(); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarSeriesSettingsAction', - '_delete', - 'Failed to remove series: ${series.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to remove series: ${series.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Remove Series', diff --git a/lib/modules/sonarr/modules/series_details/widgets/overview.dart b/lib/modules/sonarr/modules/series_details/widgets/overview.dart index 0211e007..8544f4a4 100644 --- a/lib/modules/sonarr/modules/series_details/widgets/overview.dart +++ b/lib/modules/sonarr/modules/series_details/widgets/overview.dart @@ -32,8 +32,8 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { Widget _information(BuildContext context) => LSTableBlock( children: [ LSTableContent(title: 'path', body: series.path ?? 'Unknown'), - LSTableContent(title: 'size', body: series.sizeOnDisk?.lsBytes_BytesToString(decimals: 1) ?? 'Unknown'), - LSTableContent(title: 'type', body: series.seriesType?.value?.lsLanguage_Capitalize() ?? 'Unknown'), + LSTableContent(title: 'size', body: series.sizeOnDisk?.lunaBytesToString(decimals: 1) ?? 'Unknown'), + LSTableContent(title: 'type', body: series.seriesType?.value?.lunaCapitalizeFirstLetters() ?? 'Unknown'), LSTableContent(title: 'quality', body: quality?.name ?? 'Unknown'), if(Provider.of(context, listen: false).enableVersion3) LSTableContent(title: 'language', body: language?.name ?? Constants.TEXT_EMDASH), if(tags != null && tags.length > 0) LSTableContent( @@ -41,7 +41,7 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { body: tags.fold('', (string, tag) => string += ', ${tag.label}').substring(2), ), LSTableContent(title: '', body: ''), - LSTableContent(title: 'status', body: series.status?.lsLanguage_Capitalize() ?? 'Unknown'), + LSTableContent(title: 'status', body: series.status?.lunaCapitalizeFirstLetters() ?? 'Unknown'), LSTableContent(title: 'runtime', body: series.lunaRuntime), LSTableContent(title: 'network', body: series.network ?? 'Unknown'), if(series.nextAiring != null) LSTableContent(title: 'next airing', body: series.lunaNextAiring), @@ -90,7 +90,7 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => LunaDialogs.textPreview(context, series.title, series.overview), + onTap: () async => LunaDialogs().textPreview(context, series.title, series.overview), ), decoration: LunaCardDecoration( uri: Provider.of(context, listen: false).getFanartURL(series.id), @@ -111,7 +111,7 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { padding: EdgeInsets.all(18.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.imdbId?.lsLinks_OpenIMDB(), + onTap: () async => await series?.imdbId?.lunaOpenIMDB(), ), reducedMargin: true, ), @@ -127,7 +127,7 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { padding: EdgeInsets.all(17.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.tvdbId?.toString()?.lsLinks_OpenTVDB(), + onTap: () async => await series?.tvdbId?.toString()?.lunaOpenTheTVDB(), ), reducedMargin: true, ), @@ -143,7 +143,7 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { padding: EdgeInsets.all(18.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.tvMazeId?.toString()?.lsLinks_OpenTVMaze(), + onTap: () async => await series?.tvMazeId?.toString()?.lunaOpenTVMaze(), ), reducedMargin: true, ), diff --git a/lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart b/lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart index e43e9212..e48e7adc 100644 --- a/lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart +++ b/lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart @@ -20,7 +20,7 @@ class SonarrSeriesDetailsSeasonAllTile extends StatelessWidget { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), children: [ - TextSpan(text: series?.sizeOnDisk?.lsBytes_BytesToString(decimals: 1) ?? '0.0 B'), + TextSpan(text: series?.sizeOnDisk?.lunaBytesToString(decimals: 1) ?? '0.0 B'), TextSpan(text: '\n'), TextSpan( style: TextStyle( diff --git a/lib/modules/sonarr/modules/series_details/widgets/season_tile.dart b/lib/modules/sonarr/modules/series_details/widgets/season_tile.dart index f3de6fe5..9afca377 100644 --- a/lib/modules/sonarr/modules/series_details/widgets/season_tile.dart +++ b/lib/modules/sonarr/modules/series_details/widgets/season_tile.dart @@ -29,7 +29,7 @@ class _State extends State { fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), children: [ - TextSpan(text: widget.season?.statistics?.sizeOnDisk?.lsBytes_BytesToString(decimals: 1) ?? '0.0 B'), + TextSpan(text: widget.season?.statistics?.sizeOnDisk?.lunaBytesToString(decimals: 1) ?? '0.0 B'), TextSpan(text: '\n'), TextSpan( style: TextStyle( @@ -89,14 +89,7 @@ class _State extends State { }) .catchError((error, stack) { setState(() => widget.season.monitored = _fallbackState); - LunaLogger.error( - 'SonarrSeriesDetailsSeasonTile', - '_trailingOnPressed', - 'Failed to toggle monitored state: ${widget.seriesId} / ${widget.season.seasonNumber}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to toggle monitored state: ${widget.seriesId} / ${widget.season.seasonNumber}', error, stack); }); } diff --git a/lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart b/lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart index 353491e2..10bbdaaf 100644 --- a/lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart +++ b/lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart @@ -71,14 +71,7 @@ class SonarrSeriesEditUpdateSeriesButton extends StatelessWidget { }) .catchError((error, stack) { // Log error, show error message - LunaLogger.error( - 'SonarrSeriesEditUpdateSeriesButton', - '_onTap', - 'Failed to update series: ${series.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to update series: ${series.id}', error, stack); _editState.state = LunaLoadingState.ERROR; }); } diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart b/lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart index 354f37eb..170d5242 100644 --- a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart +++ b/lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart @@ -12,7 +12,7 @@ class SonarrSeriesEditSeriesPathTile extends StatelessWidget { ); Future _onTap(BuildContext context) async { - List _values = await LunaDialogs.editText( + List _values = await LunaDialogs().editText( context, 'Series Type', prefill: context.read().seriesPath, diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart b/lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart index 340d1fbf..e1c112b1 100644 --- a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart +++ b/lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart @@ -6,7 +6,7 @@ class SonarrSeriesEditSeriesTypeTile extends StatelessWidget { @override Widget build(BuildContext context) => LSCardTile( title: LSTitle(text: 'Series Type'), - subtitle: LSSubtitle(text: context.watch().seriesType.value?.lsLanguage_Capitalize() ?? Constants.TEXT_EMDASH), + subtitle: LSSubtitle(text: context.watch().seriesType.value?.lunaCapitalizeFirstLetters() ?? Constants.TEXT_EMDASH), trailing: LSIconButton(icon: Icons.arrow_forward_ios), onTap: () async => _onTap(context), ); diff --git a/lib/modules/sonarr/modules/series_season_details/route.dart b/lib/modules/sonarr/modules/series_season_details/route.dart index d25c85f0..3ec01769 100644 --- a/lib/modules/sonarr/modules/series_season_details/route.dart +++ b/lib/modules/sonarr/modules/series_season_details/route.dart @@ -138,14 +138,7 @@ class _State extends State<_SonarrSeriesSeasonDetailsRoute> { context.read().selectedEpisodes = []; }) .catchError((error, stack) { - LunaLogger.error( - '', - '_searchSelected', - 'Failed to search for episodes: ${context.read().selectedEpisodes.join(', ')}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to search for episodes: ${context.read().selectedEpisodes.join(', ')}', error, stack); LSSnackBar( context: context, title: 'Failed to Search For Episodes', diff --git a/lib/modules/sonarr/modules/series_season_details/widgets/season_header.dart b/lib/modules/sonarr/modules/series_season_details/widgets/season_header.dart index c1ba9af9..044d3db7 100644 --- a/lib/modules/sonarr/modules/series_season_details/widgets/season_header.dart +++ b/lib/modules/sonarr/modules/series_season_details/widgets/season_header.dart @@ -42,7 +42,7 @@ class SonarrSeriesSeasonDetailsSeasonHeader extends StatelessWidget { if(values[0]) switch(values[1] as SonarrSeasonSettingsType) { case SonarrSeasonSettingsType.AUTOMATIC_SEARCH: _automaticSearch(context, seriesId, seasonNumber); break; case SonarrSeasonSettingsType.INTERACTIVE_SEARCH: _interactiveSearch(context, seriesId, seasonNumber); break; - default: LunaLogger.warning('SonarrAppBarSeriesSettingsAction', 'handler', 'Unknown case: ${(values[1] as SonarrSeasonSettingsType)}'); + default: LunaLogger().warning('SonarrAppBarSeriesSettingsAction', 'handler', 'Unknown case: ${(values[1] as SonarrSeasonSettingsType)}'); } } @@ -61,14 +61,7 @@ class SonarrSeriesSeasonDetailsSeasonHeader extends StatelessWidget { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'SonarrSeriesDetailsSeasonTile', - '_onLongPress', - 'Failed season search: $seriesId, season $seasonNumber', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed season search: $seriesId, season $seasonNumber', error, stack); LSSnackBar( context: context, title: 'Failed to Season Search', diff --git a/lib/modules/sonarr/modules/series_season_details/widgets/tile_episode.dart b/lib/modules/sonarr/modules/series_season_details/widgets/tile_episode.dart index 4d8d6bd1..3675ad3b 100644 --- a/lib/modules/sonarr/modules/series_season_details/widgets/tile_episode.dart +++ b/lib/modules/sonarr/modules/series_season_details/widgets/tile_episode.dart @@ -65,7 +65,7 @@ class _State extends State { text: [ widget.episode.episodeFile.quality.quality.name, ' ${Constants.TEXT_EMDASH} ', - widget.episode.episodeFile.size.lsBytes_BytesToString(), + widget.episode.episodeFile.size.lunaBytesToString(), ].join(), ), if(_queue == null && !widget.episode.hasFile && (widget.episode?.airDateUtc?.toLocal()?.isAfter(DateTime.now()) ?? true)) LSTextHighlighted( @@ -155,7 +155,7 @@ class _State extends State { ), color: context.watch().selectedEpisodes.contains(widget.episode.id) ? LunaColours.accent.withOpacity(0.15) - : LunaSeaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.secondary, + : LunaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.secondary, ); @@ -169,7 +169,7 @@ class _State extends State { onLongPress: () async => _handleEpisodeSettings(), color: context.watch().selectedEpisodes.contains(widget.episode.id) ? LunaColours.accent.withOpacity(0.15) - : LunaSeaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.secondary, + : LunaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.secondary, ); Widget get _subtitle => FutureBuilder( @@ -215,7 +215,7 @@ class _State extends State { children: [ TextSpan(text: widget.episode.episodeFile.quality.quality.name), TextSpan(text: ' ${Constants.TEXT_EMDASH} '), - TextSpan(text: widget.episode.episodeFile.size.lsBytes_BytesToString()), + TextSpan(text: widget.episode.episodeFile.size.lunaBytesToString()), ], ), if(_queue == null && !widget.episode.hasFile && (widget.episode?.airDateUtc?.toLocal()?.isAfter(DateTime.now()) ?? true)) TextSpan( @@ -276,14 +276,7 @@ class _State extends State { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'SonarrMissingTile', - '_trailingOnPressed', - 'Failed to search for episode: ${widget.episode.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to search for episode: ${widget.episode.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Search', @@ -321,14 +314,7 @@ class _State extends State { ); }) .catchError((error, stack) { - LunaLogger.error( - '_handleDeleteFile', - '_handleToggleMonitored', - 'Failed to delete episode file: ${widget.episode.episodeFileId}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to delete episode file: ${widget.episode.episodeFileId}', error, stack); LSSnackBar( context: context, title: 'Failed to Delete Episode File', @@ -353,14 +339,7 @@ class _State extends State { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrSeriesSeasonDetailsEpisodeTile', - '_handleToggleMonitored', - 'Failed to set episode monitored state: ${_episode.id}, ${_episode.monitored}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to set episode monitored state: ${_episode.id}, ${_episode.monitored}', error, stack); LSSnackBar( context: context, title: _episode.monitored diff --git a/lib/modules/sonarr/modules/sonarr/route.dart b/lib/modules/sonarr/modules/sonarr/route.dart index 37e788e4..d2f95b75 100644 --- a/lib/modules/sonarr/modules/sonarr/route.dart +++ b/lib/modules/sonarr/modules/sonarr/route.dart @@ -44,7 +44,7 @@ class _State extends State<_SonarrHomeRoute> { Widget build(BuildContext context) => WillPopScope( onWillPop: _onWillPop, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaSeaDatabaseValue.ENABLED_PROFILE.key ]), + valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaDatabaseValue.ENABLED_PROFILE.key ]), builder: (context, box, _) => Scaffold( key: _scaffoldKey, drawer: _drawer, diff --git a/lib/modules/sonarr/modules/sonarr/widgets/appbar.dart b/lib/modules/sonarr/modules/sonarr/widgets/appbar.dart index 45806713..1c54ae04 100644 --- a/lib/modules/sonarr/modules/sonarr/widgets/appbar.dart +++ b/lib/modules/sonarr/modules/sonarr/widgets/appbar.dart @@ -15,7 +15,7 @@ Widget SonarrAppBar({ ) : AppBar( title: PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), child: Wrap( @@ -32,7 +32,7 @@ Widget SonarrAppBar({ ), ], ), - onSelected: (result) => LunaProfile.changeProfile(context, result), + onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result), itemBuilder: (context) { return >[for(String profile in profiles) PopupMenuItem( value: profile, diff --git a/lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart b/lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart index e6766111..93af352a 100644 --- a/lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart +++ b/lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart @@ -22,7 +22,7 @@ class SonarrAppBarGlobalSettingsAction extends StatelessWidget { } } - Future _webGUI(BuildContext context) async => Provider.of(context, listen: false).host.lsLinks_OpenLink(); + Future _webGUI(BuildContext context) async => Provider.of(context, listen: false).host.lunaOpenGenericLink(); Future _viewQueue(BuildContext context) async => SonarrQueueRouter.navigateTo(context); @@ -39,14 +39,7 @@ class SonarrAppBarGlobalSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarGlobalSettingsAction', - '_updateLibrary', - 'Unable to update library', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to update library', error, stack); LSSnackBar( context: context, title: 'Failed to Update Library', @@ -66,14 +59,7 @@ class SonarrAppBarGlobalSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarGlobalSettingsAction', - '_runRSSSync', - 'Unable to run RSS sync', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to run RSS sync', error, stack); LSSnackBar( context: context, title: 'Failed to Run RSS Sync', @@ -95,14 +81,7 @@ class SonarrAppBarGlobalSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarGlobalSettingsAction', - '_searchAllMissing', - 'Unable to search for all missing episodes', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to search for all missing episodes', error, stack); LSSnackBar( context: context, title: 'Failed to Search', @@ -123,14 +102,7 @@ class SonarrAppBarGlobalSettingsAction extends StatelessWidget { ); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrAppBarGlobalSettingsAction', - '_backupDatabase', - 'Unable to backup database', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to backup database', error, stack); LSSnackBar( context: context, title: 'Failed to Backup Database', diff --git a/lib/modules/sonarr/modules/tags/route.dart b/lib/modules/sonarr/modules/tags/route.dart index a207e6cf..8caa2911 100644 --- a/lib/modules/sonarr/modules/tags/route.dart +++ b/lib/modules/sonarr/modules/tags/route.dart @@ -66,14 +66,7 @@ class _State extends State<_SonarrTagsRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrTagsRoute', - '_body', - 'Unable to fetch Sonarr tags', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr tags', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart b/lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart index f14712f7..f3b4dbff 100644 --- a/lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart +++ b/lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart @@ -27,14 +27,7 @@ class SonarrTagsAppBarActionAddTag extends StatelessWidget { context.read().resetTags(); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrTagsAppBarActionAddTag', - '_onPressed', - 'Failed to add tag: ${_values[1]}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to add tag: ${_values[1]}', error, stack); showLunaErrorSnackBar( context: context, title: 'Failed to Add Tag', diff --git a/lib/modules/sonarr/modules/tags/widgets/tag_tile.dart b/lib/modules/sonarr/modules/tags/widgets/tag_tile.dart index 59c927ae..6b40fa9e 100644 --- a/lib/modules/sonarr/modules/tags/widgets/tag_tile.dart +++ b/lib/modules/sonarr/modules/tags/widgets/tag_tile.dart @@ -60,7 +60,7 @@ class _State extends State { return '${seriesList.length} Series'; } - Future _handleInfo() async => LunaDialogs.textPreview( + Future _handleInfo() async => LunaDialogs().textPreview( context, 'Series List', seriesList == null || seriesList.length == 0 ? 'No Series' : seriesList.join('\n'), @@ -85,14 +85,7 @@ class _State extends State { context.read().resetTags(); }) .catchError((error, stack) { - LunaLogger.error( - 'SonarrTagsTagTile', - '_handleDelete', - 'Failed to delete tag: ${widget.tag.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to delete tag: ${widget.tag.id}', error, stack); showLunaErrorSnackBar( context: context, title: 'Failed to Delete Tag', diff --git a/lib/modules/sonarr/modules/upcoming/route.dart b/lib/modules/sonarr/modules/upcoming/route.dart index dcb159c5..cb7172fa 100644 --- a/lib/modules/sonarr/modules/upcoming/route.dart +++ b/lib/modules/sonarr/modules/upcoming/route.dart @@ -46,14 +46,7 @@ class _State extends State with AutomaticKeepAliveClientMix builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_SonarrUpcomingRoute', - '_body', - 'Unable to fetch Sonarr upcoming episodes', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Sonarr upcoming episodes', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart b/lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart index a595c36a..320e0d5f 100644 --- a/lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart +++ b/lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart @@ -175,14 +175,7 @@ class _State extends State { type: SNACKBAR_TYPE.success, )) .catchError((error, stack) { - LunaLogger.error( - 'SonarrUpcomingTile', - '_trailingOnPressed', - 'Failed to search for episode: ${widget.record.id}', - error, - stack, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to search for episode: ${widget.record.id}', error, stack); LSSnackBar( context: context, title: 'Failed to Search', diff --git a/lib/modules/tautulli/core/constants.dart b/lib/modules/tautulli/core/constants.dart index a5480b13..756993c8 100644 --- a/lib/modules/tautulli/core/constants.dart +++ b/lib/modules/tautulli/core/constants.dart @@ -6,18 +6,21 @@ class TautulliConstants { static const String MODULE_KEY = 'tautulli'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Tautulli', description: 'View Plex Activity', settingsDescription: 'Configure Tautulli', + helpMessage: 'Tautulli is an application that you can run alongside your Plex Media Server to monitor activity and track various statistics. Most importantly, these statistics include what has been watched, who watched it, when and where they watched it, and how it was watched.', icon: CustomIcons.tautulli, route: '/tautulli', color: Color(0xFFDBA23A), + website: 'https://tautulli.com', + github: 'https://github.com/Tautulli/Tautulli', ); //ignore: non_constant_identifier_names static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem( type: MODULE_KEY, - localizedTitle: MODULE_MAP.name, + localizedTitle: MODULE_METADATA.name, ); } diff --git a/lib/modules/tautulli/core/database.dart b/lib/modules/tautulli/core/database.dart index 0fcaccde..d4859db6 100644 --- a/lib/modules/tautulli/core/database.dart +++ b/lib/modules/tautulli/core/database.dart @@ -1,9 +1,73 @@ +import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class TautulliDatabase { - TautulliDatabase._(); +class TautulliDatabase extends LunaModuleDatabase { + @override + void registerAdapters() {} - static void registerAdapters() {} + @override + Map export() { + Map data = {}; + for(TautulliDatabaseValue value in TautulliDatabaseValue.values) { + switch(value) { + // Primitive values + case TautulliDatabaseValue.NAVIGATION_INDEX: + case TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS: + case TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS: + case TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS: + case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: + case TautulliDatabaseValue.REFRESH_RATE: + case TautulliDatabaseValue.CONTENT_LOAD_LENGTH: + case TautulliDatabaseValue.STATISTICS_STATS_COUNT: + case TautulliDatabaseValue.TERMINATION_MESSAGE: + case TautulliDatabaseValue.GRAPHS_DAYS: + case TautulliDatabaseValue.GRAPHS_LINECHART_DAYS: + case TautulliDatabaseValue.GRAPHS_MONTHS: data[value.key] = value.data; break; + } + } + return data; + } + + @override + void import(Map config) { + for(String key in config.keys) { + TautulliDatabaseValue value = valueFromKey(key); + if(value != null) switch(value) { + // Primitive values + case TautulliDatabaseValue.NAVIGATION_INDEX: + case TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS: + case TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS: + case TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS: + case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: + case TautulliDatabaseValue.REFRESH_RATE: + case TautulliDatabaseValue.CONTENT_LOAD_LENGTH: + case TautulliDatabaseValue.STATISTICS_STATS_COUNT: + case TautulliDatabaseValue.TERMINATION_MESSAGE: + case TautulliDatabaseValue.GRAPHS_DAYS: + case TautulliDatabaseValue.GRAPHS_LINECHART_DAYS: + case TautulliDatabaseValue.GRAPHS_MONTHS: value.put(config[key]); break; + } + } + } + + @override + TautulliDatabaseValue valueFromKey(String key) { + switch(key) { + case 'TAUTULLI_NAVIGATION_INDEX': return TautulliDatabaseValue.NAVIGATION_INDEX; + case 'TAUTULLI_NAVIGATION_INDEX_GRAPHS': return TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS; + case 'TAUTULLI_NAVIGATION_INDEX_LIBRARIES_DETAILS': return TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS; + case 'TAUTULLI_NAVIGATION_INDEX_MEDIA_DETAILS': return TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS; + case 'TAUTULLI_NAVIGATION_INDEX_USER_DETAILS': return TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS; + case 'TAUTULLI_REFRESH_RATE': return TautulliDatabaseValue.REFRESH_RATE; + case 'TAUTULLI_CONTENT_LOAD_LENGTH': return TautulliDatabaseValue.CONTENT_LOAD_LENGTH; + case 'TAUTULLI_STATISTICS_STATS_COUNT': return TautulliDatabaseValue.STATISTICS_STATS_COUNT; + case 'TAUTULLI_TERMINATION_MESSAGE': return TautulliDatabaseValue.TERMINATION_MESSAGE; + case 'TAUTULLI_GRAPHS_DAYS': return TautulliDatabaseValue.GRAPHS_DAYS; + case 'TAUTULLI_GRAPHS_LINECHART_DAYS': return TautulliDatabaseValue.GRAPHS_LINECHART_DAYS; + case 'TAUTULLI_GRAPHS_MONTHS': return TautulliDatabaseValue.GRAPHS_MONTHS; + default: return null; + } + } } enum TautulliDatabaseValue { @@ -24,13 +88,11 @@ enum TautulliDatabaseValue { extension TautulliDatabaseValueExtension on TautulliDatabaseValue { String get key { switch(this) { - // Default Pages case TautulliDatabaseValue.NAVIGATION_INDEX: return 'TAUTULLI_NAVIGATION_INDEX'; case TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS: return 'TAUTULLI_NAVIGATION_INDEX_GRAPHS'; case TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS: return 'TAUTULLI_NAVIGATION_INDEX_LIBRARIES_DETAILS'; case TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS: return 'TAUTULLI_NAVIGATION_INDEX_MEDIA_DETAILS'; - case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: return 'TAUTULLI_NAVIGATION_INDEX_USER_DETAILS'; - // + case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: return 'TAUTULLI_NAVIGATION_INDEX_USER_DETAILS'; case TautulliDatabaseValue.REFRESH_RATE: return 'TAUTULLI_REFRESH_RATE'; case TautulliDatabaseValue.CONTENT_LOAD_LENGTH: return 'TAUTULLI_CONTENT_LOAD_LENGTH'; case TautulliDatabaseValue.STATISTICS_STATS_COUNT: return 'TAUTULLI_STATISTICS_STATS_COUNT'; @@ -45,13 +107,11 @@ extension TautulliDatabaseValueExtension on TautulliDatabaseValue { dynamic get data { final _box = Database.lunaSeaBox; switch(this) { - // Default Pages case TautulliDatabaseValue.NAVIGATION_INDEX: return _box.get(this.key, defaultValue: 0); case TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS: return _box.get(this.key, defaultValue: 0); case TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS: return _box.get(this.key, defaultValue: 0); case TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS: return _box.get(this.key, defaultValue: 0); case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: return _box.get(this.key, defaultValue: 0); - // case TautulliDatabaseValue.REFRESH_RATE: return _box.get(this.key, defaultValue: 10); case TautulliDatabaseValue.CONTENT_LOAD_LENGTH: return _box.get(this.key, defaultValue: 125); case TautulliDatabaseValue.STATISTICS_STATS_COUNT: return _box.get(this.key, defaultValue: 3); @@ -60,7 +120,30 @@ extension TautulliDatabaseValueExtension on TautulliDatabaseValue { case TautulliDatabaseValue.GRAPHS_LINECHART_DAYS: return _box.get(this.key, defaultValue: 14); case TautulliDatabaseValue.GRAPHS_MONTHS: return _box.get(this.key, defaultValue: 6); } + throw Exception('data not found'); } - void put(dynamic value) => Database.lunaSeaBox.put(this.key, value); + void put(dynamic value) { + final box = Database.lunaSeaBox; + switch(this) { + case TautulliDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.REFRESH_RATE: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.CONTENT_LOAD_LENGTH: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.STATISTICS_STATS_COUNT: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.TERMINATION_MESSAGE: if(value.runtimeType == String) box.put(this.key, value); return; + case TautulliDatabaseValue.GRAPHS_DAYS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.GRAPHS_LINECHART_DAYS: if(value.runtimeType == int) box.put(this.key, value); return; + case TautulliDatabaseValue.GRAPHS_MONTHS: if(value.runtimeType == int) box.put(this.key, value); return; + } + LunaLogger().warning('TautulliDatabaseValueExtension', 'put', 'Attempted to enter data for invalid TautulliDatabaseValue: ${this?.toString() ?? 'null'}'); + } + + void listen(Widget Function(BuildContext, Box, Widget) builder) => ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]), + builder: builder, + ); } diff --git a/lib/modules/tautulli/core/extensions/tautulli_history_record.dart b/lib/modules/tautulli/core/extensions/tautulli_history_record.dart index b7a12914..67ebcb62 100644 --- a/lib/modules/tautulli/core/extensions/tautulli_history_record.dart +++ b/lib/modules/tautulli/core/extensions/tautulli_history_record.dart @@ -26,7 +26,7 @@ extension TautulliHistoryRecordExtension on TautulliHistoryRecord { } } - String get lsDate => DateTime.now().lsDateTime_ageString(this.date); + String get lsDate => this.date?.lunaAge ?? 'Unknown'; String get lsStatus { switch(this.watchedStatus) { diff --git a/lib/modules/tautulli/core/extensions/tautulli_session.dart b/lib/modules/tautulli/core/extensions/tautulli_session.dart index 57175701..80fede5c 100644 --- a/lib/modules/tautulli/core/extensions/tautulli_session.dart +++ b/lib/modules/tautulli/core/extensions/tautulli_session.dart @@ -22,8 +22,8 @@ extension TautulliSessionExtension on TautulliSession { String get lsDuration { double _percent = this.progressPercent/100; - String _progress = Duration(seconds: (this.streamDuration.inSeconds*_percent).floor()).lsDuration_timestamp(); - String _total = this.streamDuration.lsDuration_timestamp(); + String _progress = Duration(seconds: (this.streamDuration.inSeconds*_percent).floor()).lunaTimestamp; + String _total = this.streamDuration.lunaTimestamp; return '$_progress/$_total (${this.progressPercent}%)'; } @@ -32,22 +32,16 @@ extension TautulliSessionExtension on TautulliSession { double _percent = this.progressPercent/100; Duration _progress = Duration(seconds: (this.streamDuration.inSeconds*_percent).floor()); Duration _eta = this.streamDuration - _progress; - return DateTime.now().add(_eta).lsDateTime_time; + return DateTime.now().add(_eta).lunaTime; } catch (error, stack) { - LunaLogger.error( - 'TautulliSessionExtension', - 'lunaETA', - 'Failed to calculate ETA', - error, - stack, - ); + LunaLogger().error('Failed to calculate ETA', error, stack); return 'Unknown'; } } - String get lsBandwidth => '${this.bandwidth.lsBytes_KilobytesToString(bytes: false, decimals: 1)}ps'; + String get lsBandwidth => '${this.bandwidth.lunaKilobytesToString(bytes: false, decimals: 1)}ps'; - String get lsQuality => '${this.qualityProfile} (${this.streamBitrate.lsBytes_KilobytesToString(bytes: false, decimals: 1)}ps)'; + String get lsQuality => '${this.qualityProfile} (${this.streamBitrate.lunaKilobytesToString(bytes: false, decimals: 1)}ps)'; String get lsStream { switch(this.transcodeDecision) { @@ -55,7 +49,6 @@ extension TautulliSessionExtension on TautulliSession { String _transcodeStatus = this.transcodeThrottled ? 'Throttled' : '${this.transcodeSpeed ?? 0.0}x'; return [ 'Transcode', - if(this.transcodeHardwareFullPipeline) ' (hw)', ' ($_transcodeStatus)', ].join(); case TautulliTranscodeDecision.COPY: return 'Direct Stream'; @@ -124,7 +117,6 @@ extension TautulliSessionExtension on TautulliSession { String _transcodeStatus = this.transcodeThrottled ? 'Throttled' : '${this.transcodeSpeed ?? 0.0}x'; return [ 'Transcode', - if(this.transcodeHardwareFullPipeline) ' (hw)', ' ($_transcodeStatus)', ].join(); case TautulliTranscodeDecision.DIRECT_PLAY: diff --git a/lib/modules/tautulli/core/graphs/bar_graph.dart b/lib/modules/tautulli/core/graphs/bar_graph.dart index 5aa571b3..cdd1805f 100644 --- a/lib/modules/tautulli/core/graphs/bar_graph.dart +++ b/lib/modules/tautulli/core/graphs/bar_graph.dart @@ -38,7 +38,7 @@ class TautulliBarGraphHelper { static BarTouchData barTouchData(BuildContext context, TautulliGraphData data) => BarTouchData( enabled: true, touchTooltipData: BarTouchTooltipData( - tooltipBgColor: LunaSeaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.primary, + tooltipBgColor: LunaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.primary, tooltipRoundedRadius: Constants.UI_BORDER_RADIUS, tooltipPadding: EdgeInsets.all(8.0), maxContentWidth: MediaQuery.of(context).size.width/1.25, @@ -52,7 +52,7 @@ class TautulliBarGraphHelper { String _value = data?.series[i]?.name ?? 'Unknown'; String _text = context.read().graphYAxis == TautulliGraphYAxis.PLAYS ? (_number?.truncate() ?? 0).toString() - : Duration(seconds: _number?.truncate() ?? 0).lsDuration_fullTimestamp(); + : Duration(seconds: _number?.truncate() ?? 0).lunaTimestampWords; _body += '$_value: $_text\n'; } return BarTooltipItem( diff --git a/lib/modules/tautulli/core/graphs/line_graph.dart b/lib/modules/tautulli/core/graphs/line_graph.dart index 5efca9da..eaf9071d 100644 --- a/lib/modules/tautulli/core/graphs/line_graph.dart +++ b/lib/modules/tautulli/core/graphs/line_graph.dart @@ -55,7 +55,7 @@ class TautulliLineGraphHelper { static LineTouchData lineTouchData(BuildContext context, TautulliGraphData data) => LineTouchData( enabled: true, touchTooltipData: LineTouchTooltipData( - tooltipBgColor: LunaSeaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.primary, + tooltipBgColor: LunaDatabaseValue.THEME_AMOLED.data ? Colors.black : LunaColours.primary, tooltipRoundedRadius: Constants.UI_BORDER_RADIUS, tooltipPadding: EdgeInsets.all(8.0), maxContentWidth: MediaQuery.of(context).size.width/1.25, @@ -68,7 +68,7 @@ class TautulliLineGraphHelper { '${data.series[spots[index].barIndex].name}: ', context.read().graphYAxis == TautulliGraphYAxis.PLAYS ? '${spots[index]?.y?.truncate() ?? 0}' - : '${Duration(seconds: spots[index]?.y?.truncate() ?? 0).lsDuration_fullTimestamp()}', + : '${Duration(seconds: spots[index]?.y?.truncate() ?? 0).lunaTimestampWords}', ].join().trim(), TextStyle( color: Colors.white70, diff --git a/lib/modules/tautulli/core/router.dart b/lib/modules/tautulli/core/router.dart index b9d0108c..30ffd264 100644 --- a/lib/modules/tautulli/core/router.dart +++ b/lib/modules/tautulli/core/router.dart @@ -1,10 +1,10 @@ import 'package:fluro/fluro.dart'; +import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliRouter { - TautulliRouter._(); - - static void initialize(FluroRouter router) { +class TautulliRouter extends LunaModuleRouter { + @override + void defineAllRoutes(FluroRouter router) { TautulliHomeRouter.defineRoutes(router); // Details TautulliActivityDetailsRouter.defineRoutes(router); diff --git a/lib/modules/tautulli/core/state.dart b/lib/modules/tautulli/core/state.dart index 77e8286d..72184271 100644 --- a/lib/modules/tautulli/core/state.dart +++ b/lib/modules/tautulli/core/state.dart @@ -3,7 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; import 'package:tautulli/tautulli.dart'; -class TautulliState extends LunaGlobalState { +class TautulliState extends LunaModuleState { TautulliState() { reset(); } diff --git a/lib/modules/tautulli/modules/activity/route.dart b/lib/modules/tautulli/modules/activity/route.dart index 5e5334d9..f1ef9098 100644 --- a/lib/modules/tautulli/modules/activity/route.dart +++ b/lib/modules/tautulli/modules/activity/route.dart @@ -43,14 +43,7 @@ class _State extends State with AutomaticKeepAliveClientM builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliActivityRoute', - '_body', - 'Unable to fetch Tautulli activity', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli activity', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/activity/widgets/activity_status.dart b/lib/modules/tautulli/modules/activity/widgets/activity_status.dart index d22c307a..1aaf4667 100644 --- a/lib/modules/tautulli/modules/activity/widgets/activity_status.dart +++ b/lib/modules/tautulli/modules/activity/widgets/activity_status.dart @@ -13,6 +13,6 @@ class TautulliActivityStatus extends StatelessWidget { @override Widget build(BuildContext context) => LSHeader( text: '${activity.streamCount} ${activity.streamCount == 1 ? 'Session' : 'Sessions'}', - subtitle: 'Total Bandwidth Usage: ${activity.totalBandwidth.lsBytes_KilobytesToString(bytes: false, decimals: 1)}ps', + subtitle: 'Total Bandwidth Usage: ${activity.totalBandwidth.lunaKilobytesToString(bytes: false, decimals: 1)}ps', ); } diff --git a/lib/modules/tautulli/modules/activity_details/route.dart b/lib/modules/tautulli/modules/activity_details/route.dart index 6ceb9d6f..92ae4c04 100644 --- a/lib/modules/tautulli/modules/activity_details/route.dart +++ b/lib/modules/tautulli/modules/activity_details/route.dart @@ -78,14 +78,7 @@ class _State extends State<_TautulliActivityDetailsRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliActivityDetailsRoute', - '_body', - 'Unable to pull Tautulli activity session', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to pull Tautulli activity session', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () => _refresh()); } diff --git a/lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart b/lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart index 242db396..dc087194 100644 --- a/lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart +++ b/lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart @@ -34,14 +34,7 @@ class TautulliActivityDetailsTerminateSession extends StatelessWidget { Navigator.of(context).pop(); }) .catchError((error, trace) { - LunaLogger.error( - 'TautulliActivityDetailsTerminateSession', - '_onPressed', - 'Unable to terminate session: ${session.sessionId}', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Unable to terminate session: ${session.sessionId}', error, trace); LSSnackBar( context: context, title: 'Failed to Terminate Session', diff --git a/lib/modules/tautulli/modules/check_for_updates/route.dart b/lib/modules/tautulli/modules/check_for_updates/route.dart index 74d5ad83..7576dc8a 100644 --- a/lib/modules/tautulli/modules/check_for_updates/route.dart +++ b/lib/modules/tautulli/modules/check_for_updates/route.dart @@ -74,14 +74,7 @@ class _State extends State<_TautulliCheckForUpdatesRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliCheckForUpdatesRoute', - '_body', - 'Unable to fetch updates', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch updates', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/graphs/widgets/type_button.dart b/lib/modules/tautulli/modules/graphs/widgets/type_button.dart index f66e0edb..86eb6810 100644 --- a/lib/modules/tautulli/modules/graphs/widgets/type_button.dart +++ b/lib/modules/tautulli/modules/graphs/widgets/type_button.dart @@ -8,7 +8,7 @@ class TautulliGraphsTypeButton extends StatelessWidget { Widget build(BuildContext context) => Selector( selector: (_, state) => state.graphYAxis, builder: (context, type, _) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.merge_type), @@ -22,7 +22,7 @@ class TautulliGraphsTypeButton extends StatelessWidget { (index) => PopupMenuItem( value: TautulliGraphYAxis.values[index], child: Text( - TautulliStatsType.values[index].value.lsLanguage_Capitalize(), + TautulliStatsType.values[index].value.lunaCapitalizeFirstLetters(), style: TextStyle( fontSize: Constants.UI_FONT_SIZE_SUBTITLE, color: type == TautulliGraphYAxis.values[index] diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart index a2cceff0..272f78ab 100644 --- a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart +++ b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart @@ -14,14 +14,7 @@ class TautulliGraphsDailyPlayCountGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsDailyPlayCountGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByDate', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByDate', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart index 65bd4ba4..0d6cc3a4 100644 --- a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart +++ b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByDayOfWeekGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByDayOfWeekGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByDayOfWeek', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByDayOfWeek', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart index debdc998..f1bead13 100644 --- a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart +++ b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByTopPlatformsGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByTopPlatformsGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByTopTenPlatforms', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByTopTenPlatforms', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart index 873935c3..c29a01d2 100644 --- a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart +++ b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByTopUsersGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByTopUsersGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByTopTenUsers', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByTopTenUsers', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart index 5bb0e835..50d6137e 100644 --- a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart +++ b/lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlaysByMonthGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlaysByMonthGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByMonth', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByMonth', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart b/lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart index b62d7b76..c47ca48b 100644 --- a/lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart +++ b/lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart @@ -14,14 +14,7 @@ class TautulliGraphsDailyStreamTypeBreakdownGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsDailyStreamTypeBreakdownGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByDate', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByDate', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart index c5ba10ed..c22ca9bb 100644 --- a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart +++ b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByPlatformStreamTypeGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByPlatformStreamTypeGraph', - '_body', - 'Unable to fetch Tautulli graph data: getStreamTypeByTopTenPlatforms', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getStreamTypeByTopTenPlatforms', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart index 7c7ad8de..1e6577a0 100644 --- a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart +++ b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountBySourceResolutionGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountBySourceResolutionGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysBySourceResolution', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysBySourceResolution', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart index f8767063..45751d77 100644 --- a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart +++ b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByStreamResolutionGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByStreamResolutionGraph', - '_body', - 'Unable to fetch Tautulli graph data: getPlaysByStreamResolution', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getPlaysByStreamResolution', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart index 83f317e0..ff84dcc2 100644 --- a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart +++ b/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart @@ -13,14 +13,7 @@ class TautulliGraphsPlayCountByUserStreamTypeGraph extends StatelessWidget { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliGraphsPlayCountByUserStreamTypeGraph', - '_body', - 'Unable to fetch Tautulli graph data: getStreamTypeByTopTenUsers', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli graph data: getStreamTypeByTopTenUsers', snapshot.error, StackTrace.current); } return TautulliGraphHelper.errorContainer; } diff --git a/lib/modules/tautulli/modules/history/route.dart b/lib/modules/tautulli/modules/history/route.dart index ad7ad184..17f70019 100644 --- a/lib/modules/tautulli/modules/history/route.dart +++ b/lib/modules/tautulli/modules/history/route.dart @@ -50,14 +50,7 @@ class _State extends State with AutomaticKeepAliveClientMi builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliHistoryRoute', - '_body', - 'Unable to fetch Tautulli history', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli history', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/history_details/route.dart b/lib/modules/tautulli/modules/history_details/route.dart index 41c5d8a9..8c279dc5 100644 --- a/lib/modules/tautulli/modules/history_details/route.dart +++ b/lib/modules/tautulli/modules/history_details/route.dart @@ -104,14 +104,7 @@ class _State extends State<_TautulliHistoryDetailsRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliHistoryDetailsRoute', - '_shared', - 'Unable to pull Tautulli history session', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to pull Tautulli history session', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () => _refresh()); } diff --git a/lib/modules/tautulli/modules/history_details/widgets/information.dart b/lib/modules/tautulli/modules/history_details/widgets/information.dart index 30dfa0f2..0c29c6a4 100644 --- a/lib/modules/tautulli/modules/history_details/widgets/information.dart +++ b/lib/modules/tautulli/modules/history_details/widgets/information.dart @@ -36,9 +36,9 @@ class TautulliHistoryDetailsInformation extends StatelessWidget { children: [ LSTableContent(title: 'state', body: history.lsState), LSTableContent(title: 'date', body: DateFormat('yyyy-MM-dd').format(history.date)), - LSTableContent(title: 'started', body: history.date.lsDateTime_time), - LSTableContent(title: 'stopped', body: history.state == null ? history.stopped.lsDateTime_time : Constants.TEXT_EMDASH), - LSTableContent(title: 'paused', body: history.pausedCounter.lsDuration_fullTimestamp()), + LSTableContent(title: 'started', body: history.date.lunaTime), + LSTableContent(title: 'stopped', body: history.state == null ? history.stopped.lunaTime : Constants.TEXT_EMDASH), + LSTableContent(title: 'paused', body: history.pausedCounter.lunaTimestampWords), ], ); diff --git a/lib/modules/tautulli/modules/ipaddress_details/route.dart b/lib/modules/tautulli/modules/ipaddress_details/route.dart index c7e48c29..348c2d80 100644 --- a/lib/modules/tautulli/modules/ipaddress_details/route.dart +++ b/lib/modules/tautulli/modules/ipaddress_details/route.dart @@ -83,14 +83,7 @@ class _State extends State<_TautulliIPAddressRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliIPAddressRoute', - '_body', - 'Unable to fetch Tautulli IP address information', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli IP address information', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/libraries/route.dart b/lib/modules/tautulli/modules/libraries/route.dart index c544b53a..2fec47d6 100644 --- a/lib/modules/tautulli/modules/libraries/route.dart +++ b/lib/modules/tautulli/modules/libraries/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLibrariesRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLibrariesRoute', - '_body', - 'Unable to fetch Tautulli libraries table', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli libraries table', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/libraries/widgets/library_tile.dart b/lib/modules/tautulli/modules/libraries/widgets/library_tile.dart index 5fdd5d0f..79f25383 100644 --- a/lib/modules/tautulli/modules/libraries/widgets/library_tile.dart +++ b/lib/modules/tautulli/modules/libraries/widgets/library_tile.dart @@ -26,13 +26,13 @@ class TautulliLibrariesLibraryTile extends StatelessWidget { ? '1 Play ${Constants.TEXT_EMDASH} ' : '${library.plays} Plays ${Constants.TEXT_EMDASH} ', ), - TextSpan(text: '${library.duration.lsDuration_fullTimestamp()}\n'), + TextSpan(text: '${library.duration.lunaTimestampWords}\n'), TextSpan( style: TextStyle( color: LunaColours.accent, fontWeight: FontWeight.w600, ), - text: '${DateTime.now().lsDateTime_ageString(library.lastAccessed)}', + text: '${library.lastAccessed?.lunaAge ?? 'Unknown'}', ), ], ), diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart b/lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart index 6025dc9b..c253ea5e 100644 --- a/lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart +++ b/lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart @@ -19,7 +19,7 @@ class TautulliLibrariesDetailsInformationDetails extends StatelessWidget { if(library.parentCount != null) LSTableContent(title: _parentCount(library.parentCount), body: '${library.parentCount} ${_parentCount(library.parentCount)}'), if(library.childCount != null) LSTableContent(title: _childCount(library.childCount), body: '${library.childCount} ${_childCount(library.childCount)}'), LSTableContent(title: 'last played', body: library.lastPlayed), - LSTableContent(title: '', body: DateTime.now().lsDateTime_ageString(library.lastAccessed)), + LSTableContent(title: '', body: library.lastAccessed?.lunaAge ?? 'Unknown'), ], ); diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart b/lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart index 25a70fb7..84fbc5d3 100644 --- a/lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart +++ b/lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart @@ -32,6 +32,6 @@ class TautulliLibrariesDetailsInformationGlobalStats extends StatelessWidget { String _body(int plays, Duration duration) { String _plays = plays == 1 ? '1 Play': '$plays Plays'; - return '$_plays\n${duration.lsDuration_fullTimestamp()}'; + return '$_plays\n${duration.lunaTimestampWords}'; } } diff --git a/lib/modules/tautulli/modules/logs_logins/route.dart b/lib/modules/tautulli/modules/logs_logins/route.dart index 79d83026..3aef7b8e 100644 --- a/lib/modules/tautulli/modules/logs_logins/route.dart +++ b/lib/modules/tautulli/modules/logs_logins/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsLoginsRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsLoginsRoute', - '_body', - 'Unable to fetch Tautulli login logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli login logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart b/lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart index 4598bab1..ccbb28ae 100644 --- a/lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart +++ b/lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart @@ -30,7 +30,7 @@ class TautulliLogsLoginsLogTile extends StatelessWidget { TextSpan(text: '${login.os}\n'), TextSpan(text: '${login.host}\n'), TextSpan( - text: LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + text: LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} HH:mm').format(login.timestamp) : DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} hh:mm a').format(login.timestamp), style: TextStyle( diff --git a/lib/modules/tautulli/modules/logs_newsletters/route.dart b/lib/modules/tautulli/modules/logs_newsletters/route.dart index 0a3380a7..69acfa7a 100644 --- a/lib/modules/tautulli/modules/logs_newsletters/route.dart +++ b/lib/modules/tautulli/modules/logs_newsletters/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsNewslettersRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsNewslettersRoute', - '_body', - 'Unable to fetch Tautulli newsletter logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli newsletter logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart b/lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart index 972bf9a0..f7cb9eb9 100644 --- a/lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart +++ b/lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart @@ -30,7 +30,7 @@ class TautulliLogsNewsletterLogTile extends StatelessWidget { TextSpan(text: '${newsletter.subjectText}\n'), TextSpan(text: '${newsletter.bodyText}\n'), TextSpan( - text: LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + text: LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} HH:mm').format(newsletter.timestamp) : DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} hh:mm a').format(newsletter.timestamp), style: TextStyle( diff --git a/lib/modules/tautulli/modules/logs_notifications/route.dart b/lib/modules/tautulli/modules/logs_notifications/route.dart index 5a24bd41..ff411256 100644 --- a/lib/modules/tautulli/modules/logs_notifications/route.dart +++ b/lib/modules/tautulli/modules/logs_notifications/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsNotificationsRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsNotificationsRoute', - '_body', - 'Unable to fetch Tautulli notification logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli notification logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart b/lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart index 6eaca0fc..ad0c7add 100644 --- a/lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart +++ b/lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart @@ -30,7 +30,7 @@ class TautulliLogsNotificationLogTile extends StatelessWidget { TextSpan(text: '${notification.subjectText}\n'), TextSpan(text: '${notification.bodyText}\n'), TextSpan( - text: LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + text: LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} HH:mm').format(notification.timestamp) : DateFormat('MMMM dd, yyyy ${Constants.TEXT_EMDASH} hh:mm a').format(notification.timestamp), style: TextStyle( diff --git a/lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart b/lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart index 786b8630..ecbd6b51 100644 --- a/lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart +++ b/lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsPlexMediaScannerRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsPlexMediaScannerRoute', - '_body', - 'Unable to fetch Tautulli plex media scanner logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli plex media scanner logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/logs_plex_media_server/route.dart b/lib/modules/tautulli/modules/logs_plex_media_server/route.dart index ac915041..037ac19e 100644 --- a/lib/modules/tautulli/modules/logs_plex_media_server/route.dart +++ b/lib/modules/tautulli/modules/logs_plex_media_server/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsPlexMediaServerRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsPlexMediaServerRoute', - '_body', - 'Unable to fetch Tautulli plex media server logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli plex media server logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/logs_tautulli/route.dart b/lib/modules/tautulli/modules/logs_tautulli/route.dart index fcabbaa3..99b96a02 100644 --- a/lib/modules/tautulli/modules/logs_tautulli/route.dart +++ b/lib/modules/tautulli/modules/logs_tautulli/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliLogsTautulliRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliLogsTautulliRoute', - '_body', - 'Unable to fetch Tautulli Tautulli logs', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli Tautulli logs', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/media_details/widgets/history.dart b/lib/modules/tautulli/modules/media_details/widgets/history.dart index 264c4d10..d1a53c6a 100644 --- a/lib/modules/tautulli/modules/media_details/widgets/history.dart +++ b/lib/modules/tautulli/modules/media_details/widgets/history.dart @@ -87,14 +87,7 @@ class _State extends State with AutomaticKeepAliveC builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliMediaDetailsHistory', - '_body', - 'Unable to fetch Tautulli history: ${widget.ratingKey}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli history: ${widget.ratingKey}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata.dart b/lib/modules/tautulli/modules/media_details/widgets/metadata.dart index 3d9727e1..ddfd96b0 100644 --- a/lib/modules/tautulli/modules/media_details/widgets/metadata.dart +++ b/lib/modules/tautulli/modules/media_details/widgets/metadata.dart @@ -56,14 +56,7 @@ class _State extends State with AutomaticKeepAlive builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliMediaDetailsMetadata', - '_body', - 'Unable to fetch Tautulli metadata: ${widget.ratingKey}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli metadata: ${widget.ratingKey}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart b/lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart index 9b5f53df..24267059 100644 --- a/lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart +++ b/lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart @@ -17,8 +17,8 @@ class TautulliMediaDetailsMetadataMetadata extends StatelessWidget { title: 'Metadata', children: [ if(metadata.originallyAvailableAt != null && metadata.originallyAvailableAt.isNotEmpty) _content('released', metadata.originallyAvailableAt), - if(metadata.addedAt != null) _content('added', metadata.addedAt.lsDateTime_date), - if(metadata.duration != null) _content('duration', metadata.duration.lsDuration_timestamp()), + if(metadata.addedAt != null) _content('added', metadata.addedAt.lunaDate), + if(metadata.duration != null) _content('duration', metadata.duration.lunaTimestamp), if(metadata.mediaInfo != null && metadata.mediaInfo.length != 0) _content('bitrate', '${metadata.mediaInfo[0].bitrate ?? '??'} kbps'), if(metadata.rating != null) _content('rating', '${((metadata.rating*10).truncate())}%'), if(metadata.studio != null && metadata.studio.isNotEmpty) _content('studio', metadata.studio), diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart b/lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart index da5a447f..b6f6320e 100644 --- a/lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart +++ b/lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart @@ -27,7 +27,7 @@ class TautulliMediaDetailsMetadataSummary extends StatelessWidget { padding: EdgeInsets.all(12.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => LunaDialogs.textPreview(context, metadata.title, metadata.summary.trim()), + onTap: () async => LunaDialogs().textPreview(context, metadata.title, metadata.summary.trim()), ), ); } diff --git a/lib/modules/tautulli/modules/recently_added/route.dart b/lib/modules/tautulli/modules/recently_added/route.dart index cd7c058b..e843f6c9 100644 --- a/lib/modules/tautulli/modules/recently_added/route.dart +++ b/lib/modules/tautulli/modules/recently_added/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliRecentlyAddedRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliRecentlyAddedRoute', - '_body', - 'Unable to fetch Tautulli recently added', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli recently added', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart b/lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart index 60f5b362..b065bccc 100644 --- a/lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart +++ b/lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart @@ -122,7 +122,7 @@ class TautulliRecentlyAddedContentTile extends StatelessWidget { overflow: TextOverflow.fade, ); - Widget get _addedAt => LSSubtitle(text: DateTime.now().lsDateTime_ageString(recentlyAdded.addedAt)); + Widget get _addedAt => LSSubtitle(text: recentlyAdded.addedAt?.lunaAge ?? 'Unknown'); Widget get _library => LSSubtitle(text: recentlyAdded.libraryName); diff --git a/lib/modules/tautulli/modules/search/widgets/search_results.dart b/lib/modules/tautulli/modules/search/widgets/search_results.dart index 5600afb8..51091811 100644 --- a/lib/modules/tautulli/modules/search/widgets/search_results.dart +++ b/lib/modules/tautulli/modules/search/widgets/search_results.dart @@ -30,14 +30,7 @@ class _State extends State { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliSearchSearchResults', - '_body', - 'Unable to fetch Tautulli search results', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli search results', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/statistics/route.dart b/lib/modules/tautulli/modules/statistics/route.dart index c0eb038e..658a71e5 100644 --- a/lib/modules/tautulli/modules/statistics/route.dart +++ b/lib/modules/tautulli/modules/statistics/route.dart @@ -72,14 +72,7 @@ class _State extends State<_TautulliStatisticsRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliStatisticsRoute', - '_body', - 'Unable to fetch Tautulli statistics', - snapshot.error, - StackTrace.current, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli statistics', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/statistics/widgets/media_tile.dart b/lib/modules/tautulli/modules/statistics/widgets/media_tile.dart index fab3acec..1169badf 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/media_tile.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/media_tile.dart @@ -90,7 +90,7 @@ class TautulliStatisticsMediaTile extends StatelessWidget { TextSpan(text: '\n'), data['total_duration'] != null ? TextSpan( - text: Duration(seconds: data['total_duration']).lsDuration_fullTimestamp(), + text: Duration(seconds: data['total_duration']).lunaTimestampWords, style: TextStyle( color: context.watch().statisticsType == TautulliStatsType.DURATION ? LunaColours.accent @@ -103,10 +103,7 @@ class TautulliStatisticsMediaTile extends StatelessWidget { : TextSpan(text: '${Constants.TEXT_EMDASH}'), TextSpan(text: '\n'), data['last_play'] != null - ? TextSpan(text: - 'Last Played ' - + DateTime.now().lsDateTime_ageString(DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)), - ) + ? TextSpan(text: 'Last Played ' + DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)?.lunaAge ?? 'Unknown') : TextSpan(text: Constants.TEXT_EMDASH) ], ), diff --git a/lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart b/lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart index 98e9f588..a8fc788f 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart @@ -79,7 +79,7 @@ class TautulliStatisticsPlatformTile extends StatelessWidget { TextSpan(text: '\n'), data['total_duration'] != null ? TextSpan( - text: Duration(seconds: data['total_duration']).lsDuration_fullTimestamp(), + text: Duration(seconds: data['total_duration']).lunaTimestampWords, style: TextStyle( color: context.watch().statisticsType == TautulliStatsType.DURATION ? LunaColours.accent @@ -92,10 +92,7 @@ class TautulliStatisticsPlatformTile extends StatelessWidget { : TextSpan(text: '${Constants.TEXT_EMDASH}'), TextSpan(text: '\n'), data['last_play'] != null - ? TextSpan(text: - 'Last Used ' - + DateTime.now().lsDateTime_ageString(DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)), - ) + ? TextSpan(text: 'Last Used ' + DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)?.lunaAge ?? 'Unknown') : TextSpan(text: Constants.TEXT_EMDASH) ], ), diff --git a/lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart b/lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart index 29f9a1b5..1753f101 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart @@ -81,10 +81,7 @@ class TautulliStatisticsRecentlyWatchedTile extends StatelessWidget { : TextSpan(text: '${Constants.TEXT_EMDASH}'), TextSpan(text: '\n'), data['last_watch'] != null - ? TextSpan(text: - 'Watched ' + - DateTime.now().lsDateTime_ageString(DateTime.fromMillisecondsSinceEpoch(data['last_watch']*1000)), - ) + ? TextSpan(text: 'Watched ' + DateTime.fromMillisecondsSinceEpoch(data['last_watch']*1000)?.lunaAge ?? 'Unknown') : TextSpan(text: Constants.TEXT_EMDASH) ], ), diff --git a/lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart b/lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart index 5c0fc8ce..1d375ec8 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart @@ -74,17 +74,14 @@ class TautulliStatisticsStreamTile extends StatelessWidget { TextSpan(text: '\n'), int.tryParse(data['started']) != null ? TextSpan( - text: LunaSeaDatabaseValue.USE_24_HOUR_TIME.data + text: LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat('yyyy-MM-dd HH:mm').format(DateTime.fromMillisecondsSinceEpoch(int.tryParse(data['started']) * 1000)) : DateFormat('yyyy-MM-dd hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(int.tryParse(data['started']) * 1000)), ) : TextSpan(text: '${Constants.TEXT_EMDASH}'), TextSpan(text: '\n'), data['last_play'] != null - ? TextSpan(text: - 'Last Used ' - + DateTime.now().lsDateTime_ageString(DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)), - ) + ? TextSpan(text: 'Last Used ' + DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)?.lunaAge ?? 'Unknown') : TextSpan(text: Constants.TEXT_EMDASH) ], ), @@ -92,4 +89,4 @@ class TautulliStatisticsStreamTile extends StatelessWidget { maxLines: 3, overflow: TextOverflow.fade, ); -} \ No newline at end of file +} diff --git a/lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart b/lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart index 2a535695..efb71753 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart @@ -7,7 +7,7 @@ class TautulliStatisticsTimeRangeButton extends StatelessWidget { Widget build(BuildContext context) => Selector( selector: (_, state) => state.statisticsTimeRange, builder: (context, range, _) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.access_time), diff --git a/lib/modules/tautulli/modules/statistics/widgets/type_button.dart b/lib/modules/tautulli/modules/statistics/widgets/type_button.dart index 0f989f42..ad54dc73 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/type_button.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/type_button.dart @@ -8,7 +8,7 @@ class TautulliStatisticsTypeButton extends StatelessWidget { Widget build(BuildContext context) => Selector( selector: (_, state) => state.statisticsType, builder: (context, type, _) => PopupMenuButton( - shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data + shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data ? LSRoundedShapeWithBorder() : LSRoundedShape(), icon: LSIcon(icon: Icons.merge_type), @@ -21,7 +21,7 @@ class TautulliStatisticsTypeButton extends StatelessWidget { (index) => PopupMenuItem( value: TautulliStatsType.values[index], child: Text( - TautulliStatsType.values[index].value.lsLanguage_Capitalize(), + TautulliStatsType.values[index].value.lunaCapitalizeFirstLetters(), style: TextStyle( fontSize: Constants.UI_FONT_SIZE_SUBTITLE, color: type == TautulliStatsType.values[index] diff --git a/lib/modules/tautulli/modules/statistics/widgets/user_tile.dart b/lib/modules/tautulli/modules/statistics/widgets/user_tile.dart index d6a41c9e..43c2a0b0 100644 --- a/lib/modules/tautulli/modules/statistics/widgets/user_tile.dart +++ b/lib/modules/tautulli/modules/statistics/widgets/user_tile.dart @@ -79,7 +79,7 @@ class TautulliStatisticsUserTile extends StatelessWidget { TextSpan(text: '\n'), data['total_duration'] != null ? TextSpan( - text: Duration(seconds: data['total_duration']).lsDuration_fullTimestamp(), + text: Duration(seconds: data['total_duration']).lunaTimestampWords, style: TextStyle( color: context.watch().statisticsType == TautulliStatsType.DURATION ? LunaColours.accent @@ -92,10 +92,7 @@ class TautulliStatisticsUserTile extends StatelessWidget { : TextSpan(text: '${Constants.TEXT_EMDASH}'), TextSpan(text: '\n'), data['last_play'] != null - ? TextSpan(text: - 'Last Streamed ' - + DateTime.now().lsDateTime_ageString(DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)), - ) + ? TextSpan(text: 'Last Streamed ' + DateTime.fromMillisecondsSinceEpoch(data['last_play']*1000)?.lunaAge ?? 'Unknown') : TextSpan(text: Constants.TEXT_EMDASH) ], ), diff --git a/lib/modules/tautulli/modules/synced_items/route.dart b/lib/modules/tautulli/modules/synced_items/route.dart index eea692b5..6b487112 100644 --- a/lib/modules/tautulli/modules/synced_items/route.dart +++ b/lib/modules/tautulli/modules/synced_items/route.dart @@ -68,14 +68,7 @@ class _State extends State<_TautulliSyncedItemsRoute> { builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliSyncedItemsRoute', - '_body', - 'Unable to fetch Tautulli synced items', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli synced items', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart b/lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart index ed3b525d..266c6e27 100644 --- a/lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart +++ b/lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart @@ -22,21 +22,21 @@ class TautulliSyncedItemTile extends StatelessWidget { ), children: [ TextSpan( - text: (syncedItem.state ?? 'Unknown').lsLanguage_Capitalize(), + text: (syncedItem.state ?? 'Unknown').lunaCapitalizeFirstLetters(), style: TextStyle( color: LunaColours.accent, fontWeight: FontWeight.w600, ), ), TextSpan(text: '\t${Constants.TEXT_EMDASH}\t'), - TextSpan(text: (syncedItem.metadataType ?? 'Unknown').lsLanguage_Capitalize()), + TextSpan(text: (syncedItem.metadataType ?? 'Unknown').lunaCapitalizeFirstLetters()), TextSpan(text: '\t${Constants.TEXT_BULLET}\t'), TextSpan(text: (syncedItem.itemCompleteCount ?? 0) == 1 ? '1 Item' : '${(syncedItem.itemCompleteCount ?? 0)} Items', ), TextSpan(text: '\t${Constants.TEXT_BULLET}\t'), - TextSpan(text: (syncedItem.totalSize ?? 0).lsBytes_BytesToString(decimals: 1)), + TextSpan(text: (syncedItem.totalSize ?? 0).lunaBytesToString(decimals: 1)), TextSpan(text: '\n'), TextSpan(text: syncedItem.user ?? 'Unknown User'), TextSpan(text: '\n'), diff --git a/lib/modules/tautulli/modules/tautulli/route.dart b/lib/modules/tautulli/modules/tautulli/route.dart index c5c150d0..1d8bcf51 100644 --- a/lib/modules/tautulli/modules/tautulli/route.dart +++ b/lib/modules/tautulli/modules/tautulli/route.dart @@ -43,7 +43,7 @@ class _State extends State<_TautulliHomeRoute> { Widget build(BuildContext context) => WillPopScope( onWillPop: _onWillPop, child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaSeaDatabaseValue.ENABLED_PROFILE.key ]), + valueListenable: Database.lunaSeaBox.listenable(keys: [ LunaDatabaseValue.ENABLED_PROFILE.key ]), builder: (context, box, _) => Scaffold( key: _scaffoldKey, drawer: _drawer, diff --git a/lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart b/lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart index e2e8e5e6..c436490f 100644 --- a/lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart +++ b/lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart @@ -18,11 +18,11 @@ class TautulliGlobalSettings extends StatelessWidget { case TautulliGlobalSettingsType.DELETE_CACHE: _deleteCache(context); break; case TautulliGlobalSettingsType.DELETE_IMAGE_CACHE: _deleteImageCache(context); break; case TautulliGlobalSettingsType.DELETE_TEMP_SESSIONS: _deleteTempSessions(context); break; - default: LunaLogger.warning('TautulliGlobalSettings', '_handler', 'Unknown case: ${(values[1] as TautulliGlobalSettings)}'); + default: LunaLogger().warning('TautulliGlobalSettings', '_handler', 'Unknown case: ${(values[1] as TautulliGlobalSettings)}'); } } - Future _webGUI(BuildContext context) async => context.read().host.lsLinks_OpenLink(); + Future _webGUI(BuildContext context) async => context.read().host.lunaOpenGenericLink(); Future _backupConfig(BuildContext context) async { context.read().api.system.backupConfig() @@ -32,14 +32,7 @@ class TautulliGlobalSettings extends StatelessWidget { message: 'Backing up your configuration in the background', )) .catchError((error, trace) { - LunaLogger.error( - 'Tautulli', - '_backupConfig', - 'Failed to backup configuration', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to backup configuration', error, trace); LSSnackBar( context: context, title: 'Failed to Backup Configuration', @@ -56,14 +49,7 @@ class TautulliGlobalSettings extends StatelessWidget { message: 'Backing up your database in the background', )) .catchError((error, trace) { - LunaLogger.error( - 'Tautulli', - '_backupDB', - 'Failed to backup database', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to backup database', error, trace); LSSnackBar( context: context, title: 'Failed to Backup Database', @@ -80,14 +66,7 @@ class TautulliGlobalSettings extends StatelessWidget { message: 'Tautulli cache is being deleted', )) .catchError((error, trace) { - LunaLogger.error( - 'Tautulli', - '_deleteCache', - 'Failed to delete cache', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to delete cache', error, trace); LSSnackBar( context: context, title: 'Failed to Delete Cache', @@ -104,14 +83,7 @@ class TautulliGlobalSettings extends StatelessWidget { message: 'Tautulli image cache is being deleted', )) .catchError((error, trace) { - LunaLogger.error( - 'Tautulli', - '_deleteImageCache', - 'Failed to delete image cache', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to delete image cache', error, trace); LSSnackBar( context: context, title: 'Failed to Delete Image Cache', @@ -128,14 +100,7 @@ class TautulliGlobalSettings extends StatelessWidget { message: 'Temporary sessions are being deleted', )) .catchError((error, trace) { - LunaLogger.error( - 'Tautulli', - '_deleteTempSessions', - 'Failed to delete temporary sessions', - error, - trace, - uploadToSentry: !(error is DioError), - ); + LunaLogger().error('Failed to delete temporary sessions', error, trace); LSSnackBar( context: context, title: 'Failed to Delete Temporary Sessions', diff --git a/lib/modules/tautulli/modules/users/route.dart b/lib/modules/tautulli/modules/users/route.dart index 395cbf55..2e12663a 100644 --- a/lib/modules/tautulli/modules/users/route.dart +++ b/lib/modules/tautulli/modules/users/route.dart @@ -50,14 +50,7 @@ class _State extends State with AutomaticKeepAliveClientMixi builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliUsersRoute', - '_body', - 'Unable to fetch Tautulli users', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli users', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/users/widgets/user_tile.dart b/lib/modules/tautulli/modules/users/widgets/user_tile.dart index 41bd85d9..00e830f7 100644 --- a/lib/modules/tautulli/modules/users/widgets/user_tile.dart +++ b/lib/modules/tautulli/modules/users/widgets/user_tile.dart @@ -78,7 +78,7 @@ class TautulliUserTile extends StatelessWidget { ), Expanded( child: LSSubtitle( - text: user.lastSeen != null ? DateTime.now().lsDateTime_ageString(user.lastSeen) : 'Never', + text: user.lastSeen?.lunaAge ?? 'Never', maxLines: 1, ), ), diff --git a/lib/modules/tautulli/modules/users_details/route.dart b/lib/modules/tautulli/modules/users_details/route.dart index c6062590..d7e999a8 100644 --- a/lib/modules/tautulli/modules/users_details/route.dart +++ b/lib/modules/tautulli/modules/users_details/route.dart @@ -94,14 +94,7 @@ class _State extends State<_TautulliUserDetailsRoute> { builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - '_TautulliUserDetailsRoute', - '_body', - 'Unable to pull Tautulli user table', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to pull Tautulli user table', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () => _refresh()); } diff --git a/lib/modules/tautulli/modules/users_details/widgets/history.dart b/lib/modules/tautulli/modules/users_details/widgets/history.dart index 547a900b..029506df 100644 --- a/lib/modules/tautulli/modules/users_details/widgets/history.dart +++ b/lib/modules/tautulli/modules/users_details/widgets/history.dart @@ -57,14 +57,7 @@ class _State extends State with AutomaticKeepAliveCl builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliUserDetailsHistory', - '_body', - 'Unable to fetch Tautulli user history: ${widget.user.userId}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli user history: ${widget.user.userId}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart b/lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart index f0ce888c..8561d212 100644 --- a/lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart +++ b/lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart @@ -54,14 +54,7 @@ class _State extends State with AutomaticKeepAli builder: (context, AsyncSnapshot snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliUserDetailsIPAddresses', - '_body', - 'Unable to fetch Tautulli user IP addresses: ${widget.user.userId}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli user IP addresses: ${widget.user.userId}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } @@ -94,7 +87,7 @@ class _State extends State with AutomaticKeepAli fontSize: Constants.UI_FONT_SIZE_SUBTITLE, ), children: [ - TextSpan(text: DateTime.now().lsDateTime_ageString(record.lastSeen)), + TextSpan(text: record.lastSeen?.lunaAge ?? 'Unknown'), TextSpan(text: '\t${Constants.TEXT_EMDASH}\t'), TextSpan(text: record.playCount == 1 ? '1 Play' diff --git a/lib/modules/tautulli/modules/users_details/widgets/profile.dart b/lib/modules/tautulli/modules/users_details/widgets/profile.dart index fbf9f337..bc76ca3f 100644 --- a/lib/modules/tautulli/modules/users_details/widgets/profile.dart +++ b/lib/modules/tautulli/modules/users_details/widgets/profile.dart @@ -79,14 +79,7 @@ class _State extends State with AutomaticKeepAliveCl builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliUserDetailsProfile', - '_body', - 'Unable to fetch Tautulli user: ${widget.user.userId}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli user: ${widget.user.userId}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } @@ -118,7 +111,7 @@ class _State extends State with AutomaticKeepAliveCl children: [ LSTableContent(title: 'email', body: user.email), LSTableContent(title: 'last seen', body: widget.user.lastSeen != null - ? DateTime.now().lsDateTime_ageString(widget.user.lastSeen) + ? widget.user.lastSeen?.lunaAge ?? 'Unknown' : 'Never', ), LSTableContent(title: '', body: ''), @@ -148,7 +141,7 @@ class _State extends State with AutomaticKeepAliveCl String _globalStatsContent(int plays, Duration duration) { String _plays = plays == 1 ? '1 Play': '$plays Plays'; - return '$_plays\n${duration.lsDuration_fullTimestamp()}'; + return '$_plays\n${duration.lunaTimestampWords}'; } List _playerStats(List player) => [ diff --git a/lib/modules/tautulli/modules/users_details/widgets/synced_items.dart b/lib/modules/tautulli/modules/users_details/widgets/synced_items.dart index 7d635272..6b874e3a 100644 --- a/lib/modules/tautulli/modules/users_details/widgets/synced_items.dart +++ b/lib/modules/tautulli/modules/users_details/widgets/synced_items.dart @@ -54,14 +54,7 @@ class _State extends State with AutomaticKeepAli builder: (context, AsyncSnapshot> snapshot) { if(snapshot.hasError) { if(snapshot.connectionState != ConnectionState.waiting) { - LunaLogger.error( - 'TautulliUserDetailsSyncedItems', - '_body', - 'Unable to fetch Tautulli user synced items: ${widget.user.userId}', - snapshot.error, - null, - uploadToSentry: !(snapshot.error is DioError), - ); + LunaLogger().error('Unable to fetch Tautulli user synced items: ${widget.user.userId}', snapshot.error, StackTrace.current); } return LSErrorMessage(onTapHandler: () async => _refreshKey.currentState.show()); } diff --git a/lib/modules/wake_on_lan/core/api.dart b/lib/modules/wake_on_lan/core/api.dart index 2f51c585..1ad7d089 100644 --- a/lib/modules/wake_on_lan/core/api.dart +++ b/lib/modules/wake_on_lan/core/api.dart @@ -1,28 +1,13 @@ import 'package:lunasea/core.dart'; import 'package:wake_on_lan/wake_on_lan.dart'; -class WakeOnLANAPI extends API { +class WakeOnLANAPI { final Map _values; WakeOnLANAPI._internal(this._values); factory WakeOnLANAPI.from(ProfileHiveObject profile) => WakeOnLANAPI._internal(profile.getWakeOnLAN()); - - void logWarning(String methodName, String text) => LunaLogger.warning( - 'package:lunasea/modules/wake_on_lan/core/api.dart', - methodName, - 'Wake on LAN: $text', - ); - void logError(String methodName, String text, Object error, StackTrace trace, { - bool uploadToSentry = true, - }) => LunaLogger.error( - 'package:lunasea/modules/wake_on_lan/core/api.dart', - methodName, - 'Wake on LAN: $text', - error, - trace, - uploadToSentry: uploadToSentry, - ); + void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Wake on LAN: $text', error, trace); bool get enabled => _values['enabled']; String get broadcastAddress => _values['broadcastAddress']; @@ -37,7 +22,7 @@ class WakeOnLANAPI extends API { await WakeOnLAN.from(ipv4, mac).wake(); return true; } catch (error, stack) { - logError('wake', 'Failed to wake machine', error, stack); + logError('Failed to wake machine', error, stack); return Future.error(error); } } diff --git a/lib/modules/wake_on_lan/core/constants.dart b/lib/modules/wake_on_lan/core/constants.dart index 7424b9f4..21354201 100644 --- a/lib/modules/wake_on_lan/core/constants.dart +++ b/lib/modules/wake_on_lan/core/constants.dart @@ -6,12 +6,15 @@ class WakeOnLANConstants { static const MODULE_KEY = 'wake_on_lan'; - static const LunaModuleMap MODULE_MAP = LunaModuleMap( + static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata( name: 'Wake on LAN', description: 'Wake a Sleeping Machine', settingsDescription: 'Configure Wake on LAN', + helpMessage: 'Wake on LAN is an industry standard protocol for waking computers up from a very low power mode remotely by sending a specially constructed packet to the machine.', icon: Icons.settings_remote, route: null, color: Color(LunaColours.ACCENT_COLOR), + website: '', + github: '', ); } diff --git a/pubspec.lock b/pubspec.lock index 074dee5d..d757a686 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "12.0.0" + version: "14.0.0" analyzer: dependency: "direct dev" description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.40.6" + version: "0.41.1" archive: dependency: transitive description: @@ -49,21 +49,21 @@ packages: name: badges url: "https://pub.dartlang.org" source: hosted - version: "1.1.4" + version: "1.1.6" build: dependency: transitive description: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.4" + version: "0.4.5" build_daemon: dependency: transitive description: @@ -77,21 +77,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.4.4" + version: "1.5.1" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.10.7" + version: "1.10.11" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.2" + version: "6.1.5" built_collection: dependency: transitive description: @@ -126,7 +126,7 @@ packages: name: checked_yaml url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.4" cli_util: dependency: transitive description: @@ -141,6 +141,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.4" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+2" code_builder: dependency: transitive description: @@ -176,6 +197,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + dapackages: + dependency: "direct dev" + description: + name: dapackages + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" dart_style: dependency: transitive description: @@ -245,7 +273,70 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted + version: "2.1.4" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + url: "https://pub.dartlang.org" + source: hosted + version: "0.18.4+1" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2+3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.3" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + url: "https://pub.dartlang.org" + source: hosted version: "2.1.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+1" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+1" fixnum: dependency: transitive description: @@ -259,21 +350,7 @@ packages: name: fl_chart url: "https://pub.dartlang.org" source: hosted - version: "0.12.0" - flare_dart: - dependency: transitive - description: - name: flare_dart - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.4" - flare_flutter: - dependency: "direct main" - description: - name: flare_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" + version: "0.12.1" flash: dependency: "direct main" description: @@ -328,13 +405,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.5.0" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - url: "https://pub.dartlang.org" - source: hosted - version: "0.19.1" flutter_web_plugins: dependency: transitive description: flutter @@ -584,7 +654,7 @@ packages: name: percent_indicator url: "https://pub.dartlang.org" source: hosted - version: "2.1.8" + version: "2.1.9" permission_handler: dependency: "direct main" description: @@ -661,7 +731,7 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "0.1.5" + version: "0.1.7" quick_actions: dependency: "direct main" description: @@ -689,14 +759,21 @@ packages: name: sembast url: "https://pub.dartlang.org" source: hosted - version: "2.4.8" + version: "2.4.8+1" sentry: - dependency: "direct main" + dependency: transitive description: name: sentry url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "4.0.1" + sentry_flutter: + dependency: "direct main" + description: + name: sentry_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" share: dependency: "direct main" description: @@ -724,7 +801,7 @@ packages: name: simple_gesture_detector url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" sky_engine: dependency: transitive description: flutter @@ -736,14 +813,14 @@ packages: name: sonarr url: "https://pub.dartlang.org" source: hosted - version: "0.0.5" + version: "1.0.1" source_gen: dependency: transitive description: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "0.9.9" + version: "0.9.10+1" source_span: dependency: transitive description: @@ -792,14 +869,14 @@ packages: name: table_calendar url: "https://pub.dartlang.org" source: hosted - version: "2.3.1" + version: "2.3.3" tautulli: dependency: "direct main" description: name: tautulli url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.0+2" term_glyph: dependency: transitive description: @@ -820,7 +897,7 @@ packages: name: timing url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+2" + version: "0.1.1+3" tuple: dependency: "direct main" description: @@ -877,13 +954,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" - usage: - dependency: transitive - description: - name: usage - url: "https://pub.dartlang.org" - source: hosted - version: "3.4.2" uuid: dependency: "direct main" description: @@ -911,7 +981,7 @@ packages: name: wake_on_lan url: "https://pub.dartlang.org" source: hosted - version: "1.1.1+2" + version: "1.1.1+4" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c8e05259..3745084b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,54 +1,57 @@ name: lunasea -description: Self Hosted Manager -version: 4.1.1+40101001 +description: Self-Hosted Controller +version: 4.2.0+40200001 publish_to: 'none' environment: sdk: ">=2.7.0 <3.0.0" dependencies: - badges: ^1.1.4 + badges: ^1.1.6 + cloud_firestore: ^0.14.4 convert: ^2.1.1 dio: ^3.0.10 encrypt: ^4.1.0 expandable: ^4.1.4 - file_picker: ^2.1.0 - flare_flutter: ^2.0.6 + file_picker: ^2.1.4 + firebase_core: ^0.5.3 + firebase_auth: ^0.18.4+1 + firebase_storage: ^5.2.0 flash: ^1.3.1 fluro: ^1.7.8 flutter: sdk: flutter flutter_spinkit: ^4.1.2+1 flutter_sticky_header: ^0.5.0 - flutter_svg: ^0.19.1 - fl_chart: ^0.12.0 + fl_chart: ^0.12.1 f_logs: ^1.3.0-alpha-02 - google_nav_bar: ^3.0.1 + google_nav_bar: ^3.1.0 hive: ^1.4.4+1 hive_flutter: ^0.3.1 in_app_purchase: ^0.3.4+16 intl: ^0.16.1 package_info: ^0.4.3+2 path_provider: ^1.6.24 - percent_indicator: ^2.1.8 + percent_indicator: ^2.1.9 permission_handler: ^5.0.1+1 provider: ^4.3.2+3 quick_actions: ^0.4.0+10 - sentry: ^3.0.1 + sentry_flutter: ^4.0.1 share: ^0.6.5+4 stack_trace: ^1.9.6 - table_calendar: ^2.3.1 + table_calendar: ^2.3.3 tuple: ^1.0.3 url_launcher: ^5.7.10 uuid: ^2.2.2 xml_parser: ^0.1.2 # Comet.Tools Packages - sonarr: ^0.0.5 - tautulli: ^1.1.0 - wake_on_lan: ^1.1.1+2 + sonarr: ^1.0.1 + tautulli: ^1.1.0+2 + wake_on_lan: ^1.1.1+4 dev_dependencies: - analyzer: ^0.40.6 - build_runner: ^1.10.4 + analyzer: ^0.41.1 + build_runner: ^1.10.11 + dapackages: ^1.4.0 flutter_launcher_icons: ^0.8.1 flutter_native_splash: ^0.1.9 hive_generator: ^0.8.2 @@ -68,8 +71,6 @@ flutter: - assets/images/services/ # Branding - assets/branding/ - # Rive Animations - - assets/rive/coding.flr fonts: - family: CustomIcons fonts: @@ -90,3 +91,4 @@ scripts: build: flutter packages pub run build_runner build --delete-conflicting-outputs icons: flutter pub run flutter_launcher_icons:main splash: flutter pub pub run flutter_native_splash:create + outdated: flutter pub pub run dapackages:dapackages.dart pubspec.yaml \ No newline at end of file diff --git a/scripts/build_aab b/scripts/build_aab new file mode 100755 index 00000000..611d51d7 --- /dev/null +++ b/scripts/build_aab @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +cd .. +# Remove old builds +rm -rf output/*.aab +# Clean and build +flutter clean +flutter build appbundle +# Copy APKs to root of project +mkdir -p output +cp build/app/outputs/bundle/release/app-release.aab ./LunaSea-release.aab +# Remove build files +rm -rf build diff --git a/scripts/build_apk b/scripts/build_apk index ab2b43f9..234a44e5 100755 --- a/scripts/build_apk +++ b/scripts/build_apk @@ -2,15 +2,14 @@ cd .. # Remove old builds -rm -rf ./LunaSea-arm64-v8a-release.apk -rm -rf ./LunaSea-armeabi-v7a-release.apk -rm -rf ./LunaSea-x86_64-release.apk +rm -rf output/*.apk # Clean and build flutter clean flutter build apk --split-per-abi # Copy APKs to root of project -cp ./build/app/outputs/apk/release/app-armeabi-v7a-release.apk ./LunaSea-armeabi-v7a-release.apk -cp ./build/app/outputs/apk/release/app-arm64-v8a-release.apk ./LunaSea-arm64-v8a-release.apk -cp ./build/app/outputs/apk/release/app-x86_64-release.apk ./LunaSea-x86_64-release.apk +mkdir -p output +cp build/app/outputs/apk/release/app-armeabi-v7a-release.apk output/LunaSea-armeabi-v7a-release.apk +cp build/app/outputs/apk/release/app-arm64-v8a-release.apk output/LunaSea-arm64-v8a-release.apk +cp build/app/outputs/apk/release/app-x86_64-release.apk output/LunaSea-x86_64-release.apk # Remove build files -rm -rf ./build +rm -rf build diff --git a/scripts/build_ipa b/scripts/build_ipa index def7625a..868d5efa 100755 --- a/scripts/build_ipa +++ b/scripts/build_ipa @@ -2,17 +2,18 @@ cd .. # Remove old builds -rm -rf ./LunaSea.ipa +rm -rf output/*.ipa # Clean and build flutter clean flutter build ios # Export build to IPA cd ios -xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath ./build/Runner.xcarchive -xcodebuild -exportArchive -archivePath ./build/Runner.xcarchive -exportOptionsPlist ./ExportOptions.plist -exportPath ./build/Runner.ipa +xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath build/LunaSea.xcarchive +xcodebuild -exportArchive -archivePath build/LunaSea.xcarchive -exportOptionsPlist ExportOptions.plist -exportPath build/LunaSea.app # Copy IPA to root of project cd .. -cp ios/build/Runner.ipa/Runner.ipa ./LunaSea.ipa +mkdir -p output +cp ios/build/LunaSea.app/LunaSea.ipa output/LunaSea.ipa # Remove build files rm -rf build rm -rf build ios/build diff --git a/scripts/build_release b/scripts/build_release deleted file mode 100755 index 0286da10..00000000 --- a/scripts/build_release +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -cd .. -# Remove old builds -flutter clean -# Prepare iOS -flutter build ios -# Build Android -flutter build appbundle -cp build/app/outputs/bundle/release/app-release.aab ./lunasea.aab