diff --git a/.blackbox/blackbox-files.txt b/.blackbox/blackbox-files.txt index dad57f33..b97b9b52 100644 --- a/.blackbox/blackbox-files.txt +++ b/.blackbox/blackbox-files.txt @@ -1,6 +1,5 @@ android/app/google-services.json android/key.jks android/key.properties -ios/ExportOptions.plist ios/GoogleService-Info.plist macos/GoogleService-Info.plist diff --git a/.gitignore b/.gitignore index 1e0203f5..aa634a27 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,6 @@ coverage/ /android/key.properties /android/app/google-services.json /android/key.jks -/ios/ExportOptions.plist /ios/GoogleService-Info.plist /macos/GoogleService-Info.plist /vendor/font_awesome_flutter/lib/fonts/fa-thin-100.ttf diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b76f0ef..27c65a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,41 @@ # LunaSea Changelog -## WIP +## v6.0.0 (60000004) #### NEW -- `[Settings]` (Configuration) Show dismissable banner with module information and links +- `[Dashboard]` (Calendar) Display content poster and additional information +- `[Firebase]` (Analytics) Completely removed Analytics and Crashlytics +- `[Settings]` Show dismissable blocks with information and links +- `[Lidarr]` (Add Artist) Show artist poster in result list +- `[Lidarr]` (Add Artist) Allow user to select monitoring options +- `[Lidarr]` (Add Artist) Removed toggle to use album folders +- `[Lidarr]` (Catalogue) Show artist poster in catalogue list +- `[Lidarr]` (Missing) Display album covers +- `[Sonarr]` (Catalogue) Previous airing sorting option +- `[Sonarr]` (Season List) Display season posters - `[Sonarr]` (Queue) Option to set queue page size +- `[UI/UX]` (Tile Blocks) Allow overflowing title or body lines to be horizontally scrolled + +#### TWEAKS + +- `[UI/UX]` (Drawer) Heavily reduced the size of the header area +- `[UI/UX]` (Fonts) Normalized font sizes across the UI +- `[UI/UX]` (Images) Set the default image opacity to 20% (custom set values are uneffected) +- `[UI/UX]` (Tile Blocks) Heavily improved build and memory performance of tiles +- `[UI/UX]` (Tile Blocks) Improved consistency in trailing icon/text size +- `[UI/UX]` (Tile Blocks) Improved consistency of tile height, content spacing, and padding +- `[UI/UX]` (Tile Blocks) Natively draw placeholder posters for movies, series, albums, etc. +- `[UI/UX]` (Tile Blocks) Fade-in background image loads - `[UI/UX]` (Tooltips) Added styling to match LunaSea theme #### FIXES +- `[Dashboard]` (Calendar) Lidarr entries would not appear - `[Dashboard]` (Modules) Unbranded icons could use incorrect colours -- `[UI/UX]` (List Tiles) Fixed consistency of trailing icon/text size +- `[Lidarr]` (Albums) List would fail to load on nightly Lidarr builds +- `[Sonarr]` (Catalogue) Missing filter would fail to load when using Sonarr v4 in some cases +- `[Sonarr]` (Images) Images now use v3 API path --- diff --git a/android/app/build.gradle b/android/app/build.gradle index 840510f9..8db01e1a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -22,9 +22,8 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.google.firebase.crashlytics' +apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def keystoreProperties = new Properties() @@ -34,20 +33,25 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 30 + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } sourceSets { main.java.srcDirs += 'src/main/kotlin' } - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { applicationId "app.lunasea.lunasea" minSdkVersion 24 - targetSdkVersion 30 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 38c9ab0b..b1cb1e93 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ @@ -17,6 +17,7 @@ android:value="true" /> + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png index 8a030a0a..39250ea0 100644 Binary files a/android/app/src/main/res/drawable-xhdpi/splash.png and b/android/app/src/main/res/drawable-xhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png index b266cbb7..da363ea7 100644 Binary files a/android/app/src/main/res/drawable-xxhdpi/splash.png and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png index 659d69e0..7ab31d44 100644 Binary files a/android/app/src/main/res/drawable-xxxhdpi/splash.png and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png new file mode 100644 index 00000000..055f39c2 Binary files /dev/null and b/android/app/src/main/res/drawable/background.png differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 8c09435e..3fe6b2e8 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,16 +1,9 @@ - - - - - - + - - + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..08fc20ff --- /dev/null +++ b/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 70053942..549a77bc 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -5,6 +5,7 @@ @drawable/launch_background + false - teamID - INSERT-YOUR-TEAMID-HERE - - thinning - <none> - - diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 18df01e6..da67a8d1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - cloud_firestore (3.1.0): + - cloud_firestore (3.1.5): - Firebase/Firestore (= 8.9.0) - firebase_core - Flutter @@ -37,19 +37,11 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter - - Firebase/Analytics (8.9.0): - - Firebase/Core - Firebase/Auth (8.9.0): - Firebase/CoreOnly - FirebaseAuth (~> 8.9.0) - - Firebase/Core (8.9.0): - - Firebase/CoreOnly - - FirebaseAnalytics (~> 8.9.0) - Firebase/CoreOnly (8.9.0): - FirebaseCore (= 8.9.0) - - Firebase/Crashlytics (8.9.0): - - Firebase/CoreOnly - - FirebaseCrashlytics (~> 8.9.0) - Firebase/Firestore (8.9.0): - Firebase/CoreOnly - FirebaseFirestore (~> 8.9.0) @@ -59,47 +51,21 @@ PODS: - Firebase/Storage (8.9.0): - Firebase/CoreOnly - FirebaseStorage (~> 8.9.0) - - firebase_analytics (8.3.4): - - Firebase/Analytics (= 8.9.0) - - firebase_core - - Flutter - - firebase_auth (3.2.0): + - firebase_auth (3.3.4): - Firebase/Auth (= 8.9.0) - firebase_core - Flutter - - firebase_core (1.10.0): + - firebase_core (1.10.6): - Firebase/CoreOnly (= 8.9.0) - Flutter - - firebase_crashlytics (2.3.0): - - Firebase/Crashlytics (= 8.9.0) - - firebase_core - - Flutter - - firebase_messaging (11.1.0): + - firebase_messaging (11.2.4): - Firebase/Messaging (= 8.9.0) - firebase_core - Flutter - - firebase_storage (10.1.0): + - firebase_storage (10.2.4): - Firebase/Storage (= 8.9.0) - firebase_core - Flutter - - FirebaseAnalytics (8.9.1): - - FirebaseAnalytics/AdIdSupport (= 8.9.1) - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.6) - - GoogleUtilities/MethodSwizzler (~> 7.6) - - GoogleUtilities/Network (~> 7.6) - - "GoogleUtilities/NSData+zlib (~> 7.6)" - - nanopb (~> 2.30908.0) - - FirebaseAnalytics/AdIdSupport (8.9.1): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleAppMeasurement (= 8.9.1) - - GoogleUtilities/AppDelegateSwizzler (~> 7.6) - - GoogleUtilities/MethodSwizzler (~> 7.6) - - GoogleUtilities/Network (~> 7.6) - - "GoogleUtilities/NSData+zlib (~> 7.6)" - - nanopb (~> 2.30908.0) - FirebaseAuth (8.9.0): - FirebaseCore (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.6) @@ -109,18 +75,11 @@ PODS: - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - - FirebaseCoreDiagnostics (8.9.0): + - FirebaseCoreDiagnostics (8.10.0): - GoogleDataTransport (~> 9.1) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - nanopb (~> 2.30908.0) - - FirebaseCrashlytics (8.9.0): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleDataTransport (~> 9.1) - - GoogleUtilities/Environment (~> 7.6) - - nanopb (~> 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - FirebaseFirestore (8.9.0): - FirebaseFirestore/AutodetectLeveldb (= 8.9.0) - FirebaseFirestore/AutodetectLeveldb (8.9.0): @@ -129,7 +88,7 @@ PODS: - FirebaseFirestore/Base (8.9.0) - FirebaseFirestore/WithLeveldb (8.9.0): - FirebaseFirestore/Base - - FirebaseInstallations (8.9.0): + - FirebaseInstallations (8.10.0): - FirebaseCore (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/UserDefaults (~> 7.6) @@ -147,26 +106,6 @@ PODS: - FirebaseCore (~> 8.0) - GTMSessionFetcher/Core (~> 1.5) - Flutter (1.0.0) - - GoogleAppMeasurement (8.9.1): - - GoogleAppMeasurement/AdIdSupport (= 8.9.1) - - GoogleUtilities/AppDelegateSwizzler (~> 7.6) - - GoogleUtilities/MethodSwizzler (~> 7.6) - - GoogleUtilities/Network (~> 7.6) - - "GoogleUtilities/NSData+zlib (~> 7.6)" - - nanopb (~> 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (8.9.1): - - GoogleAppMeasurement/WithoutAdIdSupport (= 8.9.1) - - GoogleUtilities/AppDelegateSwizzler (~> 7.6) - - GoogleUtilities/MethodSwizzler (~> 7.6) - - GoogleUtilities/Network (~> 7.6) - - "GoogleUtilities/NSData+zlib (~> 7.6)" - - nanopb (~> 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (8.9.1): - - GoogleUtilities/AppDelegateSwizzler (~> 7.6) - - GoogleUtilities/MethodSwizzler (~> 7.6) - - GoogleUtilities/Network (~> 7.6) - - "GoogleUtilities/NSData+zlib (~> 7.6)" - - nanopb (~> 2.30908.0) - GoogleDataTransport (9.1.2): - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30908.0) @@ -214,7 +153,7 @@ PODS: - nanopb/encode (2.30908.0) - package_info_plus (0.4.5): - Flutter - - path_provider (0.0.1): + - path_provider_ios (0.0.1): - Flutter - PromisesObjC (2.0.0) - quick_actions (0.0.1): @@ -224,20 +163,18 @@ PODS: - SDWebImage/Core (5.12.1) - share_plus (0.0.1): - Flutter - - shared_preferences (0.0.1): + - shared_preferences_ios (0.0.1): - Flutter - - SwiftyGif (5.4.0) - - url_launcher (0.0.1): + - SwiftyGif (5.4.2) + - url_launcher_ios (0.0.1): - Flutter DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Firebase/Messaging - - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.9.0`) @@ -245,26 +182,23 @@ DEPENDENCIES: - GoogleUtilities - in_app_purchase_ios (from `.symlinks/plugins/in_app_purchase_ios/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - quick_actions (from `.symlinks/plugins/quick_actions/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: trunk: - DKImagePickerController - DKPhotoGallery - Firebase - - FirebaseAnalytics - FirebaseAuth - FirebaseCore - FirebaseCoreDiagnostics - - FirebaseCrashlytics - FirebaseInstallations - FirebaseMessaging - FirebaseStorage - - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities - GTMSessionFetcher @@ -278,14 +212,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/cloud_firestore/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" - firebase_analytics: - :path: ".symlinks/plugins/firebase_analytics/ios" firebase_auth: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" - firebase_crashlytics: - :path: ".symlinks/plugins/firebase_crashlytics/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" firebase_storage: @@ -299,16 +229,16 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/in_app_purchase_ios/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" quick_actions: :path: ".symlinks/plugins/quick_actions/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" CHECKOUT OPTIONS: FirebaseFirestore: @@ -316,43 +246,38 @@ CHECKOUT OPTIONS: :tag: 8.9.0 SPEC CHECKSUMS: - cloud_firestore: 2cff0618e560f435fc05da5c94c2d213eb7ba640 + cloud_firestore: 338122a6149c14ca376ad363626b5bf3037a5056 DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Firebase: 13d8d96499e2635428d5bf0ec675df21f95d9a95 - firebase_analytics: 7a7528bb2abf4ca22cff7a57bbf909dcab73e13d - firebase_auth: 0d56cc4d105ff70d1547a7fcb10ee574ad05adf5 - firebase_core: f770e033e790657b3505f04be4cb24c482912f11 - firebase_crashlytics: ed8c1d66dc407d617cbed798f05a5d9115da7494 - firebase_messaging: 0c8d1a1732487db7f332fb65232053e93201e2fb - firebase_storage: a3bbe618ef41930ae31da8f4691d37e4ed84f14d - FirebaseAnalytics: 4ab446ce08a3fe52e8a4303dd997cf26276bf968 + firebase_auth: e875a50b81aefb20f305dfede425647e9675c19e + firebase_core: c263d7daf1dc92fcd9895e6abdc04872b0ee07ff + firebase_messaging: dff5cd08781ee1de988565a83c977e435405cd7e + firebase_storage: 2ed331c6bd5bfa6d64f1a6c57f01151f4f146c16 FirebaseAuth: 2b78b2a32c07b3ecfa4970bdf1d3632b8304099b FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b - FirebaseCoreDiagnostics: 5daa63f1c1409d981a2d5007daa100b36eac6a34 - FirebaseCrashlytics: 40efbd81157dae307ec95612fa1328347284d2c2 + FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18 FirebaseFirestore: 7c22bff6c0e1f222920cd110e9da13a117b61fea - FirebaseInstallations: caa7c8e0d3e2345b8829d2fa9ca1b4dfbf2fcc85 + FirebaseInstallations: 830327b45345ffc859eaa9c17bcd5ae893fd5425 FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316 FirebaseStorage: 452c98c31ccb40b819764bf3039426c4388d9939 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - GoogleAppMeasurement: 837649ad3987936c232f6717c5680216f6243d24 GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237 GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91 in_app_purchase_ios: 04ed55fdd1012db54095d6801335b5f5de0a475f nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider: d1e9807085df1f9cc9318206cd649dc0b76be3de + path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 quick_actions: cd83314083fa994182e6cd9e7516167a215a4f83 SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences: 5033afbb22d372e15aff8ff766df9021b845f273 - SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 - url_launcher: b6e016d912f04be9f5bf6e8e82dc599b7ba59649 + shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32 + SwiftyGif: dec758a9dd3d278e5a855dbf279bf062c923c387 + url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af PODFILE CHECKSUM: 6140814a61b9571bc8e1daa6ec9af34598f6e610 -COCOAPODS: 1.10.2 +COCOAPODS: 1.11.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 46922840..0fc20306 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,15 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 1EA81D57F09988D75AC476F5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */; }; + 22ABA27907009C6D67E17F02 /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 7585ADE8DCB47DC8753EF72A /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -19,6 +18,7 @@ 9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */; }; 9C7092B325E77E470056AE0B /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7092B225E77E470056AE0B /* NotificationService.m */; }; 9C7092B725E77E470056AE0B /* ImageNotification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9C7092AF25E77E470056AE0B /* ImageNotification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E81187C204448290256D1292 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525B84ABAED8CC10DD278134 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,14 +58,13 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 1F2EFFABEACE166F53F7025F /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = ""; }; - 2FC45D8C03585F29FFEEBAD5 /* 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 = ""; }; + 1AEC3393F733CF63CC471C78 /* 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 50F2C55D9F5DC33EBDADB9C4 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = ""; }; + 525B84ABAED8CC10DD278134 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 76B7822037A027527BE9DFD3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -73,7 +72,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; @@ -83,9 +81,11 @@ 9C7092B125E77E470056AE0B /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = ""; }; 9C7092B225E77E470056AE0B /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = ""; }; 9C7092B425E77E470056AE0B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D94DB394AEFBDD2422D8B950 /* 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 = ""; }; - EC88C989B98F6E7BDEBE5FE5 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = ""; }; + AB50C11FF4EFC5D9961D1031 /* 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 = ""; }; + ACE755908B540FD8855E4D89 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + C041C2ED656F3E1D27D9E228 /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = ""; }; + C9A446482226EB46FF708884 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = ""; }; + FE066D193740042A640E4E50 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -94,7 +94,7 @@ buildActionMask = 2147483647; files = ( 9C2991242453FE3100B93AB2 /* StoreKit.framework in Frameworks */, - 1EA81D57F09988D75AC476F5 /* Pods_Runner.framework in Frameworks */, + E81187C204448290256D1292 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,7 +102,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7585ADE8DCB47DC8753EF72A /* Pods_ImageNotification.framework in Frameworks */, + 22ABA27907009C6D67E17F02 /* Pods_ImageNotification.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -182,8 +182,8 @@ isa = PBXGroup; children = ( 9C2991232453FE3100B93AB2 /* StoreKit.framework */, - 989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */, - AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */, + 7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */, + 525B84ABAED8CC10DD278134 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -191,12 +191,12 @@ F5D05D221249C73674C5DB0D /* Pods */ = { isa = PBXGroup; children = ( - 1F2EFFABEACE166F53F7025F /* Pods-ImageNotification.debug.xcconfig */, - EC88C989B98F6E7BDEBE5FE5 /* Pods-ImageNotification.release.xcconfig */, - 50F2C55D9F5DC33EBDADB9C4 /* Pods-ImageNotification.profile.xcconfig */, - 2FC45D8C03585F29FFEEBAD5 /* Pods-Runner.debug.xcconfig */, - 76B7822037A027527BE9DFD3 /* Pods-Runner.release.xcconfig */, - D94DB394AEFBDD2422D8B950 /* Pods-Runner.profile.xcconfig */, + C041C2ED656F3E1D27D9E228 /* Pods-ImageNotification.debug.xcconfig */, + FE066D193740042A640E4E50 /* Pods-ImageNotification.release.xcconfig */, + C9A446482226EB46FF708884 /* Pods-ImageNotification.profile.xcconfig */, + AB50C11FF4EFC5D9961D1031 /* Pods-Runner.debug.xcconfig */, + ACE755908B540FD8855E4D89 /* Pods-Runner.release.xcconfig */, + 1AEC3393F733CF63CC471C78 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -208,7 +208,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - B7F9B35AFB424DACC3F5FE98 /* [CP] Check Pods Manifest.lock */, + 3485969F7F1D5F22AA9F93DD /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -216,9 +216,8 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 9C7092B825E77E470056AE0B /* Embed App Extensions */, - 9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */, - 6D0F2A4EFEF187992207F8A9 /* [CP] Embed Pods Frameworks */, - 002D86FC0B93B0FCE4CC4735 /* [CP] Copy Pods Resources */, + FEF0F5B439D2594CBDB93362 /* [CP] Embed Pods Frameworks */, + 71292F5AA25ED2C228C6EFFB /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -234,7 +233,7 @@ isa = PBXNativeTarget; buildConfigurationList = 9C7092BC25E77E470056AE0B /* Build configuration list for PBXNativeTarget "ImageNotification" */; buildPhases = ( - 9C183B3B1872F82EEDC35FDC /* [CP] Check Pods Manifest.lock */, + 5FBBFCF3DA2647B52FD6E1AE /* [CP] Check Pods Manifest.lock */, 9C7092AB25E77E470056AE0B /* Sources */, 9C7092AC25E77E470056AE0B /* Frameworks */, 9C7092AD25E77E470056AE0B /* Resources */, @@ -260,11 +259,13 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = VPH33JQH4R; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; 9C7092AE25E77E470056AE0B = { CreatedOnToolsVersion = 12.4; + DevelopmentTeam = VPH33JQH4R; ProvisioningStyle = Automatic; }; }; @@ -311,22 +312,26 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 002D86FC0B93B0FCE4CC4735 /* [CP] Copy Pods Resources */ = { + 3485969F7F1D5F22AA9F93DD /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_ROOT}/FirebaseFirestore/FirebaseFirestore/Resources/gRPCCertificates-Cpp.bundle", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { @@ -343,85 +348,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6D0F2A4EFEF187992207F8A9 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", - "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", - "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", - "${BUILT_PRODUCTS_DIR}/in_app_purchase_ios/in_app_purchase_ios.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", - "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", - "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", - "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", - "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.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}/SwiftyGif.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - 9C183B3B1872F82EEDC35FDC /* [CP] Check Pods Manifest.lock */ = { + 5FBBFCF3DA2647B52FD6E1AE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -443,46 +370,98 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */ = { + 71292F5AA25ED2C228C6EFFB /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", - ); - name = "Firebase Crashlytics"; - outputFileListPaths = ( + "${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}/FirebaseCrashlytics/run\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; }; - B7F9B35AFB424DACC3F5FE98 /* [CP] Check Pods Manifest.lock */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( ); + name = "Run Script"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + }; + FEF0F5B439D2594CBDB93362 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", + "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", + "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", + "${BUILT_PRODUCTS_DIR}/in_app_purchase_ios/in_app_purchase_ios.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", + "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", + "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.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}/SwiftyGif.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -720,8 +699,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 0437245c..70693e4a 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -3,14 +3,11 @@ import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - var flutter_native_splash = 1 - UIApplication.shared.isStatusBarHidden = false - - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } } diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 00000000..9f447e1b --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 00000000..055f39c2 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index 0bedcf2f..00cabce8 100644 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { - "idiom" : "universal", "filename" : "LaunchImage.png", + "idiom" : "universal", "scale" : "1x" }, { - "idiom" : "universal", "filename" : "LaunchImage@2x.png", + "idiom" : "universal", "scale" : "2x" }, { - "idiom" : "universal", "filename" : "LaunchImage@3x.png", + "idiom" : "universal", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } } diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png index 359d176f..cf9fcccf 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png index 308b22c4..39250ea0 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png index 659d69e0..da363ea7 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard index 52eee7b7..4dc9a375 100644 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -16,13 +16,19 @@ - - + + - - + + + + + + + + @@ -32,6 +38,7 @@ - + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a68ff852..0c57d3d8 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -60,7 +60,7 @@ UIMainStoryboardFile Main UIStatusBarHidden - + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/lib/core.dart b/lib/core.dart index ee7a2dc9..42df5087 100644 --- a/lib/core.dart +++ b/lib/core.dart @@ -1,6 +1,7 @@ // Packages export 'package:easy_localization/easy_localization.dart'; export 'package:expandable/expandable.dart'; +export 'package:fading_edge_scrollview/fading_edge_scrollview.dart'; export 'package:flash/flash.dart'; export 'package:fluro/fluro.dart'; export 'package:hive/hive.dart'; @@ -27,4 +28,3 @@ export 'core/state.dart'; export 'core/system.dart'; export 'core/ui.dart'; export 'core/utilities.dart'; -export 'core/widgets.dart'; diff --git a/lib/core/database/luna_database.dart b/lib/core/database/luna_database.dart index 34fdd188..62e4f82b 100644 --- a/lib/core/database/luna_database.dart +++ b/lib/core/database/luna_database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -6,8 +5,6 @@ enum LunaDatabaseValue { DRAWER_AUTOMATIC_MANAGE, DRAWER_MANUAL_ORDER, ENABLED_PROFILE, - ENABLE_FIREBASE_ANALYTICS, - ENABLE_FIREBASE_CRASHLYTICS, THEME_AMOLED, THEME_AMOLED_BORDER, THEME_IMAGE_BACKGROUND_OPACITY, @@ -22,6 +19,7 @@ enum LunaDatabaseValue { QUICK_ACTIONS_TAUTULLI, QUICK_ACTIONS_SEARCH, USE_24_HOUR_TIME, + DEFAULT_LAUNCH_MODULE, } class LunaDatabase extends LunaModuleDatabase { @@ -47,6 +45,10 @@ class LunaDatabase extends LunaModuleDatabase { data[value.key] = (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).key; break; + case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE: + data[value.key] = + (LunaDatabaseValue.DEFAULT_LAUNCH_MODULE.data as LunaModule).key; + break; case LunaDatabaseValue.DRAWER_MANUAL_ORDER: data[value.key] = LunaDrawer.moduleOrderedList() ?.map((module) => module.key) @@ -71,6 +73,9 @@ class LunaDatabase extends LunaModuleDatabase { case LunaDatabaseValue.SELECTED_BROWSER: value.put(LunaBrowser.APPLE_SAFARI.fromKey(config[key])); break; + case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE: + value.put(LunaModule.DASHBOARD.fromKey(config[key])); + break; case LunaDatabaseValue.DRAWER_MANUAL_ORDER: value.put( (config[key] as List) @@ -98,7 +103,7 @@ class LunaDatabase extends LunaModuleDatabase { extension LunaDatabaseValueExtension on LunaDatabaseValue { String get key { if (this == LunaDatabaseValue.ENABLED_PROFILE) return 'profile'; - return 'LUNASEA_${describeEnum(this)}'; + return 'LUNASEA_${this.name}'; } dynamic get data { @@ -136,10 +141,6 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue { return value is bool; case LunaDatabaseValue.DRAWER_MANUAL_ORDER: return value is List; - case LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS: - return value is bool; - case LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS: - return value is bool; case LunaDatabaseValue.THEME_AMOLED: return value is bool; case LunaDatabaseValue.THEME_AMOLED_BORDER: @@ -168,6 +169,8 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue { return value is bool; case LunaDatabaseValue.USE_24_HOUR_TIME: return value is bool; + case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE: + return value is LunaModule; } throw Exception('Invalid LunaDatabaseValue'); } @@ -187,11 +190,7 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue { case LunaDatabaseValue.THEME_AMOLED_BORDER: return false; case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: - return 10; - case LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS: - return true; - case LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS: - return true; + return 20; case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: return false; case LunaDatabaseValue.QUICK_ACTIONS_RADARR: @@ -212,6 +211,8 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue { return false; case LunaDatabaseValue.USE_24_HOUR_TIME: return false; + case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE: + return LunaModule.DASHBOARD; } throw Exception('Invalid LunaDatabaseValue'); } diff --git a/lib/core/extensions/scroll_controller.dart b/lib/core/extensions/scroll_controller.dart index f0abf757..6c4348e8 100644 --- a/lib/core/extensions/scroll_controller.dart +++ b/lib/core/extensions/scroll_controller.dart @@ -8,7 +8,7 @@ extension ScrollControllerExtension on ScrollController { this.animateTo( 0.00, duration: const Duration(milliseconds: LunaUI.ANIMATION_SPEED * 2), - curve: Curves.decelerate, + curve: Curves.easeInOutQuart, ); } } diff --git a/lib/core/extensions/string.dart b/lib/core/extensions/string.dart index 5c4c8c4b..2f0fac30 100644 --- a/lib/core/extensions/string.dart +++ b/lib/core/extensions/string.dart @@ -1,3 +1,4 @@ +import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -103,4 +104,15 @@ extension StringLinksExtension on String { /// 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'); + + Future copyToClipboard({ + bool showSnackBar = true, + }) async { + await Clipboard.setData(ClipboardData(text: this)); + if (showSnackBar) + showLunaSuccessSnackBar( + title: 'Copied', + message: 'Copied content to the clipboard', + ); + } } diff --git a/lib/core/firebase.dart b/lib/core/firebase.dart index 8d2203e2..2b0b0877 100644 --- a/lib/core/firebase.dart +++ b/lib/core/firebase.dart @@ -1,7 +1,5 @@ -export 'firebase/analytics.dart'; export 'firebase/auth.dart'; export 'firebase/core.dart'; -export 'firebase/crashlytics.dart'; export 'firebase/firestore.dart'; export 'firebase/messaging.dart'; export 'firebase/storage.dart'; diff --git a/lib/core/firebase/analytics.dart b/lib/core/firebase/analytics.dart deleted file mode 100644 index 48371891..00000000 --- a/lib/core/firebase/analytics.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'dart:io'; - -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_analytics/observer.dart'; -import 'package:lunasea/core.dart'; - -class LunaFirebaseAnalytics { - /// Returns true if Firebase Analytics is compatible with this build system/platform. - static bool get isPlatformCompatible { - return LunaFirebase.isPlatformCompatible && - !Platform.isLinux && - !Platform.isMacOS && - !Platform.isWindows; - } - - /// Returns an instance of [FirebaseAnalytics]. - /// - /// Throws an error if [LunaFirebase.initialize] has not been called. - static FirebaseAnalytics get instance => FirebaseAnalytics(); - - /// Returns an instance of [FirebaseAnalyticsObserver]. - /// - /// Throws an error if [LunaFirebase.initialize] has not been called. - static FirebaseAnalyticsObserver get observer => - FirebaseAnalyticsObserver(analytics: instance); - - /// Set the enabled state of Firebase Analytics. - /// - /// If `enabled` is set to false, force-disables Analytics. - void setEnabledState() { - if (isPlatformCompatible) { - bool state = LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.data; - instance.setAnalyticsCollectionEnabled(state); - } - } - - /// Log an "app_open" event. - Future appOpened() async { - if (isPlatformCompatible) instance.logAppOpen(); - } -} diff --git a/lib/core/firebase/core.dart b/lib/core/firebase/core.dart index 1c80674b..ca3a40d4 100644 --- a/lib/core/firebase/core.dart +++ b/lib/core/firebase/core.dart @@ -1,13 +1,7 @@ import 'dart:io'; - import 'package:firebase_core/firebase_core.dart'; class LunaFirebase { - /// Return an instance of [FirebaseApp]. - /// - /// Throws an error if [LunaFirebase.initialize] has not been called. - static FirebaseApp get instance => Firebase.app(); - /// Returns true if Firebase in its entirety is compatible with this build system/platform. static bool get isPlatformCompatible => Platform.isIOS || Platform.isAndroid || Platform.isMacOS; @@ -15,6 +9,5 @@ class LunaFirebase { /// Initialize Firebase and configuration. /// /// This must be called before anything accesses Firebase services, or an exception will be thrown. - Future initialize({String name}) async => - await Firebase.initializeApp(); + Future initialize() async => await Firebase.initializeApp(); } diff --git a/lib/core/firebase/crashlytics.dart b/lib/core/firebase/crashlytics.dart deleted file mode 100644 index bd33151c..00000000 --- a/lib/core/firebase/crashlytics.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:io'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:flutter/foundation.dart'; -import 'package:lunasea/core.dart'; - -class LunaFirebaseCrashlytics { - /// Returns true if Firebase Analytics is compatible with this build system/platform. - static bool get isPlatformCompatible { - return LunaFirebase.isPlatformCompatible && - !Platform.isLinux && - !Platform.isMacOS && - !Platform.isWindows; - } - - /// Returns an instance of [FirebaseCrashlytics]. - /// - /// Throws an error if [LunaFirebase.initialize] has not been called. - static FirebaseCrashlytics get instance => FirebaseCrashlytics.instance; - - /// Set the enabled state of Firebase Crashlytics. - /// - /// If `enabled` is set to false, force-disables Analytics. - void setEnabledState() { - if (isPlatformCompatible) { - bool state = - kReleaseMode && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data; - instance.setCrashlyticsCollectionEnabled(state); - } - } -} diff --git a/lib/core/firebase/firebase_options.dart.gpg b/lib/core/firebase/firebase_options.dart.gpg new file mode 100644 index 00000000..25c0051e Binary files /dev/null and b/lib/core/firebase/firebase_options.dart.gpg differ diff --git a/lib/core/firebase/messaging.dart b/lib/core/firebase/messaging.dart index 2faeae5b..f34453a1 100644 --- a/lib/core/firebase/messaging.dart +++ b/lib/core/firebase/messaging.dart @@ -67,7 +67,7 @@ class LunaFirebaseMessaging { /// Return a [StreamSubscription] that will show a notification banner on a newly received notification. /// /// This listens on [FirebaseMessaging.onMessage], where the application must be open and in the foreground. - StreamSubscription onMessageListener() { + StreamSubscription registerOnMessageListener() { return onMessage.listen((message) { if (message == null) return; LunaModule module = (message.data ?? {}).isNotEmpty @@ -88,7 +88,7 @@ class LunaFirebaseMessaging { /// Returns a [StreamSubscription] that will handle messages/notifications that are opened while LunaSea is running in the background. /// /// This listens on [FirebaseMessaging.onMessageOpenedApp], where the application must be open but in the background. - StreamSubscription onMessageOpenedAppListener() => + StreamSubscription registerOnMessageOpenedAppListener() => onMessageOpenedApp.listen(_handleWebhook); /// Check to see if there was an initial [RemoteMessage] available to be accessed. diff --git a/lib/core/modules.dart b/lib/core/modules.dart index 232a7b7c..2d956fad 100644 --- a/lib/core/modules.dart +++ b/lib/core/modules.dart @@ -533,7 +533,7 @@ extension LunaModuleExtension on LunaModule { case LunaModule.WAKE_ON_LAN: return '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.'; case LunaModule.EXTERNAL_MODULES: - return 'LunaSea allows you to add additional modules that are not currently supported directly within the application, allowing you to open the module\'s web GUI without having to leave LunaSea!'; + return 'LunaSea allows you to add links to additional modules that are not currently supported allowing you to open the module\'s web GUI without having to leave LunaSea!'; } throw Exception('Invalid LunaModule'); } diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart index 78f8018c..aa8a3751 100644 --- a/lib/core/router/router.dart +++ b/lib/core/router/router.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; +import 'package:lunasea/main.dart'; import 'package:lunasea/modules.dart'; class LunaRouter { @@ -16,6 +17,12 @@ class LunaRouter { router.notFoundHandler = Handler( handlerFunc: (context, params) => LunaInvalidRoute(), ); + router.define( + '/', + handler: Handler(handlerFunc: (context, params) => const LunaOS()), + transitionType: transitionType, + ); + // Module routers DashboardRouter().defineAllRoutes(router); ExternalModulesRouter().defineAllRoutes(router); OverseerrRouter().defineAllRoutes(router); diff --git a/lib/core/system/desktop_window.dart b/lib/core/system/desktop_window.dart index a4dba3f4..dcd2df70 100644 --- a/lib/core/system/desktop_window.dart +++ b/lib/core/system/desktop_window.dart @@ -1,19 +1,27 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:window_size/window_size.dart' as window_size; +import 'package:bitsdojo_window/bitsdojo_window.dart'; class LunaDesktopWindow { static const double _MINIMUM_WINDOW_SIZE = 400; + static const double _INITIAL_WINDOW_SIZE = 700; + static bool get isPlatformCompatible => Platform.isWindows || Platform.isMacOS || Platform.isLinux; - /// Initialize the Desktop window: - /// - Sets the minimum window size void initialize() { if (isPlatformCompatible) { - window_size.setWindowMinSize( - const Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE), - ); + doWhenWindowReady(() { + const Size minSize = Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE); + const Size size = Size(_INITIAL_WINDOW_SIZE, _INITIAL_WINDOW_SIZE); + + final win = appWindow; + win.minSize = minSize; + win.size = size; + win.alignment = Alignment.center; + win.title = "LunaSea"; + win.show(); + }); } } } diff --git a/lib/core/system/profile.dart b/lib/core/system/profile.dart index c1245e58..f95f0e0c 100644 --- a/lib/core/system/profile.dart +++ b/lib/core/system/profile.dart @@ -6,7 +6,7 @@ class LunaProfile { List profilesList() => Database.profilesBox.keys .map((profile) => profile as String) .toList() - ..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); + ..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); /// Safely change profiles. /// diff --git a/lib/core/ui.dart b/lib/core/ui.dart index fab61243..07ac7a17 100644 --- a/lib/core/ui.dart +++ b/lib/core/ui.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; export 'ui/appbar.dart'; export 'ui/banner.dart'; +export 'ui/block.dart'; export 'ui/bottom_bar.dart'; export 'ui/bottom_modal_sheet.dart'; export 'ui/button.dart'; @@ -18,6 +19,7 @@ export 'ui/header.dart'; export 'ui/highlighted_node.dart'; export 'ui/icons.dart'; export 'ui/input_bar.dart'; +export 'ui/linear_indicator.dart'; export 'ui/list_tile.dart'; export 'ui/list_view.dart'; export 'ui/loader.dart'; @@ -30,6 +32,7 @@ export 'ui/shape.dart'; export 'ui/snackbar.dart'; export 'ui/switch.dart'; export 'ui/table.dart'; +export 'ui/text_span.dart'; export 'ui/text.dart'; export 'ui/theme.dart'; export 'ui/will_pop_scope.dart'; @@ -40,37 +43,69 @@ class LunaUI { static const String TEXT_ARROW_LEFT = '←'; static const String TEXT_ARROW_RIGHT = '→'; static const String TEXT_BULLET = '•'; - static const String TEXT_OBFUSCATED_PASSWORD = '••••••••••'; + static const String TEXT_OBFUSCATED_PASSWORD = '••••••••••••'; static const String TEXT_ELLIPSIS = '…'; static const String TEXT_EMDASH = '—'; - // Font-Related - static const double FONT_SIZE_APP_BAR = 18.0; - static const double FONT_SIZE_BUTTON = 14.0; - static const double FONT_SIZE_GRAPH_LEGEND = 10.0; - static const double FONT_SIZE_HEADER = 18.0; - static const double FONT_SIZE_HIGHLIGHTED_NODE = 12.0; - static const double FONT_SIZE_MESSAGES = 16.0; - static const double FONT_SIZE_NAVIGATION_BAR = 13.0; - static const double FONT_SIZE_SUBHEADER = 12.0; - static const double FONT_SIZE_SUBTITLE = 13.0; - static const double FONT_SIZE_TITLE = 16.0; - static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600; - // UI-Related + + // <--> Font Sizes + static const double FONT_SIZE_H1 = 18.0; + static const double FONT_SIZE_H2 = 16.0; + static const double FONT_SIZE_H3 = 14.0; + static const double FONT_SIZE_H4 = 12.0; + static const double FONT_SIZE_H5 = 10.0; + + // <--> Font Size Mappings + static const double FONT_SIZE_BUTTON = FONT_SIZE_H3; + static const double FONT_SIZE_GRAPH_LEGEND = FONT_SIZE_H5; + static const double FONT_SIZE_HEADER = FONT_SIZE_H2; + static const double FONT_SIZE_MESSAGES = FONT_SIZE_H2; + static const double FONT_SIZE_SUBHEADER = FONT_SIZE_H3; + static const double FONT_SIZE_SUBTITLE = FONT_SIZE_H3; + static const double FONT_SIZE_TITLE = FONT_SIZE_H2; + + // <--> Icons + static const double ICON_SIZE = 24.0; + + // <--> Other static const int ANIMATION_SPEED = 250; - static const int ANIMATION_IMAGE_FADE_IN_SPEED = 125; + static const int ANIMATION_SPEED_IMAGES = 175; static const double BORDER_RADIUS = 10.0; - static const double BUTTON_BACKGROUND_OPACITY = 0.80; + static const double OPACITY_DIMMED = 0.75; + static const double OPACITY_DISABLED = 0.50; + static const double OPACITY_SPLASH = 0.25; static const double ELEVATION = 0.0; + static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600; + + // <--> Margins static const double DEFAULT_MARGIN_SIZE = 12.0; + static const double MARGIN_SIZE_HALF = DEFAULT_MARGIN_SIZE / 2; + static const EdgeInsets MARGIN_DEFAULT = EdgeInsets.all(DEFAULT_MARGIN_SIZE); - static const EdgeInsets MARGIN_CARD = EdgeInsets.symmetric( - horizontal: DEFAULT_MARGIN_SIZE, - vertical: DEFAULT_MARGIN_SIZE / 2, - ); - // Border-Related - static bool get shouldUseBorder => - LunaTheme.isAMOLEDTheme && LunaTheme.useAMOLEDBorders; - static ShapeBorder get shapeBorder => shouldUseBorder - ? LunaShapeBorder.roundedWithBorder() - : LunaShapeBorder.rounded(); + static const EdgeInsets MARGIN_HALF = EdgeInsets.all(MARGIN_SIZE_HALF); + + static const EdgeInsets MARGIN_DEFAULT_HORIZONTAL = + EdgeInsets.symmetric(horizontal: DEFAULT_MARGIN_SIZE); + static const EdgeInsets MARGIN_DEFAULT_VERTICAL = + EdgeInsets.symmetric(vertical: DEFAULT_MARGIN_SIZE); + + static const EdgeInsets MARGIN_HALF_HORIZONTAL = + EdgeInsets.symmetric(horizontal: MARGIN_SIZE_HALF); + static const EdgeInsets MARGIN_HALF_VERTICAL = + EdgeInsets.symmetric(vertical: MARGIN_SIZE_HALF); + + static const EdgeInsets MARGIN_H_DEFAULT_V_HALF = EdgeInsets.symmetric( + horizontal: DEFAULT_MARGIN_SIZE, vertical: MARGIN_SIZE_HALF); + static const EdgeInsets MARGIN_H_HALF_V_DEFAULT = EdgeInsets.symmetric( + horizontal: MARGIN_SIZE_HALF, vertical: DEFAULT_MARGIN_SIZE); + + // <--> Borders + static bool get shouldUseBorder { + return LunaTheme.isAMOLEDTheme && LunaTheme.useAMOLEDBorders; + } + + static ShapeBorder get shapeBorder { + return shouldUseBorder + ? LunaShapeBorder.roundedWithBorder() + : LunaShapeBorder.rounded(); + } } diff --git a/lib/core/ui/appbar.dart b/lib/core/ui/appbar.dart index 5a2b5566..aad777d6 100644 --- a/lib/core/ui/appbar.dart +++ b/lib/core/ui/appbar.dart @@ -22,11 +22,12 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { final List profiles; final PageController pageController; final List scrollControllers; + final Color backgroundColor; @override Size get preferredSize { double _size = (height ?? APPBAR_HEIGHT); - if (bottom != null) _size += bottom.preferredSize.height; + if (bottom != null) _size += bottom.preferredSize.height - 0.0; return Size.fromHeight(_size); } @@ -42,6 +43,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { this.pageController, this.scrollControllers, this.hideLeading = false, + this.backgroundColor, }); /// Create a new [AppBar] widget pre-styled for LunaSea. @@ -58,6 +60,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { bool useDrawer = false, bool hideLeading = false, PageController pageController, + Color backgroundColor, List scrollControllers, }) { assert(title != null); @@ -72,6 +75,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { pageController: pageController, scrollControllers: scrollControllers, hideLeading: hideLeading, + backgroundColor: backgroundColor, type: _APPBAR_TYPE.DEFAULT, ); } @@ -86,6 +90,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { @required double height, EdgeInsets padding = LunaTextInputBar.appBarMargin, Alignment alignment = Alignment.topCenter, + Color backgroundColor, }) { assert(child != null); assert(height != null); @@ -98,6 +103,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { ), height: height, useDrawer: false, + backgroundColor: backgroundColor, type: _APPBAR_TYPE.EMPTY, ); } @@ -117,6 +123,8 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { List actions, PageController pageController, List scrollControllers, + PreferredSizeWidget bottom, + Color backgroundColor, }) { assert(title != null); assert(profiles != null); @@ -129,18 +137,22 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget { actions: actions, useDrawer: useDrawer, hideLeading: hideLeading, + bottom: bottom, pageController: pageController, scrollControllers: scrollControllers, + backgroundColor: backgroundColor, type: _APPBAR_TYPE.DEFAULT, ); return LunaAppBar._internal( title: title, profiles: profiles, actions: actions, + bottom: bottom, useDrawer: useDrawer, hideLeading: hideLeading, pageController: pageController, scrollControllers: scrollControllers, + backgroundColor: backgroundColor, type: _APPBAR_TYPE.DROPDOWN, ); } @@ -210,38 +222,36 @@ class _State extends State { Widget _sharedLeading(BuildContext context) { if (widget.hideLeading ?? false) return null; if (widget.useDrawer) - return IconButton( - icon: const Icon(Icons.menu_rounded), - onPressed: () async { - HapticFeedback.lightImpact(); - if (Scaffold.of(context).hasDrawer) { - Scaffold.of(context).openDrawer(); - FocusManager.instance.primaryFocus?.unfocus(); - } - }, + return SizedBox( + child: LunaIconButton.appBar( + icon: Icons.menu_rounded, + onPressed: () async { + HapticFeedback.lightImpact(); + if (Scaffold.of(context).hasDrawer) { + Scaffold.of(context).openDrawer(); + FocusManager.instance.primaryFocus?.unfocus(); + } + }, + ), + height: kToolbarHeight, ); - return InkWell( - child: const Center( - child: Icon(Icons.arrow_back_ios_new_rounded), + return SizedBox( + child: LunaIconButton.appBar( + icon: Icons.arrow_back_ios_new_rounded, + onPressed: () async => Navigator.of(context).lunaSafetyPop(), + onLongPress: () async => Navigator.of(context).lunaPopToFirst(), ), - onTap: () async { - HapticFeedback.lightImpact(); - Navigator.of(context).lunaSafetyPop(); - }, - onLongPress: () async { - HapticFeedback.heavyImpact(); - Navigator.of(context).lunaPopToFirst(); - }, - borderRadius: BorderRadius.circular(28.0), + height: kToolbarHeight, ); } Widget _default(BuildContext context) { return AppBar( + backgroundColor: widget.backgroundColor, title: Text( widget.title ?? '', overflow: TextOverflow.fade, - style: const TextStyle(fontSize: LunaUI.FONT_SIZE_APP_BAR), + style: const TextStyle(fontSize: LunaUI.FONT_SIZE_H1), ), leading: _sharedLeading(context), automaticallyImplyLeading: !(widget.hideLeading ?? false), @@ -254,6 +264,7 @@ class _State extends State { Widget _empty(BuildContext context) { return AppBar( + backgroundColor: widget.backgroundColor, automaticallyImplyLeading: false, toolbarHeight: widget.height, leadingWidth: 0.0, @@ -265,15 +276,24 @@ class _State extends State { Widget _dropdown(BuildContext context) { return AppBar( + backgroundColor: widget.backgroundColor, + automaticallyImplyLeading: !(widget.hideLeading ?? false), title: LunaPopupMenuButton( tooltip: 'Change Profiles', - child: Wrap( - direction: Axis.horizontal, - crossAxisAlignment: WrapCrossAlignment.center, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - widget.title, - style: const TextStyle(fontSize: LunaUI.FONT_SIZE_HEADER), + Flexible( + child: FadingEdgeScrollView.fromSingleChildScrollView( + child: SingleChildScrollView( + controller: ScrollController(), + scrollDirection: Axis.horizontal, + child: Text( + widget.title, + style: const TextStyle(fontSize: LunaUI.FONT_SIZE_H1), + ), + ), + ), ), const Icon(Icons.arrow_drop_down_rounded), ], @@ -290,7 +310,7 @@ class _State extends State { child: Text( profile, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: (LunaDatabaseValue.ENABLED_PROFILE.data ?? 'default') == profile @@ -306,6 +326,7 @@ class _State extends State { centerTitle: false, elevation: 0, actions: widget.actions, + bottom: widget.bottom, ); } } diff --git a/lib/core/ui/banner.dart b/lib/core/ui/banner.dart index 166c8c3f..3271192d 100644 --- a/lib/core/ui/banner.dart +++ b/lib/core/ui/banner.dart @@ -23,7 +23,7 @@ class LunaBanner extends StatelessWidget { this.iconColor = LunaColours.accent, this.backgroundColor, this.headerColor = Colors.white, - this.bodyColor = Colors.white70, + this.bodyColor = LunaColours.grey, this.buttons, }) : super(key: key); @@ -32,12 +32,13 @@ class LunaBanner extends StatelessWidget { return LunaCard( context: context, child: Container( - padding: EdgeInsets.symmetric(vertical: LunaUI.MARGIN_CARD.top), + padding: + EdgeInsets.symmetric(vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.top), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: LunaUI.MARGIN_CARD, + padding: LunaUI.MARGIN_H_DEFAULT_V_HALF, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -73,7 +74,7 @@ class LunaBanner extends StatelessWidget { ), if (bodyText?.isNotEmpty ?? false) Padding( - padding: LunaUI.MARGIN_CARD.copyWith(top: 0), + padding: LunaUI.MARGIN_H_DEFAULT_V_HALF.copyWith(top: 0), child: LunaText.subtitle( text: bodyText.toString(), color: bodyColor, @@ -84,7 +85,7 @@ class LunaBanner extends StatelessWidget { if (buttons?.isNotEmpty ?? false) LunaButtonContainer( padding: EdgeInsets.symmetric( - horizontal: LunaUI.MARGIN_CARD.left / 2), + horizontal: LunaUI.MARGIN_H_DEFAULT_V_HALF.left / 2), children: buttons, ), ], diff --git a/lib/core/ui/block.dart b/lib/core/ui/block.dart new file mode 100644 index 00000000..bc7ff0f5 --- /dev/null +++ b/lib/core/ui/block.dart @@ -0,0 +1,302 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:transparent_image/transparent_image.dart'; + +class LunaBlock extends StatelessWidget { + static const TITLE_HEIGHT = LunaUI.FONT_SIZE_H2 + 4.0; + static const SUBTITLE_HEIGHT = LunaUI.FONT_SIZE_H3 + 4.0; + + final bool disabled; + final String title; + final Color titleColor; + + /// If defined, only takes the first body subtitle from the [body] array + /// And allows that single [TextSpan] to overflow to this many lines + final int customBodyMaxLines; + final List body; + + /// Icons that lead the body lines. If defined, then the length of this list + /// must match the length of [body]. If a specific line does not need a + /// leading icon, pass in null. + final List bodyLeadingIcons; + final Color bodyLeadingIconsColor; + + final Widget leading; + final Widget trailing; + final Widget bottom; + final double bottomHeight; + + final Function onTap; + final Function onLongPress; + + final IconData posterPlaceholderIcon; + final String posterUrl; + final Map posterHeaders; + final bool posterIsSquare; + final String backgroundUrl; + final Map backgroundHeaders; + + const LunaBlock({ + Key key, + this.disabled = false, + @required this.title, + this.titleColor = Colors.white, + this.body, + this.bodyLeadingIcons, + this.bodyLeadingIconsColor = LunaColours.accent, + this.bottom, + this.bottomHeight = SUBTITLE_HEIGHT, + this.customBodyMaxLines, + this.posterPlaceholderIcon, + this.posterUrl, + this.posterHeaders = const {}, + this.posterIsSquare = false, + this.backgroundUrl, + this.backgroundHeaders = const {}, + this.onTap, + this.onLongPress, + this.leading, + this.trailing, + }) : super(key: key); + + static double calculateItemExtent( + int subtitleLines, { + bool hasBottom = false, + double bottomHeight = SUBTITLE_HEIGHT, + }) { + double height = calculateItemHeight( + subtitleLines, + hasBottom: hasBottom, + bottomHeight: bottomHeight, + ); + return height + LunaUI.MARGIN_H_DEFAULT_V_HALF.vertical; + } + + static double calculateItemHeight( + int subtitleLines, { + bool hasBottom = false, + double bottomHeight = SUBTITLE_HEIGHT, + }) { + double height = (LunaUI.DEFAULT_MARGIN_SIZE * 2) + TITLE_HEIGHT; + height += subtitleLines * SUBTITLE_HEIGHT; + if (hasBottom) height += bottomHeight; + return height; + } + + double _calculateHeight() { + int _scalar = customBodyMaxLines; + _scalar ??= body?.length ?? 0; + return calculateItemHeight( + _scalar, + hasBottom: bottom != null, + bottomHeight: bottomHeight, + ); + } + + @override + Widget build(BuildContext context) { + double _height = _calculateHeight(); + return LunaCard( + context: context, + child: InkWell( + child: Stack( + children: [ + if (backgroundUrl?.isNotEmpty ?? false) + _fadeInBackground(context, _height), + Opacity( + opacity: disabled ? LunaUI.OPACITY_DISABLED : 1.0, + child: Row( + children: [ + _poster(context, _height), + _tile(context, _height), + ], + ), + ), + ], + ), + mouseCursor: onTap != null || onLongPress != null + ? SystemMouseCursors.click + : MouseCursor.defer, + onTap: onTap, + onLongPress: onLongPress, + ), + height: _height, + ); + } + + Widget _fadeInBackground(BuildContext context, double _height) { + int _percent = LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int; + if (_percent == 0) return const SizedBox(height: 0, width: 0); + + double _opacity = _percent / 100; + if (disabled) _opacity *= LunaUI.OPACITY_DISABLED; + + return Opacity( + opacity: _opacity, + child: FadeInImage( + placeholder: MemoryImage(kTransparentImage), + height: _height, + width: MediaQuery.of(context).size.width, + fadeInDuration: const Duration( + milliseconds: LunaUI.ANIMATION_SPEED_IMAGES, + ), + fit: BoxFit.cover, + image: NetworkImage( + backgroundUrl, + headers: backgroundHeaders?.cast(), + ), + imageErrorBuilder: (context, error, stack) => SizedBox( + height: _height, + width: MediaQuery.of(context).size.width, + ), + ), + ); + } + + Widget _poster(BuildContext context, double height) { + if (posterUrl == null && posterPlaceholderIcon == null) { + return const SizedBox(width: 0.0, height: 0.0); + } + + double _dimension = height - LunaUI.DEFAULT_MARGIN_SIZE; + return Padding( + padding: const EdgeInsets.only(left: LunaUI.DEFAULT_MARGIN_SIZE / 2), + child: LunaNetworkImage( + context: context, + url: posterUrl ?? '', + headers: posterHeaders, + placeholderIcon: posterPlaceholderIcon, + height: _dimension, + width: _dimension / (posterIsSquare ? 1.0 : 1.5), + ), + ); + } + + Widget _tile(BuildContext context, double height) { + return Expanded( + // ignore: deprecated_member_use_from_same_package + child: LunaListTile( + context: context, + title: _scrollableText( + child: LunaText.title( + text: title, + color: titleColor, + overflow: TextOverflow.visible, + maxLines: 1, + ), + ), + subtitle: _subtitle(), + color: Colors.transparent, + margin: EdgeInsets.zero, + drawBorder: false, + height: height, + trailing: trailing, + leading: leading, + ), + ); + } + + Widget _subtitle() { + int maxLines = customBodyMaxLines ?? 1; + double height = SUBTITLE_HEIGHT * maxLines; + + if (bodyLeadingIcons != null) { + assert( + bodyLeadingIcons.length == body?.length, + 'bodyLeadingIcons and body should be the same size', + ); + } + + Widget _wrapper(List children) { + return Expanded( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ); + } + + Widget _entry(TextSpan textSpan, IconData icon) { + return SizedBox( + height: height, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (icon != null) + Padding( + child: Container( + child: Icon( + icon, + color: bodyLeadingIconsColor, + size: LunaUI.FONT_SIZE_H2, + ), + height: SUBTITLE_HEIGHT * maxLines, + width: SUBTITLE_HEIGHT, + alignment: Alignment.centerLeft, + ), + padding: const EdgeInsets.only( + right: LunaUI.DEFAULT_MARGIN_SIZE / 4, + ), + ), + Expanded( + child: _scrollableText( + scrollDirection: maxLines > 1 ? Axis.vertical : Axis.horizontal, + child: Container( + child: RichText( + text: TextSpan( + style: const TextStyle( + fontSize: LunaUI.FONT_SIZE_H3, + color: LunaColours.grey, + ), + children: [textSpan], + ), + overflow: TextOverflow.ellipsis, + softWrap: maxLines == 1 ? false : true, + maxLines: maxLines, + ), + alignment: Alignment.centerLeft, + ), + ), + ), + ], + ), + ); + } + + List _children = []; + + if (body?.isNotEmpty ?? false) { + if (customBodyMaxLines != null) { + _children.add(_entry(body[0], bodyLeadingIcons?.elementAtOrNull(0))); + } else { + for (int i = 0; i < body.length; i++) { + _children.add(_entry(body[i], bodyLeadingIcons?.elementAtOrNull(i))); + } + } + } + + if (bottom != null) { + _children.add(SizedBox( + height: bottomHeight, + child: bottom, + )); + } + + return _children.isEmpty ? null : _wrapper(_children); + } + + Widget _scrollableText({ + @required Widget child, + Axis scrollDirection = Axis.horizontal, + }) { + return FadingEdgeScrollView.fromSingleChildScrollView( + child: SingleChildScrollView( + controller: ScrollController(), + scrollDirection: scrollDirection, + child: child, + ), + ); + } +} diff --git a/lib/core/ui/bottom_bar.dart b/lib/core/ui/bottom_bar.dart index 77a8f2e6..6c1f5685 100644 --- a/lib/core/ui/bottom_bar.dart +++ b/lib/core/ui/bottom_bar.dart @@ -1,3 +1,4 @@ +export 'bottom_bar/action_bar_card.dart'; export 'bottom_bar/action_bar.dart'; export 'bottom_bar/badge.dart'; export 'bottom_bar/navigation_bar.dart'; diff --git a/lib/core/widgets/action_bar_card.dart b/lib/core/ui/bottom_bar/action_bar_card.dart similarity index 77% rename from lib/core/widgets/action_bar_card.dart rename to lib/core/ui/bottom_bar/action_bar_card.dart index 80608b42..cd63a1d2 100644 --- a/lib/core/widgets/action_bar_card.dart +++ b/lib/core/ui/bottom_bar/action_bar_card.dart @@ -21,7 +21,7 @@ class LunaActionBarCard extends StatelessWidget { this.onLongPress, this.backgroundColor, this.color = LunaColours.accent, - this.icon = Icons.arrow_forward_ios_rounded, + this.icon = LunaIcons.ARROW_RIGHT, this.checkboxState, this.checkboxOnChanged, }) : super(key: key); @@ -59,7 +59,7 @@ class LunaActionBarCard extends StatelessWidget { overflow: TextOverflow.fade, style: const TextStyle( fontSize: LunaUI.FONT_SIZE_SUBHEADER, - color: Colors.white70, + color: LunaColours.grey, ), ), ], @@ -77,7 +77,7 @@ class LunaActionBarCard extends StatelessWidget { ), ), ), - if (icon != null) + if (checkboxState == null && icon != null) Container( width: 30.0, alignment: Alignment.centerRight, @@ -96,26 +96,38 @@ class LunaActionBarCard extends StatelessWidget { height: LunaButton.DEFAULT_HEIGHT, ), borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - onTap: onTap == null - ? null - : () async { - HapticFeedback.lightImpact(); - onTap(); - }, - onLongPress: onLongPress == null - ? null - : () async { - HapticFeedback.heavyImpact(); - onLongPress(); - }, + onTap: _onTapHandler(), + onLongPress: _onLongPressHandler(), ), - margin: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + margin: LunaUI.MARGIN_HALF, color: backgroundColor != null - ? backgroundColor.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY) + ? backgroundColor.withOpacity(LunaUI.OPACITY_DIMMED) : LunaTheme.isAMOLEDTheme - ? Colors.black.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY) - : LunaColours.primary - .withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY), + ? Colors.black.withOpacity(LunaUI.OPACITY_DIMMED) + : LunaColours.primary.withOpacity(LunaUI.OPACITY_DIMMED), ); } + + Function _onTapHandler() { + if (onTap != null) { + return () async { + HapticFeedback.lightImpact(); + onTap(); + }; + } + if (checkboxState != null && checkboxOnChanged != null) { + return () async => checkboxOnChanged(!checkboxState); + } + return null; + } + + Function _onLongPressHandler() { + if (onLongPress != null) { + return () async { + HapticFeedback.heavyImpact(); + onLongPress(); + }; + } + return null; + } } diff --git a/lib/core/ui/bottom_bar/badge.dart b/lib/core/ui/bottom_bar/badge.dart index fb8b83f0..b42cfe86 100644 --- a/lib/core/ui/bottom_bar/badge.dart +++ b/lib/core/ui/bottom_bar/badge.dart @@ -11,8 +11,7 @@ class LunaNavigationBarBadge extends Badge { @required bool isActive, }) : super( key: key, - badgeColor: - LunaColours.accent.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY), + badgeColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_DIMMED), elevation: 0, animationDuration: const Duration(milliseconds: LunaUI.ANIMATION_SPEED), @@ -29,8 +28,7 @@ class LunaNavigationBarBadge extends Badge { child: Icon( icon, color: isActive - ? LunaColours.accent - .withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY) + ? LunaColours.accent.withOpacity(LunaUI.OPACITY_DIMMED) : Colors.white, ), showBadge: showBadge, diff --git a/lib/core/ui/bottom_bar/navigation_bar.dart b/lib/core/ui/bottom_bar/navigation_bar.dart index 1251bf92..2d60e6c8 100644 --- a/lib/core/ui/bottom_bar/navigation_bar.dart +++ b/lib/core/ui/bottom_bar/navigation_bar.dart @@ -79,11 +79,10 @@ class _State extends State { child: Padding( child: GNav( gap: 8.0, - iconSize: 24.0, duration: const Duration(milliseconds: LunaUI.ANIMATION_SPEED), tabBackgroundColor: Theme.of(context) .canvasColor - .withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY), + .withOpacity(LunaUI.OPACITY_DIMMED), activeColor: LunaColours.accent, tabs: List.generate( widget.icons.length, @@ -91,7 +90,7 @@ class _State extends State { icon: widget.icons[index], text: widget.titles[index], active: _index == index, - iconSize: 22.0, + iconSize: LunaUI.ICON_SIZE, haptic: true, padding: _index == index ? const EdgeInsets.all(10.0) @@ -100,7 +99,7 @@ class _State extends State { iconColor: Colors.white, textStyle: const TextStyle( fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_NAVIGATION_BAR, + fontSize: LunaUI.FONT_SIZE_H3, color: Colors.white, ), iconActiveColor: LunaColours.accent, @@ -109,7 +108,7 @@ class _State extends State { : widget.leadingOnTab[index], )).toList(), tabActiveBorder: LunaUI.shouldUseBorder - ? Border.all(color: Colors.white12) + ? Border.all(color: LunaColours.white10) : null, tabBorder: LunaUI.shouldUseBorder ? Border.all(color: Colors.transparent) diff --git a/lib/core/ui/bottom_modal_sheet.dart b/lib/core/ui/bottom_modal_sheet.dart index d5540b09..a1dde478 100644 --- a/lib/core/ui/bottom_modal_sheet.dart +++ b/lib/core/ui/bottom_modal_sheet.dart @@ -3,6 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class LunaBottomModalSheet { + @protected Future showModal({ @required BuildContext context, Widget Function(BuildContext context) builder, @@ -20,4 +21,13 @@ class LunaBottomModalSheet { } Widget builder(BuildContext context) => null; + + Future show({ + @required BuildContext context, + Widget Function(BuildContext context) builder, + }) async => + showModal( + context: context, + builder: builder, + ); } diff --git a/lib/core/ui/button.dart b/lib/core/ui/button.dart index a981ae97..f541da61 100644 --- a/lib/core/ui/button.dart +++ b/lib/core/ui/button.dart @@ -10,13 +10,12 @@ enum LunaButtonType { /// A Luna-styled button. class LunaButton extends Card { - static const DEFAULT_HEIGHT = 50.0; + static const DEFAULT_HEIGHT = 46.0; LunaButton._({ Key key, @required Widget child, - EdgeInsets margin = - const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + EdgeInsets margin = LunaUI.MARGIN_HALF, Color backgroundColor, double height = DEFAULT_HEIGHT, Alignment alignment = Alignment.center, @@ -47,11 +46,10 @@ class LunaButton extends Card { ), margin: margin, color: backgroundColor != null - ? backgroundColor.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY) + ? backgroundColor.withOpacity(LunaUI.OPACITY_DIMMED) : LunaTheme.isAMOLEDTheme - ? Colors.black.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY) - : LunaColours.primary - .withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY), + ? Colors.black.withOpacity(LunaUI.OPACITY_DIMMED) + : LunaColours.primary.withOpacity(LunaUI.OPACITY_DIMMED), shape: backgroundColor != null ? LunaShapeBorder.rounded() : LunaUI.shapeBorder, @@ -70,10 +68,9 @@ class LunaButton extends Card { Color backgroundColor, String text, IconData icon, - double iconSize = 22.0, + double iconSize = LunaUI.ICON_SIZE, LunaLoadingState loadingState, - EdgeInsets margin = - const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + EdgeInsets margin = LunaUI.MARGIN_HALF, double height = DEFAULT_HEIGHT, Alignment alignment = Alignment.center, Decoration decoration, @@ -163,11 +160,10 @@ class LunaButton extends Card { factory LunaButton.text({ @required String text, @required IconData icon, - double iconSize = 22.0, + double iconSize = LunaUI.ICON_SIZE, Color color = LunaColours.accent, Color backgroundColor, - EdgeInsets margin = - const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + EdgeInsets margin = LunaUI.MARGIN_HALF, double height = DEFAULT_HEIGHT, Alignment alignment = Alignment.center, Decoration decoration, @@ -188,7 +184,8 @@ class LunaButton extends Card { color: color, size: iconSize, ), - padding: const EdgeInsets.only(right: 8.0), + padding: const EdgeInsets.only( + right: LunaUI.DEFAULT_MARGIN_SIZE / 2), ), Flexible( child: Text( @@ -196,7 +193,7 @@ class LunaButton extends Card { style: const TextStyle( color: Colors.white, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_BUTTON, + fontSize: LunaUI.FONT_SIZE_H3, ), overflow: TextOverflow.fade, softWrap: false, @@ -205,7 +202,8 @@ class LunaButton extends Card { ), ], ), - padding: const EdgeInsets.symmetric(horizontal: 12.0), + padding: + const EdgeInsets.symmetric(horizontal: LunaUI.DEFAULT_MARGIN_SIZE), ), margin: margin, height: height, @@ -220,8 +218,7 @@ class LunaButton extends Card { /// Build a button that contains a [LunaLoader]. factory LunaButton.loader({ - EdgeInsets margin = - const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + EdgeInsets margin = LunaUI.MARGIN_HALF, Color color = LunaColours.accent, Color backgroundColor, double height = DEFAULT_HEIGHT, @@ -235,7 +232,7 @@ class LunaButton extends Card { child: LunaLoader( useSafeArea: false, color: color, - size: 16.0, + size: LunaUI.FONT_SIZE_H3, ), margin: margin, height: height, @@ -253,10 +250,9 @@ class LunaButton extends Card { @required IconData icon, Color color = LunaColours.accent, Color backgroundColor, - EdgeInsets margin = - const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + EdgeInsets margin = LunaUI.MARGIN_HALF, double height = DEFAULT_HEIGHT, - double iconSize = 22.0, + double iconSize = LunaUI.ICON_SIZE, Alignment alignment = Alignment.center, Decoration decoration, Function onTap, diff --git a/lib/core/ui/card.dart b/lib/core/ui/card.dart index ab761ac2..64040e7c 100644 --- a/lib/core/ui/card.dart +++ b/lib/core/ui/card.dart @@ -6,7 +6,7 @@ class LunaCard extends Card { Key key, @required BuildContext context, @required Widget child, - EdgeInsets margin = LunaUI.MARGIN_CARD, + EdgeInsets margin = LunaUI.MARGIN_H_DEFAULT_V_HALF, Color color, Decoration decoration, double height, diff --git a/lib/core/ui/colors.dart b/lib/core/ui/colors.dart index 447d595f..fcf5fe74 100644 --- a/lib/core/ui/colors.dart +++ b/lib/core/ui/colors.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; class LunaColours { /// List of LunaSea colours in order that the should appear in a list. @@ -25,20 +26,17 @@ class LunaColours { /// Core secondary colour (appbar, bottom bar, etc.), static const Color secondary = Color(0xFF282834); - /// Blue static const Color blue = Color(0xFF00A8E8); - - /// Orange + static const Color blueGrey = Color(0xFF848FA5); + static const Color grey = Color(0xFFBBBBBB); static const Color orange = Color(0xFFFF9000); - - /// Red + static const Color purple = Color(0xFF9649CB); static const Color red = Color(0xFFF71735); - /// Purple - static const Color purple = Color(0xFF9649CB); - - /// Blue Grey - static const Color blueGrey = Color(0xFF848FA5); + /// Shades of White + static const Color white = Color(0xFFFFFFFF); + static const Color white70 = Color(0xB3FFFFFF); + static const Color white10 = Color(0x1AFFFFFF); /// Returns the correct colour for a graph by what layer it is on the graph canvas. Color byGraphLayer(int index) { @@ -60,3 +58,15 @@ class LunaColours { return _LIST_COLOR_ICONS[index % _LIST_COLOR_ICONS.length]; } } + +extension LunaColor on Color { + Color disabled([bool condition = true]) { + if (condition) return this.withOpacity(LunaUI.OPACITY_DISABLED); + return this; + } + + Color enabled([bool condition = true]) { + if (condition) return this; + return this.withOpacity(LunaUI.OPACITY_DISABLED); + } +} diff --git a/lib/core/ui/decoration/card_decoration.dart b/lib/core/ui/decoration/card_decoration.dart index a1ae9e6e..a068598e 100644 --- a/lib/core/ui/decoration/card_decoration.dart +++ b/lib/core/ui/decoration/card_decoration.dart @@ -4,9 +4,14 @@ import 'package:lunasea/core.dart'; // ignore: non_constant_identifier_names Decoration LunaCardDecoration({@required String uri, @required Map headers}) { if (LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null; - if (uri == null || uri.isEmpty) + if (uri == null || uri.isEmpty) { return BoxDecoration( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS)); + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), + ); + } + + double _opacity = + (LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int) / 100; return BoxDecoration( image: DecorationImage( image: NetworkImage( @@ -15,12 +20,7 @@ Decoration LunaCardDecoration({@required String uri, @required Map headers}) { ), // 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, - ), + opacity: _opacity, fit: BoxFit.cover, ), borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), diff --git a/lib/core/ui/dialog.dart b/lib/core/ui/dialog.dart index 79fb7367..eb3b55b1 100644 --- a/lib/core/ui/dialog.dart +++ b/lib/core/ui/dialog.dart @@ -4,10 +4,9 @@ import 'package:lunasea/core.dart'; // ignore: avoid_classes_with_only_static_members abstract class LunaDialog { - static const HEADER_SIZE = LunaUI.FONT_SIZE_HEADER; - static const BODY_SIZE = LunaUI.FONT_SIZE_SUBTITLE; - static const SUBBODY_SIZE = LunaUI.FONT_SIZE_SUBHEADER; - static const BUTTON_SIZE = LunaUI.FONT_SIZE_SUBHEADER; + static const HEADER_SIZE = LunaUI.FONT_SIZE_H1; + static const BODY_SIZE = LunaUI.FONT_SIZE_H3; + static const BUTTON_SIZE = LunaUI.FONT_SIZE_H4; static Widget title({ @required String text, @@ -135,7 +134,7 @@ abstract class LunaDialog { decoration: InputDecoration( labelText: title, labelStyle: const TextStyle( - color: Colors.white54, + color: LunaColours.grey, decoration: TextDecoration.none, fontSize: LunaDialog.BODY_SIZE, ), @@ -143,7 +142,8 @@ abstract class LunaDialog { borderSide: BorderSide(color: LunaColours.accent), ), enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: LunaColours.accent.withOpacity(0.3)), + borderSide: BorderSide( + color: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH)), ), ), style: const TextStyle( @@ -172,7 +172,7 @@ abstract class LunaDialog { decoration: InputDecoration( labelText: title, labelStyle: const TextStyle( - color: Colors.white54, + color: LunaColours.grey, decoration: TextDecoration.none, fontSize: LunaDialog.BODY_SIZE, ), @@ -180,7 +180,8 @@ abstract class LunaDialog { borderSide: BorderSide(color: LunaColours.accent), ), enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: LunaColours.accent.withOpacity(0.3)), + borderSide: BorderSide( + color: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH)), ), ), style: const TextStyle( diff --git a/lib/core/ui/drawer/drawer.dart b/lib/core/ui/drawer/drawer.dart index abfa6841..d43554ee 100644 --- a/lib/core/ui/drawer/drawer.dart +++ b/lib/core/ui/drawer/drawer.dart @@ -31,12 +31,22 @@ class LunaDrawer extends StatelessWidget { valueListenable: Database.indexersBox.listenable(), builder: (context, indexerBox, widget) => Drawer( child: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.listen( - builder: (context, _, __) => ListView( - children: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data - ? _getAlphabeticalOrder(context) - : _getManualOrder(context), - padding: const EdgeInsets.only(bottom: 8.0), - physics: const ClampingScrollPhysics(), + builder: (context, _, __) => Column( + children: [ + LunaDrawerHeader(page: page), + Expanded( + child: LunaListView( + controller: PrimaryScrollController.of(context), + children: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data + ? _getAlphabeticalOrder(context) + : _getManualOrder(context), + physics: const ClampingScrollPhysics(), + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom, + ), + ), + ), + ], ), ), ), @@ -46,12 +56,9 @@ class LunaDrawer extends StatelessWidget { List _sharedHeader(BuildContext context) { return [ - LunaDrawerHeader(), - _buildEntry(context: context, module: LunaModule.DASHBOARD), - _buildEntry(context: context, module: LunaModule.SETTINGS), - const Divider( - thickness: 1.0, - color: Colors.white12, + _buildEntry( + context: context, + module: LunaModule.DASHBOARD, ), ]; } @@ -65,10 +72,10 @@ class LunaDrawer extends StatelessWidget { ..._sharedHeader(context), ..._modules.map((module) { if (module.isEnabled) { - if (module == LunaModule.WAKE_ON_LAN) return _buildWakeOnLAN(context); return _buildEntry( context: context, module: module, + onTap: module == LunaModule.WAKE_ON_LAN ? _wakeOnLAN : null, ); } return const SizedBox(height: 0.0); @@ -82,10 +89,10 @@ class LunaDrawer extends StatelessWidget { ..._sharedHeader(context), ..._modules.map((module) { if (module.isEnabled) { - if (module == LunaModule.WAKE_ON_LAN) return _buildWakeOnLAN(context); return _buildEntry( context: context, module: module, + onTap: module == LunaModule.WAKE_ON_LAN ? _wakeOnLAN : null, ); } return const SizedBox(height: 0.0); @@ -96,55 +103,54 @@ class LunaDrawer extends StatelessWidget { Widget _buildEntry({ @required BuildContext context, @required LunaModule module, + Function onTap, }) { bool currentPage = page == module.key.toLowerCase(); - return ListTile( - leading: Icon( - module.icon, - color: currentPage ? module.color : Colors.white, - ), - title: Text( - module.name, - style: TextStyle( - color: currentPage ? module.color : Colors.white, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + return SizedBox( + height: 56.0, + child: InkWell( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + child: Icon( + module.icon, + color: currentPage ? module.color : LunaColours.white, + ), + padding: LunaUI.MARGIN_DEFAULT_HORIZONTAL, + ), + Text( + module.name, + style: TextStyle( + color: currentPage ? module.color : LunaColours.white, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ), + ], ), + onTap: onTap ?? + () async { + Navigator.of(context).pop(); + if (!currentPage) module.launch(); + }, ), - onTap: () async { - Navigator.of(context).pop(); - if (!currentPage) module.launch(); - }, - contentPadding: const EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0), ); } - Widget _buildWakeOnLAN(BuildContext context) { - return ListTile( - leading: Icon(LunaModule.WAKE_ON_LAN.icon), - title: Text( - LunaModule.WAKE_ON_LAN.name, - style: const TextStyle( - color: Colors.white, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - ), - onTap: () async { - WakeOnLANAPI api = WakeOnLANAPI.fromProfile(); - await api - .wake() - .then((_) => showLunaSuccessSnackBar( - title: 'Machine is Waking Up...', - message: 'Magic packet successfully sent', - )) - .catchError((error, stack) { - LunaLogger().error('Failed to wake machine', error, stack); - return showLunaErrorSnackBar( - title: 'Failed to Wake Machine', - error: error, - ); - }); - }, - contentPadding: const EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0), - ); + Future _wakeOnLAN() async { + WakeOnLANAPI api = WakeOnLANAPI.fromProfile(); + await api + .wake() + .then((_) => showLunaSuccessSnackBar( + title: 'Machine is Waking Up...', + message: 'Magic packet successfully sent', + )) + .catchError((error, stack) { + LunaLogger().error('Failed to wake machine', error, stack); + return showLunaErrorSnackBar( + title: 'Failed to Wake Machine', + error: error, + ); + }); } } diff --git a/lib/core/ui/drawer/drawer_header.dart b/lib/core/ui/drawer/drawer_header.dart index dcf212be..f16d2bff 100644 --- a/lib/core/ui/drawer/drawer_header.dart +++ b/lib/core/ui/drawer/drawer_header.dart @@ -1,72 +1,47 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; -class LunaDrawerHeader extends UserAccountsDrawerHeader { - LunaDrawerHeader({ +class LunaDrawerHeader extends StatelessWidget { + final String page; + + const LunaDrawerHeader({ Key key, - }) : super( - key: key, - accountName: StreamBuilder( - stream: LunaFirebaseAuth.instance.authStateChanges(), - builder: (context, snapshot) => - LunaText.title(text: LunaFirebaseAuth().email ?? 'LunaSea'), - ), - accountEmail: LunaDatabaseValue.ENABLED_PROFILE.listen( - builder: (context, lunaBox, widget) => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profilesBox, widget) => Padding( - child: LunaPopupMenuButton( - tooltip: 'Change Profiles', - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - LunaText.subtitle( - text: LunaDatabaseValue.ENABLED_PROFILE.data, - ), - const Icon( - Icons.arrow_drop_down_rounded, - color: Colors.white70, - size: LunaUI.FONT_SIZE_HEADER, - ), - ], - ), - onSelected: (result) { - HapticFeedback.selectionClick(); - LunaProfile().safelyChangeProfiles(result); - }, - itemBuilder: (context) { - return >[ - for (String profile in (profilesBox as Box).keys) - PopupMenuItem( - value: profile, - child: Text( - profile, - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: (LunaDatabaseValue.ENABLED_PROFILE.data ?? - 'default') == - profile - ? LunaColours.accent - : Colors.white, - ), - ), - ) - ]; - }, - ), - padding: const EdgeInsets.only(right: 12.0), - ), + @required this.page, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LunaDatabaseValue.ENABLED_PROFILE.listen( + builder: (context, box, _) => Container( + child: LunaAppBar.dropdown( + backgroundColor: Colors.transparent, + hideLeading: true, + useDrawer: false, + title: Database.profilesBox.keys.length == 1 + ? 'LunaSea' + : Database.currentProfile ?? 'LunaSea', + profiles: Database.profilesBox.keys.cast().toList(), + actions: [ + LunaIconButton( + icon: LunaIcons.SETTINGS, + onPressed: page == LunaModule.SETTINGS.key + ? Navigator.of(context).pop + : LunaModule.SETTINGS.launch, + ) + ], + ), + decoration: BoxDecoration( + color: LunaColours.accent, + image: DecorationImage( + image: const AssetImage(LunaAssets.brandingLogo), + colorFilter: ColorFilter.mode( + LunaColours.primary.withOpacity(0.15), + BlendMode.dstATop, ), + fit: BoxFit.cover, ), - decoration: BoxDecoration( - color: LunaColours.accent, - image: DecorationImage( - image: const AssetImage(LunaAssets.brandingLogo), - colorFilter: ColorFilter.mode( - LunaColours.primary.withOpacity(0.15), BlendMode.dstATop), - fit: BoxFit.cover, - ), - ), - ); + ), + ), + ); + } } diff --git a/lib/core/ui/floating_action_button/floating_action_button_extended.dart b/lib/core/ui/floating_action_button/floating_action_button_extended.dart index 40a18d31..3a797ae5 100644 --- a/lib/core/ui/floating_action_button/floating_action_button_extended.dart +++ b/lib/core/ui/floating_action_button/floating_action_button_extended.dart @@ -29,7 +29,7 @@ class LunaFloatingActionButtonExtended extends StatelessWidget { style: TextStyle( color: color, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, letterSpacing: 0.35, ), ), diff --git a/lib/core/ui/header.dart b/lib/core/ui/header.dart index 78aad054..d5cf7b0f 100644 --- a/lib/core/ui/header.dart +++ b/lib/core/ui/header.dart @@ -4,13 +4,11 @@ import 'package:lunasea/core.dart'; class LunaHeader extends StatelessWidget { final String text; final String subtitle; - final EdgeInsets padding; const LunaHeader({ Key key, @required this.text, this.subtitle, - this.padding, }) : super(key: key); Widget _headerText() { @@ -18,7 +16,7 @@ class LunaHeader extends StatelessWidget { text.toUpperCase(), style: const TextStyle( fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_HEADER, + fontSize: LunaUI.FONT_SIZE_H2, color: Colors.white, ), ); @@ -28,28 +26,31 @@ class LunaHeader extends StatelessWidget { return Padding( child: Container( height: 2.0, - width: 48.0, + width: LunaUI.DEFAULT_MARGIN_SIZE * 3, decoration: BoxDecoration( borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), color: LunaColours.accent, ), ), - padding: EdgeInsets.only( - top: 4.0, - left: 1.0, - bottom: subtitle != null ? 6.0 : 0.0, + padding: const EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE / 2, + left: 0, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2, ), ); } Widget _subtitle() { - return Text( - subtitle, - style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBHEADER, - color: Colors.white70, - fontWeight: FontWeight.w300, + return Padding( + child: Text( + subtitle, + style: const TextStyle( + fontSize: LunaUI.FONT_SIZE_H4, + color: LunaColours.grey, + fontWeight: FontWeight.w300, + ), ), + padding: const EdgeInsets.only(bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2), ); } @@ -64,13 +65,11 @@ class LunaHeader extends StatelessWidget { if (subtitle != null) _subtitle(), ], ), - padding: padding ?? - const EdgeInsets.fromLTRB( - LunaUI.DEFAULT_MARGIN_SIZE, - LunaUI.DEFAULT_MARGIN_SIZE / 2, - LunaUI.DEFAULT_MARGIN_SIZE, - LunaUI.DEFAULT_MARGIN_SIZE / 1.5, - ), + padding: const EdgeInsets.only( + left: LunaUI.DEFAULT_MARGIN_SIZE, + right: LunaUI.DEFAULT_MARGIN_SIZE, + top: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), ); } } diff --git a/lib/core/ui/highlighted_node.dart b/lib/core/ui/highlighted_node.dart index fa373f5e..c2191410 100644 --- a/lib/core/ui/highlighted_node.dart +++ b/lib/core/ui/highlighted_node.dart @@ -23,7 +23,7 @@ class LunaHighlightedNode extends StatelessWidget { text, maxLines: 1, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_HIGHLIGHTED_NODE, + fontSize: LunaUI.FONT_SIZE_H4, color: textColor, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), diff --git a/lib/core/ui/icons.dart b/lib/core/ui/icons.dart index 14970873..1cdfb61f 100644 --- a/lib/core/ui/icons.dart +++ b/lib/core/ui/icons.dart @@ -1,2 +1,3 @@ export 'icons/icon_brands.dart'; export 'icons/icon_button.dart'; +export 'icons/icon.dart'; diff --git a/lib/core/ui/icons/icon.dart b/lib/core/ui/icons/icon.dart new file mode 100644 index 00000000..7b36aca5 --- /dev/null +++ b/lib/core/ui/icons/icon.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class LunaIcons { + static const IconData ADD = Icons.add_rounded; + static const IconData ARROW_RIGHT = Icons.arrow_forward_ios_rounded; + static const IconData ARROW_DROPDOWN = Icons.arrow_drop_down_rounded; + static const IconData CLOUD_DELETE = Icons.cloud_off_rounded; + static const IconData CLOUD_DOWNLOAD = Icons.cloud_download_rounded; + static const IconData CLOUD_UPLOAD = Icons.cloud_upload_rounded; + static const IconData CONNECTION_TEST = Icons.wifi_tethering_rounded; + static const IconData DELETE = Icons.delete_rounded; + static const IconData DEVICES = Icons.devices_rounded; + static const IconData DOWNLOAD = Icons.file_download_rounded; + static const IconData ERROR = Icons.error_rounded; + static const IconData HOME = Icons.home_rounded; + static const IconData MONITOR_OFF = Icons.turned_in_not_rounded; + static const IconData MONITOR_ON = Icons.turned_in_rounded; + static const IconData MUSIC = Icons.music_note_rounded; + static const IconData PLAY = Icons.play_arrow_rounded; + static const IconData PROFILES = Icons.switch_account_rounded; + static const IconData REFRESH = Icons.refresh_rounded; + static const IconData RENAME = Icons.text_format_rounded; + static const IconData SEARCH = Icons.search_rounded; + static const IconData SETTINGS = Icons.settings_rounded; + static const IconData SHUFFLE = Icons.shuffle_rounded; + static const IconData TRANSLATE = Icons.translate_rounded; + static const IconData USER = Icons.person_rounded; + static const IconData VIDEO_CAM = Icons.videocam_rounded; + static const IconData WATCHED = Icons.visibility_rounded; +} diff --git a/lib/core/ui/icons/icon_button.dart b/lib/core/ui/icons/icon_button.dart index 9742c07a..8efb4445 100644 --- a/lib/core/ui/icons/icon_button.dart +++ b/lib/core/ui/icons/icon_button.dart @@ -12,23 +12,49 @@ class LunaIconButton extends StatelessWidget { final Function onLongPress; final LunaLoadingState loadingState; final AlignmentGeometry alignment; - final EdgeInsetsGeometry padding; + final MouseCursor mouseCursor; - LunaIconButton({ + const LunaIconButton.arrow({ Key key, this.text, - this.textSize = 10.0, - this.icon, - this.iconSize = 24.0, + this.textSize = LunaUI.FONT_SIZE_H5, + this.icon = LunaIcons.ARROW_RIGHT, + this.iconSize = LunaUI.ICON_SIZE, this.alignment = Alignment.center, - this.padding = const EdgeInsets.all(8.0), this.color = Colors.white, this.onPressed, this.onLongPress, this.loadingState, - }) : super(key: key) { - assert((text != null || icon != null), 'both text and icon cannot be null'); - } + this.mouseCursor, + }) : super(key: key); + + const LunaIconButton.appBar({ + Key key, + this.text, + this.textSize = LunaUI.FONT_SIZE_H5, + this.icon, + this.iconSize = LunaUI.ICON_SIZE, + this.alignment = Alignment.center, + this.color = Colors.white, + this.onPressed, + this.onLongPress, + this.loadingState, + this.mouseCursor, + }) : super(key: key); + + const LunaIconButton({ + Key key, + this.text, + this.textSize = LunaUI.FONT_SIZE_H5, + this.icon, + this.iconSize = LunaUI.ICON_SIZE, + this.alignment = Alignment.center, + this.color = Colors.white, + this.onPressed, + this.onLongPress, + this.loadingState, + this.mouseCursor, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -37,9 +63,14 @@ class LunaIconButton extends StatelessWidget { icon: loadingState == LunaLoadingState.ACTIVE ? _loader() : _icon(), iconSize: iconSize, alignment: alignment, - padding: padding, + padding: EdgeInsets.zero, onPressed: _onPressed(), + mouseCursor: mouseCursor ?? + (onPressed != null ? SystemMouseCursors.click : MouseCursor.defer), ), + hoverColor: Colors.transparent, + mouseCursor: mouseCursor ?? + (onLongPress != null ? SystemMouseCursors.click : MouseCursor.defer), onLongPress: _onLongPress(), ); } @@ -64,13 +95,14 @@ class LunaIconButton extends StatelessWidget { Widget _loader() { return LunaLoader( - size: 12.0, + size: LunaUI.FONT_SIZE_H4, color: color, useSafeArea: false, ); } Widget _icon() { + assert((text != null || icon != null), 'both text and icon cannot be null'); if (loadingState == LunaLoadingState.ERROR) { return Icon( Icons.error_rounded, diff --git a/lib/core/ui/input_bar.dart b/lib/core/ui/input_bar.dart index 64ab4bde..a5b5694e 100644 --- a/lib/core/ui/input_bar.dart +++ b/lib/core/ui/input_bar.dart @@ -2,13 +2,14 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; class LunaTextInputBar extends StatefulWidget { - static const double appBarHeight = 62.0; - static const double appBarInnerHeight = 62.0 - 13.0; + static const double defaultHeight = 50.0; + static const double defaultAppBarHeight = + defaultHeight + LunaUI.DEFAULT_MARGIN_SIZE; static const EdgeInsets appBarMargin = EdgeInsets.fromLTRB( - 12.0, - 1.0, - 12.0, - 12.0, + LunaUI.DEFAULT_MARGIN_SIZE, + 0, + LunaUI.DEFAULT_MARGIN_SIZE, + LunaUI.DEFAULT_MARGIN_SIZE, ); final TextEditingController controller; @@ -38,7 +39,7 @@ class LunaTextInputBar extends StatefulWidget { this.keyboardType = TextInputType.text, this.labelText, this.labelIcon = Icons.search_rounded, - this.margin = LunaUI.MARGIN_CARD, + this.margin = LunaUI.MARGIN_H_DEFAULT_V_HALF, this.autofocus = false, this.obscureText = false, this.isFormField = false, @@ -79,21 +80,21 @@ class _State extends State { child: widget.isFormField ? _isForm : _isNotForm, ), ), - height: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, color: Theme.of(context).canvasColor, ); TextStyle get _sharedTextStyle => const TextStyle( - color: Colors.white, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + color: LunaColours.white, + fontSize: LunaUI.FONT_SIZE_H3, ); InputDecoration get _sharedInputDecoration => InputDecoration( labelText: widget.labelText ?? 'lunasea.SearchTextBar'.tr(), labelStyle: const TextStyle( - color: Colors.white54, + color: LunaColours.grey, decoration: TextDecoration.none, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), suffixIcon: AnimatedOpacity( child: InkWell( diff --git a/lib/core/ui/linear_indicator.dart b/lib/core/ui/linear_indicator.dart new file mode 100644 index 00000000..7db47535 --- /dev/null +++ b/lib/core/ui/linear_indicator.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; + +class LunaLinearPercentIndicator extends StatelessWidget { + static const _LINE_HEIGHT = 4.0; + static const double height = _LINE_HEIGHT + LunaUI.DEFAULT_MARGIN_SIZE / 2; + + final double percent; + final Color progressColor; + final Color backgroundColor; + + const LunaLinearPercentIndicator({ + Key key, + this.percent, + this.progressColor = LunaColours.accent, + this.backgroundColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: height, + alignment: Alignment.bottomCenter, + child: LinearPercentIndicator( + percent: percent, + padding: const EdgeInsets.symmetric(horizontal: 2.0), + lineHeight: 4.0, + progressColor: progressColor, + backgroundColor: + backgroundColor ?? progressColor.withOpacity(LunaUI.OPACITY_SPLASH), + ), + ); + } +} diff --git a/lib/core/ui/list_tile.dart b/lib/core/ui/list_tile.dart index 81a9c9d5..d34a46a9 100644 --- a/lib/core/ui/list_tile.dart +++ b/lib/core/ui/list_tile.dart @@ -1 +1,2 @@ +export 'list_tile/expandable_list_tile.dart'; export 'list_tile/list_tile.dart'; diff --git a/lib/core/ui/list_tile/expandable_list_tile.dart b/lib/core/ui/list_tile/expandable_list_tile.dart new file mode 100644 index 00000000..16611b11 --- /dev/null +++ b/lib/core/ui/list_tile/expandable_list_tile.dart @@ -0,0 +1,179 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaExpandableListTile extends StatefulWidget { + final String title; + final List collapsedSubtitles; + final Widget collapsedTrailing; + final Widget collapsedLeading; + final Color backgroundColor; + final Function onLongPress; + final List expandedHighlightedNodes; + final List expandedTableContent; + final List expandedTableButtons; + final bool initialExpanded; + + /// Create a [LunaExpandableListTile] which is a list tile that expands into a table-style card. + /// + /// If [expandedWidget] is supplied, that widget is used as the body within the expanded card. + /// Any + LunaExpandableListTile({ + Key key, + @required this.title, + @required this.collapsedSubtitles, + @required this.expandedTableContent, + this.collapsedTrailing, + this.collapsedLeading, + this.onLongPress, + this.expandedHighlightedNodes, + this.expandedTableButtons, + this.backgroundColor, + this.initialExpanded = false, + }) : super(key: key) { + assert(title != null); + assert(collapsedSubtitles != null); + assert(expandedTableContent != null); + } + + @override + State createState() => _State(); +} + +class _State extends State { + ExpandableController controller; + + @override + void initState() { + super.initState(); + controller = ExpandableController(initialExpanded: widget.initialExpanded); + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ExpandableNotifier( + controller: controller, + child: Expandable( + collapsed: collapsed(), + expanded: expanded(), + ), + ); + } + + List _parseSubtitles() { + if (widget.collapsedSubtitles.isEmpty) return []; + List _result = []; + for (int i = 0; i < widget.collapsedSubtitles.length; i++) { + _result.add(widget.collapsedSubtitles[i]); + } + return _result; + } + + Widget collapsed() { + return LunaBlock( + title: widget.title ?? LunaUI.TEXT_EMDASH, + body: _parseSubtitles(), + onTap: controller.toggle, + onLongPress: widget.onLongPress, + trailing: widget.collapsedTrailing, + leading: widget.collapsedLeading, + // color: widget.backgroundColor, + ); + } + + Widget expanded() { + return LunaCard( + context: context, + child: InkWell( + child: Padding( + padding: EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE, + bottom: widget.expandedTableButtons?.isEmpty ?? true + ? (LunaUI.DEFAULT_MARGIN_SIZE / 4 * 3) + : LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + child: LunaText.title( + text: widget.title ?? LunaUI.TEXT_EMDASH, + softWrap: true, + maxLines: 8, + ), + padding: const EdgeInsets.only( + left: LunaUI.DEFAULT_MARGIN_SIZE, + right: LunaUI.DEFAULT_MARGIN_SIZE, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), + ), + if (widget.expandedHighlightedNodes != null) + Padding( + child: Wrap( + direction: Axis.horizontal, + spacing: LunaUI.DEFAULT_MARGIN_SIZE / 2, + runSpacing: LunaUI.DEFAULT_MARGIN_SIZE / 2, + children: widget.expandedHighlightedNodes, + ), + padding: const EdgeInsets.only( + left: LunaUI.DEFAULT_MARGIN_SIZE, + right: LunaUI.DEFAULT_MARGIN_SIZE, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), + ), + ...widget.expandedTableContent + .map((child) => Padding( + child: child, + padding: const EdgeInsets.symmetric( + horizontal: LunaUI.DEFAULT_MARGIN_SIZE, + ), + )) + .toList(), + if (widget.expandedTableButtons != null) + Padding( + child: Wrap( + children: [ + ...List.generate( + widget.expandedTableButtons.length, + (index) { + int bCount = widget.expandedTableButtons.length; + double widthFactor = 0.5; + + if (index == (bCount - 1) && bCount.isOdd) { + widthFactor = 1; + } + + return FractionallySizedBox( + child: widget.expandedTableButtons[index], + widthFactor: widthFactor, + ); + }, + ), + ], + ), + padding: const EdgeInsets.symmetric( + horizontal: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), + ), + ], + ), + ), + ], + ), + ), + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), + onTap: controller.toggle, + onLongPress: widget.onLongPress, + ), + color: widget.backgroundColor, + ); + } +} diff --git a/lib/core/ui/list_tile/list_tile.dart b/lib/core/ui/list_tile/list_tile.dart index d398e6d0..8e336f60 100644 --- a/lib/core/ui/list_tile/list_tile.dart +++ b/lib/core/ui/list_tile/list_tile.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; +@Deprecated("Use LunaBlock instead") class LunaListTile extends Card { LunaListTile({ Key key, - EdgeInsets margin = LunaUI.MARGIN_CARD, @required BuildContext context, @required Widget title, + @required double height, Widget subtitle, Widget trailing, Widget leading, @@ -14,57 +15,65 @@ class LunaListTile extends Card { Decoration decoration, Function onTap, Function onLongPress, - bool contentPadding = false, - EdgeInsets customContentPadding, + bool drawBorder = true, + EdgeInsets margin = LunaUI.MARGIN_H_DEFAULT_V_HALF, }) : super( key: key, child: Container( + height: height, child: InkWell( child: Row( children: [ + if (leading != null) + SizedBox( + width: LunaUI.DEFAULT_MARGIN_SIZE * 4 + + LunaUI.DEFAULT_MARGIN_SIZE / 2, + child: leading, + ), Expanded( - child: ListTile( - title: title, - subtitle: subtitle, - // trailing: trailing, - leading: leading, - mouseCursor: onTap != null || onLongPress != null - ? SystemMouseCursors.click - : null, - contentPadding: contentPadding - ? customContentPadding?.copyWith( - right: trailing != null ? 0 : null, - ) ?? - LunaUI.MARGIN_CARD.copyWith( - right: trailing != null ? 0 : null, - ) - : LunaUI.MARGIN_CARD.copyWith( - top: 0, - bottom: 0, - right: trailing != null ? 0 : null, - ), + child: Padding( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + child: title, + height: LunaBlock.TITLE_HEIGHT, + ), + if (subtitle != null) subtitle, + ], + ), + padding: EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE, + bottom: LunaUI.DEFAULT_MARGIN_SIZE, + left: leading != null ? 0 : LunaUI.DEFAULT_MARGIN_SIZE, + right: + trailing != null ? 0 : LunaUI.DEFAULT_MARGIN_SIZE, + ), ), ), if (trailing != null) Padding( - padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right), + padding: const EdgeInsets.only( + right: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), child: SizedBox( - width: 48.0, + width: LunaUI.DEFAULT_MARGIN_SIZE * 4, child: trailing, ), ), ], ), borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - mouseCursor: SystemMouseCursors.click, onTap: onTap, onLongPress: onLongPress, + mouseCursor: MouseCursor.defer, ), decoration: decoration, ), margin: margin, elevation: LunaUI.ELEVATION, - shape: LunaUI.shapeBorder, + shape: drawBorder ? LunaUI.shapeBorder : LunaShapeBorder.rounded(), color: color ?? Theme.of(context).primaryColor, ); } diff --git a/lib/core/ui/list_view.dart b/lib/core/ui/list_view.dart index d69e9519..b5489a72 100644 --- a/lib/core/ui/list_view.dart +++ b/lib/core/ui/list_view.dart @@ -5,3 +5,4 @@ export 'list_view/list_view_modal_builder.dart'; export 'list_view/paged_list_view.dart'; export 'list_view/reorderable_list_view.dart'; export 'list_view/reorderable_list_view_builder.dart'; +export 'list_view/reorderable_list_view_dragger.dart'; diff --git a/lib/core/ui/list_view/list_view.dart b/lib/core/ui/list_view/list_view.dart index 54c18b34..ddcded71 100644 --- a/lib/core/ui/list_view/list_view.dart +++ b/lib/core/ui/list_view/list_view.dart @@ -28,7 +28,7 @@ class LunaListView extends StatelessWidget { itemExtent: itemExtent, padding: padding ?? MediaQuery.of(context).padding.add(EdgeInsets.symmetric( - vertical: LunaUI.MARGIN_CARD.bottom, + vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, )), physics: physics, ), diff --git a/lib/core/ui/list_view/list_view_builder.dart b/lib/core/ui/list_view/list_view_builder.dart index c94a9f69..2eaeefa6 100644 --- a/lib/core/ui/list_view/list_view_builder.dart +++ b/lib/core/ui/list_view/list_view_builder.dart @@ -31,7 +31,7 @@ class LunaListViewBuilder extends StatelessWidget { keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, padding: padding ?? MediaQuery.of(context).padding.add(EdgeInsets.symmetric( - vertical: LunaUI.MARGIN_CARD.bottom, + vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, )), physics: physics, itemCount: itemCount, diff --git a/lib/core/ui/list_view/list_view_modal.dart b/lib/core/ui/list_view/list_view_modal.dart index b6565e00..61b607cc 100644 --- a/lib/core/ui/list_view/list_view_modal.dart +++ b/lib/core/ui/list_view/list_view_modal.dart @@ -53,12 +53,14 @@ class LunaListViewModal extends StatelessWidget { return EdgeInsets.fromLTRB( _padding.left + _viewInsets.left, appBar != null - ? LunaUI.MARGIN_CARD.top - : _padding.top + _viewInsets.top + LunaUI.MARGIN_CARD.top, + ? LunaUI.MARGIN_H_DEFAULT_V_HALF.top + : _padding.top + _viewInsets.top + LunaUI.MARGIN_H_DEFAULT_V_HALF.top, _padding.right + _viewInsets.right, actionBar != null ? 0 - : _padding.bottom + _viewInsets.bottom + LunaUI.MARGIN_CARD.bottom, + : _padding.bottom + + _viewInsets.bottom + + LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, ); } } diff --git a/lib/core/ui/list_view/list_view_modal_builder.dart b/lib/core/ui/list_view/list_view_modal_builder.dart index b185fbc2..8091de88 100644 --- a/lib/core/ui/list_view/list_view_modal_builder.dart +++ b/lib/core/ui/list_view/list_view_modal_builder.dart @@ -66,12 +66,14 @@ class LunaListViewModalBuilder extends StatelessWidget { return EdgeInsets.fromLTRB( _padding.left + _viewInsets.left, appBar != null - ? LunaUI.MARGIN_CARD.top - : _padding.top + _viewInsets.top + LunaUI.MARGIN_CARD.top, + ? LunaUI.MARGIN_H_DEFAULT_V_HALF.top + : _padding.top + _viewInsets.top + LunaUI.MARGIN_H_DEFAULT_V_HALF.top, _padding.right + _viewInsets.right, actionBar != null ? 0 - : _padding.bottom + _viewInsets.bottom + LunaUI.MARGIN_CARD.bottom, + : _padding.bottom + + _viewInsets.bottom + + LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, ); } } diff --git a/lib/core/ui/list_view/paged_list_view.dart b/lib/core/ui/list_view/paged_list_view.dart index 741ef6c2..eb801ff0 100644 --- a/lib/core/ui/list_view/paged_list_view.dart +++ b/lib/core/ui/list_view/paged_list_view.dart @@ -68,11 +68,11 @@ class _State extends State> { ), padding: const EdgeInsets.only(bottom: 0.0), ), - newPageErrorIndicatorBuilder: (context) => LunaIconButton( + newPageErrorIndicatorBuilder: (context) => const LunaIconButton( icon: Icons.error_rounded, color: LunaColours.red, ), - noMoreItemsIndicatorBuilder: (context) => LunaIconButton( + noMoreItemsIndicatorBuilder: (context) => const LunaIconButton( icon: Icons.check_rounded, color: LunaColours.accent, ), @@ -85,8 +85,9 @@ class _State extends State> { padding: widget.padding ?? MediaQuery.of(context) .padding - .copyWith(bottom: LunaUI.MARGIN_CARD.bottom) - .add(EdgeInsets.only(top: LunaUI.MARGIN_CARD.top)), + .copyWith(bottom: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom) + .add( + EdgeInsets.only(top: LunaUI.MARGIN_H_DEFAULT_V_HALF.top)), physics: const AlwaysScrollableScrollPhysics(), ), ), diff --git a/lib/core/ui/list_view/reorderable_list_view.dart b/lib/core/ui/list_view/reorderable_list_view.dart index 540d2d60..52548e46 100644 --- a/lib/core/ui/list_view/reorderable_list_view.dart +++ b/lib/core/ui/list_view/reorderable_list_view.dart @@ -30,7 +30,7 @@ class LunaReorderableListView extends StatelessWidget { children: children, padding: padding ?? MediaQuery.of(context).padding.add(EdgeInsets.symmetric( - vertical: LunaUI.MARGIN_CARD.bottom, + vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, )), physics: physics, onReorder: onReorder, diff --git a/lib/core/ui/list_view/reorderable_list_view_builder.dart b/lib/core/ui/list_view/reorderable_list_view_builder.dart index d6409ec5..c60459d0 100644 --- a/lib/core/ui/list_view/reorderable_list_view_builder.dart +++ b/lib/core/ui/list_view/reorderable_list_view_builder.dart @@ -34,7 +34,7 @@ class LunaReorderableListViewBuilder extends StatelessWidget { keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, padding: padding ?? MediaQuery.of(context).padding.add(EdgeInsets.symmetric( - vertical: LunaUI.MARGIN_CARD.bottom, + vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom, )), physics: physics, itemCount: itemCount, diff --git a/lib/core/widgets/reorderable_list_dragger.dart b/lib/core/ui/list_view/reorderable_list_view_dragger.dart similarity index 56% rename from lib/core/widgets/reorderable_list_dragger.dart rename to lib/core/ui/list_view/reorderable_list_view_dragger.dart index 22b134a0..0fe0acf4 100644 --- a/lib/core/widgets/reorderable_list_dragger.dart +++ b/lib/core/ui/list_view/reorderable_list_view_dragger.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; -class LunaReorderableListDragger extends StatelessWidget { +class LunaReorderableListViewDragger extends StatelessWidget { final int index; - const LunaReorderableListDragger({ + const LunaReorderableListViewDragger({ Key key, @required this.index, }) : super(key: key); @@ -15,7 +16,10 @@ class LunaReorderableListDragger extends StatelessWidget { children: [ ReorderableDragStartListener( index: index, - child: const Icon(Icons.menu_rounded), + child: const LunaIconButton( + icon: Icons.menu_rounded, + mouseCursor: SystemMouseCursors.click, + ), ), ], ); diff --git a/lib/core/ui/message.dart b/lib/core/ui/message.dart index 16d1fd81..6d2221b2 100644 --- a/lib/core/ui/message.dart +++ b/lib/core/ui/message.dart @@ -98,7 +98,7 @@ class LunaMessage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Card( - margin: LunaUI.MARGIN_CARD, + margin: LunaUI.MARGIN_H_DEFAULT_V_HALF, elevation: LunaUI.ELEVATION, shape: LunaUI.shapeBorder, child: Row( diff --git a/lib/core/ui/network_image.dart b/lib/core/ui/network_image.dart index 1df8b00e..447b86b1 100644 --- a/lib/core/ui/network_image.dart +++ b/lib/core/ui/network_image.dart @@ -1,37 +1,57 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; +import 'package:transparent_image/transparent_image.dart'; class LunaNetworkImage extends ClipRRect { LunaNetworkImage({ Key key, - double height, - double width, + @required BuildContext context, + @required double height, + @required double width, String url, - @required String placeholderAsset, + IconData placeholderIcon, + @Deprecated('Use placeholderIcon') String placeholderAsset, Map headers, - bool roundCorners = true, }) : super( key: key, child: SizedBox( child: Stack( - fit: StackFit.expand, + alignment: Alignment.center, children: [ - Image.asset( - placeholderAsset, + Container( height: height, width: width, + child: Center( + child: placeholderIcon != null + ? Icon( + placeholderIcon, + color: LunaColours.accent, + size: height / 3, + ) + : null, + ), + decoration: BoxDecoration( + color: Theme.of(context).canvasColor, + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), + border: LunaUI.shouldUseBorder + ? Border.all(color: LunaColours.white10) + : null, + ), ), - if (url != null && url.isNotEmpty) + if (url?.isNotEmpty ?? false) FadeInImage( height: height, width: width, fadeInDuration: const Duration( - milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED), + milliseconds: LunaUI.ANIMATION_SPEED_IMAGES, + ), fadeOutDuration: const Duration(milliseconds: 1), - placeholder: AssetImage(placeholderAsset), + placeholder: MemoryImage(kTransparentImage), fit: BoxFit.cover, - image: NetworkImage(url, - headers: headers?.cast()), + image: NetworkImage( + url, + headers: headers?.cast(), + ), imageErrorBuilder: (context, error, stack) => SizedBox( height: height, width: width, @@ -42,11 +62,7 @@ class LunaNetworkImage extends ClipRRect { height: height, width: width, ), - borderRadius: - roundCorners ? BorderRadius.circular(LunaUI.BORDER_RADIUS) : null, - ) { - // assert(height != null); - // assert(width != null); - assert(placeholderAsset != null); - } + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), + ); } diff --git a/lib/core/ui/shape.dart b/lib/core/ui/shape.dart index 78598fe4..8562b490 100644 --- a/lib/core/ui/shape.dart +++ b/lib/core/ui/shape.dart @@ -6,7 +6,9 @@ class LunaShapeBorder extends RoundedRectangleBorder { bool useBorder = false, }) : super( borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - side: useBorder ? const BorderSide(color: Colors.white12) : BorderSide.none, + side: useBorder + ? const BorderSide(color: LunaColours.white10) + : BorderSide.none, ); factory LunaShapeBorder.rounded() => LunaShapeBorder._internal(); diff --git a/lib/core/ui/snackbar/snackbar.dart b/lib/core/ui/snackbar/snackbar.dart index 7b6f5dc2..1f5bd9cb 100644 --- a/lib/core/ui/snackbar/snackbar.dart +++ b/lib/core/ui/snackbar/snackbar.dart @@ -36,8 +36,6 @@ extension LunaSnackbarTypeExtension on LunaSnackbarType { } } -const _PADDING = 8.0; - Future showLunaSnackBar({ @required String title, @required LunaSnackbarType type, @@ -56,31 +54,28 @@ Future showLunaSnackBar({ controller: controller, boxShadows: [ BoxShadow( - blurRadius: 6.0, - spreadRadius: 4.0, - color: Colors.black.withOpacity(0.10)) + blurRadius: LunaUI.DEFAULT_MARGIN_SIZE / 2, + spreadRadius: LunaUI.DEFAULT_MARGIN_SIZE / 4, + color: Colors.black.withOpacity(LunaUI.OPACITY_SPLASH / 2), + ), ], horizontalDismissDirection: HorizontalDismissDirection.horizontal, - margin: const EdgeInsets.all(_PADDING), + margin: LunaUI.MARGIN_DEFAULT, behavior: FlashBehavior.floating, position: position, borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), borderColor: - LunaUI.shouldUseBorder ? Colors.white12 : Colors.transparent, + LunaUI.shouldUseBorder ? LunaColours.white10 : Colors.transparent, child: FlashBar( - title: Text( - title, - style: const TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_TITLE, - ), + title: LunaText.title( + text: title, + maxLines: 4, + overflow: TextOverflow.ellipsis, ), - content: Text( - message, - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), + content: LunaText.subtitle( + text: message, + maxLines: 8, + overflow: TextOverflow.ellipsis, ), shouldIconPulse: false, icon: Padding( @@ -88,7 +83,9 @@ Future showLunaSnackBar({ icon: type.icon, color: type.color, ), - padding: const EdgeInsets.only(left: _PADDING), + padding: const EdgeInsets.only( + left: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), ), primaryAction: showButton ? TextButton( diff --git a/lib/core/ui/table/table_card.dart b/lib/core/ui/table/table_card.dart index fa61b6aa..ed8412e6 100644 --- a/lib/core/ui/table/table_card.dart +++ b/lib/core/ui/table/table_card.dart @@ -19,9 +19,14 @@ class LunaTableCard extends StatelessWidget { context: context, child: Padding( child: _body(), - padding: buttons == null - ? const EdgeInsets.symmetric(vertical: 8.0) - : const EdgeInsets.only(top: 8.0, bottom: 6.0), + padding: EdgeInsets.only( + left: LunaUI.DEFAULT_MARGIN_SIZE / 2, + right: LunaUI.DEFAULT_MARGIN_SIZE / 2, + top: LunaUI.DEFAULT_MARGIN_SIZE - LunaUI.DEFAULT_MARGIN_SIZE / 4, + bottom: buttons?.isEmpty ?? true + ? LunaUI.DEFAULT_MARGIN_SIZE - LunaUI.DEFAULT_MARGIN_SIZE / 4 + : 0, + ), ), ); } @@ -40,7 +45,8 @@ class LunaTableCard extends StatelessWidget { return Row( children: [ Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), + padding: const EdgeInsets.symmetric( + horizontal: LunaUI.DEFAULT_MARGIN_SIZE), child: LunaText.title(text: title), ), ], @@ -49,12 +55,13 @@ class LunaTableCard extends StatelessWidget { List _content() { return content - ?.map((content) => Padding( - child: content, - padding: const EdgeInsets.symmetric(horizontal: 12.0), - )) - ?.toList() ?? - []; + .map((child) => Padding( + child: child, + padding: const EdgeInsets.symmetric( + horizontal: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), + )) + .toList(); } Widget _buttons() { @@ -64,7 +71,10 @@ class LunaTableCard extends StatelessWidget { children: buttons.map((button) => Expanded(child: button)).toList(), ), - padding: const EdgeInsets.symmetric(horizontal: 6.0), + padding: const EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE / 2 - LunaUI.DEFAULT_MARGIN_SIZE / 4, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2, + ), ); } } diff --git a/lib/core/ui/table/table_content.dart b/lib/core/ui/table/table_content.dart index 08d31464..02ab6f0f 100644 --- a/lib/core/ui/table/table_content.dart +++ b/lib/core/ui/table/table_content.dart @@ -1,56 +1,94 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; +enum _Type { + CONTENT, + SPACER, +} + class LunaTableContent extends StatelessWidget { final String title; final String body; final bool bodyIsUrl; final int titleFlex; final int bodyFlex; + final double spacerSize; final TextAlign titleAlign; final TextAlign bodyAlign; - final Color titleColor; - final Color bodyColor; - final EdgeInsets padding; + final _Type type; - const LunaTableContent({ + const LunaTableContent._({ Key key, - @required this.title, - @required this.body, + this.title, + this.body, this.bodyIsUrl = false, this.titleAlign = TextAlign.end, this.bodyAlign = TextAlign.start, this.titleFlex = 5, this.bodyFlex = 10, - this.titleColor = Colors.white70, - this.bodyColor = Colors.white, - this.padding = const EdgeInsets.symmetric(vertical: 4.0), - }) : super(key: key); + this.spacerSize = LunaUI.DEFAULT_MARGIN_SIZE, + @required this.type, + }); + + factory LunaTableContent.spacer({ + Key key, + double spacerSize = LunaUI.DEFAULT_MARGIN_SIZE, + }) => + LunaTableContent._( + key: key, + type: _Type.SPACER, + spacerSize: spacerSize, + ); + + factory LunaTableContent({ + Key key, + @required String title, + @required String body, + bool bodyIsUrl = false, + TextAlign titleAlign = TextAlign.end, + TextAlign bodyAlign = TextAlign.start, + int titleFlex = 1, + int bodyFlex = 2, + }) => + LunaTableContent._( + key: key, + title: title, + body: body, + bodyIsUrl: bodyIsUrl, + titleAlign: titleAlign, + bodyAlign: bodyAlign, + titleFlex: titleFlex, + bodyFlex: bodyFlex, + type: _Type.CONTENT, + ); @override Widget build(BuildContext context) { - return Padding( - child: Row( - children: [ - _title(), - SizedBox(width: LunaUI.MARGIN_DEFAULT.left, height: 0.0), - _subtitle(), - ], - crossAxisAlignment: CrossAxisAlignment.start, - ), - padding: padding, + if (type == _Type.SPACER) return SizedBox(height: spacerSize); + return Row( + children: [ + _title(), + _subtitle(), + ], + crossAxisAlignment: CrossAxisAlignment.start, ); } Widget _title() { return Expanded( - child: Text( - title?.toUpperCase() ?? LunaUI.TEXT_EMDASH, - textAlign: titleAlign, - style: TextStyle( - color: titleColor, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + child: Padding( + child: Text( + title?.toUpperCase() ?? LunaUI.TEXT_EMDASH, + textAlign: titleAlign, + style: const TextStyle( + color: LunaColours.grey, + fontSize: LunaUI.FONT_SIZE_H3, + ), + ), + padding: const EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE / 4, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 4, + right: LunaUI.DEFAULT_MARGIN_SIZE / 4, ), ), flex: titleFlex, @@ -60,24 +98,24 @@ class LunaTableContent extends StatelessWidget { Widget _subtitle() { return Expanded( child: InkWell( - child: Text( - body ?? LunaUI.TEXT_EMDASH, - textAlign: bodyAlign, - style: TextStyle( - color: bodyColor, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + child: Padding( + child: Text( + body ?? LunaUI.TEXT_EMDASH, + textAlign: bodyAlign, + style: const TextStyle( + color: LunaColours.white, + fontSize: LunaUI.FONT_SIZE_H3, + ), + ), + padding: const EdgeInsets.only( + top: LunaUI.DEFAULT_MARGIN_SIZE / 4, + bottom: LunaUI.DEFAULT_MARGIN_SIZE / 4, + left: LunaUI.DEFAULT_MARGIN_SIZE / 2, ), ), onTap: !bodyIsUrl ? null : body.lunaOpenGenericLink, - onLongPress: !bodyIsUrl - ? null - : () async { - await Clipboard.setData(ClipboardData(text: body)); - showLunaSuccessSnackBar( - title: 'Copied Content', - message: 'Copied link to the clipboard'); - }, - borderRadius: BorderRadius.circular(5.0), + onLongPress: !bodyIsUrl ? null : () => body.copyToClipboard(), + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), ), flex: bodyFlex, ); diff --git a/lib/core/ui/text.dart b/lib/core/ui/text.dart index 401387a5..b58ff8f6 100644 --- a/lib/core/ui/text.dart +++ b/lib/core/ui/text.dart @@ -27,21 +27,22 @@ class LunaText extends Text { @required String text, int maxLines = 1, bool softWrap = false, + @Deprecated('Dead property') bool darken, TextAlign textAlign = TextAlign.start, + TextOverflow overflow = TextOverflow.fade, Color color = Colors.white, - bool darken = false, }) => LunaText( text: text, key: key, maxLines: maxLines, - overflow: TextOverflow.fade, + overflow: overflow, softWrap: softWrap, textAlign: textAlign, style: TextStyle( - color: darken ? color.withOpacity(0.30) : color, + color: color, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_TITLE, + fontSize: LunaUI.FONT_SIZE_H2, ), ); @@ -51,9 +52,10 @@ class LunaText extends Text { @required String text, int maxLines = 1, bool softWrap = false, + @Deprecated('Dead property') bool darken, TextAlign textAlign = TextAlign.start, - Color color = Colors.white70, - bool darken = false, + TextOverflow overflow = TextOverflow.fade, + Color color = LunaColours.grey, FontStyle fontStyle = FontStyle.normal, }) => LunaText( @@ -62,10 +64,10 @@ class LunaText extends Text { softWrap: softWrap, maxLines: maxLines, textAlign: textAlign, - overflow: TextOverflow.fade, + overflow: overflow, style: TextStyle( - color: darken ? color.withOpacity(0.30) : color, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + color: color, + fontSize: LunaUI.FONT_SIZE_H3, fontStyle: fontStyle, ), ); diff --git a/lib/core/ui/text_span.dart b/lib/core/ui/text_span.dart new file mode 100644 index 00000000..ad28850d --- /dev/null +++ b/lib/core/ui/text_span.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaTextSpan extends TextSpan { + const LunaTextSpan.extended({ + @required String text, + }) : super( + text: text, + style: const TextStyle( + height: LunaBlock.SUBTITLE_HEIGHT / LunaUI.FONT_SIZE_H3, + ), + ); +} diff --git a/lib/core/ui/theme.dart b/lib/core/ui/theme.dart index ea8e4389..1292ab3f 100644 --- a/lib/core/ui/theme.dart +++ b/lib/core/ui/theme.dart @@ -35,10 +35,10 @@ class LunaTheme { brightness: Brightness.dark, canvasColor: LunaColours.primary, primaryColor: LunaColours.secondary, - highlightColor: LunaColours.splash.withOpacity(0.15), + highlightColor: LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH / 2), cardColor: LunaColours.secondary, - hoverColor: LunaColours.accent.withOpacity(0.15), - splashColor: LunaColours.accent.withOpacity(0.30), + hoverColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH / 2), + splashColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH), dialogBackgroundColor: LunaColours.secondary, toggleableActiveColor: LunaColours.accent, iconTheme: const IconThemeData( @@ -50,7 +50,7 @@ class LunaTheme { borderRadius: BorderRadius.all(Radius.circular(LunaUI.BORDER_RADIUS)), ), textStyle: TextStyle( - color: Colors.white70, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_SUBHEADER, ), preferBelow: true, @@ -68,10 +68,10 @@ class LunaTheme { brightness: Brightness.dark, canvasColor: Colors.black, primaryColor: Colors.black, - highlightColor: LunaColours.splash.withOpacity(0.15), + highlightColor: LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH / 2), cardColor: Colors.black, - hoverColor: LunaColours.accent.withOpacity(0.15), - splashColor: LunaColours.accent.withOpacity(0.30), + hoverColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH / 2), + splashColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH), dialogBackgroundColor: Colors.black, toggleableActiveColor: LunaColours.accent, iconTheme: const IconThemeData( @@ -83,10 +83,11 @@ class LunaTheme { borderRadius: const BorderRadius.all( Radius.circular(LunaUI.BORDER_RADIUS), ), - border: useAMOLEDBorders ? Border.all(color: Colors.white12) : null, + border: + useAMOLEDBorders ? Border.all(color: LunaColours.white10) : null, ), textStyle: const TextStyle( - color: Colors.white70, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_SUBHEADER, ), preferBelow: true, @@ -121,7 +122,7 @@ class LunaTheme { return TextButtonThemeData( style: ButtonStyle( overlayColor: MaterialStateProperty.all( - LunaColours.splash.withOpacity(0.30), + LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH), ), ), ); diff --git a/lib/core/utilities/changelog.dart b/lib/core/utilities/changelog.dart index 92416720..6a4e68a8 100644 --- a/lib/core/utilities/changelog.dart +++ b/lib/core/utilities/changelog.dart @@ -4,123 +4,141 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:package_info_plus/package_info_plus.dart'; -class LunaChangelog { - /// Check if the current build number is not equal to the stored build number. - /// - /// If they are not the same, stores the current build number and shows the changelog. - Future checkAndShowChangelog() async { - PackageInfo.fromPlatform().then((package) { - if (package != null && - AlertsDatabaseValue.CHANGELOG.data != package.buildNumber) { - AlertsDatabaseValue.CHANGELOG.put(package.buildNumber); - showChangelog(package.version, package.buildNumber); - } - }).catchError((error, stack) { - LunaLogger().error('Failed to fetch platform information', error, stack); - }); +class LunaChangelogSheet extends LunaBottomModalSheet { + _Changelog _changelog; + String _version; + String _buildNumber; + + static Future delayedShow([ + Duration duration = const Duration(seconds: 1), + bool checkBuildNumber = true, + ]) async { + Future.delayed( + duration, + () async => LunaChangelogSheet().show( + context: LunaState.navigatorKey.currentContext, + checkBuildNumber: checkBuildNumber, + ), + ); } - /// Load the changelog asset and show a bottom modal sheet with the changelog. - Future showChangelog(String version, String buildNumber) async { - await DefaultAssetBundle.of(LunaState.navigatorKey.currentContext) - .loadString('assets/changelog.json') - .then((data) { - _Changelog changelog = _Changelog.fromJson(json.decode(data)); - // TODO: Abstract this - LunaBottomModalSheet().showModal( - context: LunaState.navigatorKey.currentContext, - builder: (context) => LunaListViewModal( - children: [ - if (version != null && buildNumber != null) - LunaHeader( - text: '$version ($buildNumber)', subtitle: changelog.motd), - if (version == null || buildNumber == null) - LunaHeader(text: 'LunaSea', subtitle: changelog.motd), - if ((changelog.changesNew?.length ?? 0) != 0) - const LunaHeader(text: 'New'), - if ((changelog.changesNew?.length ?? 0) != 0) - LunaTableCard( - content: List.generate( - changelog.changesNew.length, - (index) => LunaTableContent( - title: changelog.changesNew[index].module, - body: changelog.changesNew[index].changes - .fold( - '', - (data, object) => - data += '${LunaUI.TEXT_BULLET}\t$object\n') - .trim(), - ), - ), - ), - if ((changelog.changesTweaks?.length ?? 0) != 0) - const LunaHeader(text: 'Tweaks'), - if ((changelog.changesTweaks?.length ?? 0) != 0) - LunaTableCard( - content: List.generate( - changelog.changesTweaks.length, - (index) => LunaTableContent( - title: changelog.changesTweaks[index].module, - body: changelog.changesTweaks[index].changes - .fold( - '', - (data, object) => - data += '${LunaUI.TEXT_BULLET}\t$object\n') - .trim(), - ), - ), - ), - if ((changelog.changesFixes?.length ?? 0) != 0) - const LunaHeader(text: 'Fixes'), - if ((changelog.changesFixes?.length ?? 0) != 0) - LunaTableCard( - content: List.generate( - changelog.changesFixes.length, - (index) => LunaTableContent( - title: changelog.changesFixes[index].module, - body: changelog.changesFixes[index].changes - .fold( - '', - (data, object) => - data += '${LunaUI.TEXT_BULLET}\t$object\n') - .trim(), - ), - ), - ), - if ((changelog.changesPlatform?.length ?? 0) != 0) - const LunaHeader(text: 'Platform-Specific'), - if ((changelog.changesPlatform?.length ?? 0) != 0) - LunaTableCard( - content: List.generate( - changelog.changesPlatform.length, - (index) => LunaTableContent( - title: changelog.changesPlatform[index].module, - body: changelog.changesPlatform[index].changes - .fold( - '', - (data, object) => - data += '${LunaUI.TEXT_BULLET}\t$object\n') - .trim(), - ), - ), - ), - const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2), - ], - actionBar: LunaBottomActionBar( - actions: [ - LunaButton.text( - text: 'Full Changelog', - icon: Icons.track_changes_rounded, - onTap: LunaLinks.CHANGELOG.launch, - ), - ], + @override + Future show({ + @required BuildContext context, + Widget Function(BuildContext context) builder, + bool checkBuildNumber = false, + }) async { + try { + _changelog = + await DefaultAssetBundle.of(LunaState.navigatorKey.currentContext) + .loadString('assets/changelog.json') + .then((data) => _Changelog.fromJson(json.decode(data))); + + PackageInfo _package = await PackageInfo.fromPlatform(); + if (_package == null) return; + + _version = _package.version; + _buildNumber = _package.buildNumber; + + if (checkBuildNumber) { + AlertsDatabaseValue _db = AlertsDatabaseValue.CHANGELOG; + if (_db.data == _package.buildNumber) return; + _db.put(_package.buildNumber); + } + + return this.showModal(context: context, builder: builder); + } catch (error, stack) { + LunaLogger().error('Failed to show changelog sheet', error, stack); + } + } + + @override + Widget builder(BuildContext context) { + return LunaListViewModal( + children: [ + if (_version != null && _buildNumber != null) + LunaHeader( + text: '$_version ($_buildNumber)', subtitle: _changelog.motd), + if (_version == null || _buildNumber == null) + LunaHeader(text: 'LunaSea', subtitle: _changelog.motd), + if ((_changelog.changesNew?.length ?? 0) != 0) + const LunaHeader(text: 'New'), + if ((_changelog.changesNew?.length ?? 0) != 0) + LunaTableCard( + content: List.generate( + _changelog.changesNew.length, + (index) { + String _body = _changelog.changesNew[index].changes + .fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n') + .trim(); + return LunaTableContent( + title: _changelog.changesNew[index].module, + body: _body, + ); + }, + ), ), - ), - ); - }).catchError((error, stack) { - LunaLogger().error('Failed to fetched changelog asset', error, stack); - showLunaErrorSnackBar(title: 'Failed to Load Changelog', error: error); - }); + if ((_changelog.changesTweaks?.length ?? 0) != 0) + const LunaHeader(text: 'Tweaks'), + if ((_changelog.changesTweaks?.length ?? 0) != 0) + LunaTableCard( + content: List.generate( + _changelog.changesTweaks.length, + (index) { + String _body = _changelog.changesTweaks[index].changes + .fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n') + .trim(); + return LunaTableContent( + title: _changelog.changesTweaks[index].module, + body: _body, + ); + }, + ), + ), + if ((_changelog.changesFixes?.length ?? 0) != 0) + const LunaHeader(text: 'Fixes'), + if ((_changelog.changesFixes?.length ?? 0) != 0) + LunaTableCard( + content: List.generate( + _changelog.changesFixes.length, (index) { + String _body = _changelog.changesFixes[index].changes + .fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n') + .trim(); + return LunaTableContent( + title: _changelog.changesFixes[index].module, + body: _body, + ); + }), + ), + if ((_changelog.changesPlatform?.length ?? 0) != 0) + const LunaHeader(text: 'Platform-Specific'), + if ((_changelog.changesPlatform?.length ?? 0) != 0) + LunaTableCard( + content: List.generate( + _changelog.changesPlatform.length, + (index) { + String _body = _changelog.changesPlatform[index].changes + .fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n') + .trim(); + return LunaTableContent( + title: _changelog.changesPlatform[index].module, + body: _body, + ); + }, + ), + ), + const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2), + ], + actionBar: LunaBottomActionBar( + actions: [ + LunaButton.text( + text: 'Full Changelog', + icon: Icons.track_changes_rounded, + onTap: LunaLinks.CHANGELOG.launch, + ), + ], + ), + ); } } diff --git a/lib/core/utilities/logger.dart b/lib/core/utilities/logger.dart index e790e986..f55aa850 100644 --- a/lib/core/utilities/logger.dart +++ b/lib/core/utilities/logger.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; class LunaLogger { @@ -10,13 +10,10 @@ class LunaLogger { /// Initialize the logger by setting the timestamp format and capturing errors on [FlutterError.onError]. void initialize() { - LunaFirebaseAnalytics().setEnabledState(); - LunaFirebaseCrashlytics().setEnabledState(); FlutterError.onError = (FlutterErrorDetails details, {bool forceReport = false}) async { if (kDebugMode) FlutterError.dumpErrorToConsole(details, forceReport: forceReport); - recordCrashlyticsFlutterError(details); Zone.current.handleUncaughtError(details.exception, details.stack); }; _compactDatabase(); @@ -41,7 +38,7 @@ class LunaLogger { if (log != null) logs.add(log.toMap()); }); // Create a string - JsonEncoder encoder = const JsonEncoder.withIndent(' '); + JsonEncoder encoder = JsonEncoder.withIndent(' '.repeat(4)); String data = encoder.convert(logs); return data; } @@ -49,36 +46,6 @@ class LunaLogger { /// Clear all logs currently saved to the database. Future clearLogs() async => Database().clearLogsBox(); - /// Records a Crashlytics error if it passes the following checks: - /// - In release mode - /// - Crashlytics is enabled in the instance - /// - Crashlytics is enabled in Hive - /// - The error is not a DioError - Future recordCrashlyticsError( - dynamic error, StackTrace stackTrace) async { - if (kReleaseMode && - LunaFirebaseCrashlytics.isPlatformCompatible && - LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled && - LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data && - error is! DioError) - LunaFirebaseCrashlytics.instance.recordError(error, stackTrace); - } - - /// Records a Crashlytics flutter error if it passes the following checks: - /// - In release mode - /// - Crashlytics is enabled in the instance - /// - Crashlytics is enabled in Hive - /// - The error is not a DioError - Future recordCrashlyticsFlutterError( - FlutterErrorDetails details) async { - if (kReleaseMode && - LunaFirebaseCrashlytics.isPlatformCompatible && - LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled && - LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data && - error is! DioError) - LunaFirebaseCrashlytics.instance.recordFlutterError(details); - } - /// Log a new debug-level log. void debug(String message) { LunaLogHiveObject log = LunaLogHiveObject.withMessage( @@ -101,26 +68,35 @@ class LunaLogger { /// Log a new error-level log. void error(String message, dynamic error, StackTrace stackTrace) { - recordCrashlyticsError(error, stackTrace); - LunaLogHiveObject log = LunaLogHiveObject.withError( - type: LunaLogType.ERROR, - message: message, - error: error, - stackTrace: stackTrace, - ); - Database.logsBox.add(log); + if (error is! NetworkImageLoadException) { + LunaLogHiveObject log = LunaLogHiveObject.withError( + type: LunaLogType.ERROR, + message: message, + error: error, + stackTrace: stackTrace, + ); + if (kDebugMode) { + print(message); + print(error); + print(stackTrace); + } + Database.logsBox.add(log); + } } /// Log a new critical-level log. void critical(dynamic error, StackTrace stackTrace) { - if (error is! DioError) { - recordCrashlyticsError(error, stackTrace); + if (error is! NetworkImageLoadException) { LunaLogHiveObject log = LunaLogHiveObject.withError( type: LunaLogType.CRITICAL, message: error?.toString() ?? 'Unknown critical error', error: error, stackTrace: stackTrace, ); + if (kDebugMode) { + print(error); + print(stackTrace); + } Database.logsBox.add(log); } } diff --git a/lib/core/widgets.dart b/lib/core/widgets.dart deleted file mode 100644 index ec70d127..00000000 --- a/lib/core/widgets.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'widgets/action_bar_card.dart'; -export 'widgets/expandable_list_tile.dart'; -export 'widgets/five_line_card_with_poster.dart'; -export 'widgets/four_line_card_with_poster.dart'; -export 'widgets/reorderable_list_dragger.dart'; -export 'widgets/two_line_card_with_poster.dart'; diff --git a/lib/core/widgets/expandable_list_tile.dart b/lib/core/widgets/expandable_list_tile.dart deleted file mode 100644 index 2b058c9e..00000000 --- a/lib/core/widgets/expandable_list_tile.dart +++ /dev/null @@ -1,189 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaExpandableListTile extends StatefulWidget { - final String title; - final List collapsedSubtitles; - final Widget collapsedTrailing; - final Widget collapsedLeading; - final Color backgroundColor; - final Function onLongPress; - final List expandedHighlightedNodes; - final List expandedTableContent; - final List expandedTableButtons; - final Widget expandedCustomWidget; - final bool initialExpanded; - - /// Create a [LunaExpandableListTile] which is a list tile that expands into a table-style card. - /// - /// If [expandedWidget] is supplied, that widget is used as the body within the expanded card. - /// Any - LunaExpandableListTile({ - Key key, - @required this.title, - @required this.collapsedSubtitles, - this.collapsedTrailing, - this.collapsedLeading, - this.onLongPress, - this.expandedHighlightedNodes, - this.expandedTableContent, - this.expandedTableButtons, - this.expandedCustomWidget, - this.backgroundColor, - this.initialExpanded = false, - }) : super(key: key) { - assert(collapsedSubtitles != null); - if (expandedCustomWidget == null) { - assert(expandedTableContent != null); - } - } - - @override - State createState() => _State(); -} - -class _State extends State { - ExpandableController controller; - - @override - void initState() { - super.initState(); - controller = ExpandableController(initialExpanded: widget.initialExpanded); - } - - @override - void dispose() { - controller?.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ExpandableNotifier( - controller: controller, - child: Expandable( - collapsed: collapsed(), - expanded: expanded(), - ), - ); - } - - List _parseSubtitles() { - if (widget.collapsedSubtitles.isEmpty) return []; - List _result = []; - for (int i = 0; i < widget.collapsedSubtitles.length; i++) { - _result.add(widget.collapsedSubtitles[i]); - if (i != widget.collapsedSubtitles.length - 1) { - _result.add(const TextSpan(text: '\n')); - } - } - return _result; - } - - Widget collapsed() { - return LunaListTile( - context: context, - title: LunaText.title(text: widget.title ?? LunaUI.TEXT_EMDASH), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: Colors.white70, - ), - children: _parseSubtitles(), - ), - overflow: TextOverflow.fade, - softWrap: false, - maxLines: widget.collapsedSubtitles.length, - ), - onTap: controller.toggle, - onLongPress: widget.onLongPress, - contentPadding: widget.collapsedSubtitles.length > 1, - trailing: widget.collapsedTrailing, - leading: widget.collapsedLeading, - color: widget.backgroundColor, - ); - } - - Widget expanded() { - Widget child = widget.expandedCustomWidget ?? - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - child: LunaText.title( - text: widget.title ?? LunaUI.TEXT_EMDASH, - softWrap: true, - maxLines: 8, - ), - padding: const EdgeInsets.only( - bottom: 8.0, left: 12.0, right: 12.0, top: 10.0), - ), - if (widget.expandedHighlightedNodes != null) - Padding( - child: Wrap( - direction: Axis.horizontal, - spacing: 6.0, - runSpacing: 6.0, - children: widget.expandedHighlightedNodes, - ), - padding: const EdgeInsets.only( - bottom: 8.0, left: 12.0, right: 12.0, top: 0.0), - ), - Padding( - child: Column( - children: [ - ...widget.expandedTableContent - .map((content) => Padding( - child: content, - padding: const EdgeInsets.symmetric( - horizontal: 12.0), - )) - .toList(), - if (widget.expandedTableButtons != null) - Padding( - child: Wrap( - children: [ - ...List.generate( - widget.expandedTableButtons.length, - (index) { - double widthFactor = 0.5; - if (index == - (widget.expandedTableButtons.length - - 1) && - widget.expandedTableButtons.length.isOdd) - widthFactor = 1; - return FractionallySizedBox( - child: widget.expandedTableButtons[index], - widthFactor: widthFactor, - ); - }), - ], - ), - padding: - const EdgeInsets.symmetric(horizontal: 6.0), - ), - ], - ), - padding: const EdgeInsets.only(bottom: 6.0), - ), - ], - ), - ), - ], - ); - return LunaCard( - context: context, - child: InkWell( - child: child, - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - onTap: controller.toggle, - onLongPress: widget.onLongPress, - ), - color: widget.backgroundColor, - ); - } -} diff --git a/lib/core/widgets/five_line_card_with_poster.dart b/lib/core/widgets/five_line_card_with_poster.dart deleted file mode 100644 index 8ac4e6bc..00000000 --- a/lib/core/widgets/five_line_card_with_poster.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaFiveLineCardWithPoster extends StatelessWidget { - final Function onTap; - final Function onLongPress; - final String backgroundUrl; - final String posterUrl; - final String posterPlaceholder; - final String title; - final TextSpan subtitle1; - final TextSpan subtitle2; - final TextSpan subtitle3; - final TextSpan subtitle4; - final int subtitle1MaxLines; - final int subtitle2MaxLines; - final int subtitle3MaxLines; - final int subtitle4MaxLines; - final Widget customSubtitle1; - final Widget customSubtitle2; - final Widget customSubtitle3; - final Widget customSubtitle4; - final bool darken; - final Color titleColor; - final Map posterHeaders; - final LunaIconButton trailing; - static const double itemExtent = 105.0; - static const double _padding = 8.0; - - const LunaFiveLineCardWithPoster({ - Key key, - @required this.posterPlaceholder, - @required this.posterUrl, - @required this.posterHeaders, - @required this.title, - this.backgroundUrl, - this.titleColor = Colors.white, - this.subtitle1, - this.subtitle2, - this.subtitle3, - this.subtitle4, - this.subtitle1MaxLines = 1, - this.subtitle2MaxLines = 1, - this.subtitle3MaxLines = 1, - this.subtitle4MaxLines = 1, - this.customSubtitle1, - this.customSubtitle2, - this.customSubtitle3, - this.customSubtitle4, - this.darken = false, - this.onTap, - this.onLongPress, - this.trailing, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - child: Row( - children: [ - _poster(context), - _body(context), - if (trailing != null) _trailing(), - ], - ), - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - ), - decoration: backgroundUrl == null - ? null - : LunaCardDecoration( - uri: backgroundUrl, - headers: posterHeaders, - ), - ); - } - - Widget _poster(BuildContext context) { - return LunaNetworkImage( - url: posterUrl, - placeholderAsset: posterPlaceholder, - height: itemExtent, - width: itemExtent / 1.5, - headers: posterHeaders, - ); - } - - Widget _body(BuildContext context) { - return Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title(), - if (customSubtitle1 != null) customSubtitle1, - if (customSubtitle1 == null && subtitle1 != null) - _subtitle(subtitle1, subtitle1MaxLines), - if (customSubtitle2 != null) customSubtitle2, - if (customSubtitle2 == null && subtitle2 != null) - _subtitle(subtitle2, subtitle2MaxLines), - if (customSubtitle3 != null) customSubtitle3, - if (customSubtitle3 == null && subtitle3 != null) - _subtitle(subtitle3, subtitle3MaxLines), - if (customSubtitle4 != null) customSubtitle4, - if (customSubtitle4 == null && subtitle4 != null) - _subtitle(subtitle4, subtitle4MaxLines), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (itemExtent - (_padding * 2)), - ), - padding: const EdgeInsets.all(_padding), - ), - ); - } - - Widget _title() { - return LunaText.title( - text: title, - color: titleColor, - darken: darken, - maxLines: 1, - ); - } - - Widget _subtitle(TextSpan text, int maxLines) { - return RichText( - text: TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: darken ? Colors.white30 : Colors.white70, - ), - children: [text], - ), - overflow: TextOverflow.fade, - softWrap: maxLines == 1 ? false : true, - maxLines: maxLines, - ); - } - - Widget _trailing() { - return Padding( - padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right), - child: SizedBox( - width: 48.0, - height: itemExtent, - child: trailing, - ), - ); - } -} diff --git a/lib/core/widgets/four_line_card_with_poster.dart b/lib/core/widgets/four_line_card_with_poster.dart deleted file mode 100644 index e88b7165..00000000 --- a/lib/core/widgets/four_line_card_with_poster.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaFourLineCardWithPoster extends StatelessWidget { - final Function onTap; - final Function onLongPress; - final String backgroundUrl; - final String posterUrl; - final String posterPlaceholder; - final String title; - final TextSpan subtitle1; - final TextSpan subtitle2; - final TextSpan subtitle3; - final int subtitle1MaxLines; - final int subtitle2MaxLines; - final int subtitle3MaxLines; - final Widget customSubtitle1; - final Widget customSubtitle2; - final Widget customSubtitle3; - final bool darken; - final Color titleColor; - final Map posterHeaders; - final LunaIconButton trailing; - static const double itemExtent = 90.0; - static const double padding = 8.0; - - const LunaFourLineCardWithPoster({ - Key key, - @required this.posterPlaceholder, - @required this.posterUrl, - @required this.posterHeaders, - @required this.title, - this.backgroundUrl, - this.titleColor = Colors.white, - this.subtitle1, - this.subtitle2, - this.subtitle3, - this.subtitle1MaxLines = 1, - this.subtitle2MaxLines = 1, - this.subtitle3MaxLines = 1, - this.customSubtitle1, - this.customSubtitle2, - this.customSubtitle3, - this.darken = false, - this.onTap, - this.onLongPress, - this.trailing, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - child: Row( - children: [ - _poster(context), - _body(context), - if (trailing != null) _trailing(), - ], - ), - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - ), - decoration: backgroundUrl == null - ? null - : LunaCardDecoration( - uri: backgroundUrl, - headers: posterHeaders, - ), - height: itemExtent, - ); - } - - Widget _poster(BuildContext context) { - return LunaNetworkImage( - url: posterUrl, - placeholderAsset: posterPlaceholder, - height: itemExtent, - width: itemExtent / 1.5, - headers: posterHeaders, - ); - } - - Widget _body(BuildContext context) { - return Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title(), - if (customSubtitle1 != null) customSubtitle1, - if (customSubtitle1 == null && subtitle1 != null) - _subtitle(subtitle1, subtitle1MaxLines), - if (customSubtitle2 != null) customSubtitle2, - if (customSubtitle2 == null && subtitle2 != null) - _subtitle(subtitle2, subtitle2MaxLines), - if (customSubtitle3 != null) customSubtitle3, - if (customSubtitle3 == null && subtitle3 != null) - _subtitle(subtitle3, subtitle3MaxLines), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (itemExtent - (padding * 2)), - ), - padding: const EdgeInsets.all(padding), - ), - ); - } - - Widget _title() { - return LunaText.title( - text: title, - color: titleColor, - darken: darken, - maxLines: 1, - ); - } - - Widget _subtitle(TextSpan text, int maxLines) { - return RichText( - text: TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: darken ? Colors.white30 : Colors.white70, - ), - children: [text], - ), - overflow: TextOverflow.fade, - softWrap: maxLines == 1 ? false : true, - maxLines: maxLines, - ); - } - - Widget _trailing() { - return Padding( - padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right), - child: SizedBox( - width: 48.0, - height: itemExtent, - child: trailing, - ), - ); - } -} diff --git a/lib/core/widgets/two_line_card_with_poster.dart b/lib/core/widgets/two_line_card_with_poster.dart deleted file mode 100644 index 0a7ff2e6..00000000 --- a/lib/core/widgets/two_line_card_with_poster.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaTwoLineCardWithPoster extends StatelessWidget { - final Function onTap; - final Function onLongPress; - final String backgroundUrl; - final String posterUrl; - final String posterPlaceholder; - final String title; - final TextSpan subtitle1; - final TextSpan subtitle2; - final Widget customSubtitle1; - final Widget customSubtitle2; - final bool darken; - final Color titleColor; - final Map posterHeaders; - final LunaIconButton trailing; - static const double itemExtent = 67.0; - static const double _padding = 8.0; - - const LunaTwoLineCardWithPoster({ - Key key, - @required this.posterPlaceholder, - @required this.posterUrl, - @required this.posterHeaders, - @required this.title, - this.backgroundUrl, - this.titleColor = Colors.white, - this.subtitle1, - this.subtitle2, - this.customSubtitle1, - this.customSubtitle2, - this.darken = false, - this.onTap, - this.onLongPress, - this.trailing, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - child: Row( - children: [ - _poster(context), - _body(context), - if (trailing != null) _trailing(), - ], - ), - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - ), - decoration: backgroundUrl == null - ? null - : LunaCardDecoration( - uri: backgroundUrl, - headers: posterHeaders, - ), - ); - } - - Widget _poster(BuildContext context) { - return LunaNetworkImage( - url: posterUrl, - placeholderAsset: posterPlaceholder, - height: itemExtent, - width: itemExtent, - headers: posterHeaders, - ); - } - - Widget _body(BuildContext context) { - return Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title(), - if (customSubtitle1 != null) customSubtitle1, - if (customSubtitle1 == null && subtitle1 != null) - _subtitle(subtitle1), - if (customSubtitle2 != null) customSubtitle2, - if (customSubtitle2 == null && subtitle2 != null) - _subtitle(subtitle2), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (itemExtent - (_padding * 2)), - ), - padding: const EdgeInsets.all(_padding), - ), - ); - } - - Widget _title() { - return LunaText.title( - text: title, - color: titleColor, - darken: darken, - maxLines: 1, - ); - } - - Widget _subtitle(TextSpan text) { - return RichText( - text: TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: darken ? Colors.white30 : Colors.white70, - ), - children: [text], - ), - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 1, - ); - } - - Widget _trailing() { - return Padding( - padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right), - child: SizedBox( - width: 48.0, - height: itemExtent, - child: trailing, - ), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 5db35e7b..7867a7f0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,13 +3,28 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:lunasea/core.dart'; +import 'modules/dashboard.dart'; + /// LunaSea Entry Point: Initialize & Run Application /// /// Runs app in guarded zone to attempt to capture fatal (crashing) errors Future main() async { + WidgetsFlutterBinding.ensureInitialized(); runZonedGuarded( () async { - await _init(); + //LunaSea initialization + await Database().initialize(); + await LunaFirebase().initialize(); + LunaLogger().initialize(); + LunaTheme().initialize(); + LunaDesktopWindow().initialize(); + LunaNetworking().initialize(); + LunaImageCache().initialize(); + LunaRouter().initialize(); + await LunaInAppPurchases().initialize(); + await LunaLocalization().initialize(); + + // Launch application LunaLocalization localization = LunaLocalization(); return runApp( EasyLocalization( @@ -26,24 +41,39 @@ Future main() async { ); } -/// Initializes LunaSea before running the BIOS Widget. -Future _init() async { - WidgetsFlutterBinding.ensureInitialized(); - //LunaSea initialization - await Database().initialize(); - await LunaFirebase().initialize(); - LunaLogger().initialize(); - LunaTheme().initialize(); - LunaDesktopWindow().initialize(); - LunaNetworking().initialize(); - LunaImageCache().initialize(); - LunaRouter().initialize(); - await LunaInAppPurchases().initialize(); - await LunaLocalization().initialize(); +class LunaBIOS extends StatelessWidget { + const LunaBIOS({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LunaState.providers( + child: ValueListenableBuilder( + valueListenable: Database.lunaSeaBox.listenable(keys: [ + LunaDatabaseValue.THEME_AMOLED.key, + LunaDatabaseValue.THEME_AMOLED_BORDER.key, + ]), + builder: (context, box, _) { + return MaterialApp( + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + routes: LunaRouter().routes, + onGenerateRoute: LunaRouter.router.generator, + navigatorKey: LunaState.navigatorKey, + darkTheme: LunaTheme().activeTheme(), + theme: LunaTheme().activeTheme(), + title: 'LunaSea', + ); + }, + ), + ); + } } -class LunaBIOS extends StatefulWidget { - const LunaBIOS({ +class LunaOS extends StatefulWidget { + const LunaOS({ Key key, }) : super(key: key); @@ -51,73 +81,35 @@ class LunaBIOS extends StatefulWidget { State createState() => _State(); } -class _State extends State { - StreamSubscription _firebaseOnMessageListener; - StreamSubscription _firebaseOnMessageOpenedAppListener; - +class _State extends State { @override void initState() { super.initState(); SchedulerBinding.instance.addPostFrameCallback((_) => _boot()); } - @override - void dispose() { - Future.wait([ - if (_firebaseOnMessageListener != null) - _firebaseOnMessageListener.cancel(), - if (_firebaseOnMessageOpenedAppListener != null) - _firebaseOnMessageOpenedAppListener.cancel(), - Database().deinitialize(), - LunaInAppPurchases().deinitialize(), - ]).then((_) => super.dispose()); + Future _initNotifications() async { + LunaFirebaseMessaging _messaging = LunaFirebaseMessaging(); + LunaFirebaseFirestore _firestore = LunaFirebaseFirestore(); + await _messaging.requestNotificationPermissions(); + + _firestore.addDeviceToken(); + _messaging.checkAndHandleInitialMessage(); + _messaging.registerOnMessageListener(); + _messaging.registerOnMessageOpenedAppListener(); } - /// Runs the first-step boot sequence that is required for widgets Future _boot() async { - // Initialize notifications - // - Request notification permission - // - Add device token to Firebase (if logged in) - // - Check and handle initial message (if found) - // - Add notification listeners for onMessage and onMessageOpenedApp - await LunaFirebaseMessaging().requestNotificationPermissions(); - LunaFirebaseFirestore().addDeviceToken(); - LunaFirebaseMessaging().checkAndHandleInitialMessage(); - _firebaseOnMessageListener = LunaFirebaseMessaging().onMessageListener(); - _firebaseOnMessageOpenedAppListener = - LunaFirebaseMessaging().onMessageOpenedAppListener(); - // Remaining boot sequence - Intl.defaultLocale = - LunaLanguage.ENGLISH.fromLocale(context.locale)?.languageTag ?? 'en'; + _initNotifications(); + + String tag = LunaLanguage.ENGLISH.fromLocale(context.locale)?.languageTag; + tag ??= LunaLanguage.ENGLISH.languageTag; + Intl.defaultLocale = tag; + LunaQuickActions().initialize(); - LunaChangelog().checkAndShowChangelog(); - LunaFirebaseAnalytics().appOpened(); + LunaChangelogSheet.delayedShow(); } @override - Widget build(BuildContext context) => LunaState.providers( - child: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [ - LunaDatabaseValue.THEME_AMOLED.key, - LunaDatabaseValue.THEME_AMOLED_BORDER.key, - ]), - builder: (context, box, _) { - return MaterialApp( - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - routes: LunaRouter().routes, - onGenerateRoute: LunaRouter.router.generator, - navigatorKey: LunaState.navigatorKey, - navigatorObservers: [ - if (LunaFirebaseAnalytics.isPlatformCompatible) - LunaFirebaseAnalytics.observer, - ], - darkTheme: LunaTheme().activeTheme(), - theme: LunaTheme().activeTheme(), - title: 'LunaSea', - ); - }, - ), - ); + Widget build(BuildContext context) => DashboardHomeRouter().widget(); } diff --git a/lib/modules/dashboard/core/api/api.dart b/lib/modules/dashboard/core/api/api.dart index 024dafb1..16e43285 100644 --- a/lib/modules/dashboard/core/api/api.dart +++ b/lib/modules/dashboard/core/api/api.dart @@ -22,8 +22,8 @@ class CalendarAPI { ); } - Future> getUpcoming(DateTime today) async { - Map _upcoming = {}; + Future>> getUpcoming(DateTime today) async { + Map> _upcoming = {}; if (lidarr['enabled'] && DashboardDatabaseValue.CALENDAR_ENABLE_LIDARR.data) await _getLidarrUpcoming(_upcoming, today); if (radarr['enabled'] && DashboardDatabaseValue.CALENDAR_ENABLE_RADARR.data) @@ -34,7 +34,9 @@ class CalendarAPI { } Future _getLidarrUpcoming( - Map map, DateTime today) async { + Map> map, + DateTime today, + ) async { Map _headers = Map.from(lidarr['headers']); Dio _client = Dio( @@ -43,7 +45,7 @@ class CalendarAPI { queryParameters: { if (lidarr['key'] != '') 'apikey': lidarr['key'], 'start': _startDate(today), - 'end': _startDate(today), + 'end': _endDate(today), }, headers: _headers, followRedirects: true, @@ -56,12 +58,15 @@ class CalendarAPI { DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.lunaFloor; if (date != null && _isDateWithinBounds(date, today)) { - List day = map[date] ?? []; + List day = map[date] ?? []; day.add(CalendarLidarrData( id: entry['id'] ?? 0, title: entry['artist']['artistName'] ?? 'Unknown Artist', albumTitle: entry['title'] ?? 'Unknown Album Title', artistId: entry['artist']['id'] ?? 0, + totalTrackCount: entry['statistics'] != null + ? entry['statistics']['totalTrackCount'] ?? 0 + : 0, hasAllFiles: (entry['statistics'] != null ? entry['statistics']['percentOfTracks'] ?? 0 : 0) == @@ -74,7 +79,9 @@ class CalendarAPI { } Future _getRadarrUpcoming( - Map map, DateTime today) async { + Map> map, + DateTime today, + ) async { Map _headers = Map.from(radarr['headers']); Dio _client = Dio( @@ -109,7 +116,7 @@ class CalendarAPI { ? digitalRelease : physicalRelease; if (release != null && _isDateWithinBounds(release, today)) { - List day = map[release] ?? []; + List day = map[release] ?? []; day.add(CalendarRadarrData( id: entry['id'] ?? 0, title: entry['title'] ?? 'Unknown Title', @@ -119,6 +126,7 @@ class CalendarAPI { : '', year: entry['year'] ?? 0, runtime: entry['runtime'] ?? 0, + studio: entry['studio'] ?? LunaUI.TEXT_EMDASH, )); map[release] = day; } @@ -128,7 +136,9 @@ class CalendarAPI { } Future _getSonarrUpcoming( - Map map, DateTime today) async { + Map> map, + DateTime today, + ) async { Map _headers = Map.from(sonarr['headers']); Dio _client = Dio( @@ -153,7 +163,7 @@ class CalendarAPI { DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.lunaFloor; if (date != null && _isDateWithinBounds(date, today)) { - List day = map[date] ?? []; + List day = map[date] ?? []; day.add(CalendarSonarrData( id: entry['id'] ?? 0, seriesID: entry['seriesId'] ?? 0, diff --git a/lib/modules/dashboard/core/api/data/abstract.dart b/lib/modules/dashboard/core/api/data/abstract.dart index 7318a699..89d52642 100644 --- a/lib/modules/dashboard/core/api/data/abstract.dart +++ b/lib/modules/dashboard/core/api/data/abstract.dart @@ -3,9 +3,12 @@ import 'package:flutter/material.dart'; abstract class CalendarData { int id; String title; + List get body; - TextSpan get subtitle; String get bannerURI; + String backgroundUrl(BuildContext context); + String posterUrl(BuildContext context); + Widget trailing(BuildContext context); Future enterContent(BuildContext context); Future trailingOnPress(BuildContext context); diff --git a/lib/modules/dashboard/core/api/data/lidarr.dart b/lib/modules/dashboard/core/api/data/lidarr.dart index ca3493d4..8fdeb29e 100644 --- a/lib/modules/dashboard/core/api/data/lidarr.dart +++ b/lib/modules/dashboard/core/api/data/lidarr.dart @@ -7,6 +7,7 @@ class CalendarLidarrData extends CalendarData { final Map api = Database.currentProfileObject.getLidarr(); String albumTitle; int artistId; + int totalTrackCount; bool hasAllFiles; CalendarLidarrData({ @@ -15,6 +16,7 @@ class CalendarLidarrData extends CalendarData { @required this.albumTitle, @required this.artistId, @required this.hasAllFiles, + @required this.totalTrackCount, }) : super(id, title); @override @@ -27,36 +29,35 @@ class CalendarLidarrData extends CalendarData { } @override - TextSpan get subtitle => TextSpan( + List get body { + return [ + TextSpan( + text: albumTitle, style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontStyle: FontStyle.italic, ), - children: [ - TextSpan( - text: albumTitle, - style: const TextStyle( - fontStyle: FontStyle.italic, - ), + ), + TextSpan( + text: totalTrackCount == 1 ? '1 Track' : '$totalTrackCount Tracks', + ), + if (!hasAllFiles) + const TextSpan( + text: 'Not Downloaded', + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.red, ), - if (!hasAllFiles) - const TextSpan( - text: '\nNot Downloaded', - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.red, - ), - ), - if (hasAllFiles) - const TextSpan( - text: '\nDownloaded', - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.accent, - ), - ) - ], - ); + ), + if (hasAllFiles) + const TextSpan( + text: 'Downloaded', + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.accent, + ), + ) + ]; + } @override Future enterContent(BuildContext context) async => @@ -94,4 +95,22 @@ class CalendarLidarrData extends CalendarData { title: albumTitle, ), ); + + @override + String backgroundUrl(BuildContext context) { + return api['enabled'] + ? (api['host'] as String).endsWith('/') + ? '${api['host']}api/v1/MediaCover/Artist/$artistId/fanart-360.jpg?apikey=${api['key']}' + : '${api['host']}/api/v1/MediaCover/Artist/$artistId/fanart-360.jpg?apikey=${api['key']}' + : ''; + } + + @override + String posterUrl(BuildContext context) { + return api['enabled'] + ? (api['host'] as String).endsWith('/') + ? '${api['host']}api/v1/MediaCover/Artist/$artistId/poster-500.jpg?apikey=${api['key']}' + : '${api['host']}/api/v1/MediaCover/Artist/$artistId/poster-500.jpg?apikey=${api['key']}' + : ''; + } } diff --git a/lib/modules/dashboard/core/api/data/radarr.dart b/lib/modules/dashboard/core/api/data/radarr.dart index 8eeb17d2..8681bc77 100644 --- a/lib/modules/dashboard/core/api/data/radarr.dart +++ b/lib/modules/dashboard/core/api/data/radarr.dart @@ -9,6 +9,7 @@ class CalendarRadarrData extends CalendarData { String fileQualityProfile; int year; int runtime; + String studio; CalendarRadarrData({ @required int id, @@ -17,49 +18,46 @@ class CalendarRadarrData extends CalendarData { @required this.fileQualityProfile, @required this.year, @required this.runtime, + @required this.studio, }) : super(id, title); @override - TextSpan get subtitle => TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), + List get body { + return [ + TextSpan( children: [ - TextSpan( - children: [ - TextSpan(text: year.toString()), - TextSpan(text: LunaUI.TEXT_BULLET.lunaPad(1, '\t')), - TextSpan(text: runtime.lunaRuntime()), - ], - ), - const TextSpan(text: '\n'), - if (!hasFile) - const TextSpan( - text: 'Not Downloaded', - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.red, - ), - ), - if (hasFile) - TextSpan( - text: 'Downloaded ($fileQualityProfile)', - style: const TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.accent, - ), - ) + TextSpan(text: year.toString()), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: runtime.lunaRuntime()), ], - ); + ), + TextSpan(text: studio), + if (!hasFile) + const TextSpan( + text: 'Not Downloaded', + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.red, + ), + ), + if (hasFile) + TextSpan( + text: 'Downloaded ($fileQualityProfile)', + style: const TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.accent, + ), + ) + ]; + } @override String get bannerURI { if (api['enabled']) { if ((api['host'] as String).endsWith('/')) { - return '${api['host']}api/MediaCover/$id/fanart-360.jpg?apikey=${api['key']}'; + return '${api['host']}api/v3/MediaCover/$id/fanart-360.jpg?apikey=${api['key']}'; } - return '${api['host']}/api/MediaCover/$id/fanart-360.jpg?apikey=${api['key']}'; + return '${api['host']}/api/v3/MediaCover/$id/fanart-360.jpg?apikey=${api['key']}'; } return ''; } @@ -84,4 +82,14 @@ class CalendarRadarrData extends CalendarData { @override Future trailingOnLongPress(BuildContext context) async => RadarrReleasesRouter().navigateTo(context, movieId: id); + + @override + String backgroundUrl(BuildContext context) { + return context.read().getFanartURL(this.id); + } + + @override + String posterUrl(BuildContext context) { + return context.read().getPosterURL(this.id); + } } diff --git a/lib/modules/dashboard/core/api/data/sonarr.dart b/lib/modules/dashboard/core/api/data/sonarr.dart index d856c7da..05615915 100644 --- a/lib/modules/dashboard/core/api/data/sonarr.dart +++ b/lib/modules/dashboard/core/api/data/sonarr.dart @@ -26,39 +26,40 @@ class CalendarSonarrData extends CalendarData { }) : super(id, title); @override - TextSpan get subtitle => TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ + List get body { + return [ + TextSpan( + children: [ TextSpan( - text: 'Season $seasonNumber Episode $episodeNumber: ', - ), - TextSpan( - text: episodeTitle, - style: const TextStyle( - fontStyle: FontStyle.italic, - ), - ), - if (!hasFile) - TextSpan( - text: hasAired ? '\nMissing' : '\nUnaired', - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: hasAired ? LunaColours.red : LunaColours.blue, - ), - ), - if (hasFile) - TextSpan( - text: '\nDownloaded ($fileQualityProfile)', - style: const TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.accent, - ), - ) + text: seasonNumber == 0 ? 'Specials' : 'Season $seasonNumber'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: 'Episode $episodeNumber'), ], - ); + ), + TextSpan( + style: const TextStyle( + fontStyle: FontStyle.italic, + ), + text: episodeTitle ?? 'lunasea.Unknown'.tr(), + ), + if (!hasFile) + TextSpan( + text: hasAired ? 'sonarr.Missing'.tr() : 'sonarr.Unaired'.tr(), + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: hasAired ? LunaColours.red : LunaColours.blue, + ), + ), + if (hasFile) + TextSpan( + text: 'Downloaded ($fileQualityProfile)', + style: const TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.accent, + ), + ), + ]; + } bool get hasAired { if (airTimeObject != null) return DateTime.now().isAfter(airTimeObject); @@ -82,12 +83,10 @@ class CalendarSonarrData extends CalendarData { ); @override - Widget trailing(BuildContext context) => InkWell( - child: LunaIconButton( - text: airTimeString, - onPressed: () async => trailingOnPress(context), - onLongPress: () => trailingOnLongPress(context), - ), + Widget trailing(BuildContext context) => LunaIconButton( + text: airTimeString, + onPressed: () async => trailingOnPress(context), + onLongPress: () => trailingOnLongPress(context), ); DateTime get airTimeObject { @@ -134,4 +133,14 @@ class CalendarSonarrData extends CalendarData { context, episodeId: id, ); + + @override + String backgroundUrl(BuildContext context) { + return context.read().getFanartURL(this.seriesID); + } + + @override + String posterUrl(BuildContext context) { + return context.read().getPosterURL(this.seriesID); + } } diff --git a/lib/modules/dashboard/core/database.dart b/lib/modules/dashboard/core/database.dart index 52e08ed4..5871b54e 100644 --- a/lib/modules/dashboard/core/database.dart +++ b/lib/modules/dashboard/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/dashboard.dart'; @@ -89,7 +88,7 @@ class DashboardDatabase extends LunaModuleDatabase { extension DashboardDatabaseValueExtension on DashboardDatabaseValue { String get key { - return 'HOME_${describeEnum(this)}'; + return 'HOME_${this.name}'; } dynamic get data { diff --git a/lib/modules/dashboard/core/state.dart b/lib/modules/dashboard/core/state.dart index 1e4330ac..e56f01a9 100644 --- a/lib/modules/dashboard/core/state.dart +++ b/lib/modules/dashboard/core/state.dart @@ -37,8 +37,8 @@ class DashboardState extends LunaModuleState { notifyListeners(); } - Future> _upcoming; - Future> get upcoming => _upcoming; + Future>> _upcoming; + Future>> get upcoming => _upcoming; void resetUpcoming() { if (_api != null) _upcoming = _api.getUpcoming(DateTime.now()); notifyListeners(); diff --git a/lib/modules/dashboard/routes/calendar/widgets/calendar.dart b/lib/modules/dashboard/routes/calendar/widgets/calendar.dart index b40d0032..f4e5e383 100644 --- a/lib/modules/dashboard/routes/calendar/widgets/calendar.dart +++ b/lib/modules/dashboard/routes/calendar/widgets/calendar.dart @@ -6,7 +6,7 @@ import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import 'package:table_calendar/table_calendar.dart'; class DashboardCalendarWidget extends StatefulWidget { - final Map events; + final Map> events; const DashboardCalendarWidget({ Key key, @@ -24,28 +24,28 @@ class _State extends State { CalendarFormat _calendarFormat; final TextStyle dayTileStyle = const TextStyle( - color: Colors.white, + color: LunaColours.white, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ); final TextStyle outsideDayTileStyle = const TextStyle( - color: Colors.white54, + color: LunaColours.white70, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ); final TextStyle unavailableTitleStyle = const TextStyle( - color: Colors.white12, + color: LunaColours.white10, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ); final TextStyle weekdayTitleStyle = const TextStyle( color: LunaColours.accent, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ); final double _calendarBulletSize = 8.0; - List _selectedEvents; + List _selectedEvents; @override void initState() { @@ -84,7 +84,8 @@ class _State extends State { _calendarList(), ], ), - padding: EdgeInsets.only(top: LunaUI.MARGIN_CARD.top), + padding: + EdgeInsets.only(top: LunaUI.MARGIN_H_DEFAULT_V_HALF.top), ); } return _schedule(); @@ -95,7 +96,7 @@ class _State extends State { Widget _markerBuilder( BuildContext context, DateTime date, - List events, + List events, ) { Color color; int missingCount = _countMissingContent(date, events); @@ -173,11 +174,13 @@ class _State extends State { isTodayHighlighted: true, outsideDaysVisible: false, selectedDecoration: BoxDecoration( - color: LunaColours.accent.withOpacity(0.20), + color: + LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH), shape: BoxShape.circle, ), todayDecoration: BoxDecoration( - color: LunaColours.primary.withOpacity(0.60), + color: LunaColours.primary + .withOpacity(LunaUI.OPACITY_DISABLED), shape: BoxShape.circle, ), weekendTextStyle: dayTileStyle, @@ -206,8 +209,9 @@ class _State extends State { }, onDaySelected: _onDaySelected, ), - padding: LunaUI.MARGIN_DEFAULT - .subtract(const EdgeInsets.symmetric(horizontal: 6.0)), + padding: const EdgeInsets.symmetric( + vertical: LunaUI.DEFAULT_MARGIN_SIZE, + ), ), ), ); @@ -217,7 +221,7 @@ class _State extends State { /// -1: Date is after today (with content) /// -100: Event list was empty or null - int _countMissingContent(DateTime date, List events) { + int _countMissingContent(DateTime date, List events) { DateTime _date = date.lunaFloor; DateTime _now = DateTime.now().lunaFloor; @@ -304,7 +308,7 @@ class _State extends State { ]; } - Widget _entry(dynamic event) { + Widget _entry(CalendarData event) { Map headers; switch (event.runtimeType) { case CalendarLidarrData: @@ -317,25 +321,19 @@ class _State extends State { headers = Database.currentProfileObject.getSonarr()['headers']; break; default: - headers = {}; + headers = const {}; break; } - return LunaListTile( - context: context, - title: LunaText.title(text: event.title), - subtitle: RichText( - text: event.subtitle, - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 2, - ), + return LunaBlock( + title: event.title, + body: event.body, + posterHeaders: headers, + backgroundHeaders: headers, + posterUrl: event.posterUrl(context), + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + backgroundUrl: event.backgroundUrl(context), trailing: event.trailing(context), onTap: () async => event.enterContent(context), - contentPadding: true, - decoration: LunaCardDecoration( - uri: event.bannerURI, - headers: headers, - ), ); } } diff --git a/lib/modules/dashboard/routes/dashboard/route.dart b/lib/modules/dashboard/routes/dashboard/route.dart index f4a963ad..f0402d97 100644 --- a/lib/modules/dashboard/routes/dashboard/route.dart +++ b/lib/modules/dashboard/routes/dashboard/route.dart @@ -3,7 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/dashboard.dart'; class DashboardHomeRouter extends DashboardPageRouter { - DashboardHomeRouter() : super('/'); + DashboardHomeRouter() : super('/dashboard'); @override _DashboardHomeRoute widget() => _DashboardHomeRoute(); diff --git a/lib/modules/dashboard/routes/dashboard/widgets/appbar_switch_view_action.dart b/lib/modules/dashboard/routes/dashboard/widgets/appbar_switch_view_action.dart index 40e453fa..6521f5b5 100644 --- a/lib/modules/dashboard/routes/dashboard/widgets/appbar_switch_view_action.dart +++ b/lib/modules/dashboard/routes/dashboard/widgets/appbar_switch_view_action.dart @@ -48,7 +48,7 @@ class _State extends State selector: (_, state) => state.calendarStartingType, builder: (context, view, _) { if (_showButton) { - return LunaIconButton( + return LunaIconButton.appBar( icon: view.icon, onPressed: () { if (view == CalendarStartingType.CALENDAR) { diff --git a/lib/modules/dashboard/routes/modules/route.dart b/lib/modules/dashboard/routes/modules/route.dart index 3f31cc12..2cf7c025 100644 --- a/lib/modules/dashboard/routes/modules/route.dart +++ b/lib/modules/dashboard/routes/modules/route.dart @@ -38,6 +38,7 @@ class _State extends State } return LunaListView( controller: DashboardNavigationBar.scrollControllers[0], + itemExtent: LunaBlock.calculateItemExtent(1), children: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data ? _buildAlphabeticalList() : _buildManuallyOrderedList(), @@ -83,20 +84,18 @@ class _State extends State } Widget _buildFromLunaModule(LunaModule module, int listIndex) { - return LunaListTile( - context: context, - title: LunaText.title(text: module.name), - subtitle: LunaText.subtitle(text: module.description), + return LunaBlock( + title: module.name, + body: [TextSpan(text: module.description)], trailing: LunaIconButton(icon: module.icon, color: module.color), onTap: module.launch, ); } Widget _buildWakeOnLAN(BuildContext context, int listIndex) { - return LunaListTile( - context: context, - title: LunaText.title(text: LunaModule.WAKE_ON_LAN.name), - subtitle: LunaText.subtitle(text: LunaModule.WAKE_ON_LAN.description), + return LunaBlock( + title: LunaModule.WAKE_ON_LAN.name, + body: [TextSpan(text: LunaModule.WAKE_ON_LAN.description)], trailing: LunaIconButton( icon: LunaModule.WAKE_ON_LAN.icon, color: LunaModule.WAKE_ON_LAN.color, diff --git a/lib/modules/external_modules/routes/external_modules/route.dart b/lib/modules/external_modules/routes/external_modules/route.dart index 2b9f4bb1..a00c3e90 100644 --- a/lib/modules/external_modules/routes/external_modules/route.dart +++ b/lib/modules/external_modules/routes/external_modules/route.dart @@ -51,6 +51,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } return LunaListView( controller: scrollController, + itemExtent: LunaBlock.calculateItemExtent(1), children: _list, ); } diff --git a/lib/modules/external_modules/routes/external_modules/widgets/module_tile.dart b/lib/modules/external_modules/routes/external_modules/widgets/module_tile.dart index 91644742..3729a02b 100644 --- a/lib/modules/external_modules/routes/external_modules/widgets/module_tile.dart +++ b/lib/modules/external_modules/routes/external_modules/widgets/module_tile.dart @@ -11,11 +11,10 @@ class ExternalModulesModuleTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: module.displayName), - subtitle: LunaText.subtitle(text: module.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: module.displayName, + body: [TextSpan(text: module.host)], + trailing: const LunaIconButton.arrow(), onTap: () async => module.host.lunaOpenGenericLink(), ); } diff --git a/lib/modules/lidarr/core/api/api.dart b/lib/modules/lidarr/core/api/api.dart index a628d360..6dfd1a0b 100644 --- a/lib/modules/lidarr/core/api/api.dart +++ b/lib/modules/lidarr/core/api/api.dart @@ -50,7 +50,7 @@ class LidarrAPI { entries.add(LidarrCatalogueData( title: entry['artistName'] ?? 'Unknown Artist', sortTitle: entry['sortName'] ?? 'Unknown Artist', - overview: entry['overview'] ?? 'No summary is available.', + overview: entry['overview'] ?? 'No Summary Available', path: entry['path'] ?? 'Unknown Path', artistID: entry['id'] ?? 0, artistType: entry['artistType'] ?? 'Unknown Artist Type', @@ -130,7 +130,7 @@ class LidarrAPI { return LidarrCatalogueData( title: response.data['artistName'] ?? 'Unknown Artist', sortTitle: response.data['sortName'] ?? 'Unknown Artist', - overview: response.data['overview'] ?? 'No summary is available.', + overview: response.data['overview'] ?? 'No Summary Available', artistType: response.data['artistType'] ?? 'Unknown Artist Type', path: response.data['path'] ?? 'Unknown Path', artistID: response.data['id'] ?? 0, @@ -228,7 +228,10 @@ class LidarrAPI { : entry['statistics']['totalTrackCount'] ?? 0, percentageTracks: entry['statistics'] == null ? 0 - : entry['statistics']['percentOfTracks'] ?? 0, + : entry['statistics']['percentOfTracks'] + ?.toString() + ?.toDouble() ?? + 0, releaseDate: entry['releaseDate'] ?? '', )); } @@ -662,7 +665,7 @@ class LidarrAPI { title: entry['artistName'] ?? 'Unknown Artist Name', foreignArtistId: entry['foreignArtistId'] ?? '', overview: entry['overview'] == null || entry['overview'] == '' - ? 'No summary is available.' + ? 'No Summary Available' : entry['overview'], tadbId: entry['tadbId'] ?? 0, artistType: entry['artistType'] ?? 'Unknown Artist Type', @@ -685,8 +688,7 @@ class LidarrAPI { LidarrQualityProfile quality, LidarrRootFolder rootFolder, LidarrMetadataProfile metadata, - bool monitored, - bool albumFolders, + LidarrMonitorStatus monitorStatus, {bool search = false}) async { try { Response response = await _dio.post( @@ -697,10 +699,11 @@ class LidarrAPI { 'qualityProfileId': quality.id, 'metadataProfileId': metadata.id, 'rootFolderPath': rootFolder.path, - 'monitored': monitored, - 'albumFolder': albumFolders, + 'monitored': monitorStatus != LidarrMonitorStatus.NONE, + 'albumFolder': true, 'addOptions': { 'searchForMissingAlbums': search, + 'monitor': monitorStatus.key, }, }), ); diff --git a/lib/modules/lidarr/core/api/data.dart b/lib/modules/lidarr/core/api/data.dart index fa9b706e..a154b27e 100644 --- a/lib/modules/lidarr/core/api/data.dart +++ b/lib/modules/lidarr/core/api/data.dart @@ -3,6 +3,7 @@ export 'data/catalogue.dart'; export 'data/history.dart'; export 'data/metadata.dart'; export 'data/missing.dart'; +export 'data/monitor_status.dart'; export 'data/qualityprofile.dart'; export 'data/release.dart'; export 'data/rootfolder.dart'; diff --git a/lib/modules/lidarr/core/api/data/catalogue.dart b/lib/modules/lidarr/core/api/data/catalogue.dart index 99a660e9..aefa1d8e 100644 --- a/lib/modules/lidarr/core/api/data/catalogue.dart +++ b/lib/modules/lidarr/core/api/data/catalogue.dart @@ -58,8 +58,7 @@ class LidarrCatalogueData { return 'Unknown'; } - String subtitle(LidarrCatalogueSorting sorting) => - '$albums\t•\t$tracks\n${_sortSubtitle(sorting)}'; + String subtitle(LidarrCatalogueSorting sorting) => _sortSubtitle(sorting); String _sortSubtitle(LidarrCatalogueSorting sorting) { switch (sorting) { diff --git a/lib/modules/lidarr/core/api/data/history.dart b/lib/modules/lidarr/core/api/data/history.dart index 1d88978e..ab1f0d44 100644 --- a/lib/modules/lidarr/core/api/data/history.dart +++ b/lib/modules/lidarr/core/api/data/history.dart @@ -122,9 +122,7 @@ class LidarrHistoryDataTrackFileImported extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]} ($quality)', style: const TextStyle( @@ -150,9 +148,7 @@ class LidarrHistoryDataDownloadImported extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]} ($quality)', style: const TextStyle( @@ -175,9 +171,7 @@ class LidarrHistoryDataAlbumImportIncomplete extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]}', style: const TextStyle( @@ -203,9 +197,7 @@ class LidarrHistoryDataTrackFileDeleted extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]} (${super.historyReasonMessages[reason] ?? reason})', @@ -229,9 +221,7 @@ class LidarrHistoryDataTrackFileRenamed extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]}', style: const TextStyle( @@ -254,9 +244,7 @@ class LidarrHistoryDataTrackFileRetagged extends LidarrHistoryData { @override List get subtitle { return [ - TextSpan( - text: '$timestampString\n', - ), + TextSpan(text: timestampString), TextSpan( text: '${LidarrConstants.EVENT_TYPE_MESSAGES[eventType]}', style: const TextStyle( diff --git a/lib/modules/lidarr/core/api/data/missing.dart b/lib/modules/lidarr/core/api/data/missing.dart index e1441d46..4804aa0c 100644 --- a/lib/modules/lidarr/core/api/data/missing.dart +++ b/lib/modules/lidarr/core/api/data/missing.dart @@ -43,6 +43,18 @@ class LidarrMissingData { return 'Unknown Date/Time'; } + String albumCoverURI({bool highRes = false}) { + if (api['enabled']) { + String _base = (api['host'] as String).endsWith('/') + ? '${api['host']}api/v1/MediaCover/Album' + : '${api['host']}/api/v1/MediaCover/Album'; + return highRes + ? '$_base/$albumID/cover.jpg?apikey=${api['key']}' + : '$_base/$albumID/cover-500.jpg?apikey=${api['key']}'; + } + return ''; + } + String posterURI({bool highRes = false}) { if (api['enabled']) { String _base = (api['host'] as String).endsWith('/') diff --git a/lib/modules/lidarr/core/api/data/monitor_status.dart b/lib/modules/lidarr/core/api/data/monitor_status.dart new file mode 100644 index 00000000..43e0201f --- /dev/null +++ b/lib/modules/lidarr/core/api/data/monitor_status.dart @@ -0,0 +1,71 @@ +enum LidarrMonitorStatus { + ALL, + FUTURE, + MISSING, + EXISTING, + ONLY_FIRST_ALBUM, + ONLY_LAST_ALBUM, + NONE, +} + +extension LidarrMonitorStatusExtension on LidarrMonitorStatus { + LidarrMonitorStatus fromKey(String key) { + switch (key) { + case 'all': + return LidarrMonitorStatus.ALL; + case 'future': + return LidarrMonitorStatus.FUTURE; + case 'missing': + return LidarrMonitorStatus.MISSING; + case 'existing': + return LidarrMonitorStatus.EXISTING; + case 'first': + return LidarrMonitorStatus.ONLY_FIRST_ALBUM; + case 'latest': + return LidarrMonitorStatus.ONLY_LAST_ALBUM; + case 'none': + return LidarrMonitorStatus.NONE; + } + return null; + } + + String get key { + switch (this) { + case LidarrMonitorStatus.ALL: + return 'all'; + case LidarrMonitorStatus.FUTURE: + return 'future'; + case LidarrMonitorStatus.MISSING: + return 'missing'; + case LidarrMonitorStatus.EXISTING: + return 'existing'; + case LidarrMonitorStatus.ONLY_FIRST_ALBUM: + return 'first'; + case LidarrMonitorStatus.ONLY_LAST_ALBUM: + return 'latest'; + case LidarrMonitorStatus.NONE: + return 'none'; + } + throw Exception('Invalid LidarrMonitorStatus'); + } + + String get readable { + switch (this) { + case LidarrMonitorStatus.ALL: + return 'All Albums'; + case LidarrMonitorStatus.FUTURE: + return 'Future Albums'; + case LidarrMonitorStatus.MISSING: + return 'Missing Albums'; + case LidarrMonitorStatus.EXISTING: + return 'Existing Albums'; + case LidarrMonitorStatus.ONLY_FIRST_ALBUM: + return 'Only First Album'; + case LidarrMonitorStatus.ONLY_LAST_ALBUM: + return 'Only Latest Album'; + case LidarrMonitorStatus.NONE: + return 'None'; + } + throw Exception('Invalid LidarrMonitorStatus'); + } +} diff --git a/lib/modules/lidarr/core/api/data/track.dart b/lib/modules/lidarr/core/api/data/track.dart index 877c7aa0..d3d5e12b 100644 --- a/lib/modules/lidarr/core/api/data/track.dart +++ b/lib/modules/lidarr/core/api/data/track.dart @@ -20,21 +20,18 @@ class LidarrTrackData { TextSpan file(bool monitored) { if (hasFile) { - return TextSpan( + return const TextSpan( text: 'Downloaded', style: TextStyle( - color: monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30), + color: LunaColours.accent, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), ); } else { - return TextSpan( + return const TextSpan( text: 'Not Downloaded', style: TextStyle( - color: - monitored ? LunaColours.red : LunaColours.red.withOpacity(0.30), + color: LunaColours.red, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), ); diff --git a/lib/modules/lidarr/core/database.dart b/lib/modules/lidarr/core/database.dart index 7a5277b4..2a4fde19 100644 --- a/lib/modules/lidarr/core/database.dart +++ b/lib/modules/lidarr/core/database.dart @@ -1,11 +1,14 @@ -import 'package:flutter/foundation.dart'; +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart' hide LidarrDatabaseValueExtension; enum LidarrDatabaseValue { NAVIGATION_INDEX, + @Deprecated("Replaced by ADD_MONITORED_STATUS") ADD_MONITORED, + ADD_MONITORED_STATUS, ADD_ALBUM_FOLDERS, ADD_QUALITY_PROFILE, ADD_METADATA_PROFILE, @@ -26,7 +29,6 @@ class LidarrDatabase extends LunaModuleDatabase { for (LidarrDatabaseValue value in LidarrDatabaseValue.values) { switch (value) { // Non-exported values - case LidarrDatabaseValue.ADD_MONITORED: case LidarrDatabaseValue.ADD_ALBUM_FOLDERS: case LidarrDatabaseValue.ADD_QUALITY_PROFILE: case LidarrDatabaseValue.ADD_METADATA_PROFILE: @@ -47,17 +49,16 @@ class LidarrDatabase extends LunaModuleDatabase { 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; + // Primitive values + default: + value.put(config[key]); + break; } } } @@ -73,7 +74,7 @@ class LidarrDatabase extends LunaModuleDatabase { extension LidarrDatabaseValueExtension on LidarrDatabaseValue { String get key { - return 'LIDARR_${describeEnum(this)}'; + return 'LIDARR_${this.name}'; } dynamic get data { @@ -117,6 +118,8 @@ extension LidarrDatabaseValueExtension on LidarrDatabaseValue { return value is LidarrMetadataProfile; case LidarrDatabaseValue.ADD_ROOT_FOLDER: return value is LidarrRootFolder; + case LidarrDatabaseValue.ADD_MONITORED_STATUS: + return value is String; } throw Exception('Invalid LidarrDatabaseValue'); } @@ -135,6 +138,8 @@ extension LidarrDatabaseValueExtension on LidarrDatabaseValue { return null; case LidarrDatabaseValue.ADD_ROOT_FOLDER: return null; + case LidarrDatabaseValue.ADD_MONITORED_STATUS: + return LidarrMonitorStatus.ALL.key; } throw Exception('Invalid LidarrDatabaseValue'); } diff --git a/lib/modules/lidarr/core/dialogs.dart b/lib/modules/lidarr/core/dialogs.dart index bf75798e..45738c26 100644 --- a/lib/modules/lidarr/core/dialogs.dart +++ b/lib/modules/lidarr/core/dialogs.dart @@ -3,7 +3,34 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; class LidarrDialogs { - LidarrDialogs._(); + Future> selectMonitoringOption( + BuildContext context, + ) async { + bool _flag = false; + LidarrMonitorStatus _value; + + void _setValues(bool flag, LidarrMonitorStatus value) { + _flag = flag; + _value = value; + Navigator.of(context, rootNavigator: true).pop(); + } + + await LunaDialog.dialog( + context: context, + title: 'Monitoring Options', + content: List.generate( + LidarrMonitorStatus.values.length, + (index) => LunaDialog.tile( + text: LidarrMonitorStatus.values[index].readable, + icon: LunaIcons.MONITOR_ON, + iconColor: LunaColours().byListIndex(index), + onTap: () => _setValues(true, LidarrMonitorStatus.values[index]), + ), + ), + contentPadding: LunaDialog.listDialogContentPadding(), + ); + return Tuple2(_flag, _value); + } static Future> editQualityProfile( BuildContext context, List qualities) async { @@ -202,7 +229,9 @@ class LidarrDialogs { subtitle: LunaDialog.richText( children: [ LunaDialog.bolded( - text: folders[index].freeSpace.lunaBytesToString()), + text: folders[index].freeSpace.lunaBytesToString(), + fontSize: LunaDialog.BUTTON_SIZE, + ), ], ), icon: Icons.folder_rounded, diff --git a/lib/modules/lidarr/routes/add_details.dart b/lib/modules/lidarr/routes/add_details.dart index 054f07e6..92a3d057 100644 --- a/lib/modules/lidarr/routes/add_details.dart +++ b/lib/modules/lidarr/routes/add_details.dart @@ -162,57 +162,24 @@ class _State extends State with LunaScrollControllerMixin { LidarrDescriptionBlock( title: _arguments.data.title ?? 'Unknown', description: _arguments.data.overview == '' - ? 'No summary is available.' + ? 'No Summary Available' : _arguments.data.overview, uri: _arguments.data.posterURI ?? '', - fallbackImage: LunaAssets.blankUser, squareImage: true, headers: Database.currentProfileObject.getLidarr()['headers'], ), - ValueListenableBuilder( - valueListenable: Database.lunaSeaBox - .listenable(keys: [LidarrDatabaseValue.ADD_MONITORED.key]), - builder: (context, box, widget) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Monitored'), - subtitle: LunaText.subtitle( - text: 'Monitor artist for new releases'), - trailing: LunaSwitch( - value: LidarrDatabaseValue.ADD_MONITORED.data, - onChanged: (value) => - LidarrDatabaseValue.ADD_MONITORED.put(value), - ), - ); - }), - ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable( - keys: [LidarrDatabaseValue.ADD_ALBUM_FOLDERS.key]), - builder: (context, box, widget) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Use Album Folders'), - subtitle: - LunaText.subtitle(text: 'Sort tracks into album folders'), - trailing: LunaSwitch( - value: LidarrDatabaseValue.ADD_ALBUM_FOLDERS.data, - onChanged: (value) => - LidarrDatabaseValue.ADD_ALBUM_FOLDERS.put(value), - ), - ); - }), ValueListenableBuilder( valueListenable: Database.lunaSeaBox .listenable(keys: [LidarrDatabaseValue.ADD_ROOT_FOLDER.key]), builder: (context, box, widget) { LidarrRootFolder _rootfolder = LidarrDatabaseValue.ADD_ROOT_FOLDER.data; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Root Folder'), - subtitle: LunaText.subtitle( - text: _rootfolder?.path ?? 'Unknown Root Folder'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Root Folder', + body: [ + TextSpan(text: _rootfolder?.path ?? 'Unknown Root Folder'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { List _values = await LidarrDialogs.editRootFolder(context, _rootFolders); @@ -222,18 +189,36 @@ class _State extends State with LunaScrollControllerMixin { ); }, ), + LidarrDatabaseValue.ADD_MONITORED_STATUS.listen( + builder: (context, box, _) { + LidarrDatabaseValue _db = LidarrDatabaseValue.ADD_MONITORED_STATUS; + LidarrMonitorStatus _status = + LidarrMonitorStatus.ALL.fromKey(_db.data); + _status ??= LidarrMonitorStatus.ALL; + + return LunaBlock( + title: 'Monitor', + trailing: const LunaIconButton.arrow(), + body: [TextSpan(text: _status.readable)], + onTap: () async { + Tuple2 _result = + await LidarrDialogs().selectMonitoringOption(context); + if (_result.item1) _db.put(_result.item2.key); + }, + ); + }), ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable( keys: [LidarrDatabaseValue.ADD_QUALITY_PROFILE.key]), builder: (context, box, widget) { LidarrQualityProfile _profile = LidarrDatabaseValue.ADD_QUALITY_PROFILE.data; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Quality Profile'), - subtitle: LunaText.subtitle( - text: _profile?.name ?? 'Unknown Profile'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Quality Profile', + body: [ + TextSpan(text: _profile?.name ?? 'Unknown Profile'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { List _values = await LidarrDialogs.editQualityProfile( context, _qualityProfiles); @@ -249,12 +234,12 @@ class _State extends State with LunaScrollControllerMixin { builder: (context, box, widget) { LidarrMetadataProfile _profile = LidarrDatabaseValue.ADD_METADATA_PROFILE.data; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Metadata Profile'), - subtitle: LunaText.subtitle( - text: _profile?.name ?? 'Unknown Profile'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Metadata Profile', + body: [ + TextSpan(text: _profile?.name ?? 'Unknown Profile'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { List _values = await LidarrDialogs.editMetadataProfile( context, _metadataProfiles); @@ -275,8 +260,8 @@ class _State extends State with LunaScrollControllerMixin { LidarrDatabaseValue.ADD_QUALITY_PROFILE.data, LidarrDatabaseValue.ADD_ROOT_FOLDER.data, LidarrDatabaseValue.ADD_METADATA_PROFILE.data, - LidarrDatabaseValue.ADD_MONITORED.data ?? true, - LidarrDatabaseValue.ADD_ALBUM_FOLDERS.data ?? true, + LidarrMonitorStatus.ALL + .fromKey(LidarrDatabaseValue.ADD_MONITORED_STATUS.data), search: search, ) .then((id) => Navigator.of(context) diff --git a/lib/modules/lidarr/routes/catalogue.dart b/lib/modules/lidarr/routes/catalogue.dart index faede9bc..f830ed6a 100644 --- a/lib/modules/lidarr/routes/catalogue.dart +++ b/lib/modules/lidarr/routes/catalogue.dart @@ -50,8 +50,9 @@ class _State extends State Widget _appBar() { return LunaAppBar.empty( child: LidarrCatalogueSearchBar( - scrollController: LidarrNavigationBar.scrollControllers[0]), - height: 62.0, + scrollController: LidarrNavigationBar.scrollControllers[0], + ), + height: LunaTextInputBar.defaultAppBarHeight, ); } @@ -106,6 +107,7 @@ class _State extends State return LunaListViewBuilder( controller: LidarrNavigationBar.scrollControllers[0], itemCount: filtered.length, + itemExtent: LunaBlock.calculateItemExtent(2), itemBuilder: (context, index) => LidarrCatalogueTile( data: filtered[index], scaffoldKey: _scaffoldKey, diff --git a/lib/modules/lidarr/routes/edit_artist.dart b/lib/modules/lidarr/routes/edit_artist.dart index 4a5e4cfd..f1e6454b 100644 --- a/lib/modules/lidarr/routes/edit_artist.dart +++ b/lib/modules/lidarr/routes/edit_artist.dart @@ -130,46 +130,31 @@ class _State extends State with LunaScrollControllerMixin { Widget get _list => LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'Monitored'), - subtitle: - LunaText.subtitle(text: 'Monitor artist for new releases'), + LunaBlock( + title: 'Monitored', trailing: LunaSwitch( value: _monitored, onChanged: (value) => setState(() => _monitored = value), ), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Use Album Folders'), - subtitle: LunaText.subtitle(text: 'Sort tracks into album folders'), - trailing: LunaSwitch( - value: _albumFolders, - onChanged: (value) => setState(() => _albumFolders = value), - ), - ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Artist Path'), - subtitle: LunaText.subtitle(text: _path), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: _changePath, - ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Quality Profile'), - subtitle: LunaText.subtitle(text: _qualityProfile.name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaBlock( + title: 'Quality Profile', + body: [TextSpan(text: _qualityProfile.name)], + trailing: const LunaIconButton.arrow(), onTap: _changeProfile, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Metadata Profile'), - subtitle: LunaText.subtitle(text: _metadataProfile.name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaBlock( + title: 'Metadata Profile', + body: [TextSpan(text: _metadataProfile.name)], + trailing: const LunaIconButton.arrow(), onTap: _changeMetadata, ), + LunaBlock( + title: 'Artist Path', + body: [TextSpan(text: _path)], + trailing: const LunaIconButton.arrow(), + onTap: _changePath, + ), ], ); diff --git a/lib/modules/lidarr/routes/history.dart b/lib/modules/lidarr/routes/history.dart index 05f16959..ad3e797b 100644 --- a/lib/modules/lidarr/routes/history.dart +++ b/lib/modules/lidarr/routes/history.dart @@ -90,6 +90,7 @@ class _State extends State with AutomaticKeepAliveClientMixin { return LunaListViewBuilder( controller: LidarrNavigationBar.scrollControllers[2], itemCount: _results.length, + itemExtent: LidarrHistoryTile.extent, itemBuilder: (context, index) => LidarrHistoryTile( entry: _results[index], scaffoldKey: _scaffoldKey, diff --git a/lib/modules/lidarr/routes/missing.dart b/lib/modules/lidarr/routes/missing.dart index dad9912c..5d067f02 100644 --- a/lib/modules/lidarr/routes/missing.dart +++ b/lib/modules/lidarr/routes/missing.dart @@ -87,6 +87,7 @@ class _State extends State with AutomaticKeepAliveClientMixin { return LunaListViewBuilder( controller: LidarrNavigationBar.scrollControllers[1], itemCount: _results.length, + itemExtent: LidarrMissingTile.extent, itemBuilder: (context, index) => LidarrMissingTile( scaffoldKey: _scaffoldKey, entry: _results[index], diff --git a/lib/modules/lidarr/widgets/add_search_bar.dart b/lib/modules/lidarr/widgets/add_search_bar.dart index 2bd5f9e2..fa5f4e95 100644 --- a/lib/modules/lidarr/widgets/add_search_bar.dart +++ b/lib/modules/lidarr/widgets/add_search_bar.dart @@ -13,7 +13,8 @@ class LidarrAddSearchBar extends StatefulWidget implements PreferredSizeWidget { }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(LunaTextInputBar.appBarHeight); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); @@ -53,7 +54,7 @@ class _State extends State { ), ], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ); } } diff --git a/lib/modules/lidarr/widgets/add_search_result_tile.dart b/lib/modules/lidarr/widgets/add_search_result_tile.dart index 22939633..8430c51f 100644 --- a/lib/modules/lidarr/widgets/add_search_result_tile.dart +++ b/lib/modules/lidarr/widgets/add_search_result_tile.dart @@ -13,21 +13,29 @@ class LidarrAddSearchResultTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title(text: data.title, darken: alreadyAdded), - subtitle: LunaText.subtitle( - text: '${data.overview.trim()}\n\n', - darken: alreadyAdded, - maxLines: 2, - ), - contentPadding: true, - trailing: alreadyAdded - ? null - : LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + Widget build(BuildContext context) => LunaBlock( + title: data.title, + disabled: alreadyAdded, + body: [ + LunaTextSpan.extended(text: data.overview.trim()), + ], + customBodyMaxLines: 3, + trailing: alreadyAdded ? null : const LunaIconButton.arrow(), + posterIsSquare: true, + posterHeaders: Database.currentProfileObject.getLidarr()['headers'], + posterPlaceholderIcon: LunaIcons.USER, + posterUrl: _posterUrl, onTap: () async => _enterDetails(context), ); + String get _posterUrl { + Map image = data?.images?.firstWhere( + (e) => e['coverType'] == 'poster', + orElse: () => {}, + ); + return image['url']; + } + Future _enterDetails(BuildContext context) async { if (alreadyAdded) { showLunaInfoSnackBar( diff --git a/lib/modules/lidarr/widgets/catalogue_hide_button.dart b/lib/modules/lidarr/widgets/catalogue_hide_button.dart index c2eb1f7a..c0b213e1 100644 --- a/lib/modules/lidarr/widgets/catalogue_hide_button.dart +++ b/lib/modules/lidarr/widgets/catalogue_hide_button.dart @@ -30,8 +30,8 @@ class _State extends State { borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/lidarr/widgets/catalogue_search_bar.dart b/lib/modules/lidarr/widgets/catalogue_search_bar.dart index d176ba7c..e19be3a5 100644 --- a/lib/modules/lidarr/widgets/catalogue_search_bar.dart +++ b/lib/modules/lidarr/widgets/catalogue_search_bar.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; -class LidarrCatalogueSearchBar extends StatefulWidget { +class LidarrCatalogueSearchBar extends StatefulWidget + implements PreferredSizeWidget { final ScrollController scrollController; const LidarrCatalogueSearchBar({ @@ -10,6 +11,10 @@ class LidarrCatalogueSearchBar extends StatefulWidget { @required this.scrollController, }) : super(key: key); + @override + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); + @override State createState() => _State(); } @@ -25,25 +30,26 @@ class _State extends State { @override Widget build(BuildContext context) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Consumer( - builder: (context, state, _) => LunaTextInputBar( - controller: _controller, - scrollController: widget.scrollController, - autofocus: false, - onChanged: (value) => - context.read().searchCatalogueFilter = value, - margin: EdgeInsets.zero, + return SizedBox( + child: Row( + children: [ + Expanded( + child: Consumer( + builder: (context, state, _) => LunaTextInputBar( + controller: _controller, + scrollController: widget.scrollController, + autofocus: false, + onChanged: (value) => + context.read().searchCatalogueFilter = value, + margin: EdgeInsets.zero, + ), ), ), - ), - LidarrCatalogueHideButton(controller: widget.scrollController), - LidarrCatalogueSortButton(controller: widget.scrollController), - ], + LidarrCatalogueHideButton(controller: widget.scrollController), + LidarrCatalogueSortButton(controller: widget.scrollController), + ], + ), + height: LunaTextInputBar.defaultAppBarHeight, ); } } diff --git a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart index 1f4e8cff..66df317d 100644 --- a/lib/modules/lidarr/widgets/catalogue_sorting_button.dart +++ b/lib/modules/lidarr/widgets/catalogue_sorting_button.dart @@ -18,8 +18,8 @@ class _State extends State { @override Widget build(BuildContext context) => LunaCard( context: context, - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, child: Consumer( builder: (context, model, _) => LunaPopupMenuButton( @@ -45,7 +45,7 @@ class _State extends State { Text( LidarrCatalogueSorting.values[index].readable, style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), ), if (model.sortCatalogueType == @@ -54,7 +54,7 @@ class _State extends State { model.sortCatalogueAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], diff --git a/lib/modules/lidarr/widgets/catalogue_tile.dart b/lib/modules/lidarr/widgets/catalogue_tile.dart index 33783869..2a75283e 100644 --- a/lib/modules/lidarr/widgets/catalogue_tile.dart +++ b/lib/modules/lidarr/widgets/catalogue_tile.dart @@ -22,35 +22,39 @@ class LidarrCatalogueTile extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title( - text: widget.data.title, - darken: !widget.data.monitored, - ), - subtitle: Selector( - selector: (_, state) => state.sortCatalogueType, - builder: (context, type, _) => LunaText.subtitle( - text: widget.data.subtitle(type), - darken: !widget.data.monitored, - maxLines: 2, + Widget build(BuildContext context) { + return Selector( + selector: (_, state) => state.sortCatalogueType, + builder: (context, sortingType, _) => LunaBlock( + title: widget.data.title, + disabled: !widget.data.monitored, + body: [ + TextSpan( + children: [ + TextSpan(text: widget.data.albums), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: widget.data.tracks), + ], ), - ), + TextSpan(text: widget.data.subtitle(sortingType)), + ], trailing: LunaIconButton( icon: widget.data.monitored - ? Icons.turned_in_rounded - : Icons.turned_in_not_rounded, - color: widget.data.monitored ? Colors.white : Colors.white30, + ? LunaIcons.MONITOR_ON + : LunaIcons.MONITOR_OFF, onPressed: _toggleMonitoredStatus, ), - contentPadding: true, - decoration: LunaCardDecoration( - uri: widget.data.bannerURI(), - headers: Database.currentProfileObject.getLidarr()['headers'], - ), + posterPlaceholderIcon: LunaIcons.USER, + posterUrl: widget.data.posterURI(), + posterHeaders: Database.currentProfileObject.getLidarr()['headers'], + backgroundUrl: widget.data.bannerURI(), + backgroundHeaders: Database.currentProfileObject.getLidarr()['headers'], + posterIsSquare: true, onTap: () async => _enterArtist(), onLongPress: () async => _handlePopup(), - ); + ), + ); + } Future _toggleMonitoredStatus() async { final _api = LidarrAPI.from(Database.currentProfileObject); diff --git a/lib/modules/lidarr/widgets/description_block.dart b/lib/modules/lidarr/widgets/description_block.dart index c213ff9b..301d06e2 100644 --- a/lib/modules/lidarr/widgets/description_block.dart +++ b/lib/modules/lidarr/widgets/description_block.dart @@ -5,7 +5,6 @@ class LidarrDescriptionBlock extends StatefulWidget { final String description; final String title; final String uri; - final String fallbackImage; final bool squareImage; final Map headers; @@ -14,7 +13,6 @@ class LidarrDescriptionBlock extends StatefulWidget { @required this.description, @required this.title, @required this.uri, - @required this.fallbackImage, @required this.headers, this.squareImage = false, }) : super(key: key); @@ -24,54 +22,27 @@ class LidarrDescriptionBlock extends StatefulWidget { } class _State extends State { - final double _imageDimension = 105.0; - @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - child: Row( - children: [ - widget.uri != null - ? LunaNetworkImage( - height: _imageDimension, - width: widget.squareImage - ? _imageDimension - : _imageDimension / 1.5, - url: widget.uri, - placeholderAsset: widget.fallbackImage, - headers: ((Database.currentProfileObject - .getLidarr()['headers'] ?? - {}) as Map) - .cast(), - ) - : Container(), - Expanded( - child: Container( - height: _imageDimension, - child: Padding( - child: Text( - widget.description ?? 'No summary is available.', - overflow: TextOverflow.ellipsis, - maxLines: 5, - style: const TextStyle( - color: Colors.white, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - textAlign: TextAlign.start, - ), - padding: const EdgeInsets.all(12.0), - ), - alignment: Alignment.topLeft, - ), - ), - ], + return LunaBlock( + title: widget.title, + body: [ + LunaTextSpan.extended( + text: widget.description?.isNotEmpty ?? false + ? widget.description + : 'No Summary Available', ), - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - onTap: () => LunaDialogs().textPreview(context, widget.title, - widget.description.trim() ?? 'No summary is available.'), + ], + onTap: () async => LunaDialogs().textPreview( + context, + widget.title, + widget.description.trim() ?? 'No Summary Available', ), + customBodyMaxLines: 3, + posterPlaceholderIcon: LunaIcons.USER, + posterHeaders: widget.headers, + posterIsSquare: widget.squareImage, + posterUrl: widget.uri, ); } } diff --git a/lib/modules/lidarr/widgets/details_album_tile.dart b/lib/modules/lidarr/widgets/details_album_tile.dart index 52bf91de..f12c2e7e 100644 --- a/lib/modules/lidarr/widgets/details_album_tile.dart +++ b/lib/modules/lidarr/widgets/details_album_tile.dart @@ -17,78 +17,34 @@ class LidarrDetailsAlbumTile extends StatefulWidget { } class _State extends State { - final double _imageDimension = 70.0; - @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - widget.data.albumCoverURI() != null && - widget.data.albumCoverURI() != '' - ? LunaNetworkImage( - url: widget.data.albumCoverURI(), - placeholderAsset: LunaAssets.blankAudio, - headers: ((Database.currentProfileObject - .getLidarr()['headers'] ?? - {}) as Map) - .cast(), - height: _imageDimension, - width: _imageDimension, - ) - : Container(), - Expanded( - child: Padding( - child: Column( - children: [ - LunaText.title( - text: widget.data.title, - darken: !widget.data.monitored), - RichText( - text: TextSpan( - text: widget.data.tracks, - style: TextStyle( - color: widget.data.monitored - ? Colors.white70 - : Colors.white30, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: '\n${widget.data.releaseDateString}', - style: TextStyle( - color: widget.data.monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30), - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - ], - ), - ), - ], - crossAxisAlignment: CrossAxisAlignment.start, - ), - padding: const EdgeInsets.symmetric(horizontal: 10.0), - ), - ), - Padding( - child: LunaIconButton( - icon: widget.data.monitored - ? Icons.turned_in_rounded - : Icons.turned_in_not_rounded, - color: widget.data.monitored ? Colors.white : Colors.white30, - onPressed: _toggleMonitoredStatus, - ), - padding: const EdgeInsets.fromLTRB(8.0, 0.0, 12.0, 0.0), - ), - ], + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data.title, + disabled: !widget.data.monitored, + posterHeaders: Database.currentProfileObject.getLidarr()['headers'] ?? {}, + posterPlaceholderIcon: LunaIcons.MUSIC, + posterIsSquare: true, + posterUrl: widget.data.albumCoverURI(), + body: [ + TextSpan(text: widget.data.tracks), + TextSpan( + text: widget.data.releaseDateString, + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), - onTap: () async => _enterAlbum(), ), - ); + ], + trailing: LunaIconButton( + icon: widget.data.monitored + ? Icons.turned_in_rounded + : Icons.turned_in_not_rounded, + onPressed: _toggleMonitoredStatus, + ), + onTap: _enterAlbum, + ); + } Future _toggleMonitoredStatus() async { LidarrAPI _api = LidarrAPI.from(Database.currentProfileObject); diff --git a/lib/modules/lidarr/widgets/details_album_track_tile.dart b/lib/modules/lidarr/widgets/details_album_track_tile.dart index 0cc8de2b..4674906f 100644 --- a/lib/modules/lidarr/widgets/details_album_track_tile.dart +++ b/lib/modules/lidarr/widgets/details_album_track_tile.dart @@ -18,35 +18,16 @@ class LidarrDetailsTrackTile extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: - LunaText.title(text: widget.data.title, darken: !widget.monitored), - subtitle: RichText( - text: TextSpan( - style: TextStyle( - color: widget.monitored ? Colors.white70 : Colors.white30, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: - '${widget?.data?.duration?.lunaTimestamp(divisor: 1000)}\n', - ), - widget?.data?.file(widget.monitored), - ]), + Widget build(BuildContext context) => LunaBlock( + title: widget.data.title, + body: [ + TextSpan(text: widget?.data?.duration?.lunaTimestamp(divisor: 1000)), + widget?.data?.file(widget.monitored), + ], + disabled: !widget.monitored, + leading: LunaIconButton( + text: widget?.data?.trackNumber, + textSize: LunaUI.FONT_SIZE_H4, ), - leading: IconButton( - icon: Text( - widget?.data?.trackNumber, - style: TextStyle( - color: widget.monitored ? Colors.white : Colors.white30, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_TITLE, - ), - ), - onPressed: null, - ), - contentPadding: true, ); } diff --git a/lib/modules/lidarr/widgets/details_overview.dart b/lib/modules/lidarr/widgets/details_overview.dart index 35636db2..b9c1de57 100644 --- a/lib/modules/lidarr/widgets/details_overview.dart +++ b/lib/modules/lidarr/widgets/details_overview.dart @@ -28,10 +28,9 @@ class _State extends State LidarrDescriptionBlock( title: widget?.data?.title ?? 'Unknown', description: widget?.data?.overview == '' - ? 'No summary is available.' + ? 'No Summary Available' : widget?.data?.overview, uri: widget?.data?.posterURI() ?? '', - fallbackImage: LunaAssets.blankUser, squareImage: true, headers: Database.currentProfileObject.getLidarr()['headers'], ), diff --git a/lib/modules/lidarr/widgets/history_tile.dart b/lib/modules/lidarr/widgets/history_tile.dart index c7144026..30c6ff3c 100644 --- a/lib/modules/lidarr/widgets/history_tile.dart +++ b/lib/modules/lidarr/widgets/history_tile.dart @@ -3,6 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; class LidarrHistoryTile extends StatefulWidget { + static final double extent = LunaBlock.calculateItemExtent(2); final LidarrHistoryData entry; final GlobalKey scaffoldKey; final Function refresh; @@ -20,22 +21,10 @@ class LidarrHistoryTile extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title(text: widget.entry.title), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: widget.entry.subtitle, - ), - ), - trailing: LunaIconButton( - icon: Icons.arrow_forward_ios_rounded, - ), - contentPadding: true, + Widget build(BuildContext context) => LunaBlock( + title: widget.entry.title, + body: widget.entry.subtitle, + trailing: const LunaIconButton.arrow(), onTap: () async => _enterArtist(), ); diff --git a/lib/modules/lidarr/widgets/missing_tile.dart b/lib/modules/lidarr/widgets/missing_tile.dart index 2dec9635..797a06f5 100644 --- a/lib/modules/lidarr/widgets/missing_tile.dart +++ b/lib/modules/lidarr/widgets/missing_tile.dart @@ -3,6 +3,8 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/lidarr.dart'; class LidarrMissingTile extends StatefulWidget { + static final double extent = LunaBlock.calculateItemExtent(2); + final LidarrMissingData entry; final GlobalKey scaffoldKey; final Function refresh; @@ -20,48 +22,40 @@ class LidarrMissingTile extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title(text: widget.entry.artistTitle), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: widget.entry.title, - style: const TextStyle( - color: Colors.white70, - fontStyle: FontStyle.italic, - ), - ), - TextSpan( - text: '\nReleased ${widget.entry.releaseDateString}', - style: const TextStyle( - color: LunaColours.red, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - ], + Widget build(BuildContext context) { + return LunaBlock( + title: widget.entry.artistTitle, + body: [ + TextSpan( + text: widget.entry.title, + style: const TextStyle( + color: LunaColours.grey, + fontStyle: FontStyle.italic, ), - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 2, ), - trailing: LunaIconButton( - icon: Icons.search_rounded, - onPressed: () async => _search(), - onLongPress: () async => _interactiveSearch(), + TextSpan( + text: 'Released ${widget.entry.releaseDateString}', + style: const TextStyle( + color: LunaColours.red, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), ), - onTap: () async => _enterAlbum(), - onLongPress: () async => _enterArtist(), - decoration: LunaCardDecoration( - uri: widget.entry.bannerURI(), - headers: Database.currentProfileObject.getLidarr()['headers'], - ), - contentPadding: true, - ); + ], + trailing: LunaIconButton( + icon: LunaIcons.SEARCH, + onPressed: () async => _search(), + onLongPress: () async => _interactiveSearch(), + ), + onTap: () async => _enterAlbum(), + onLongPress: () async => _enterArtist(), + posterUrl: widget.entry.albumCoverURI(), + posterHeaders: Database.currentProfileObject.getLidarr()['headers'], + posterIsSquare: true, + posterPlaceholderIcon: LunaIcons.MUSIC, + backgroundUrl: widget.entry.bannerURI(), + backgroundHeaders: Database.currentProfileObject.getLidarr()['headers'], + ); + } Future _search() async { final _api = LidarrAPI.from(Database.currentProfileObject); diff --git a/lib/modules/lidarr/widgets/releases_hide_button.dart b/lib/modules/lidarr/widgets/releases_hide_button.dart index ee33ca82..fb13fea6 100644 --- a/lib/modules/lidarr/widgets/releases_hide_button.dart +++ b/lib/modules/lidarr/widgets/releases_hide_button.dart @@ -27,8 +27,8 @@ class _State extends State { model.hideRejectedReleases = !model.hideRejectedReleases, ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: LunaTextInputBar.appBarMargin .subtract(const EdgeInsets.only(left: 12.0)), color: Theme.of(context).canvasColor, diff --git a/lib/modules/lidarr/widgets/releases_search_bar.dart b/lib/modules/lidarr/widgets/releases_search_bar.dart index 4b321c72..9780ae5f 100644 --- a/lib/modules/lidarr/widgets/releases_search_bar.dart +++ b/lib/modules/lidarr/widgets/releases_search_bar.dart @@ -12,7 +12,8 @@ class LidarrReleasesSearchBar extends StatefulWidget }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(LunaTextInputBar.appBarHeight); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); @@ -44,7 +45,7 @@ class _State extends State { LidarrReleasesSortButton(controller: widget.scrollController), ], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ); } } diff --git a/lib/modules/lidarr/widgets/releases_sorting_button.dart b/lib/modules/lidarr/widgets/releases_sorting_button.dart index ef937055..357573d0 100644 --- a/lib/modules/lidarr/widgets/releases_sorting_button.dart +++ b/lib/modules/lidarr/widgets/releases_sorting_button.dart @@ -18,8 +18,8 @@ class _State extends State { @override Widget build(BuildContext context) => LunaCard( context: context, - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, child: Consumer( builder: (context, model, _) => LunaPopupMenuButton( @@ -45,7 +45,7 @@ class _State extends State { Text( LidarrReleasesSorting.values[index].readable, style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), ), if (model.sortReleasesType == @@ -54,7 +54,7 @@ class _State extends State { model.sortReleasesAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], diff --git a/lib/modules/nzbget/core/database.dart b/lib/modules/nzbget/core/database.dart index c92cb015..e5231bb0 100644 --- a/lib/modules/nzbget/core/database.dart +++ b/lib/modules/nzbget/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -49,7 +48,7 @@ class NZBGetDatabase extends LunaModuleDatabase { extension NZBGetDatabaseValueExtension on NZBGetDatabaseValue { String get key { - return 'NZBGET_${describeEnum(this)}'; + return 'NZBGET_${this.name}'; } dynamic get data { diff --git a/lib/modules/nzbget/routes/history.dart b/lib/modules/nzbget/routes/history.dart index e5c453e2..e030cb29 100644 --- a/lib/modules/nzbget/routes/history.dart +++ b/lib/modules/nzbget/routes/history.dart @@ -48,7 +48,7 @@ class _State extends State return LunaAppBar.empty( child: NZBGetHistorySearchBar( scrollController: NZBGetNavigationBar.scrollControllers[1]), - height: 62.0, + height: LunaTextInputBar.defaultAppBarHeight, ); } diff --git a/lib/modules/nzbget/routes/statistics.dart b/lib/modules/nzbget/routes/statistics.dart index 6516161c..70e3fabb 100644 --- a/lib/modules/nzbget/routes/statistics.dart +++ b/lib/modules/nzbget/routes/statistics.dart @@ -108,7 +108,7 @@ class _State extends State with LunaScrollControllerMixin { title: 'Post', body: _statistics.postPaused ? 'Paused' : 'Active'), LunaTableContent( title: 'Scan', body: _statistics.scanPaused ? 'Paused' : 'Active'), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent(title: 'Uptime', body: _statistics.uptimeString), LunaTableContent( title: 'Speed Limit', body: _statistics.speedLimitString), diff --git a/lib/modules/nzbget/widgets/app_bar_stats.dart b/lib/modules/nzbget/widgets/app_bar_stats.dart index c8a95059..e212fd46 100644 --- a/lib/modules/nzbget/widgets/app_bar_stats.dart +++ b/lib/modules/nzbget/widgets/app_bar_stats.dart @@ -24,8 +24,8 @@ class NZBGetAppBarStats extends StatelessWidget { child: RichText( text: TextSpan( style: const TextStyle( - color: Colors.white54, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + color: LunaColours.grey, + fontSize: LunaUI.FONT_SIZE_H3, ), children: [ TextSpan( diff --git a/lib/modules/nzbget/widgets/history_hide_button.dart b/lib/modules/nzbget/widgets/history_hide_button.dart index d78d7257..df2040f5 100644 --- a/lib/modules/nzbget/widgets/history_hide_button.dart +++ b/lib/modules/nzbget/widgets/history_hide_button.dart @@ -26,8 +26,8 @@ class _State extends State { onPressed: () => model.historyHideFailed = !model.historyHideFailed, ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.only(left: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/nzbget/widgets/log_tile.dart b/lib/modules/nzbget/widgets/log_tile.dart index 9d92f025..ede5e353 100644 --- a/lib/modules/nzbget/widgets/log_tile.dart +++ b/lib/modules/nzbget/widgets/log_tile.dart @@ -11,12 +11,13 @@ class NZBGetLogTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title(text: data.text), - subtitle: LunaText.subtitle(text: data.timestamp), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async => - LunaDialogs().textPreview(context, 'Log Entry', data.text), - ); + Widget build(BuildContext context) { + return LunaBlock( + title: data.text, + body: [TextSpan(text: data.timestamp)], + trailing: const LunaIconButton.arrow(), + onTap: () async => + LunaDialogs().textPreview(context, 'Log Entry', data.text), + ); + } } diff --git a/lib/modules/nzbget/widgets/queue_tile.dart b/lib/modules/nzbget/widgets/queue_tile.dart index 9f23141d..8fb66007 100644 --- a/lib/modules/nzbget/widgets/queue_tile.dart +++ b/lib/modules/nzbget/widgets/queue_tile.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/nzbget.dart'; -import 'package:percent_indicator/linear_percent_indicator.dart'; class NZBGetQueueTile extends StatefulWidget { final int index; @@ -25,37 +24,13 @@ class NZBGetQueueTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title( - text: widget.data.name, - darken: widget.data.paused, + return LunaBlock( + title: widget.data.name, + body: [TextSpan(text: widget.data.subtitle)], + bottom: LunaLinearPercentIndicator( + percent: min(1.0, max(0, widget.data.percentageDone / 100)), ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - child: LinearPercentIndicator( - percent: min(1.0, max(0, widget.data.percentageDone / 100)), - padding: const EdgeInsets.symmetric(horizontal: 2.0), - progressColor: widget.data.paused - ? LunaColours.accent.withOpacity(0.30) - : LunaColours.accent, - backgroundColor: widget.data.paused - ? LunaColours.accent.withOpacity(0.05) - : LunaColours.accent.withOpacity(0.15), - lineHeight: 4.0, - ), - padding: const EdgeInsets.symmetric(vertical: 6.0), - ), - LunaText.subtitle( - text: widget.data.subtitle, - darken: widget.data.paused, - ), - ], - ), - trailing: LunaReorderableListDragger(index: widget.index), + trailing: LunaReorderableListViewDragger(index: widget.index), onTap: _handlePopup, ); } diff --git a/lib/modules/overseerr/core/database.dart b/lib/modules/overseerr/core/database.dart index e40b0932..8c523dc8 100644 --- a/lib/modules/overseerr/core/database.dart +++ b/lib/modules/overseerr/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -50,7 +49,7 @@ class OverseerrDatabase extends LunaModuleDatabase { extension OverseerrDatabaseValueExtension on OverseerrDatabaseValue { String get key { - return 'OVERSEERR_${describeEnum(this)}'; + return 'OVERSEERR_${this.name}'; } dynamic get data { diff --git a/lib/modules/overseerr/routes/requests/widgets/request_tile.dart b/lib/modules/overseerr/routes/requests/widgets/request_tile.dart index 1f05b0eb..56c2b26f 100644 --- a/lib/modules/overseerr/routes/requests/widgets/request_tile.dart +++ b/lib/modules/overseerr/routes/requests/widgets/request_tile.dart @@ -12,13 +12,16 @@ class OverseerrRequestTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaTwoLineCardWithPoster( + return LunaBlock( title: request.requestedBy.displayName ?? 'overseerr.UnknownUser'.tr(), - subtitle1: const TextSpan(text: 'Placeholder 1'), - subtitle2: const TextSpan(text: 'Placeholder 2'), - posterPlaceholder: LunaAssets.blankUser, + body: const [ + TextSpan(text: 'Placeholder 1'), + TextSpan(text: 'Placeholder 2'), + ], + posterPlaceholderIcon: LunaIcons.USER, posterHeaders: context.read().headers, posterUrl: request.requestedBy.avatar, + posterIsSquare: true, ); } } diff --git a/lib/modules/overseerr/routes/users/widgets/user_tile.dart b/lib/modules/overseerr/routes/users/widgets/user_tile.dart index 8ac3e7a9..03f0c1ee 100644 --- a/lib/modules/overseerr/routes/users/widgets/user_tile.dart +++ b/lib/modules/overseerr/routes/users/widgets/user_tile.dart @@ -12,19 +12,22 @@ class OverseerrUserTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaTwoLineCardWithPoster( + return LunaBlock( title: user.lunaDisplayName(), - subtitle1: TextSpan(text: user.lunaEmail()), - subtitle2: TextSpan( - text: user.lunaAmountOfRequests(), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, + body: [ + TextSpan(text: user.lunaEmail()), + TextSpan( + text: user.lunaAmountOfRequests(), + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), ), - ), - posterPlaceholder: LunaAssets.blankUser, + ], + posterPlaceholderIcon: LunaIcons.USER, posterHeaders: context.read().headers, posterUrl: user.avatar, + posterIsSquare: true, ); } } diff --git a/lib/modules/radarr/core/bottom_modal_sheets.dart b/lib/modules/radarr/core/bottom_modal_sheets.dart index cad1047c..10895156 100644 --- a/lib/modules/radarr/core/bottom_modal_sheets.dart +++ b/lib/modules/radarr/core/bottom_modal_sheets.dart @@ -4,8 +4,7 @@ import 'package:lunasea/modules/radarr.dart'; class RadarrBottomModalSheets { Future configureManualImport(BuildContext context) async { - // TODO: Abstract this - await LunaBottomModalSheet().showModal( + await LunaBottomModalSheet().show( context: context, builder: (_) => ChangeNotifierProvider.value( value: context.read(), @@ -18,15 +17,17 @@ class RadarrBottomModalSheets { .manualImport .relativePath, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.SelectMovie'.tr()), - subtitle: LunaText.subtitle( + LunaBlock( + title: 'radarr.SelectMovie'.tr(), + body: [ + TextSpan( text: context .watch() .manualImport - .lunaMovie), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .lunaMovie, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 result = await selectMovie(context); if (result.item1) @@ -35,26 +36,30 @@ class RadarrBottomModalSheets { .fetchUpdates(context, result.item2.id); }, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.SelectQuality'.tr()), - subtitle: LunaText.subtitle( + LunaBlock( + title: 'radarr.SelectQuality'.tr(), + body: [ + TextSpan( text: context .watch() .manualImport - .lunaQualityProfile), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .lunaQualityProfile, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => selectQuality(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.SelectLanguage'.tr()), - subtitle: LunaText.subtitle( + LunaBlock( + title: 'radarr.SelectLanguage'.tr(), + body: [ + TextSpan( text: context .watch() .manualImport - .lunaLanguage), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .lunaLanguage, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { List languages = await context.read().languages; @@ -69,23 +74,24 @@ class RadarrBottomModalSheets { } Future selectQuality(BuildContext context) async { - // TODO: Abstract this - await LunaBottomModalSheet().showModal( + await LunaBottomModalSheet().show( context: context, builder: (_) => ChangeNotifierProvider.value( value: context.read(), builder: (context, _) => LunaListViewModal( children: [ LunaHeader(text: 'radarr.SelectQuality'.tr()), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Quality'.tr()), - subtitle: LunaText.subtitle( + LunaBlock( + title: 'radarr.Quality'.tr(), + body: [ + TextSpan( text: context .watch() .manualImport - .lunaQualityProfile), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .lunaQualityProfile, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { List profiles = await context.read().qualityDefinitions; @@ -98,9 +104,8 @@ class RadarrBottomModalSheets { .updateQuality(result.item2.quality); }, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Proper'), + LunaBlock( + title: 'Proper', trailing: Switch( value: context .watch() @@ -120,9 +125,8 @@ class RadarrBottomModalSheets { }, ), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Real'), + LunaBlock( + title: 'Real', trailing: Switch( value: context .watch() @@ -165,8 +169,7 @@ class RadarrBottomModalSheets { return _filtered; } - // TODO: Abstract this - await LunaBottomModalSheet().showModal( + await LunaBottomModalSheet().show( context: context, builder: (_) => ChangeNotifierProvider.value( value: context.read(), @@ -204,21 +207,16 @@ class RadarrBottomModalSheets { String overview = movies[index].overview; if (overview?.isEmpty ?? true) overview = 'radarr.NoSummaryIsAvailable'.tr(); - return LunaListTile( - context: context, - title: LunaText.title(text: title), - subtitle: LunaText( - text: '$overview\n', - softWrap: true, - maxLines: 2, - overflow: TextOverflow.fade, - style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: Colors.white70, - fontStyle: FontStyle.italic, + return LunaBlock( + title: title, + body: [ + TextSpan( + text: overview, + style: const TextStyle( + fontStyle: FontStyle.italic, + ), ), - ), - contentPadding: true, + ], onTap: () { result = true; movie = movies[index]; @@ -232,8 +230,8 @@ class RadarrBottomModalSheets { const RadarrManualImportDetailsConfigureMoviesSearchBar(), hideLeading: true, ), - appBarHeight: - LunaAppBar.APPBAR_HEIGHT + LunaTextInputBar.appBarHeight, + appBarHeight: LunaAppBar.APPBAR_HEIGHT + + LunaTextInputBar.defaultAppBarHeight, ); } return const LunaLoader(); diff --git a/lib/modules/radarr/core/database.dart b/lib/modules/radarr/core/database.dart index c13abbb6..3c2452bd 100644 --- a/lib/modules/radarr/core/database.dart +++ b/lib/modules/radarr/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; @@ -22,6 +21,7 @@ enum RadarrDatabaseValue { ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID, ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID, ADD_MOVIE_DEFAULT_TAGS, + ADD_MOVIE_SEARCH_FOR_MISSING, ADD_DISCOVER_USE_SUGGESTIONS, MANUAL_IMPORT_DEFAULT_MODE, QUEUE_PAGE_SIZE, @@ -120,7 +120,7 @@ class RadarrDatabase extends LunaModuleDatabase { extension RadarrDatabaseValueExtension on RadarrDatabaseValue { String get key { - return 'RADARR_${describeEnum(this)}'; + return 'RADARR_${this.name}'; } dynamic get data { @@ -200,6 +200,8 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { return false; case RadarrDatabaseValue.CONTENT_PAGE_SIZE: return 25; + case RadarrDatabaseValue.ADD_MOVIE_SEARCH_FOR_MISSING: + return false; } throw Exception('Invalid RadarrDatabaseValue'); } @@ -254,6 +256,8 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { return value is bool; case RadarrDatabaseValue.CONTENT_PAGE_SIZE: return value is int; + case RadarrDatabaseValue.ADD_MOVIE_SEARCH_FOR_MISSING: + return value is bool; } throw Exception('Invalid RadarrDatabaseValue'); } diff --git a/lib/modules/radarr/core/dialogs.dart b/lib/modules/radarr/core/dialogs.dart index 5daf0e23..fa5ae7a2 100644 --- a/lib/modules/radarr/core/dialogs.dart +++ b/lib/modules/radarr/core/dialogs.dart @@ -597,7 +597,9 @@ class RadarrDialogs { subtitle: LunaDialog.richText( children: [ LunaDialog.bolded( - text: folders[index].freeSpace.lunaBytesToString()) + text: folders[index].freeSpace.lunaBytesToString(), + fontSize: LunaDialog.BUTTON_SIZE, + ), ], ), icon: Icons.folder_rounded, @@ -696,4 +698,29 @@ class RadarrDialogs { return Tuple2(_flag, int.tryParse(_textController.text) ?? 50); } + + Future addMovieOptions(BuildContext context) async { + await LunaDialog.dialog( + context: context, + title: 'lunasea.Options'.tr(), + buttons: [ + LunaDialog.button( + text: 'lunasea.Close'.tr(), + onPressed: () => Navigator.of(context, rootNavigator: true).pop(), + ), + ], + showCancelButton: false, + content: [ + RadarrDatabaseValue.ADD_MOVIE_SEARCH_FOR_MISSING.listen( + builder: (context, value, _) => LunaDialog.checkbox( + title: 'radarr.StartSearchForMissingMovie'.tr(), + value: RadarrDatabaseValue.ADD_MOVIE_SEARCH_FOR_MISSING.data, + onChanged: (value) => + RadarrDatabaseValue.ADD_MOVIE_SEARCH_FOR_MISSING.put(value), + ), + ), + ], + contentPadding: LunaDialog.listDialogContentPadding(), + ); + } } diff --git a/lib/modules/radarr/core/extensions/radarr_movie.dart b/lib/modules/radarr/core/extensions/radarr_movie.dart index 3ffac222..6bd52369 100644 --- a/lib/modules/radarr/core/extensions/radarr_movie.dart +++ b/lib/modules/radarr/core/extensions/radarr_movie.dart @@ -92,36 +92,42 @@ extension LunaRadarrMovieExtension on RadarrMovie { return this.sizeOnDisk.lunaBytesToString(); } - Text lunaHasFileTextObject(bool isMonitored) { + Text lunaHasFileTextObject() { if (this.hasFile) return Text( lunaFileSize, - style: TextStyle( - color: isMonitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30), - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + style: const TextStyle( + color: LunaColours.accent, + fontSize: LunaUI.FONT_SIZE_H3, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), ); - return const Text(''); + return const Text( + '', + style: TextStyle( + fontSize: LunaUI.FONT_SIZE_H3, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ); } - Text lunaNextReleaseTextObject(bool isMonitored) { + Text lunaNextReleaseTextObject() { DateTime now = DateTime.now(); // If we already have a file or it is released - if (this.hasFile || lunaIsReleased) return const Text(''); + if (this.hasFile || lunaIsReleased) + return const Text( + '', + style: TextStyle(fontSize: LunaUI.FONT_SIZE_H3), + ); // In Cinemas if (this.inCinemas != null && this.inCinemas.toLocal().isAfter(now)) { String _date = this.inCinemas.lunaDaysDifference?.toUpperCase() ?? LunaUI.TEXT_EMDASH; return Text( _date == 'TODAY' ? _date : 'IN $_date', - style: TextStyle( - color: isMonitored - ? LunaColours.orange - : LunaColours.orange.withOpacity(0.30), - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + style: const TextStyle( + color: LunaColours.orange, + fontSize: LunaUI.FONT_SIZE_H3, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), ); @@ -133,17 +139,21 @@ extension LunaRadarrMovieExtension on RadarrMovie { _release.lunaDaysDifference?.toUpperCase() ?? LunaUI.TEXT_EMDASH; return Text( _date == 'TODAY' ? _date : 'IN $_date', - style: TextStyle( - color: isMonitored - ? LunaColours.blue - : LunaColours.blue.withOpacity(0.30), - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + style: const TextStyle( + color: LunaColours.blue, + fontSize: LunaUI.FONT_SIZE_H3, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), ); } // Unknown case - return const Text(''); + return const Text( + '', + style: TextStyle( + fontSize: LunaUI.FONT_SIZE_H3, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ); } /// Compare two movies by their release dates. Returns an integer value compatible with `.sort()` in arrays. diff --git a/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart b/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart index 2e210b52..d46ef19e 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart @@ -19,15 +19,15 @@ class RadarrAddMovieDiscoveryResultTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( + return LunaBlock( backgroundUrl: widget.movie.remotePoster, posterUrl: widget.movie.remotePoster, posterHeaders: context.watch().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, title: widget.movie.title, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle2MaxLines: 2, + body: [_subtitle1()], + bottom: _subtitle2(), + bottomHeight: LunaBlock.SUBTITLE_HEIGHT * 2, onTap: _onTap, onLongPress: _onLongPress, ); @@ -45,16 +45,29 @@ class _State extends State { ); } - TextSpan _subtitle2() { + Widget _subtitle2() { String summary; if (widget.movie.overview == null || widget.movie.overview.isEmpty) { summary = 'radarr.NoSummaryIsAvailable'.tr(); } else { - summary = '${widget.movie.overview}\n'; + summary = widget.movie.overview; } - return TextSpan( - style: const TextStyle(fontStyle: FontStyle.italic), - text: summary, + return SizedBox( + height: LunaBlock.SUBTITLE_HEIGHT * 2, + child: RichText( + text: TextSpan( + style: const TextStyle( + fontStyle: FontStyle.italic, + fontSize: LunaUI.FONT_SIZE_H3, + color: LunaColours.grey, + ), + children: [ + LunaTextSpan.extended(text: summary), + ], + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), ); } diff --git a/lib/modules/radarr/routes/add_movie/widgets/page_search.dart b/lib/modules/radarr/routes/add_movie/widgets/page_search.dart index 945d21e9..0a7f89af 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/page_search.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/page_search.dart @@ -49,7 +49,7 @@ class _State extends State autofocus: widget.autofocusSearchBar, scrollController: RadarrAddMovieNavigationBar.scrollControllers[0], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ); } diff --git a/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart b/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart index 59532188..6577b92f 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart @@ -23,17 +23,17 @@ class RadarrAddMovieSearchResultTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( + return LunaBlock( backgroundUrl: widget.movie.remotePoster, posterUrl: widget.movie.remotePoster, posterHeaders: context.watch().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, title: widget.movie.title, - darken: widget.exists, titleColor: widget.isExcluded ? LunaColours.red : Colors.white, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle2MaxLines: 2, + disabled: widget.exists, + body: [_subtitle1()], + bottom: _subtitle2(), + bottomHeight: LunaBlock.SUBTITLE_HEIGHT * 2, onTap: _onTap, onLongPress: _onLongPress, ); @@ -51,16 +51,29 @@ class _State extends State { ); } - TextSpan _subtitle2() { + Widget _subtitle2() { String summary; if (widget.movie.overview == null || widget.movie.overview.isEmpty) { - summary = '${"radarr.NoSummaryIsAvailable".tr()}\n'; + summary = 'radarr.NoSummaryIsAvailable'.tr(); } else { - summary = '${widget.movie.overview}\n'; + summary = widget.movie.overview; } - return TextSpan( - style: const TextStyle(fontStyle: FontStyle.italic), - text: summary, + return SizedBox( + height: LunaBlock.SUBTITLE_HEIGHT * 2, + child: RichText( + text: TextSpan( + style: const TextStyle( + fontStyle: FontStyle.italic, + fontSize: LunaUI.FONT_SIZE_H3, + color: LunaColours.grey, + ), + children: [ + LunaTextSpan.extended(text: summary), + ], + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), ); } diff --git a/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart b/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart index 1d91a658..53adc5a4 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart @@ -17,7 +17,7 @@ class RadarrAddMovieSearchSearchBar extends StatefulWidget @override Size get preferredSize => - const Size.fromHeight(LunaTextInputBar.appBarHeight); + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); @@ -34,7 +34,7 @@ class _State extends State { @override Widget build(BuildContext context) { - return Padding( + return SizedBox( child: Row( children: [ Expanded( @@ -55,7 +55,7 @@ class _State extends State { ), ], ), - padding: const EdgeInsets.only(top: 1.0, bottom: 1.0), + height: LunaTextInputBar.defaultAppBarHeight, ); } } diff --git a/lib/modules/radarr/routes/add_movie_details/route.dart b/lib/modules/radarr/routes/add_movie_details/route.dart index c1fea9aa..82163e0a 100644 --- a/lib/modules/radarr/routes/add_movie_details/route.dart +++ b/lib/modules/radarr/routes/add_movie_details/route.dart @@ -156,8 +156,8 @@ class _State extends State<_Widget> exists: false, isExcluded: false, ), - const RadarrAddMovieDetailsMonitoredTile(), const RadarrAddMovieDetailsRootFolderTile(), + const RadarrAddMovieDetailsMonitoredTile(), const RadarrAddMovieDetailsMinimumAvailabilityTile(), const RadarrAddMovieDetailsQualityProfileTile(), const RadarrAddMovieDetailsTagsTile(), diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/bottom_action_bar.dart b/lib/modules/radarr/routes/add_movie_details/widgets/bottom_action_bar.dart index 11fc229a..077aa7bc 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/bottom_action_bar.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/bottom_action_bar.dart @@ -11,18 +11,16 @@ class RadarrAddMovieDetailsActionBar extends StatelessWidget { Widget build(BuildContext context) { return LunaBottomActionBar( actions: [ - LunaButton( - type: LunaButtonType.TEXT, - text: 'radarr.AddMovie'.tr(), - icon: Icons.add_rounded, - onTap: () async => _onTap(context, false), - loadingState: context.watch().state, + LunaActionBarCard( + title: 'lunasea.Options'.tr(), + subtitle: 'radarr.StartSearchFor'.tr(), + onTap: () async => RadarrDialogs().addMovieOptions(context), ), LunaButton( type: LunaButtonType.TEXT, - text: 'radarr.AddMovieAndSearch'.tr(), - icon: Icons.search_rounded, - onTap: () async => _onTap(context, true), + text: 'lunasea.Add'.tr(), + icon: Icons.add_rounded, + onTap: () async => _onTap(context, false), loadingState: context.watch().state, ), ], diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart index 00e2fbf0..176cc7be 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart @@ -9,21 +9,21 @@ class RadarrAddMovieDetailsMinimumAvailabilityTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.MinimumAvailability'.tr()), - subtitle: Selector( - selector: (_, state) => state.availability, - builder: (context, availability, _) => LunaText.subtitle( - text: availability?.readable ?? LunaUI.TEXT_EMDASH), - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - Tuple2 values = - await RadarrDialogs().editMinimumAvailability(context); - if (values.item1) - context.read().availability = - values.item2; + return Selector( + selector: (_, state) => state.availability, + builder: (context, availability, _) { + return LunaBlock( + title: 'radarr.MinimumAvailability'.tr(), + body: [TextSpan(text: availability?.readable ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + Tuple2 values = + await RadarrDialogs().editMinimumAvailability(context); + if (values.item1) + context.read().availability = + values.item2; + }, + ); }, ); } diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart index 0e42a5c4..d711e626 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart @@ -9,10 +9,8 @@ class RadarrAddMovieDetailsMonitoredTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Monitored'.tr()), - subtitle: LunaText.subtitle(text: 'radarr.MonitoredDescription'.tr()), + return LunaBlock( + title: 'radarr.Monitor'.tr(), trailing: Selector( selector: (_, state) => state.monitored, builder: (context, monitored, _) => LunaSwitch( diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart index 2e029662..49790322 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart @@ -9,24 +9,22 @@ class RadarrAddMovieDetailsQualityProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.QualityProfile'.tr()), - subtitle: Selector( - selector: (_, state) => state.qualityProfile, - builder: (context, profile, _) => - LunaText.subtitle(text: profile?.name ?? LunaUI.TEXT_EMDASH), + return Selector( + selector: (_, state) => state.qualityProfile, + builder: (context, profile, _) => LunaBlock( + title: 'radarr.QualityProfile'.tr(), + body: [TextSpan(text: profile?.name ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + List qualityProfiles = + await context.read().qualityProfiles; + Tuple2 values = await RadarrDialogs() + .editQualityProfile(context, qualityProfiles); + if (values.item1) + context.read().qualityProfile = + values.item2; + }, ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - List qualityProfiles = - await context.read().qualityProfiles; - Tuple2 values = - await RadarrDialogs().editQualityProfile(context, qualityProfiles); - if (values.item1) - context.read().qualityProfile = - values.item2; - }, ); } } diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart index e4b86c34..c3830532 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart @@ -9,23 +9,22 @@ class RadarrAddMovieDetailsRootFolderTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.RootFolder'.tr()), - subtitle: Selector( - selector: (_, state) => state.rootFolder, - builder: (context, folder, _) => - LunaText.subtitle(text: folder?.path ?? LunaUI.TEXT_EMDASH), + return Selector( + selector: (_, state) => state.rootFolder, + builder: (context, folder, _) => LunaBlock( + title: 'radarr.RootFolder'.tr(), + body: [TextSpan(text: folder?.path ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + List folders = + await context.read().rootFolders; + Tuple2 values = + await RadarrDialogs().editRootFolder(context, folders); + if (values.item1) + context.read().rootFolder = + values.item2; + }, ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - List folders = - await context.read().rootFolders; - Tuple2 values = - await RadarrDialogs().editRootFolder(context, folders); - if (values.item1) - context.read().rootFolder = values.item2; - }, ); } } diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart index 9ba29d4f..c97bddd0 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart @@ -9,18 +9,20 @@ class RadarrAddMovieDetailsTagsTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Tags'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'radarr.Tags'.tr(), + body: [ + TextSpan( text: context.watch().tags.isEmpty ? LunaUI.TEXT_EMDASH : context .watch() .tags .map((e) => e.label) - .join(', ')), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .join(', '), + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => await RadarrDialogs().setAddTags(context), ); } diff --git a/lib/modules/radarr/routes/catalogue/route.dart b/lib/modules/radarr/routes/catalogue/route.dart index 46186eeb..957f788e 100644 --- a/lib/modules/radarr/routes/catalogue/route.dart +++ b/lib/modules/radarr/routes/catalogue/route.dart @@ -49,7 +49,7 @@ class _State extends State child: RadarrCatalogueSearchBar( scrollController: RadarrNavigationBar.scrollControllers[0], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ); } @@ -128,46 +128,47 @@ class _State extends State onTap: _refreshKey.currentState.show, ); return Selector( - selector: (_, state) => state.moviesSearchQuery, - builder: (context, query, _) { - List _filtered = _filterAndSort(movies, query); - if ((_filtered?.length ?? 0) == 0) - return LunaListView( - controller: RadarrNavigationBar.scrollControllers[0], - children: [ - LunaMessage.inList(text: 'radarr.NoMoviesFound'.tr()), - if ((query ?? '').isNotEmpty) - LunaButtonContainer( - children: [ - LunaButton.text( - icon: null, - text: query.length > 20 - ? 'radarr.SearchFor'.tr(args: [ - '"${query.substring(0, min(20, query.length))}${LunaUI.TEXT_ELLIPSIS}"' - ]) - : 'radarr.SearchFor'.tr(args: ['"$query"']), - backgroundColor: LunaColours.accent, - onTap: () async => RadarrAddMovieRouter().navigateTo( - context, - query: query ?? '', - ), - ), - ], - ), - ], - ); - return LunaListViewBuilder( + selector: (_, state) => state.moviesSearchQuery, + builder: (context, query, _) { + List _filtered = _filterAndSort(movies, query); + if ((_filtered?.length ?? 0) == 0) + return LunaListView( controller: RadarrNavigationBar.scrollControllers[0], - itemCount: _filtered.length, - itemExtent: RadarrCatalogueTile.itemExtent, - itemBuilder: (context, index) => RadarrCatalogueTile( - movie: _filtered[index], - profile: qualityProfiles.firstWhere( - (element) => element.id == _filtered[index].qualityProfileId, - orElse: () => null, - ), - ), + children: [ + LunaMessage.inList(text: 'radarr.NoMoviesFound'.tr()), + if ((query ?? '').isNotEmpty) + LunaButtonContainer( + children: [ + LunaButton.text( + icon: null, + text: query.length > 20 + ? 'radarr.SearchFor'.tr(args: [ + '"${query.substring(0, min(20, query.length))}${LunaUI.TEXT_ELLIPSIS}"' + ]) + : 'radarr.SearchFor'.tr(args: ['"$query"']), + backgroundColor: LunaColours.accent, + onTap: () async => RadarrAddMovieRouter().navigateTo( + context, + query: query ?? '', + ), + ), + ], + ), + ], ); - }); + return LunaListViewBuilder( + controller: RadarrNavigationBar.scrollControllers[0], + itemCount: _filtered.length, + itemExtent: RadarrCatalogueTile.itemExtent, + itemBuilder: (context, index) => RadarrCatalogueTile( + movie: _filtered[index], + profile: qualityProfiles.firstWhere( + (element) => element.id == _filtered[index].qualityProfileId, + orElse: () => null, + ), + ), + ); + }, + ); } } diff --git a/lib/modules/radarr/routes/catalogue/widgets/movie_tile.dart b/lib/modules/radarr/routes/catalogue/widgets/movie_tile.dart index 261b5848..e6c05847 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/movie_tile.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/movie_tile.dart @@ -3,8 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; class RadarrCatalogueTile extends StatefulWidget { - static final itemExtent = - LunaFourLineCardWithPoster.itemExtent + LunaUI.MARGIN_CARD.vertical; + static final itemExtent = LunaBlock.calculateItemExtent(2, hasBottom: true); final RadarrMovie movie; final RadarrQualityProfile profile; @@ -24,17 +23,20 @@ class _State extends State { Widget build(BuildContext context) { return Selector>>( selector: (_, state) => state.movies, - builder: (context, movies, _) => LunaFourLineCardWithPoster( + builder: (context, movies, _) => LunaBlock( backgroundUrl: - context.read().getPosterURL(widget.movie.id), + context.read().getFanartURL(widget.movie.id), posterUrl: context.read().getPosterURL(widget.movie.id), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, - darken: !widget.movie.monitored, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + disabled: !widget.movie.monitored, title: widget.movie.title, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - customSubtitle3: _subtitle3(), + body: [ + _subtitle1(), + _subtitle2(), + ], + posterIsSquare: false, + bottom: _subtitle3(), onTap: _onTap, onLongPress: _onLongPress, ), @@ -44,10 +46,8 @@ class _State extends State { TextSpan _buildChildTextSpan(String text, RadarrMoviesSorting sorting) { TextStyle style; if (context.read().moviesSortType == sorting) - style = TextStyle( - color: widget.movie.monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30), + style = const TextStyle( + color: LunaColours.accent, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ); return TextSpan( @@ -98,45 +98,59 @@ class _State extends State { if (context.read().moviesSortType == RadarrMoviesSorting.IN_CINEMAS) _buildChildTextSpan( - widget.movie.lunaInCinemasOn, RadarrMoviesSorting.IN_CINEMAS), + widget.movie.lunaInCinemasOn, + RadarrMoviesSorting.IN_CINEMAS, + ), ], ); } Widget _buildReleaseIcon(IconData icon, Color color, bool highlight) { - Color _color = (highlight ? color : Colors.grey) - .withOpacity(widget.movie.monitored ? 1 : 0.30); return Padding( - child: Icon(icon, size: 16.0, color: _color), - padding: const EdgeInsets.only(right: 8.0), + child: Container( + child: Icon( + icon, + size: LunaUI.FONT_SIZE_H2, + color: highlight ? color : LunaColours.grey.disabled(), + ), + width: LunaBlock.SUBTITLE_HEIGHT, + height: LunaBlock.SUBTITLE_HEIGHT, + alignment: Alignment.centerLeft, + ), + padding: const EdgeInsets.only(right: LunaUI.DEFAULT_MARGIN_SIZE / 4), ); } Widget _subtitle3() { - return Row( - children: [ - _buildReleaseIcon( - Icons.videocam_rounded, - LunaColours.orange, - widget.movie.lunaIsInCinemas, - ), - _buildReleaseIcon( - Icons.album_rounded, - LunaColours.blue, - widget.movie.lunaIsReleased, - ), - _buildReleaseIcon( - Icons.check_circle_rounded, - LunaColours.accent, - widget.movie.hasFile, - ), - Padding( - child: widget.movie.hasFile - ? widget.movie.lunaHasFileTextObject(widget.movie.monitored) - : widget.movie.lunaNextReleaseTextObject(widget.movie.monitored), - padding: const EdgeInsets.only(top: 1.5), - ), - ], + return SizedBox( + height: LunaBlock.SUBTITLE_HEIGHT, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildReleaseIcon( + Icons.videocam_rounded, + LunaColours.orange, + widget.movie.lunaIsInCinemas, + ), + _buildReleaseIcon( + Icons.album_rounded, + LunaColours.blue, + widget.movie.lunaIsReleased, + ), + _buildReleaseIcon( + Icons.check_circle_rounded, + LunaColours.accent, + widget.movie.hasFile, + ), + Container( + height: LunaBlock.SUBTITLE_HEIGHT, + child: widget.movie.hasFile + ? widget.movie.lunaHasFileTextObject() + : widget.movie.lunaNextReleaseTextObject(), + alignment: Alignment.center, + ), + ], + ), ); } diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar.dart index 607e29aa..1f2309bc 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar.dart @@ -12,7 +12,8 @@ class RadarrCatalogueSearchBar extends StatefulWidget }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart index 10a3caaf..cb4f4973 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart @@ -35,7 +35,7 @@ class _State extends State { child: Text( RadarrMoviesFilter.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.moviesFilterType == RadarrMoviesFilter.values[index] ? LunaColours.accent @@ -46,8 +46,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart index f20fb222..9f0f0509 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart @@ -44,7 +44,7 @@ class _State extends State { Text( RadarrMoviesSorting.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.moviesSortType == RadarrMoviesSorting.values[index] ? LunaColours.accent @@ -56,7 +56,7 @@ class _State extends State { state.moviesSortAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], @@ -67,8 +67,8 @@ class _State extends State { ), margin: EdgeInsets.zero, color: Theme.of(context).canvasColor, - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, ); } } diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart index 480de84a..fc7b98e9 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart @@ -9,10 +9,8 @@ class RadarrMoviesEditMonitoredTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Monitored'.tr()), - subtitle: LunaText.subtitle(text: 'radarr.MonitoredDescription'.tr()), + return LunaBlock( + title: 'radarr.Monitored'.tr(), trailing: LunaSwitch( value: context.watch().monitored, onChanged: (value) => diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart index 7f423461..2ded6b58 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart @@ -9,20 +9,21 @@ class RadarrMoviesEditPathTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.MoviePath'.tr()), - subtitle: LunaText.subtitle( - text: context.watch().path ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - Tuple2 _values = await LunaDialogs().editText( - context, 'radarr.MoviePath'.tr(), - prefill: context.read().path); - if (_values.item1) - context.read().path = _values.item2; - }, + return Selector( + selector: (_, state) => state.path, + builder: (context, path, _) => LunaBlock( + title: 'radarr.MoviePath'.tr(), + body: [TextSpan(text: path ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + Tuple2 _values = await LunaDialogs().editText( + context, + 'radarr.MoviePath'.tr(), + prefill: path, + ); + if (_values.item1) path = _values.item2; + }, + ), ); } } diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart index 439a3ed5..6c4fa5c1 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart @@ -12,19 +12,19 @@ class RadarrMoviesEditQualityProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.QualityProfile'.tr()), - subtitle: LunaText.subtitle( - text: context.watch().qualityProfile?.name ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - Tuple2 values = - await RadarrDialogs().editQualityProfile(context, profiles); - if (values.item1) - context.read().qualityProfile = values.item2; - }, + return Selector( + selector: (_, state) => state.qualityProfile, + builder: (context, profile, _) => LunaBlock( + title: 'radarr.QualityProfile'.tr(), + body: [TextSpan(text: profile?.name ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + Tuple2 values = + await RadarrDialogs().editQualityProfile(context, profiles); + if (values.item1) + context.read().qualityProfile = values.item2; + }, + ), ); } } diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart index 552b7dc8..6c253dd2 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart @@ -9,18 +9,19 @@ class RadarrMoviesEditMinimumAvailabilityTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.MinimumAvailability'.tr()), - subtitle: LunaText.subtitle( - text: context.watch().availability.readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - Tuple2 _values = - await RadarrDialogs().editMinimumAvailability(context); - if (_values.item1) - context.read().availability = _values.item2; - }, + return Selector( + selector: (_, state) => state.availability, + builder: (context, availability, _) => LunaBlock( + title: 'radarr.MinimumAvailability'.tr(), + body: [TextSpan(text: availability.readable)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + Tuple2 _values = + await RadarrDialogs().editMinimumAvailability(context); + if (_values.item1) + context.read().availability = _values.item2; + }, + ), ); } } diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart index 46fea143..c54bcd8c 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart @@ -9,19 +9,20 @@ class RadarrMoviesEditTagsTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Tags'.tr()), - subtitle: LunaText.subtitle( - text: context.watch().tags.isEmpty - ? LunaUI.TEXT_EMDASH - : context - .watch() - .tags - .map((e) => e.label) - .join(', ')), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async => await RadarrDialogs().setEditTags(context), + return Selector>( + selector: (_, state) => state.tags, + builder: (context, tags, _) => LunaBlock( + title: 'radarr.Tags'.tr(), + body: [ + TextSpan( + text: tags.isEmpty + ? LunaUI.TEXT_EMDASH + : tags.map((e) => e.label).join(', '), + ), + ], + trailing: const LunaIconButton.arrow(), + onTap: () async => await RadarrDialogs().setEditTags(context), + ), ); } } diff --git a/lib/modules/radarr/routes/manual_import/route.dart b/lib/modules/radarr/routes/manual_import/route.dart index 9f289fb8..77939ffc 100644 --- a/lib/modules/radarr/routes/manual_import/route.dart +++ b/lib/modules/radarr/routes/manual_import/route.dart @@ -39,7 +39,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaAppBar( title: 'radarr.ManualImport'.tr(), scrollControllers: [scrollController], - bottom: RadarrManualImportPathBar(scrollController: scrollController), + bottom: LunaAppBar.empty( + height: LunaTextInputBar.defaultAppBarHeight, + child: RadarrManualImportPathBar(scrollController: scrollController), + ), ); } diff --git a/lib/modules/radarr/routes/manual_import/widgets/appbar_path_bar.dart b/lib/modules/radarr/routes/manual_import/widgets/appbar_path_bar.dart index 0e70ea34..f06bf413 100644 --- a/lib/modules/radarr/routes/manual_import/widgets/appbar_path_bar.dart +++ b/lib/modules/radarr/routes/manual_import/widgets/appbar_path_bar.dart @@ -12,7 +12,8 @@ class RadarrManualImportPathBar extends StatefulWidget }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); @@ -21,7 +22,7 @@ class RadarrManualImportPathBar extends StatefulWidget class _State extends State { @override Widget build(BuildContext context) { - return Padding( + return SizedBox( child: Row( children: [ Expanded( @@ -42,12 +43,12 @@ class _State extends State { .fetchDirectories(context, value); } }, - margin: const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 14.0), + margin: EdgeInsets.zero, ), ), ], ), - padding: const EdgeInsets.only(top: 1.0, bottom: 1.0), + height: LunaTextInputBar.defaultAppBarHeight, ); } } diff --git a/lib/modules/radarr/routes/manual_import/widgets/directory_tile.dart b/lib/modules/radarr/routes/manual_import/widgets/directory_tile.dart index d8d7a30c..43f6f81c 100644 --- a/lib/modules/radarr/routes/manual_import/widgets/directory_tile.dart +++ b/lib/modules/radarr/routes/manual_import/widgets/directory_tile.dart @@ -22,26 +22,19 @@ class _State extends State { if (widget.directory == null || widget.directory.path == null || widget.directory.path.isEmpty) return const SizedBox(height: 0.0); - return LunaListTile( - context: context, - title: - LunaText.title(text: widget.directory?.name ?? LunaUI.TEXT_EMDASH), - subtitle: LunaText.subtitle( - text: widget.directory.path, - ), - trailing: LunaIconButton( - icon: Icons.arrow_forward_ios_rounded, - loadingState: _loadingState, - ), - onTap: () async { - if (_loadingState == LunaLoadingState.INACTIVE) { - if (mounted) - setState(() => _loadingState = LunaLoadingState.ACTIVE); - context.read().fetchDirectories( - context, - widget.directory.path, - ); - } - }); + return LunaBlock( + title: widget.directory?.name ?? LunaUI.TEXT_EMDASH, + body: [TextSpan(text: widget.directory.path)], + trailing: LunaIconButton.arrow(loadingState: _loadingState), + onTap: () async { + if (_loadingState == LunaLoadingState.INACTIVE) { + if (mounted) setState(() => _loadingState = LunaLoadingState.ACTIVE); + context.read().fetchDirectories( + context, + widget.directory.path, + ); + } + }, + ); } } diff --git a/lib/modules/radarr/routes/manual_import/widgets/parent_directory_tile.dart b/lib/modules/radarr/routes/manual_import/widgets/parent_directory_tile.dart index 59a02c21..aa0f8e52 100644 --- a/lib/modules/radarr/routes/manual_import/widgets/parent_directory_tile.dart +++ b/lib/modules/radarr/routes/manual_import/widgets/parent_directory_tile.dart @@ -22,10 +22,9 @@ class _State extends State { if (widget.fileSystem == null || widget.fileSystem.parent == null || widget.fileSystem.parent.isEmpty) return const SizedBox(height: 0.0); - return LunaListTile( - context: context, - title: LunaText.title(text: LunaUI.TEXT_ELLIPSIS), - subtitle: LunaText.subtitle(text: 'radarr.ParentDirectory'.tr()), + return LunaBlock( + title: LunaUI.TEXT_ELLIPSIS, + body: [TextSpan(text: 'radarr.ParentDirectory'.tr())], trailing: LunaIconButton( icon: Icons.arrow_upward_rounded, loadingState: _loadingState, diff --git a/lib/modules/radarr/routes/manual_import_details/widgets/appbar_configure_movies.dart b/lib/modules/radarr/routes/manual_import_details/widgets/appbar_configure_movies.dart index bb411489..73f72c84 100644 --- a/lib/modules/radarr/routes/manual_import_details/widgets/appbar_configure_movies.dart +++ b/lib/modules/radarr/routes/manual_import_details/widgets/appbar_configure_movies.dart @@ -10,7 +10,7 @@ class RadarrManualImportDetailsConfigureMoviesSearchBar extends StatefulWidget @override Size get preferredSize => - const Size.fromHeight(LunaTextInputBar.appBarHeight); + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); @@ -39,7 +39,7 @@ class _State extends State { ), ], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ), ); } diff --git a/lib/modules/radarr/routes/manual_import_details/widgets/import_tile.dart b/lib/modules/radarr/routes/manual_import_details/widgets/import_tile.dart index 8e3bc53e..fb5b75fa 100644 --- a/lib/modules/radarr/routes/manual_import_details/widgets/import_tile.dart +++ b/lib/modules/radarr/routes/manual_import_details/widgets/import_tile.dart @@ -31,7 +31,7 @@ class RadarrManualImportDetailsTile extends StatelessWidget { .watch() .selectedFiles .contains(manualImport.id) - ? LunaColours.accent.withOpacity(0.15) + ? LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH) : null, ), ); diff --git a/lib/modules/radarr/routes/missing/widgets/missing_tile.dart b/lib/modules/radarr/routes/missing/widgets/missing_tile.dart index 32400d4c..2b4e7755 100644 --- a/lib/modules/radarr/routes/missing/widgets/missing_tile.dart +++ b/lib/modules/radarr/routes/missing/widgets/missing_tile.dart @@ -3,8 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; class RadarrMissingTile extends StatefulWidget { - static final itemExtent = - LunaFourLineCardWithPoster.itemExtent + LunaUI.MARGIN_CARD.vertical; + static final itemExtent = LunaBlock.calculateItemExtent(3); final RadarrMovie movie; final RadarrQualityProfile profile; @@ -24,17 +23,19 @@ class _State extends State { Widget build(BuildContext context) { return Selector>>( selector: (_, state) => state.missing, - builder: (context, missing, _) => LunaFourLineCardWithPoster( + builder: (context, missing, _) => LunaBlock( backgroundUrl: - context.read().getPosterURL(widget.movie.id), + context.read().getFanartURL(widget.movie.id), posterUrl: context.read().getPosterURL(widget.movie.id), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, - darken: !widget.movie.monitored, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + disabled: !widget.movie.monitored, title: widget.movie.title, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle3: _subtitle3(), + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], trailing: _trailing(), onTap: _onTap, ), diff --git a/lib/modules/radarr/routes/more/route.dart b/lib/modules/radarr/routes/more/route.dart index dd8a8499..2e5affec 100644 --- a/lib/modules/radarr/routes/more/route.dart +++ b/lib/modules/radarr/routes/more/route.dart @@ -29,53 +29,47 @@ class _State extends State with AutomaticKeepAliveClientMixin { Widget _body() { return LunaListView( controller: RadarrNavigationBar.scrollControllers[3], + itemExtent: LunaBlock.calculateItemExtent(1), children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.History'.tr()), - subtitle: LunaText.subtitle(text: 'radarr.HistoryDescription'.tr()), + LunaBlock( + title: 'radarr.History'.tr(), + body: [TextSpan(text: 'radarr.HistoryDescription'.tr())], trailing: LunaIconButton( icon: Icons.history_rounded, color: LunaColours().byListIndex(0), ), onTap: () async => RadarrHistoryRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.ManualImport'.tr()), - subtitle: - LunaText.subtitle(text: 'radarr.ManualImportDescription'.tr()), + LunaBlock( + title: 'radarr.ManualImport'.tr(), + body: [TextSpan(text: 'radarr.ManualImportDescription'.tr())], trailing: LunaIconButton( icon: Icons.download_done_rounded, color: LunaColours().byListIndex(1), ), onTap: () async => RadarrManualImportRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Queue'.tr()), - subtitle: LunaText.subtitle(text: 'radarr.QueueDescription'.tr()), + LunaBlock( + title: 'radarr.Queue'.tr(), + body: [TextSpan(text: 'radarr.QueueDescription'.tr())], trailing: LunaIconButton( icon: Icons.queue_rounded, color: LunaColours().byListIndex(2), ), onTap: () async => RadarrQueueRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.SystemStatus'.tr()), - subtitle: - LunaText.subtitle(text: 'radarr.SystemStatusDescription'.tr()), + LunaBlock( + title: 'radarr.SystemStatus'.tr(), + body: [TextSpan(text: 'radarr.SystemStatusDescription'.tr())], trailing: LunaIconButton( icon: Icons.computer_rounded, color: LunaColours().byListIndex(3), ), onTap: () async => RadarrSystemStatusRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'radarr.Tags'.tr()), - subtitle: LunaText.subtitle(text: 'radarr.TagsDescription'.tr()), + LunaBlock( + title: 'radarr.Tags'.tr(), + body: [TextSpan(text: 'radarr.TagsDescription'.tr())], trailing: LunaIconButton( icon: Icons.style_rounded, color: LunaColours().byListIndex(4), diff --git a/lib/modules/radarr/routes/movie_details/route.dart b/lib/modules/radarr/routes/movie_details/route.dart index 4440ae4c..66e76edb 100644 --- a/lib/modules/radarr/routes/movie_details/route.dart +++ b/lib/modules/radarr/routes/movie_details/route.dart @@ -120,6 +120,7 @@ class _State extends State<_Widget> with LunaLoadCallbackMixin { ? null : [ LunaIconButton( + iconSize: LunaUI.ICON_SIZE, icon: Icons.edit_rounded, onPressed: () async => RadarrMoviesEditRouter() .navigateTo(context, movieId: widget.movieId), diff --git a/lib/modules/radarr/routes/movie_details/widgets/appbar_settings_action.dart b/lib/modules/radarr/routes/movie_details/widgets/appbar_settings_action.dart index 385e3686..dafeb819 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/appbar_settings_action.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/appbar_settings_action.dart @@ -25,6 +25,7 @@ class RadarrAppBarMovieSettingsAction extends StatelessWidget { if (movie != null) return LunaIconButton( icon: Icons.more_vert_rounded, + iconSize: LunaUI.ICON_SIZE, onPressed: () async { Tuple2 values = await RadarrDialogs().movieSettings(context, movie); diff --git a/lib/modules/radarr/routes/movie_details/widgets/cast_crew_tile.dart b/lib/modules/radarr/routes/movie_details/widgets/cast_crew_tile.dart index fc570830..b7e6a2bb 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/cast_crew_tile.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/cast_crew_tile.dart @@ -12,35 +12,36 @@ class RadarrMovieDetailsCastCrewTile extends StatelessWidget { @override Widget build(BuildContext context) { - String position; - switch (credits.type) { - case RadarrCreditType.CREW: - position = credits.job.isEmpty ? LunaUI.TEXT_EMDASH : credits.job; - break; - case RadarrCreditType.CAST: - position = - credits.character.isEmpty ? LunaUI.TEXT_EMDASH : credits.character; - break; - default: - position = LunaUI.TEXT_EMDASH; - break; - } - return LunaTwoLineCardWithPoster( + return LunaBlock( title: credits.personName, - posterPlaceholder: LunaAssets.blankUser, + posterPlaceholderIcon: LunaIcons.USER, posterUrl: credits.images.isEmpty ? null : credits.images[0].url, - posterHeaders: const {}, - subtitle1: TextSpan(text: position), - subtitle2: TextSpan( - text: credits.type.readable, - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: credits.type == RadarrCreditType.CAST - ? LunaColours.accent - : LunaColours.orange, + body: [ + TextSpan(text: _position), + TextSpan( + text: credits.type.readable, + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: credits.type == RadarrCreditType.CAST + ? LunaColours.accent + : LunaColours.orange, + ), ), - ), + ], onTap: credits.personTmdbId?.toString()?.lunaOpenTheMovieDBCredits, ); } + + String get _position { + switch (credits.type) { + case RadarrCreditType.CREW: + return credits.job.isEmpty ? LunaUI.TEXT_EMDASH : credits.job; + case RadarrCreditType.CAST: + return credits.character.isEmpty + ? LunaUI.TEXT_EMDASH + : credits.character; + default: + return LunaUI.TEXT_EMDASH; + } + } } diff --git a/lib/modules/radarr/routes/movie_details/widgets/files_file_block.dart b/lib/modules/radarr/routes/movie_details/widgets/files_file_block.dart index e632fce2..d01248e8 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/files_file_block.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/files_file_block.dart @@ -89,8 +89,7 @@ class _State extends State { } Future _viewMediaInfo() async { - // TODO: Abstract this - LunaBottomModalSheet().showModal( + LunaBottomModalSheet().show( context: context, builder: (context) => LunaListViewModal( children: [ diff --git a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_description_tile.dart b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_description_tile.dart index 47c62e2f..4426d80f 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_description_tile.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_description_tile.dart @@ -12,18 +12,20 @@ class RadarrMovieDetailsOverviewDescriptionTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( - posterPlaceholder: LunaAssets.blankVideo, + return LunaBlock( + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, backgroundUrl: context.read().getFanartURL(movie.id), posterUrl: context.read().getPosterURL(movie.id), posterHeaders: context.read().headers, title: movie.title, - subtitle1: TextSpan( - text: movie.overview == null || movie.overview.isEmpty - ? 'No summary is available.\n\n\n' - : '${movie.overview}\n\n\n', - ), - subtitle1MaxLines: 3, + body: [ + LunaTextSpan.extended( + text: movie.overview == null || movie.overview.isEmpty + ? 'sonarr.NoSummaryAvailable'.tr() + : movie.overview, + ), + ], + customBodyMaxLines: 3, onTap: () async => LunaDialogs().textPreview(context, movie.title, movie.overview), ); diff --git a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_information_block.dart b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_information_block.dart index dfcb715d..ff2a01b7 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_information_block.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_information_block.dart @@ -26,14 +26,14 @@ class RadarrMovieDetailsOverviewInformationBlock extends StatelessWidget { LunaTableContent( title: 'availability', body: movie?.lunaMinimumAvailability), LunaTableContent(title: 'tags', body: movie?.lunaTags(tags)), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent(title: 'status', body: movie?.status?.readable), LunaTableContent(title: 'in cinemas', body: movie?.lunaInCinemasOn), LunaTableContent(title: 'digital', body: movie?.lunaDigitalReleaseDate), LunaTableContent( title: 'physical', body: movie?.lunaPhysicalReleaseDate), LunaTableContent(title: 'added on', body: movie?.lunaDateAdded), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent(title: 'year', body: movie?.lunaYear), LunaTableContent(title: 'studio', body: movie?.lunaStudio), LunaTableContent(title: 'runtime', body: movie?.lunaRuntime), diff --git a/lib/modules/radarr/routes/radarr/widgets/appbar_add_movie_action.dart b/lib/modules/radarr/routes/radarr/widgets/appbar_add_movie_action.dart index c2df89e1..3becb8ec 100644 --- a/lib/modules/radarr/routes/radarr/widgets/appbar_add_movie_action.dart +++ b/lib/modules/radarr/routes/radarr/widgets/appbar_add_movie_action.dart @@ -11,6 +11,7 @@ class RadarrAppBarAddMoviesAction extends StatelessWidget { Widget build(BuildContext context) { return LunaIconButton( icon: Icons.add_rounded, + iconSize: LunaUI.ICON_SIZE, onPressed: () async => RadarrAddMovieRouter().navigateTo( context, query: '', diff --git a/lib/modules/radarr/routes/radarr/widgets/appbar_global_settings_action.dart b/lib/modules/radarr/routes/radarr/widgets/appbar_global_settings_action.dart index 33d71a71..4f2c9ecf 100644 --- a/lib/modules/radarr/routes/radarr/widgets/appbar_global_settings_action.dart +++ b/lib/modules/radarr/routes/radarr/widgets/appbar_global_settings_action.dart @@ -11,6 +11,7 @@ class RadarrAppBarGlobalSettingsAction extends StatelessWidget { Widget build(BuildContext context) { return LunaIconButton( icon: Icons.more_vert_rounded, + iconSize: LunaUI.ICON_SIZE, onPressed: () async { Tuple2 values = await RadarrDialogs().globalSettings(context); diff --git a/lib/modules/radarr/routes/releases/widgets/search_bar.dart b/lib/modules/radarr/routes/releases/widgets/search_bar.dart index 9d5e7330..c137c4be 100644 --- a/lib/modules/radarr/routes/releases/widgets/search_bar.dart +++ b/lib/modules/radarr/routes/releases/widgets/search_bar.dart @@ -12,7 +12,8 @@ class RadarrReleasesSearchBar extends StatefulWidget }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); diff --git a/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart b/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart index 6696f7fd..911eb07f 100644 --- a/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart +++ b/lib/modules/radarr/routes/releases/widgets/search_bar_filter_button.dart @@ -36,7 +36,7 @@ class _State extends State { child: Text( RadarrReleasesFilter.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.filterType == RadarrReleasesFilter.values[index] ? LunaColours.accent : Colors.white, @@ -46,8 +46,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 14.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart b/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart index 60233714..9a7f45fc 100644 --- a/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart +++ b/lib/modules/radarr/routes/releases/widgets/search_bar_sort_button.dart @@ -44,7 +44,7 @@ class _State extends State { Text( RadarrReleasesSorting.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.sortType == RadarrReleasesSorting.values[index] ? LunaColours.accent @@ -56,7 +56,7 @@ class _State extends State { state.sortAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], @@ -65,8 +65,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 13.5), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/radarr/routes/system_status/widgets/disk_space_tile.dart b/lib/modules/radarr/routes/system_status/widgets/disk_space_tile.dart index 399a98c2..3196364f 100644 --- a/lib/modules/radarr/routes/system_status/widgets/disk_space_tile.dart +++ b/lib/modules/radarr/routes/system_status/widgets/disk_space_tile.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; -import 'package:percent_indicator/linear_percent_indicator.dart'; class RadarrDiskSpaceTile extends StatelessWidget { final RadarrDiskSpace diskSpace; @@ -21,34 +20,24 @@ class RadarrDiskSpaceTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: diskSpace?.lunaPath ?? rootFolder?.lunaPath), - subtitle: Stack( - children: [ - LunaText.subtitle( - text: diskSpace?.lunaSpace ?? rootFolder?.lunaSpace, - maxLines: 2, - ), - if (diskSpace != null) percentBar(), - ], - ), + return LunaBlock( + title: diskSpace?.lunaPath ?? rootFolder?.lunaPath, + body: [TextSpan(text: diskSpace?.lunaSpace ?? rootFolder?.lunaSpace)], + bottom: diskSpace != null ? _bottomWidget() : null, + bottomHeight: LunaLinearPercentIndicator.height, trailing: LunaIconButton( text: diskSpace?.lunaPercentageString ?? (rootFolder?.unmappedFolders?.length ?? 0).toString(), + textSize: LunaUI.FONT_SIZE_H4, color: diskSpace?.lunaColor ?? LunaColours.accent, ), - contentPadding: diskSpace != null, ); } - Widget percentBar() { - return LinearPercentIndicator( + Widget _bottomWidget() { + return LunaLinearPercentIndicator( percent: (diskSpace.lunaPercentage ?? 0) / 100, - padding: const EdgeInsets.only(left: 2.0, right: 2.0, top: 26.0), - progressColor: diskSpace.lunaColor, - backgroundColor: diskSpace.lunaColor.withOpacity(0.30), - lineHeight: 4.0, + progressColor: diskSpace.lunaColor ?? LunaColours.accent, ); } } diff --git a/lib/modules/radarr/routes/system_status/widgets/health_tile.dart b/lib/modules/radarr/routes/system_status/widgets/health_tile.dart index 9cec7f20..2bc5982a 100644 --- a/lib/modules/radarr/routes/system_status/widgets/health_tile.dart +++ b/lib/modules/radarr/routes/system_status/widgets/health_tile.dart @@ -34,7 +34,7 @@ class RadarrHealthCheckTile extends StatelessWidget { style: TextStyle( color: healthCheck.type.lunaColour, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), ); } diff --git a/lib/modules/radarr/routes/tags/widgets/tag_tile.dart b/lib/modules/radarr/routes/tags/widgets/tag_tile.dart index d8506a3c..52f9abae 100644 --- a/lib/modules/radarr/routes/tags/widgets/tag_tile.dart +++ b/lib/modules/radarr/routes/tags/widgets/tag_tile.dart @@ -33,10 +33,9 @@ class _State extends State with LunaLoadCallbackMixin { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: widget.tag.label), - subtitle: LunaText.subtitle(text: _subtitle()), + return LunaBlock( + title: widget.tag.label, + body: [TextSpan(text: _subtitle())], trailing: _trailing(), onTap: _movieDialog, ); @@ -53,7 +52,7 @@ class _State extends State with LunaLoadCallbackMixin { // Default to true, to not try to delete a tag that actually does have movies attached if (movieList?.isNotEmpty ?? true) return null; return LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, onPressed: _delete, ); diff --git a/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart b/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart index 9f3a0695..7bd6e8f8 100644 --- a/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart +++ b/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart @@ -3,8 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; class RadarrUpcomingTile extends StatefulWidget { - static final itemExtent = - LunaFourLineCardWithPoster.itemExtent + LunaUI.MARGIN_CARD.vertical; + static final itemExtent = LunaBlock.calculateItemExtent(3); final RadarrMovie movie; final RadarrQualityProfile profile; @@ -24,20 +23,25 @@ class _State extends State { Widget build(BuildContext context) { return Selector>>( selector: (_, state) => state.missing, - builder: (context, missing, _) => LunaFourLineCardWithPoster( - backgroundUrl: - context.read().getPosterURL(widget.movie.id), - posterUrl: context.read().getPosterURL(widget.movie.id), - posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, - darken: !widget.movie.monitored, - title: widget.movie.title, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle3: _subtitle3(), - trailing: _trailing(), - onTap: _onTap, - ), + builder: (context, missing, _) { + return LunaBlock( + title: widget.movie.title, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + trailing: _trailing(), + backgroundUrl: + context.read().getFanartURL(widget.movie.id), + posterHeaders: context.read().headers, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + posterIsSquare: false, + posterUrl: context.read().getPosterURL(widget.movie.id), + onTap: _onTap, + disabled: !widget.movie.monitored, + ); + }, ); } @@ -70,19 +74,15 @@ class _State extends State { String _days; String type; if (widget.movie.lunaIsInCinemas && !widget.movie.lunaIsReleased) { - color = widget.movie.monitored - ? LunaColours.blue - : LunaColours.blue.withOpacity(0.30); + color = LunaColours.blue; _days = widget.movie.lunaEarlierReleaseDate.lunaDaysDifference; type = 'release'; } else if (!widget.movie.lunaIsInCinemas && !widget.movie.lunaIsReleased) { - color = widget.movie.monitored - ? LunaColours.orange - : LunaColours.orange.withOpacity(0.30); + color = LunaColours.orange; _days = widget.movie.inCinemas.lunaDaysDifference; type = 'cinema'; } else { - color = widget.movie.monitored ? Colors.white70 : Colors.white30; + color = LunaColours.grey; _days = LunaUI.TEXT_EMDASH; type = 'unknown'; } @@ -117,9 +117,10 @@ class _State extends State { return LunaIconButton( icon: Icons.search_rounded, onPressed: () async => RadarrAPIHelper().automaticSearch( - context: context, - movieId: widget.movie.id, - title: widget.movie.title), + context: context, + movieId: widget.movie.id, + title: widget.movie.title, + ), onLongPress: () async => RadarrReleasesRouter().navigateTo(context, movieId: widget.movie.id), ); diff --git a/lib/modules/sabnzbd/core/database.dart b/lib/modules/sabnzbd/core/database.dart index 924c3ef3..909707e6 100644 --- a/lib/modules/sabnzbd/core/database.dart +++ b/lib/modules/sabnzbd/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -49,7 +48,7 @@ class SABnzbdDatabase extends LunaModuleDatabase { extension SABnzbdDatabaseValueExtension on SABnzbdDatabaseValue { String get key { - return 'SABNZBD_${describeEnum(this)}'; + return 'SABNZBD_${this.name}'; } dynamic get data { diff --git a/lib/modules/sabnzbd/routes/history.dart b/lib/modules/sabnzbd/routes/history.dart index 2d034439..1f2c4b32 100644 --- a/lib/modules/sabnzbd/routes/history.dart +++ b/lib/modules/sabnzbd/routes/history.dart @@ -48,7 +48,7 @@ class _State extends State return LunaAppBar.empty( child: SABnzbdHistorySearchBar( scrollController: SABnzbdNavigationBar.scrollControllers[1]), - height: 62.0, + height: LunaTextInputBar.defaultAppBarHeight, ); } diff --git a/lib/modules/sabnzbd/routes/history_stages.dart b/lib/modules/sabnzbd/routes/history_stages.dart index 39609f09..e0d172e3 100644 --- a/lib/modules/sabnzbd/routes/history_stages.dart +++ b/lib/modules/sabnzbd/routes/history_stages.dart @@ -53,21 +53,23 @@ class _State extends State controller: scrollController, children: List.generate( _arguments.data.stageLog.length, - (index) => LunaListTile( - context: context, - title: LunaText.title( - text: _arguments.data.stageLog[index]['name']), - subtitle: LunaText.subtitle( - text: _arguments.data.stageLog[index]['actions'][0] - .replaceAll('
', '.\n')), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - String _data = _arguments.data.stageLog[index]['actions'] - .join(',\n') - .replaceAll('
', '.\n'); - LunaDialogs().textPreview( - context, _arguments.data.stageLog[index]['name'], _data); - }), + (index) => LunaBlock( + title: _arguments.data.stageLog[index]['name'], + body: [ + TextSpan( + text: _arguments.data.stageLog[index]['actions'][0] + .replaceAll('
', '.\n'), + ), + ], + trailing: const LunaIconButton.arrow(), + onTap: () async { + String _data = _arguments.data.stageLog[index]['actions'] + .join(',\n') + .replaceAll('
', '.\n'); + LunaDialogs().textPreview( + context, _arguments.data.stageLog[index]['name'], _data); + }, + ), ), ); } diff --git a/lib/modules/sabnzbd/widgets/app_bar_stats.dart b/lib/modules/sabnzbd/widgets/app_bar_stats.dart index fb0d1f76..c036f822 100644 --- a/lib/modules/sabnzbd/widgets/app_bar_stats.dart +++ b/lib/modules/sabnzbd/widgets/app_bar_stats.dart @@ -24,8 +24,8 @@ class SABnzbdAppBarStats extends StatelessWidget { child: RichText( text: TextSpan( style: const TextStyle( - color: Colors.white54, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + color: LunaColours.grey, + fontSize: LunaUI.FONT_SIZE_H3, ), children: [ TextSpan( diff --git a/lib/modules/sabnzbd/widgets/history_hide_button.dart b/lib/modules/sabnzbd/widgets/history_hide_button.dart index 03b91fe9..1130204c 100644 --- a/lib/modules/sabnzbd/widgets/history_hide_button.dart +++ b/lib/modules/sabnzbd/widgets/history_hide_button.dart @@ -26,8 +26,8 @@ class _State extends State { onPressed: () => model.historyHideFailed = !model.historyHideFailed, ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.only(left: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sabnzbd/widgets/queue_tile.dart b/lib/modules/sabnzbd/widgets/queue_tile.dart index 16a3195d..e1dc0587 100644 --- a/lib/modules/sabnzbd/widgets/queue_tile.dart +++ b/lib/modules/sabnzbd/widgets/queue_tile.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sabnzbd.dart'; -import 'package:percent_indicator/linear_percent_indicator.dart'; class SABnzbdQueueTile extends StatefulWidget { final int index; @@ -24,39 +23,18 @@ class SABnzbdQueueTile extends StatefulWidget { class _State extends State { @override - Widget build(BuildContext context) => LunaListTile( - context: context, - title: LunaText.title( - text: widget.data.name, - darken: widget.data.isPaused, - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - child: LinearPercentIndicator( - percent: min(1.0, max(0, widget.data.percentageDone / 100)), - padding: const EdgeInsets.symmetric(horizontal: 2.0), - progressColor: widget.data.isPaused - ? LunaColours.accent.withOpacity(0.30) - : LunaColours.accent, - backgroundColor: widget.data.isPaused - ? LunaColours.accent.withOpacity(0.05) - : LunaColours.accent.withOpacity(0.15), - lineHeight: 4.0, - ), - padding: const EdgeInsets.symmetric(vertical: 6.0), - ), - LunaText.subtitle( - text: widget.data.subtitle, - darken: widget.data.isPaused, - ), - ], - ), - trailing: LunaReorderableListDragger(index: widget.index), - onTap: _handlePopup, - ); + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data.name, + body: [TextSpan(text: widget.data.subtitle)], + bottomHeight: LunaLinearPercentIndicator.height, + bottom: LunaLinearPercentIndicator( + percent: min(1.0, max(0, widget.data.percentageDone / 100)), + ), + trailing: LunaReorderableListViewDragger(index: widget.index), + onTap: _handlePopup, + ); + } Future _handlePopup() async { _Helper _helper = _Helper(widget.queueContext, widget.data, widget.refresh); diff --git a/lib/modules/search/core/database.dart b/lib/modules/search/core/database.dart index 43022f7b..011133a9 100644 --- a/lib/modules/search/core/database.dart +++ b/lib/modules/search/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -50,7 +49,7 @@ class SearchDatabase extends LunaModuleDatabase { extension SearchDatabaseValueExtension on SearchDatabaseValue { String get key { - return 'SEARCH_${describeEnum(this)}'; + return 'SEARCH_${this.name}'; } dynamic get data { diff --git a/lib/modules/search/core/dialogs.dart b/lib/modules/search/core/dialogs.dart index 9237349d..f6e855ef 100644 --- a/lib/modules/search/core/dialogs.dart +++ b/lib/modules/search/core/dialogs.dart @@ -32,7 +32,7 @@ class SearchDialogs { child: Text( LunaDatabaseValue.ENABLED_PROFILE.data, style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), ), ), @@ -64,7 +64,7 @@ class SearchDialogs { child: Text( profile, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: (LunaDatabaseValue.ENABLED_PROFILE.data ?? 'default') == profile diff --git a/lib/modules/search/routes/categories/widgets/category_tile.dart b/lib/modules/search/routes/categories/widgets/category_tile.dart index 9b2fe6e4..ad828d69 100644 --- a/lib/modules/search/routes/categories/widgets/category_tile.dart +++ b/lib/modules/search/routes/categories/widgets/category_tile.dart @@ -14,14 +14,13 @@ class SearchCategoryTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: category.name ?? 'lunasea.Unknown'.tr()), - subtitle: LunaText.subtitle( - text: category.subcategoriesTitleList, - ), + return LunaBlock( + title: category.name ?? 'lunasea.Unknown'.tr(), + body: [TextSpan(text: category.subcategoriesTitleList)], trailing: LunaIconButton( - icon: category.icon, color: LunaColours().byListIndex(index)), + icon: category.icon, + color: LunaColours().byListIndex(index), + ), onTap: () async { context.read().activeCategory = category; SearchSubcategoriesRouter().navigateTo(context); diff --git a/lib/modules/search/routes/indexers/widgets/indexer_tile.dart b/lib/modules/search/routes/indexers/widgets/indexer_tile.dart index 9ae470d3..dd32d699 100644 --- a/lib/modules/search/routes/indexers/widgets/indexer_tile.dart +++ b/lib/modules/search/routes/indexers/widgets/indexer_tile.dart @@ -12,11 +12,10 @@ class SearchIndexerTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: indexer.displayName), - subtitle: LunaText.subtitle(text: indexer.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: indexer.displayName, + body: [TextSpan(text: indexer.host)], + trailing: const LunaIconButton.arrow(), onTap: () async { context.read().indexer = indexer; SearchCategoriesRouter().navigateTo(context); diff --git a/lib/modules/search/routes/results/widgets/result_tile.dart b/lib/modules/search/routes/results/widgets/result_tile.dart index 3478e778..b139d0c7 100644 --- a/lib/modules/search/routes/results/widgets/result_tile.dart +++ b/lib/modules/search/routes/results/widgets/result_tile.dart @@ -48,7 +48,7 @@ class SearchResultTile extends StatelessWidget { if (SearchDatabaseValue.SHOW_LINKS.data && data.linkComments != null && data.linkDownload != null) - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), if (SearchDatabaseValue.SHOW_LINKS.data && data.linkComments != null) LunaTableContent( title: 'search.Comments'.tr(), diff --git a/lib/modules/search/routes/search/widgets/search_bar.dart b/lib/modules/search/routes/search/widgets/search_bar.dart index 911e56b3..a09fc2ee 100644 --- a/lib/modules/search/routes/search/widgets/search_bar.dart +++ b/lib/modules/search/routes/search/widgets/search_bar.dart @@ -13,7 +13,8 @@ class SearchSearchBar extends StatefulWidget implements PreferredSizeWidget { }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); diff --git a/lib/modules/search/routes/subcategories/widgets/all_subcategories_tile.dart b/lib/modules/search/routes/subcategories/widgets/all_subcategories_tile.dart index a76b3f40..c2a30dca 100644 --- a/lib/modules/search/routes/subcategories/widgets/all_subcategories_tile.dart +++ b/lib/modules/search/routes/subcategories/widgets/all_subcategories_tile.dart @@ -9,19 +9,19 @@ class SearchSubcategoryAllTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'search.AllSubcategories'.tr()), - subtitle: LunaText.subtitle( - text: context.read().activeCategory?.name ?? - 'lunasea.Unknown'.tr()), - trailing: LunaIconButton( - icon: context.read().activeCategory?.icon, - color: LunaColours().byListIndex(0)), - onTap: () async { - context.read().activeSubcategory = null; - SearchResultsRouter().navigateTo(context); - }, + return Selector( + selector: (_, state) => state.activeCategory, + builder: (context, category, _) => LunaBlock( + title: 'search.AllSubcategories'.tr(), + body: [TextSpan(text: category?.name ?? 'lunasea.Unknown'.tr())], + trailing: LunaIconButton( + icon: context.read().activeCategory?.icon, + color: LunaColours().byListIndex(0)), + onTap: () async { + context.read().activeSubcategory = null; + SearchResultsRouter().navigateTo(context); + }, + ), ); } } diff --git a/lib/modules/search/routes/subcategories/widgets/subcategory_tile.dart b/lib/modules/search/routes/subcategories/widgets/subcategory_tile.dart index 48700475..85dc0eba 100644 --- a/lib/modules/search/routes/subcategories/widgets/subcategory_tile.dart +++ b/lib/modules/search/routes/subcategories/widgets/subcategory_tile.dart @@ -16,23 +16,25 @@ class SearchSubcategoryTile extends StatelessWidget { selector: (_, state) => state.activeCategory, builder: (context, category, _) { NewznabSubcategoryData subcategory = category.subcategories[index]; - return LunaListTile( - context: context, - title: LunaText.title( - text: subcategory?.name ?? 'lunasea.Unknown'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: subcategory?.name ?? 'lunasea.Unknown'.tr(), + body: [ + TextSpan( text: [ category?.name ?? 'lunasea.Unknown'.tr(), subcategory?.name ?? 'lunasea.Unknown'.tr(), ].join(' > '), - ), - trailing: LunaIconButton( - icon: category?.icon, - color: LunaColours().byListIndex(index + 1)), - onTap: () async { - context.read().activeSubcategory = subcategory; - SearchResultsRouter().navigateTo(context); - }); + ) + ], + trailing: LunaIconButton( + icon: category?.icon, + color: LunaColours().byListIndex(index + 1), + ), + onTap: () async { + context.read().activeSubcategory = subcategory; + SearchResultsRouter().navigateTo(context); + }, + ); }, ); } diff --git a/lib/modules/settings/core/banners.dart b/lib/modules/settings/core/banners.dart index 771decaf..2848cfb5 100644 --- a/lib/modules/settings/core/banners.dart +++ b/lib/modules/settings/core/banners.dart @@ -3,21 +3,23 @@ import 'package:lunasea/core.dart'; enum SettingsBanners { NOTIFICATIONS_MODULE_SUPPORT, + QUICK_ACTIONS_SUPPORT, + PROFILES_SUPPORT, } extension SettingsBannersExtension on SettingsBanners { String get key { - switch (this) { - case SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT: - return 'SETTINGS_NOTIFICATIONS_MODULE_SUPPORT'; - } - throw Exception('Invalid SettingsBanners'); + return 'SETTINGS_${this.name}'; } String get header { switch (this) { case SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT: return 'settings.BannersNotificationModuleSupportHeader'.tr(); + case SettingsBanners.QUICK_ACTIONS_SUPPORT: + return 'settings.QuickActions'.tr(); + case SettingsBanners.PROFILES_SUPPORT: + return 'settings.Profiles'.tr(); } throw Exception('Invalid SettingsBanners'); } @@ -26,6 +28,17 @@ extension SettingsBannersExtension on SettingsBanners { switch (this) { case SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT: return 'settings.BannersNotificationModuleSupportBody'.tr(); + case SettingsBanners.QUICK_ACTIONS_SUPPORT: + return [ + 'Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing LunaSea\'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'); + + case SettingsBanners.PROFILES_SUPPORT: + return [ + 'Profiles allow you to add multiple instances of modules into LunaSea. You can switch between profiles in the main navigation drawer.', + 'Newznab indexer searching and external modules are enabled and shared across all profiles.', + ].join('\n\n'); } throw Exception('Invalid SettingsBanners'); } @@ -33,15 +46,21 @@ extension SettingsBannersExtension on SettingsBanners { Color get iconColor { switch (this) { case SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT: + case SettingsBanners.QUICK_ACTIONS_SUPPORT: + case SettingsBanners.PROFILES_SUPPORT: + default: return LunaColours.accent; } - throw Exception('Invalid SettingsBanners'); } IconData get icon { switch (this) { case SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT: return Icons.info_outline_rounded; + case SettingsBanners.QUICK_ACTIONS_SUPPORT: + return Icons.rounded_corner_rounded; + case SettingsBanners.PROFILES_SUPPORT: + return LunaIcons.PROFILES; } throw Exception('Invalid SettingsBanners'); } @@ -55,7 +74,7 @@ extension SettingsBannersExtension on SettingsBanners { /// Create a new [ValueListenableBuilder] ValueListenableBuilder banner({ Color headerColor = Colors.white, - Color bodyColor = Colors.white70, + Color bodyColor = LunaColours.grey, }) => ValueListenableBuilder( valueListenable: Database.alertsBox.listenable(keys: [key]), diff --git a/lib/modules/settings/core/dialogs.dart b/lib/modules/settings/core/dialogs.dart index 478817bc..128b4466 100644 --- a/lib/modules/settings/core/dialogs.dart +++ b/lib/modules/settings/core/dialogs.dart @@ -484,7 +484,7 @@ class SettingsDialogs { LunaDialog.bolded( text: 'settings.DeleteAccountWarning1'.tr().toUpperCase(), color: LunaColours.red, - fontSize: LunaDialog.SUBBODY_SIZE, + fontSize: LunaDialog.BUTTON_SIZE, ), LunaDialog.textSpanContent(text: '\n\n'), LunaDialog.textSpanContent( @@ -689,7 +689,7 @@ class SettingsDialogs { content: List.generate( profiles.length, (index) => LunaDialog.tile( - icon: Icons.settings_rounded, + icon: LunaIcons.USER, iconColor: LunaColours().byListIndex(index), text: profiles[index], onTap: () => _setValues(true, profiles[index]), @@ -1155,7 +1155,7 @@ class SettingsDialogs { LunaDialog.bolded( text: '${'settings.FirebaseCrashlyticsHint1'.tr()}\n\n', color: LunaColours.red, - fontSize: LunaDialog.SUBBODY_SIZE, + fontSize: LunaDialog.BUTTON_SIZE, ), LunaDialog.textSpanContent( text: '${'settings.FirebaseCrashlyticsHint2'.tr()}\n\n', @@ -1169,7 +1169,7 @@ class SettingsDialogs { LunaDialog.bolded( text: 'settings.FirebaseCrashlyticsHint5'.tr(), color: LunaColours.accent, - fontSize: LunaDialog.SUBBODY_SIZE, + fontSize: LunaDialog.BUTTON_SIZE, ), ], alignment: TextAlign.center, diff --git a/lib/modules/settings/core/pages/headers.dart b/lib/modules/settings/core/pages/headers.dart index aa5e6b86..16b7de7b 100644 --- a/lib/modules/settings/core/pages/headers.dart +++ b/lib/modules/settings/core/pages/headers.dart @@ -69,25 +69,27 @@ class _State extends State with LunaScrollControllerMixin { Widget _noHeadersFound() => LunaMessage.inList(text: 'settings.NoHeadersAdded'.tr()); - List _headerList() { + List _headerList() { Map headers = (_headers() ?? {}).cast(); List _sortedKeys = headers.keys.toList()..sort(); return _sortedKeys - .map((key) => _headerTile(key, headers[key])) + .map((key) => _headerBlock(key, headers[key])) .toList(); } - LunaListTile _headerTile(String key, String value) { - return LunaListTile( - context: context, - title: LunaText.title(text: key), - subtitle: LunaText.subtitle(text: value), + LunaBlock _headerBlock(String key, String value) { + return LunaBlock( + title: key, + body: [TextSpan(text: value)], trailing: LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, onPressed: () async { - await HeaderUtility() - .deleteHeader(context, headers: _headers(), key: key); + await HeaderUtility().deleteHeader( + context, + key: key, + headers: _headers(), + ); _resetState(); }), ); diff --git a/lib/modules/settings/core/router.dart b/lib/modules/settings/core/router.dart index 4656d0f4..978a7d1a 100644 --- a/lib/modules/settings/core/router.dart +++ b/lib/modules/settings/core/router.dart @@ -19,6 +19,7 @@ class SettingsRouter extends LunaModuleRouter { // Configuration/Dashboard SettingsConfigurationDashboardRouter().defineRoute(router); SettingsConfigurationDashboardCalendarSettingsRouter().defineRoute(router); + SettingsConfigurationDashboardDefaultPagesRouter().defineRoute(router); // Configuration/External Modules SettingsConfigurationExternalModulesRouter().defineRoute(router); SettingsConfigurationExternalModulesAddRouter().defineRoute(router); @@ -26,6 +27,7 @@ class SettingsRouter extends LunaModuleRouter { // Configuration/Lidarr SettingsConfigurationLidarrRouter().defineRoute(router); SettingsConfigurationLidarrConnectionDetailsRouter().defineRoute(router); + SettingsConfigurationLidarrDefaultPagesRouter().defineRoute(router); SettingsConfigurationLidarrHeadersRouter().defineRoute(router); // Configuration/NZBGet SettingsConfigurationNZBGetRouter().defineRoute(router); diff --git a/lib/modules/settings/routes/account/pages/signed_out.dart b/lib/modules/settings/routes/account/pages/signed_out.dart index 63713cbc..c8075dd3 100644 --- a/lib/modules/settings/routes/account/pages/signed_out.dart +++ b/lib/modules/settings/routes/account/pages/signed_out.dart @@ -108,7 +108,7 @@ class _State extends State { style: const TextStyle( color: LunaColours.accent, fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), textAlign: TextAlign.center, ), diff --git a/lib/modules/settings/routes/account/widgets/config_backup_tile.dart b/lib/modules/settings/routes/account/widgets/config_backup_tile.dart index 575404c3..e15a5e44 100644 --- a/lib/modules/settings/routes/account/widgets/config_backup_tile.dart +++ b/lib/modules/settings/routes/account/widgets/config_backup_tile.dart @@ -20,14 +20,11 @@ class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.BackupToCloud'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.BackupToCloudDescription'.tr(), - ), + return LunaBlock( + title: 'settings.BackupToCloud'.tr(), + body: [TextSpan(text: 'settings.BackupToCloudDescription'.tr())], trailing: LunaIconButton( - icon: Icons.cloud_upload_rounded, + icon: LunaIcons.CLOUD_UPLOAD, loadingState: _loadingState, ), onTap: () async => _backup(context), diff --git a/lib/modules/settings/routes/account/widgets/config_delete_tile.dart b/lib/modules/settings/routes/account/widgets/config_delete_tile.dart index 9f63df0f..fd719bbe 100644 --- a/lib/modules/settings/routes/account/widgets/config_delete_tile.dart +++ b/lib/modules/settings/routes/account/widgets/config_delete_tile.dart @@ -20,13 +20,11 @@ class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.DeleteCloudBackup'.tr()), - subtitle: - LunaText.subtitle(text: 'settings.DeleteCloudBackupDescription'.tr()), + return LunaBlock( + title: 'settings.DeleteCloudBackup'.tr(), + body: [TextSpan(text: 'settings.DeleteCloudBackupDescription'.tr())], trailing: LunaIconButton( - icon: Icons.cloud_off_rounded, + icon: LunaIcons.CLOUD_DELETE, loadingState: _loadingState, ), onTap: () async => _delete(context), diff --git a/lib/modules/settings/routes/account/widgets/config_restore_tile.dart b/lib/modules/settings/routes/account/widgets/config_restore_tile.dart index c91b1d53..70b34c16 100644 --- a/lib/modules/settings/routes/account/widgets/config_restore_tile.dart +++ b/lib/modules/settings/routes/account/widgets/config_restore_tile.dart @@ -20,14 +20,11 @@ class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.RestoreFromCloud'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.RestoreFromCloudDescription'.tr(), - ), + return LunaBlock( + title: 'settings.RestoreFromCloud'.tr(), + body: [TextSpan(text: 'settings.RestoreFromCloudDescription'.tr())], trailing: LunaIconButton( - icon: Icons.cloud_download_rounded, + icon: LunaIcons.CLOUD_DOWNLOAD, loadingState: _loadingState, ), onTap: () async => _restore(context), diff --git a/lib/modules/settings/routes/account/widgets/delete_account_tile.dart b/lib/modules/settings/routes/account/widgets/delete_account_tile.dart index a9887b4b..b5b0a1d3 100644 --- a/lib/modules/settings/routes/account/widgets/delete_account_tile.dart +++ b/lib/modules/settings/routes/account/widgets/delete_account_tile.dart @@ -20,22 +20,21 @@ class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.DeleteAccount'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.DeleteAccountDescription'.tr(), - ), + return LunaBlock( + title: 'settings.DeleteAccount'.tr(), + body: [ + TextSpan(text: 'settings.DeleteAccountDescription'.tr()), + ], trailing: LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, loadingState: _loadingState, ), - onTap: () async => _delete(context), + onTap: _delete, ); } - Future _delete(BuildContext context) async { + Future _delete() async { if (_loadingState == LunaLoadingState.ACTIVE) return; updateState(LunaLoadingState.ACTIVE); diff --git a/lib/modules/settings/routes/configuration/route.dart b/lib/modules/settings/routes/configuration/route.dart index 87f16b6e..1efc3065 100644 --- a/lib/modules/settings/routes/configuration/route.dart +++ b/lib/modules/settings/routes/configuration/route.dart @@ -58,59 +58,48 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Appearance'.tr()), - subtitle: - LunaText.subtitle(text: 'settings.AppearanceDescription'.tr()), - trailing: LunaIconButton(icon: Icons.brush_rounded), + LunaBlock( + title: 'settings.Appearance'.tr(), + body: [TextSpan(text: 'settings.AppearanceDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.brush_rounded), onTap: () async => SettingsConfigurationAppearanceRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Drawer'.tr()), - subtitle: LunaText.subtitle(text: 'settings.DrawerDescription'.tr()), - trailing: LunaIconButton(icon: Icons.menu_rounded), + LunaBlock( + title: 'settings.Drawer'.tr(), + body: [TextSpan(text: 'settings.DrawerDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.menu_rounded), onTap: () async => SettingsConfigurationDrawerRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Localization'.tr()), - subtitle: - LunaText.subtitle(text: 'settings.LocalizationDescription'.tr()), - trailing: LunaIconButton(icon: Icons.translate_rounded), + LunaBlock( + title: 'settings.Localization'.tr(), + body: [TextSpan(text: 'settings.LocalizationDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.translate_rounded), onTap: () async => SettingsConfigurationLocalizationRouter().navigateTo(context), ), if (Platform.isIOS) LunaDatabaseValue.SELECTED_BROWSER.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.OpenLinksIn'.tr()), - subtitle: LunaText.subtitle( - text: (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser) - .name), - trailing: LunaIconButton( - icon: (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser) - .icon), - onTap: () async { - Tuple2 result = - await SettingsDialogs().changeBrowser(context); - if (result.item1) - LunaDatabaseValue.SELECTED_BROWSER.put(result.item2); - }, - ), + builder: (context, box, widget) { + LunaDatabaseValue _db = LunaDatabaseValue.SELECTED_BROWSER; + return LunaBlock( + title: 'settings.OpenLinksIn'.tr(), + body: [TextSpan(text: (_db.data as LunaBrowser).name)], + trailing: LunaIconButton(icon: (_db.data as LunaBrowser).icon), + onTap: () async { + Tuple2 result = + await SettingsDialogs().changeBrowser(context); + if (result.item1) _db.put(result.item2); + }, + ); + }, ), if (LunaQuickActions.isPlatformCompatible) - LunaListTile( - context: context, - title: LunaText.title(text: 'settings.QuickActions'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.QuickActionsDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.rounded_corner_rounded), + LunaBlock( + title: 'settings.QuickActions'.tr(), + body: [TextSpan(text: 'settings.QuickActionsDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.rounded_corner_rounded), onTap: () async => SettingsConfigurationQuickActionsRouter().navigateTo(context), ), @@ -127,12 +116,11 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _tileFromModuleMap(LunaModule module) { - return LunaListTile( - context: context, - title: LunaText.title(text: module.name), - subtitle: LunaText.subtitle( - text: 'settings.ConfigureModule'.tr(args: [module.name]), - ), + return LunaBlock( + title: module.name, + body: [ + TextSpan(text: 'settings.ConfigureModule'.tr(args: [module.name])) + ], trailing: LunaIconButton(icon: module.icon), onTap: () async => module.settingsPage.navigateTo(context), ); diff --git a/lib/modules/settings/routes/configuration_appearance/route.dart b/lib/modules/settings/routes/configuration_appearance/route.dart index 6a4d9bc7..b5f67c05 100644 --- a/lib/modules/settings/routes/configuration_appearance/route.dart +++ b/lib/modules/settings/routes/configuration_appearance/route.dart @@ -51,17 +51,17 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _amoledTheme() { - return LunaDatabaseValue.THEME_AMOLED.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.AmoledTheme'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.AmoledThemeDescription'.tr(), - ), + LunaDatabaseValue _db = LunaDatabaseValue.THEME_AMOLED; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'settings.AmoledTheme'.tr(), + body: [ + TextSpan(text: 'settings.AmoledThemeDescription'.tr()), + ], trailing: LunaSwitch( - value: LunaDatabaseValue.THEME_AMOLED.data, + value: _db.data, onChanged: (value) { - LunaDatabaseValue.THEME_AMOLED.put(value); + _db.put(value); LunaTheme().initialize(); }, ), @@ -77,12 +77,11 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { LunaDatabaseValue.THEME_AMOLED.key, ], ), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.AmoledThemeBorders'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.AmoledThemeBordersDescription'.tr(), - ), + builder: (context, _, __) => LunaBlock( + title: 'settings.AmoledThemeBorders'.tr(), + body: [ + TextSpan(text: 'settings.AmoledThemeBordersDescription'.tr()), + ], trailing: LunaSwitch( value: LunaDatabaseValue.THEME_AMOLED_BORDER.data, onChanged: LunaDatabaseValue.THEME_AMOLED.data @@ -94,22 +93,20 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _imageBackgroundOpacity() { - return LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.BackgroundImageOpacity'.tr()), - subtitle: LunaText.subtitle( - text: LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0 - ? 'lunasea.Disabled'.tr() - : '${LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data}%', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaDatabaseValue _db = LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'settings.BackgroundImageOpacity'.tr(), + body: [ + TextSpan( + text: _db.data == 0 ? 'lunasea.Disabled'.tr() : '${_db.data}%', + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 result = await SettingsDialogs().changeBackgroundImageOpacity(context); - if (result.item1) { - LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.put(result.item2); - } + if (result.item1) _db.put(result.item2); }, ), ); diff --git a/lib/modules/settings/routes/configuration_dashboard/pages.dart b/lib/modules/settings/routes/configuration_dashboard/pages.dart index df56a3ce..ce7efe9d 100644 --- a/lib/modules/settings/routes/configuration_dashboard/pages.dart +++ b/lib/modules/settings/routes/configuration_dashboard/pages.dart @@ -1 +1,2 @@ export 'pages/calendar_settings.dart'; +export 'pages/default_pages.dart'; diff --git a/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart b/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart index 397b2d42..e5b846b9 100644 --- a/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart +++ b/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart @@ -61,177 +61,163 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _pastDaysInSchedule() { - return DashboardDatabaseValue.CALENDAR_SHOW_PAST_DAYS.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Past Days In Schedule View'), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_SHOW_PAST_DAYS; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Past Days In Schedule View', trailing: LunaSwitch( - value: DashboardDatabaseValue.CALENDAR_SHOW_PAST_DAYS.data, - onChanged: DashboardDatabaseValue.CALENDAR_SHOW_PAST_DAYS.put, + value: _db.data, + onChanged: _db.put, ), ), ); } Widget _pastDays() { - return DashboardDatabaseValue.CALENDAR_DAYS_PAST.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Past Days'), - subtitle: LunaText.subtitle( - text: DashboardDatabaseValue.CALENDAR_DAYS_PAST.data == 1 - ? '1 Day' - : '${DashboardDatabaseValue.CALENDAR_DAYS_PAST.data} Days', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_DAYS_PAST; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Past Days', + body: [ + TextSpan(text: _db.data == 1 ? '1 Day' : '${_db.data} Days'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 result = await DashboardDialogs().setPastDays(context); - if (result.item1) { - DashboardDatabaseValue.CALENDAR_DAYS_PAST.put(result.item2); - } + if (result.item1) _db.put(result.item2); }, ), ); } Widget _futureDays() { - return DashboardDatabaseValue.CALENDAR_DAYS_FUTURE.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Future Days'), - subtitle: LunaText.subtitle( - text: DashboardDatabaseValue.CALENDAR_DAYS_FUTURE.data == 1 - ? '1 Day' - : '${DashboardDatabaseValue.CALENDAR_DAYS_FUTURE.data} Days', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_DAYS_FUTURE; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Future Days', + body: [ + TextSpan(text: _db.data == 1 ? '1 Day' : '${_db.data} Days'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 result = await DashboardDialogs().setFutureDays(context); - if (result.item1) { - DashboardDatabaseValue.CALENDAR_DAYS_FUTURE.put(result.item2); - } + if (result.item1) _db.put(result.item2); }, ), ); } Widget _modulesLidarr() { - return DashboardDatabaseValue.CALENDAR_ENABLE_LIDARR.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: LunaModule.LIDARR.name), - subtitle: LunaText.subtitle( - text: - 'settings.ShowCalendarEntries'.tr(args: [LunaModule.LIDARR.name]), - ), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_ENABLE_LIDARR; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: LunaModule.LIDARR.name, + body: [ + TextSpan( + text: 'settings.ShowCalendarEntries'.tr( + args: [LunaModule.LIDARR.name], + ), + ) + ], trailing: LunaSwitch( - value: DashboardDatabaseValue.CALENDAR_ENABLE_LIDARR.data, - onChanged: (value) { - DashboardDatabaseValue.CALENDAR_ENABLE_LIDARR.put(value); - }, + value: _db.data, + onChanged: _db.put, ), ), ); } Widget _modulesRadarr() { - return DashboardDatabaseValue.CALENDAR_ENABLE_RADARR.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: LunaModule.RADARR.name), - subtitle: LunaText.subtitle( - text: - 'settings.ShowCalendarEntries'.tr(args: [LunaModule.RADARR.name]), - ), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_ENABLE_RADARR; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: LunaModule.RADARR.name, + body: [ + TextSpan( + text: 'settings.ShowCalendarEntries'.tr( + args: [LunaModule.RADARR.name], + ), + ) + ], trailing: LunaSwitch( - value: DashboardDatabaseValue.CALENDAR_ENABLE_RADARR.data, - onChanged: (value) { - DashboardDatabaseValue.CALENDAR_ENABLE_RADARR.put(value); - }, + value: _db.data, + onChanged: _db.put, ), ), ); } Widget _modulesSonarr() { - return DashboardDatabaseValue.CALENDAR_ENABLE_SONARR.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: LunaModule.SONARR.name), - subtitle: LunaText.subtitle( - text: - 'settings.ShowCalendarEntries'.tr(args: [LunaModule.SONARR.name]), - ), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_ENABLE_SONARR; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: LunaModule.SONARR.name, + body: [ + TextSpan( + text: 'settings.ShowCalendarEntries'.tr( + args: [LunaModule.SONARR.name], + ), + ) + ], trailing: LunaSwitch( - value: DashboardDatabaseValue.CALENDAR_ENABLE_SONARR.data, - onChanged: (value) { - DashboardDatabaseValue.CALENDAR_ENABLE_SONARR.put(value); - }, + value: _db.data, + onChanged: _db.put, ), ), ); } Widget _startingView() { - return DashboardDatabaseValue.CALENDAR_STARTING_TYPE.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.StartingView'.tr()), - subtitle: LunaText.subtitle( - text: (DashboardDatabaseValue.CALENDAR_STARTING_TYPE.data - as CalendarStartingType) - .name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_STARTING_TYPE; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'settings.StartingView'.tr(), + body: [ + TextSpan(text: (_db.data as CalendarStartingType).name), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editCalendarStartingView(context); - if (_values.item1) { - DashboardDatabaseValue.CALENDAR_STARTING_TYPE.put(_values.item2); - } + if (_values.item1) _db.put(_values.item2); }, ), ); } Widget _startingDay() { - return DashboardDatabaseValue.CALENDAR_STARTING_DAY.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.StartingDay'.tr()), - subtitle: LunaText.subtitle( - text: (DashboardDatabaseValue.CALENDAR_STARTING_DAY.data - as CalendarStartingDay) - .name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_STARTING_DAY; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'settings.StartingDay'.tr(), + body: [ + TextSpan(text: (_db.data as CalendarStartingDay).name), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 results = await SettingsDialogs().editCalendarStartingDay(context); - if (results.item1) { - DashboardDatabaseValue.CALENDAR_STARTING_DAY.put(results.item2); - } + if (results.item1) _db.put(results.item2); }, ), ); } Widget _startingSize() { - return DashboardDatabaseValue.CALENDAR_STARTING_SIZE.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Starting Size'), - subtitle: LunaText.subtitle( - text: (DashboardDatabaseValue.CALENDAR_STARTING_SIZE.data - as CalendarStartingSize) - .name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + DashboardDatabaseValue _db = DashboardDatabaseValue.CALENDAR_STARTING_SIZE; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Starting Size', + body: [ + TextSpan(text: (_db.data as CalendarStartingSize).name), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editCalendarStartingSize(context); - if (_values.item1) { - DashboardDatabaseValue.CALENDAR_STARTING_SIZE.put(_values.item2); - } + if (_values.item1) _db.put(_values.item2); }, ), ); diff --git a/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart b/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart new file mode 100644 index 00000000..d31098b2 --- /dev/null +++ b/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/dashboard.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationDashboardDefaultPagesRouter + extends SettingsPageRouter { + SettingsConfigurationDashboardDefaultPagesRouter() + : super('/settings/configuration/dashboard/pages'); + + @override + _Widget widget() => _Widget(); + + @override + void defineRoute(FluroRouter router) { + super.noParameterRouteDefinition(router); + } +} + +class _Widget extends StatefulWidget { + @override + State<_Widget> createState() => _State(); +} + +class _State extends State<_Widget> with LunaScrollControllerMixin { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return LunaScaffold( + scaffoldKey: _scaffoldKey, + appBar: _appBar(), + body: _body(), + ); + } + + Widget _appBar() { + return LunaAppBar( + title: 'settings.DefaultPages'.tr(), + scrollControllers: [scrollController], + ); + } + + Widget _body() { + return LunaListView( + controller: scrollController, + children: [ + _homePage(), + ], + ); + } + + Widget _homePage() { + DashboardDatabaseValue _db = DashboardDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: DashboardNavigationBar.titles[_db.data])], + trailing: LunaIconButton(icon: DashboardNavigationBar.icons[_db.data]), + onTap: () async { + Tuple2 values = + await DashboardDialogs().defaultPage(context); + if (values.item1) { + DashboardDatabaseValue.NAVIGATION_INDEX.put(values.item2); + } + }, + ), + ); + } +} diff --git a/lib/modules/settings/routes/configuration_dashboard/route.dart b/lib/modules/settings/routes/configuration_dashboard/route.dart index 1dd9251c..bbf67b21 100644 --- a/lib/modules/settings/routes/configuration_dashboard/route.dart +++ b/lib/modules/settings/routes/configuration_dashboard/route.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/dashboard.dart'; import 'package:lunasea/modules/settings.dart'; class SettingsConfigurationDashboardRouter extends SettingsPageRouter { @@ -45,39 +44,26 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { controller: scrollController, children: [ _calendarSettingsPage(), - _homePage(), + _defaultPagesPage(), ], ); } - Widget _homePage() { - return DashboardDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Default Page'), - subtitle: LunaText.subtitle( - text: DashboardNavigationBar - .titles[DashboardDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LunaIconButton( - icon: DashboardNavigationBar - .icons[DashboardDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async { - Tuple2 values = - await DashboardDialogs().defaultPage(context); - if (values.item1) { - DashboardDatabaseValue.NAVIGATION_INDEX.put(values.item2); - } - }, - ), + Widget _defaultPagesPage() { + return LunaBlock( + title: 'settings.DefaultPages'.tr(), + body: [TextSpan(text: 'settings.DefaultPagesDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async => SettingsConfigurationDashboardDefaultPagesRouter() + .navigateTo(context), ); } Widget _calendarSettingsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Calendar Settings'), - subtitle: LunaText.subtitle(text: 'Customize the Unified Calendar'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Calendar Settings', + body: const [TextSpan(text: 'Customize the Unified Calendar')], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationDashboardCalendarSettingsRouter() .navigateTo(context); diff --git a/lib/modules/settings/routes/configuration_drawer/route.dart b/lib/modules/settings/routes/configuration_drawer/route.dart index 14ee2033..76d48569 100644 --- a/lib/modules/settings/routes/configuration_drawer/route.dart +++ b/lib/modules/settings/routes/configuration_drawer/route.dart @@ -48,60 +48,27 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _body() { return Column( children: [ - LunaListTile( - context: context, - margin: LunaUI.MARGIN_DEFAULT.subtract( - EdgeInsets.only(bottom: LunaUI.MARGIN_CARD.bottom), - ), - title: LunaText.title( - text: 'settings.AutomaticallyManageOrder'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.AutomaticallyManageOrderDescription'.tr(), - ), + SizedBox(height: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom), + LunaBlock( + title: 'settings.AutomaticallyManageOrder'.tr(), + body: [ + TextSpan(text: 'settings.AutomaticallyManageOrderDescription'.tr()), + ], trailing: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.listen( builder: (context, _, __) => LunaSwitch( value: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data, - onChanged: (value) { - LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.put(value); - }, + onChanged: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.put, ), ), ), const LunaDivider(), Expanded( child: LunaReorderableListViewBuilder( - padding: MediaQuery.of(context) - .padding - .copyWith(top: 0) - .add(EdgeInsets.only(bottom: LunaUI.MARGIN_CARD.bottom)), + padding: MediaQuery.of(context).padding.copyWith(top: 0).add( + EdgeInsets.only(bottom: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom)), controller: scrollController, itemCount: _modules.length, - itemBuilder: (context, index) { - return LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.listen( - key: ObjectKey(_modules[index]), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title( - text: _modules[index].name, - darken: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data, - ), - subtitle: LunaText.subtitle( - text: _modules[index].description, - darken: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data, - ), - leading: LunaIconButton( - icon: _modules[index].icon, - color: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data - ? Colors.white30 - : Colors.white, - ), - trailing: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data - ? null - : LunaReorderableListDragger(index: index), - ), - ); - }, + itemBuilder: (context, index) => _reorderableModuleTile(index), onReorder: (oIndex, nIndex) { if (oIndex > _modules.length) oIndex = _modules.length; if (oIndex < nIndex) nIndex--; @@ -115,4 +82,19 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { ], ); } + + Widget _reorderableModuleTile(int index) { + return LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.listen( + key: ObjectKey(_modules[index]), + builder: (context, _, __) => LunaBlock( + disabled: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data, + title: _modules[index].name, + body: [TextSpan(text: _modules[index].description)], + leading: LunaIconButton(icon: _modules[index].icon), + trailing: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data + ? null + : LunaReorderableListViewDragger(index: index), + ), + ); + } } diff --git a/lib/modules/settings/routes/configuration_externalmodules/pages/add_module.dart b/lib/modules/settings/routes/configuration_externalmodules/pages/add_module.dart index e34db46b..d83cead8 100644 --- a/lib/modules/settings/routes/configuration_externalmodules/pages/add_module.dart +++ b/lib/modules/settings/routes/configuration_externalmodules/pages/add_module.dart @@ -78,45 +78,41 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _displayNameTile() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.DisplayName'.tr()), - subtitle: LunaText.subtitle( - text: _module.displayName == null || _module.displayName.isEmpty - ? 'lunasea.NotSet'.tr() - : _module.displayName, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _displayName = _module.displayName ?? ''; + return LunaBlock( + title: 'settings.DisplayName'.tr(), + body: [ + TextSpan( + text: _displayName.isEmpty ? 'lunasea.NotSet'.tr() : _displayName, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await LunaDialogs().editText( context, 'settings.DisplayName'.tr(), - prefill: _module.displayName, + prefill: _displayName, ); - if (values.item1 && mounted) - setState(() => _module.displayName = values.item2); + if (values.item1) setState(() => _module.displayName = values.item2); }, ); } Widget _hostTile() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Host'.tr()), - subtitle: LunaText.subtitle( - text: _module.host == null || _module.host.isEmpty - ? 'lunasea.NotSet'.tr() - : _module.host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _host = _module.host ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [ + TextSpan(text: _host.isEmpty ? 'lunasea.NotSet'.tr() : _host), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await SettingsDialogs().editExternalModuleHost( context, - prefill: _module.host, + prefill: _host, ); - if (values.item1 && mounted) - setState(() => _module.host = values.item2); + if (values.item1) setState(() => _module.host = values.item2); }, ); } diff --git a/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart b/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart index e742a57e..5747113c 100644 --- a/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart +++ b/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart @@ -123,20 +123,20 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _displayNameTile() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.DisplayName'.tr()), - subtitle: LunaText.subtitle( - text: _module.displayName == null || _module.displayName.isEmpty - ? 'lunasea.NotSet'.tr() - : _module.displayName, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _displayName = _module.displayName ?? ''; + return LunaBlock( + title: 'settings.DisplayName'.tr(), + body: [ + TextSpan( + text: _displayName.isEmpty ? 'lunasea.NotSet'.tr() : _displayName, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await LunaDialogs().editText( context, 'settings.DisplayName'.tr(), - prefill: _module.displayName, + prefill: _displayName, ); if (values.item1) _module.displayName = values.item2; _module.save(); @@ -145,20 +145,18 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _hostTile() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Host'.tr()), - subtitle: LunaText.subtitle( - text: _module.host == null || _module.host.isEmpty - ? 'lunasea.NotSet'.tr() - : _module.host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _host = _module.host ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [ + TextSpan(text: _host.isEmpty ? 'lunasea.NotSet'.tr() : _host), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await SettingsDialogs().editExternalModuleHost( context, - prefill: _module.host, + prefill: _host, ); if (values.item1) _module.host = values.item2; _module.save(); diff --git a/lib/modules/settings/routes/configuration_externalmodules/route.dart b/lib/modules/settings/routes/configuration_externalmodules/route.dart index 92fec857..148a3d61 100644 --- a/lib/modules/settings/routes/configuration_externalmodules/route.dart +++ b/lib/modules/settings/routes/configuration_externalmodules/route.dart @@ -77,7 +77,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Database.externalModulesBox.values.toList(); modules.sort((a, b) => a.displayName.toLowerCase().compareTo(b.displayName.toLowerCase())); - List list = List.generate( + List list = List.generate( modules.length, (index) => _moduleTile(modules[index], modules[index].key), ); @@ -85,16 +85,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _moduleTile(ExternalModuleHiveObject module, int index) { - return LunaListTile( - context: context, - title: LunaText.title(text: module.displayName), - subtitle: LunaText.subtitle(text: module.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async => - SettingsConfigurationExternalModulesEditRouter().navigateTo( - context, - moduleId: index, - ), + return LunaBlock( + title: module.displayName, + body: [TextSpan(text: module.host)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + return SettingsConfigurationExternalModulesEditRouter().navigateTo( + context, + moduleId: index, + ); + }, ); } } diff --git a/lib/modules/settings/routes/configuration_lidarr/pages.dart b/lib/modules/settings/routes/configuration_lidarr/pages.dart index 0ea4953e..2e10309d 100644 --- a/lib/modules/settings/routes/configuration_lidarr/pages.dart +++ b/lib/modules/settings/routes/configuration_lidarr/pages.dart @@ -1,2 +1,3 @@ export 'pages/connection_details.dart'; +export 'pages/default_pages.dart'; export 'pages/headers.dart'; diff --git a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart index 38874eee..06668050 100644 --- a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart @@ -65,18 +65,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.lidarrHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Host'.tr()), - subtitle: LunaText.subtitle( - text: (host ?? '').isEmpty ? 'lunasea.NotSet'.tr() : host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.lidarrHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, - prefill: Database.currentProfileObject.lidarrHost ?? '', + prefill: host, ); if (_values.item1) { Database.currentProfileObject.lidarrHost = _values.item2; @@ -88,21 +85,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.lidarrKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.ApiKey'.tr()), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty - ? 'lunasea.NotSet'.tr() - : LunaUI.TEXT_OBFUSCATED_PASSWORD, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.lidarrKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( context, 'settings.ApiKey'.tr(), - prefill: Database.currentProfileObject.lidarrKey ?? '', + prefill: apiKey, ); if (_values.item1) { Database.currentProfileObject.lidarrKey = _values.item2; @@ -163,13 +161,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.CustomHeaders'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.CustomHeadersDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationLidarrHeadersRouter().navigateTo(context); }, diff --git a/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart new file mode 100644 index 00000000..0de2ec77 --- /dev/null +++ b/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/modules/lidarr.dart'; +import 'package:lunasea/modules/settings.dart'; + +class SettingsConfigurationLidarrDefaultPagesRouter extends SettingsPageRouter { + SettingsConfigurationLidarrDefaultPagesRouter() + : super('/settings/configuration/lidarr/pages'); + + @override + _Widget widget() => _Widget(); + + @override + void defineRoute(FluroRouter router) { + super.noParameterRouteDefinition(router); + } +} + +class _Widget extends StatefulWidget { + @override + State<_Widget> createState() => _State(); +} + +class _State extends State<_Widget> with LunaScrollControllerMixin { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return LunaScaffold( + scaffoldKey: _scaffoldKey, + appBar: _appBar(), + body: _body(), + ); + } + + Widget _appBar() { + return LunaAppBar( + title: 'settings.DefaultPages'.tr(), + scrollControllers: [scrollController], + ); + } + + Widget _body() { + return LunaListView( + controller: scrollController, + children: [ + _homePage(), + ], + ); + } + + Widget _homePage() { + LidarrDatabaseValue _db = LidarrDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: LidarrNavigationBar.titles[_db.data])], + trailing: LunaIconButton( + icon: LidarrNavigationBar.icons[_db.data], + ), + onTap: () async { + List values = await LidarrDialogs.defaultPage(context); + if (values[0]) _db.put(values[1]); + }, + ), + ); + } +} diff --git a/lib/modules/settings/routes/configuration_lidarr/route.dart b/lib/modules/settings/routes/configuration_lidarr/route.dart index ea1a4d39..882987e9 100644 --- a/lib/modules/settings/routes/configuration_lidarr/route.dart +++ b/lib/modules/settings/routes/configuration_lidarr/route.dart @@ -35,7 +35,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( scrollControllers: [scrollController], - title: 'Lidarr', + title: LunaModule.LIDARR.name, ); } @@ -47,7 +47,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { _enabledToggle(), _connectionDetailsPage(), const LunaDivider(), - _homePage(), + _defaultPagesPage(), //_defaultPagesPage(), ], ); @@ -56,9 +56,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.LIDARR.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.LIDARR.name}', trailing: LunaSwitch( value: Database.currentProfileObject.lidarrEnabled ?? false, onChanged: (value) { @@ -72,15 +71,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.ConnectionDetails'.tr()), - subtitle: LunaText.subtitle( - text: 'settings.ConnectionDetailsDescription'.tr( - args: [LunaModule.LIDARR.name], + return LunaBlock( + title: 'settings.ConnectionDetails'.tr(), + body: [ + TextSpan( + text: 'settings.ConnectionDetailsDescription'.tr( + args: [LunaModule.LIDARR.name], + ), ), - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationLidarrConnectionDetailsRouter() .navigateTo(context); @@ -88,24 +88,13 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { ); } - Widget _homePage() { - return LidarrDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'settings.DefaultPage'.tr()), - subtitle: LunaText.subtitle( - text: LidarrNavigationBar - .titles[LidarrDatabaseValue.NAVIGATION_INDEX.data], - ), - trailing: LunaIconButton( - icon: LidarrNavigationBar - .icons[LidarrDatabaseValue.NAVIGATION_INDEX.data], - ), - onTap: () async { - List values = await LidarrDialogs.defaultPage(context); - if (values[0]) LidarrDatabaseValue.NAVIGATION_INDEX.put(values[1]); - }, - ), + Widget _defaultPagesPage() { + return LunaBlock( + title: 'settings.DefaultPages'.tr(), + body: [TextSpan(text: 'settings.DefaultPagesDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async => + SettingsConfigurationLidarrDefaultPagesRouter().navigateTo(context), ); } } diff --git a/lib/modules/settings/routes/configuration_localization/route.dart b/lib/modules/settings/routes/configuration_localization/route.dart index d4286fe3..70875c32 100644 --- a/lib/modules/settings/routes/configuration_localization/route.dart +++ b/lib/modules/settings/routes/configuration_localization/route.dart @@ -50,14 +50,11 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _language() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.Language'.tr()), - subtitle: LunaText.subtitle( - text: LunaLanguage.ENGLISH.fromLocale(context.locale)?.name ?? - LunaUI.TEXT_EMDASH, - ), - trailing: LunaIconButton(icon: Icons.language_rounded), + String _language = LunaLanguage.ENGLISH.fromLocale(context.locale)?.name; + return LunaBlock( + title: 'settings.Language'.tr(), + body: [TextSpan(text: _language ?? LunaUI.TEXT_EMDASH)], + trailing: const LunaIconButton(icon: Icons.language_rounded), onTap: () async { Tuple2 result = await SettingsDialogs().changeLanguage(context); @@ -70,14 +67,14 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _use24HourTime() { - return LunaDatabaseValue.USE_24_HOUR_TIME.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Use 24 Hour Time'), - subtitle: LunaText.subtitle(text: 'Show Timestamps in 24 Hour Style'), + LunaDatabaseValue _db = LunaDatabaseValue.USE_24_HOUR_TIME; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'Use 24 Hour Time', + body: const [TextSpan(text: 'Show Timestamps in 24 Hour Style')], trailing: LunaSwitch( - value: LunaDatabaseValue.USE_24_HOUR_TIME.data, - onChanged: (value) => LunaDatabaseValue.USE_24_HOUR_TIME.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); diff --git a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart index 240a4f8b..4b46fd70 100644 --- a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart @@ -66,18 +66,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.nzbgetHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: LunaText.subtitle( - text: (host ?? '').isEmpty ? 'Not Set' : host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.nzbgetHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, - prefill: Database.currentProfileObject.nzbgetHost ?? '', + prefill: host, ); if (_values.item1) { Database.currentProfileObject.nzbgetHost = _values.item2; @@ -89,19 +86,18 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _username() { - String username = Database.currentProfileObject.nzbgetUser; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Username'), - subtitle: LunaText.subtitle( - text: (username ?? '').isEmpty ? 'Not Set' : username, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String username = Database.currentProfileObject.nzbgetUser ?? ''; + return LunaBlock( + title: 'settings.Username'.tr(), + body: [ + TextSpan(text: username.isEmpty ? 'lunasea.NotSet'.tr() : username), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( context, - 'NZBGet Username', - prefill: Database.currentProfileObject.nzbgetUser ?? '', + 'settings.Username'.tr(), + prefill: username, ); if (_values.item1) { Database.currentProfileObject.nzbgetUser = _values.item2; @@ -113,19 +109,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _password() { - String password = Database.currentProfileObject.nzbgetPass; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Password'), - subtitle: LunaText.subtitle( - text: (password ?? '').isEmpty ? 'Not Set' : '••••••••••••', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String password = Database.currentProfileObject.nzbgetPass ?? ''; + return LunaBlock( + title: 'settings.Password'.tr(), + body: [ + TextSpan( + text: password.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( context, - 'NZBGet Password', - prefill: Database.currentProfileObject.nzbgetPass ?? '', + 'settings.Password'.tr(), + prefill: password, extraText: [ LunaDialog.textSpanContent( text: @@ -148,8 +147,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _testConnection() { return LunaButton.text( - text: 'Test Connection', - icon: Icons.wifi_tethering_rounded, + text: 'settings.TestConnection'.tr(), + icon: LunaIcons.CONNECTION_TEST, onTap: () async { ProfileHiveObject _profile = Database.currentProfileObject; if (_profile.nzbgetHost == null || _profile.nzbgetHost.isEmpty) { @@ -177,13 +176,13 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async => - SettingsConfigurationNZBGetHeadersRouter().navigateTo(context), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async { + SettingsConfigurationNZBGetHeadersRouter().navigateTo(context); + }, ); } } diff --git a/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart b/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart index 74f92ede..11a0430e 100644 --- a/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart @@ -35,7 +35,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( - title: 'Default Pages', + title: 'settings.DefaultPages'.tr(), scrollControllers: [scrollController], ); } @@ -50,21 +50,17 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _homePage() { - return NZBGetDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Home'), - subtitle: LunaText.subtitle( - text: NZBGetNavigationBar - .titles[NZBGetDatabaseValue.NAVIGATION_INDEX.data], - ), + NZBGetDatabaseValue _db = NZBGetDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: NZBGetNavigationBar.titles[_db.data])], trailing: LunaIconButton( - icon: NZBGetNavigationBar - .icons[NZBGetDatabaseValue.NAVIGATION_INDEX.data], + icon: NZBGetNavigationBar.icons[_db.data], ), onTap: () async { List values = await NZBGetDialogs.defaultPage(context); - if (values[0]) NZBGetDatabaseValue.NAVIGATION_INDEX.put(values[1]); + if (values[0]) _db.put(values[1]); }, ), ); diff --git a/lib/modules/settings/routes/configuration_nzbget/route.dart b/lib/modules/settings/routes/configuration_nzbget/route.dart index 063af137..8c5cbf82 100644 --- a/lib/modules/settings/routes/configuration_nzbget/route.dart +++ b/lib/modules/settings/routes/configuration_nzbget/route.dart @@ -47,7 +47,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { _enabledToggle(), _connectionDetailsPage(), const LunaDivider(), - _homePage(), + _defaultPagesPage(), //_defaultPagesPage(), ], ); @@ -56,9 +56,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.NZBGET.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.NZBGET.name}', trailing: LunaSwitch( value: Database.currentProfileObject.nzbgetEnabled ?? false, onChanged: (value) { @@ -72,11 +71,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle(text: 'Connection Details for NZBGet'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Connection Details', + body: const [TextSpan(text: 'Connection Details for NZBGet')], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationNZBGetConnectionDetailsRouter() .navigateTo(context); @@ -84,24 +82,13 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { ); } - Widget _homePage() { - return NZBGetDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Default Page'), - subtitle: LunaText.subtitle( - text: NZBGetNavigationBar - .titles[NZBGetDatabaseValue.NAVIGATION_INDEX.data], - ), - trailing: LunaIconButton( - icon: NZBGetNavigationBar - .icons[NZBGetDatabaseValue.NAVIGATION_INDEX.data], - ), - onTap: () async { - List values = await NZBGetDialogs.defaultPage(context); - if (values[0]) NZBGetDatabaseValue.NAVIGATION_INDEX.put(values[1]); - }, - ), + Widget _defaultPagesPage() { + return LunaBlock( + title: 'settings.DefaultPages'.tr(), + body: [TextSpan(text: 'settings.DefaultPagesDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async => + SettingsConfigurationNZBGetDefaultPagesRouter().navigateTo(context), ); } } diff --git a/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart index cef78d7c..3e8a9024 100644 --- a/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart @@ -65,14 +65,11 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.overseerrHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: LunaText.subtitle( - text: (host ?? '').isEmpty ? 'Not Set' : host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.overseerrHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, @@ -88,19 +85,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.overseerrKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'API Key'), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.overseerrKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( context, - 'Overseerr API Key', - prefill: Database.currentProfileObject.overseerrKey ?? '', + 'settings.ApiKey'.tr(), + prefill: apiKey, ); if (_values.item1) { Database.currentProfileObject.overseerrKey = _values.item2; @@ -113,8 +113,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _testConnection() { return LunaButton.text( - text: 'Test Connection', - icon: Icons.wifi_tethering_rounded, + text: 'settings.TestConnection'.tr(), + icon: LunaIcons.CONNECTION_TEST, onTap: () async { ProfileHiveObject _profile = Database.currentProfileObject; if (_profile.overseerrHost == null || _profile.overseerrHost.isEmpty) { @@ -162,11 +162,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationOverseerrHeadersRouter().navigateTo(context); }, diff --git a/lib/modules/settings/routes/configuration_overseerr/route.dart b/lib/modules/settings/routes/configuration_overseerr/route.dart index 66c04b9d..896eed07 100644 --- a/lib/modules/settings/routes/configuration_overseerr/route.dart +++ b/lib/modules/settings/routes/configuration_overseerr/route.dart @@ -54,9 +54,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.OVERSEERR.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.OVERSEERR.name}', trailing: LunaSwitch( value: Database.currentProfileObject.overseerrEnabled ?? false, onChanged: (value) { @@ -70,17 +69,18 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle( - text: 'Connection Details for ${LunaModule.OVERSEERR.name}', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - SettingsConfigurationOverseerrConnectionDetailsRouter() - .navigateTo(context); - }, + return LunaBlock( + title: 'settings.ConnectionDetails'.tr(), + body: [ + TextSpan( + text: 'settings.ConnectionDetailsDescription'.tr( + args: [LunaModule.OVERSEERR.name], + ), + ), + ], + trailing: const LunaIconButton.arrow(), + onTap: () async => SettingsConfigurationOverseerrConnectionDetailsRouter() + .navigateTo(context), ); } } diff --git a/lib/modules/settings/routes/configuration_quickactions/route.dart b/lib/modules/settings/routes/configuration_quickactions/route.dart index 38e36506..c06a02bc 100644 --- a/lib/modules/settings/routes/configuration_quickactions/route.dart +++ b/lib/modules/settings/routes/configuration_quickactions/route.dart @@ -36,20 +36,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( scrollControllers: [scrollController], - title: 'Quick Actions', - actions: [ - LunaIconButton( - icon: Icons.help_outline_rounded, - onPressed: () async => LunaDialogs().textPreview( - context, - 'Quick Actions', - [ - 'Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing LunaSea\'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'), - ), - ) - ], + title: 'settings.QuickActions'.tr(), ); } @@ -57,6 +44,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ + SettingsBanners.QUICK_ACTIONS_SUPPORT.banner(), _actionTile( LunaModule.LIDARR.name, LunaDatabaseValue.QUICK_ACTIONS_LIDARR, @@ -95,9 +83,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _actionTile(String title, LunaDatabaseValue action) { - return LunaListTile( - context: context, - title: LunaText.title(text: title), + return LunaBlock( + title: title, trailing: ValueListenableBuilder( valueListenable: Database.lunaSeaBox.listenable(keys: [action.key]), builder: (context, _, __) => LunaSwitch( diff --git a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart index 0ef68a43..c1010950 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart @@ -65,18 +65,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.radarrHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: LunaText.subtitle( - text: (host ?? '').isEmpty ? 'Not Set' : host, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.radarrHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, - prefill: Database.currentProfileObject.radarrHost ?? '', + prefill: host, ); if (_values.item1) { Database.currentProfileObject.radarrHost = _values.item2; @@ -88,19 +85,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.radarrKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'API Key'), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.radarrKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( context, - 'Radarr API Key', - prefill: Database.currentProfileObject.radarrKey ?? '', + 'settings.ApiKey'.tr(), + prefill: apiKey, ); if (_values.item1) { Database.currentProfileObject.radarrKey = _values.item2; @@ -113,8 +113,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _testConnection() { return LunaButton.text( - text: 'Test Connection', - icon: Icons.wifi_tethering_rounded, + text: 'settings.TestConnection'.tr(), + icon: LunaIcons.CONNECTION_TEST, onTap: () async { ProfileHiveObject _profile = Database.currentProfileObject; if (_profile.radarrHost == null || _profile.radarrHost.isEmpty) { @@ -162,11 +162,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async { SettingsConfigurationRadarrHeadersRouter().navigateTo(context); }, diff --git a/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart index 69c0a68f..59acff41 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart @@ -53,100 +53,87 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _homePage() { - return RadarrDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Home'), - subtitle: LunaText.subtitle( - text: RadarrNavigationBar - .titles[RadarrDatabaseValue.NAVIGATION_INDEX.data], - ), - trailing: LunaIconButton( - icon: RadarrNavigationBar - .icons[RadarrDatabaseValue.NAVIGATION_INDEX.data], - ), + RadarrDatabaseValue _db = RadarrDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Home', + body: [TextSpan(text: RadarrNavigationBar.titles[_db.data])], + trailing: LunaIconButton(icon: RadarrNavigationBar.icons[_db.data]), onTap: () async { Tuple2 values = await RadarrDialogs().setDefaultPage( context, titles: RadarrNavigationBar.titles, icons: RadarrNavigationBar.icons, ); - if (values.item1) - RadarrDatabaseValue.NAVIGATION_INDEX.put(values.item2); + if (values.item1) _db.put(values.item2); }, ), ); } Widget _movieDetailsPage() { - return RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Movie Details'), - subtitle: LunaText.subtitle( - text: RadarrMovieDetailsNavigationBar.titles[ - RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS.data]), + RadarrDatabaseValue _db = + RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Movie Details', + body: [ + TextSpan(text: RadarrMovieDetailsNavigationBar.titles[_db.data]), + ], trailing: LunaIconButton( - icon: RadarrMovieDetailsNavigationBar.icons[ - RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS.data]), + icon: RadarrMovieDetailsNavigationBar.icons[_db.data], + ), onTap: () async { Tuple2 values = await RadarrDialogs().setDefaultPage( context, titles: RadarrMovieDetailsNavigationBar.titles, icons: RadarrMovieDetailsNavigationBar.icons, ); - if (values.item1) - RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS - .put(values.item2); + if (values.item1) _db.put(values.item2); }, ), ); } Widget _addMoviePage() { - return RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Add Movie'), - subtitle: LunaText.subtitle( - text: RadarrAddMovieNavigationBar - .titles[RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE.data]), - trailing: LunaIconButton( - icon: RadarrAddMovieNavigationBar - .icons[RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE.data]), + RadarrDatabaseValue _db = RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Add Movie', + body: [TextSpan(text: RadarrAddMovieNavigationBar.titles[_db.data])], + trailing: + LunaIconButton(icon: RadarrAddMovieNavigationBar.icons[_db.data]), onTap: () async { Tuple2 values = await RadarrDialogs().setDefaultPage( context, titles: RadarrAddMovieNavigationBar.titles, icons: RadarrAddMovieNavigationBar.icons, ); - if (values.item1) - RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE.put(values.item2); + if (values.item1) _db.put(values.item2); }, ), ); } Widget _systemStatusPage() { - return RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'System Status'), - subtitle: LunaText.subtitle( - text: RadarrSystemStatusNavigationBar.titles[ - RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS.data]), + RadarrDatabaseValue _db = + RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'System Status', + body: [ + TextSpan(text: RadarrSystemStatusNavigationBar.titles[_db.data]), + ], trailing: LunaIconButton( - icon: RadarrSystemStatusNavigationBar.icons[ - RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS.data]), + icon: RadarrSystemStatusNavigationBar.icons[_db.data], + ), onTap: () async { Tuple2 values = await RadarrDialogs().setDefaultPage( context, titles: RadarrSystemStatusNavigationBar.titles, icons: RadarrSystemStatusNavigationBar.icons, ); - if (values.item1) - RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS - .put(values.item2); + if (values.item1) _db.put(values.item2); }, ), ); diff --git a/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart b/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart index ecda6cc8..58ddd8e4 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart @@ -57,26 +57,21 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _sortingMovies() { - return RadarrDatabaseValue.DEFAULT_SORTING_MOVIES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Movies Sort Category'), - subtitle: LunaText.subtitle( - text: (RadarrDatabaseValue.DEFAULT_SORTING_MOVIES.data - as RadarrMoviesSorting) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + RadarrDatabaseValue _db = RadarrDatabaseValue.DEFAULT_SORTING_MOVIES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Movies Sort Category', + body: [TextSpan(text: (_db.data as RadarrMoviesSorting).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { List titles = RadarrMoviesSorting.values - .map((e) => e.readable) + .map((sorting) => sorting.readable) .toList(); Tuple2 values = await RadarrDialogs() .setDefaultSortingOrFiltering(context, titles: titles); if (values.item1) { - RadarrDatabaseValue.DEFAULT_SORTING_MOVIES - .put(RadarrMoviesSorting.values[values.item2]); - context.read().moviesSortType = - RadarrDatabaseValue.DEFAULT_SORTING_MOVIES.data; + _db.put(RadarrMoviesSorting.values[values.item2]); + context.read().moviesSortType = _db.data; context.read().moviesSortAscending = RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.data; } @@ -86,23 +81,24 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _sortingMoviesDirection() { - return RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Movies Sort Direction'), - subtitle: LunaText.subtitle( - text: RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.data - ? 'Ascending' - : 'Descending', - ), + RadarrDatabaseValue _db = + RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Movies Sort Direction', + body: [ + TextSpan( + text: + _db.data ? 'lunasea.Ascending'.tr() : 'lunasea.Descending'.tr(), + ), + ], trailing: LunaSwitch( - value: RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.data, + value: _db.data, onChanged: (value) { - RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.put(value); + _db.put(value); context.read().moviesSortType = RadarrDatabaseValue.DEFAULT_SORTING_MOVIES.data; - context.read().moviesSortAscending = - RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING.data; + context.read().moviesSortAscending = _db.data; }, ), ), @@ -110,25 +106,21 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _filteringMovies() { - return RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Movies Filter Category'), - subtitle: LunaText.subtitle( - text: (RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES.data - as RadarrMoviesFilter) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + RadarrDatabaseValue _db = RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Movies Filter Category', + body: [TextSpan(text: (_db.data as RadarrMoviesFilter).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { - List titles = - RadarrMoviesFilter.values.map((e) => e.readable).toList(); + List titles = RadarrMoviesFilter.values + .map((filter) => filter.readable) + .toList(); Tuple2 values = await RadarrDialogs() .setDefaultSortingOrFiltering(context, titles: titles); if (values.item1) { - RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES - .put(RadarrMoviesFilter.values[values.item2]); - context.read().moviesFilterType = - RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES.data; + _db.put(RadarrMoviesFilter.values[values.item2]); + context.read().moviesFilterType = _db.data; } }, ), @@ -136,66 +128,58 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _sortingReleases() { - return RadarrDatabaseValue.DEFAULT_SORTING_RELEASES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Sort Category'), - subtitle: LunaText.subtitle( - text: (RadarrDatabaseValue.DEFAULT_SORTING_RELEASES.data - as RadarrReleasesSorting) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + RadarrDatabaseValue _db = RadarrDatabaseValue.DEFAULT_SORTING_RELEASES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Sort Category', + body: [TextSpan(text: (_db.data as RadarrReleasesSorting).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { List titles = RadarrReleasesSorting.values - .map((e) => e.readable) + .map((sorting) => sorting.readable) .toList(); Tuple2 values = await RadarrDialogs() .setDefaultSortingOrFiltering(context, titles: titles); - if (values.item1) - RadarrDatabaseValue.DEFAULT_SORTING_RELEASES - .put(RadarrReleasesSorting.values[values.item2]); + if (values.item1) _db.put(RadarrReleasesSorting.values[values.item2]); }, ), ); } Widget _sortingReleasesDirection() { - return RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Sort Direction'), - subtitle: LunaText.subtitle( - text: RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data - ? 'Ascending' - : 'Descending'), + RadarrDatabaseValue _db = + RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Sort Direction', + body: [ + TextSpan( + text: + _db.data ? 'lunasea.Ascending'.tr() : 'lunasea.Descending'.tr(), + ), + ], trailing: LunaSwitch( - value: RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data, - onChanged: (value) => - RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); } Widget _filteringReleases() { - return RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Filter Category'), - subtitle: LunaText.subtitle( - text: (RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES.data - as RadarrReleasesFilter) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + RadarrDatabaseValue _db = RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Filter Category', + body: [TextSpan(text: (_db.data as RadarrReleasesFilter).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { List titles = RadarrReleasesFilter.values - .map((e) => e.readable) + .map((filter) => filter.readable) .toList(); Tuple2 values = await RadarrDialogs() .setDefaultSortingOrFiltering(context, titles: titles); - if (values.item1) - RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES - .put(RadarrReleasesFilter.values[values.item2]); + if (values.item1) _db.put(RadarrReleasesFilter.values[values.item2]); }, ), ); diff --git a/lib/modules/settings/routes/configuration_radarr/route.dart b/lib/modules/settings/routes/configuration_radarr/route.dart index 612041e6..1021a30f 100644 --- a/lib/modules/settings/routes/configuration_radarr/route.dart +++ b/lib/modules/settings/routes/configuration_radarr/route.dart @@ -58,9 +58,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.RADARR.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.RADARR.name}', trailing: LunaSwitch( value: Database.currentProfileObject.radarrEnabled ?? false, onChanged: (value) { @@ -74,70 +73,60 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle(text: 'Connection Details for Radarr'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Connection Details', + body: const [TextSpan(text: 'Connection Details for Radarr')], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationRadarrConnectionDetailsRouter() .navigateTo(context), ); } Widget _defaultPagesPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Pages'), - subtitle: LunaText.subtitle(text: 'Set Default Landing Pages'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Default Pages', + body: const [TextSpan(text: 'Set Default Landing Pages')], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationRadarrDefaultPagesRouter().navigateTo(context), ); } Widget _defaultSortingFilteringPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Sorting & Filtering'), - subtitle: - LunaText.subtitle(text: 'Set Default Sorting & Filtering Methods'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Default Sorting & Filtering', + body: const [TextSpan(text: 'Set Default Sorting & Filtering Methods')], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationRadarrDefaultSortingRouter().navigateTo(context), ); } Widget _discoverUseRadarrSuggestionsToggle() { - return RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Discover Suggestions'), - subtitle: LunaText.subtitle(text: 'Add Suggested Releases in Discover'), + RadarrDatabaseValue _db = RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'Discover Suggestions', + body: const [TextSpan(text: 'Add Suggested Releases in Discover')], trailing: LunaSwitch( - value: RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS.data, - onChanged: (value) => - RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); } Widget _queueSize() { - return RadarrDatabaseValue.QUEUE_PAGE_SIZE.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Queue Size'), - subtitle: LunaText.subtitle( - text: RadarrDatabaseValue.QUEUE_PAGE_SIZE.data == 1 - ? '1 Item' - : '${RadarrDatabaseValue.QUEUE_PAGE_SIZE.data} Items', - ), - trailing: LunaIconButton(icon: Icons.queue_rounded), + RadarrDatabaseValue _db = RadarrDatabaseValue.QUEUE_PAGE_SIZE; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'Queue Size', + body: [TextSpan(text: _db.data == 1 ? '1 Item' : '${_db.data} Items')], + trailing: const LunaIconButton(icon: Icons.queue_rounded), onTap: () async { Tuple2 result = await RadarrDialogs().setQueuePageSize(context); - if (result.item1) - RadarrDatabaseValue.QUEUE_PAGE_SIZE.put(result.item2); + if (result.item1) _db.put(result.item2); }, ), ); diff --git a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart index 77dfb179..71188033 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart @@ -65,17 +65,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.sabnzbdHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: - LunaText.subtitle(text: (host ?? '').isEmpty ? 'Not Set' : host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.sabnzbdHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, - prefill: Database.currentProfileObject.sabnzbdHost ?? '', + prefill: host, ); if (_values.item1) { Database.currentProfileObject.sabnzbdHost = _values.item2; @@ -87,18 +85,23 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.sabnzbdKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'API Key'), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••', - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.sabnzbdKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( - context, 'SABnzbd API Key', - prefill: Database.currentProfileObject.sabnzbdKey ?? ''); + context, + 'settings.ApiKey'.tr(), + prefill: apiKey, + ); if (_values.item1) { Database.currentProfileObject.sabnzbdKey = _values.item2; Database.currentProfileObject.save(); @@ -146,11 +149,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSABnzbdHeadersRouter().navigateTo(context), ); diff --git a/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart b/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart index c1f2c9ab..08c354d5 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart @@ -36,7 +36,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( - title: 'Default Pages', + title: 'settings.DefaultPages'.tr(), scrollControllers: [scrollController], ); } @@ -51,21 +51,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _homePage() { - return SABnzbdDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Home'), - subtitle: LunaText.subtitle( - text: SABnzbdNavigationBar - .titles[SABnzbdDatabaseValue.NAVIGATION_INDEX.data], - ), - trailing: LunaIconButton( - icon: SABnzbdNavigationBar - .icons[SABnzbdDatabaseValue.NAVIGATION_INDEX.data], - ), + SABnzbdDatabaseValue _db = SABnzbdDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: SABnzbdNavigationBar.titles[_db.data])], + trailing: LunaIconButton(icon: SABnzbdNavigationBar.icons[_db.data]), onTap: () async { List values = await SABnzbdDialogs.defaultPage(context); - if (values[0]) SABnzbdDatabaseValue.NAVIGATION_INDEX.put(values[1]); + if (values[0]) _db.put(values[1]); }, ), ); diff --git a/lib/modules/settings/routes/configuration_sabnzbd/route.dart b/lib/modules/settings/routes/configuration_sabnzbd/route.dart index 1cb1e325..bc720959 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/route.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/route.dart @@ -48,7 +48,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { _enabledToggle(), _connectionDetailsPage(), const LunaDivider(), - _homePage(), + _defaultPagesPage(), //_defaultPagesPage(), ], ); @@ -57,9 +57,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.SABNZBD.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.SABNZBD.name}', trailing: LunaSwitch( value: Database.currentProfileObject.sabnzbdEnabled ?? false, onChanged: (value) { @@ -73,34 +72,28 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle(text: 'Connection Details for SABnzbd'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.ConnectionDetails'.tr(), + body: [ + TextSpan( + text: 'settings.ConnectionDetailsDescription'.tr( + args: [LunaModule.SABNZBD.name], + ), + ) + ], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSABnzbdConnectionDetailsRouter() .navigateTo(context), ); } - Widget _homePage() { - return SABnzbdDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Default Page'), - subtitle: LunaText.subtitle( - text: SABnzbdNavigationBar - .titles[SABnzbdDatabaseValue.NAVIGATION_INDEX.data], - ), - trailing: LunaIconButton( - icon: SABnzbdNavigationBar - .icons[SABnzbdDatabaseValue.NAVIGATION_INDEX.data], - ), - onTap: () async { - List values = await SABnzbdDialogs.defaultPage(context); - if (values[0]) SABnzbdDatabaseValue.NAVIGATION_INDEX.put(values[1]); - }, - ), + Widget _defaultPagesPage() { + return LunaBlock( + title: 'settings.DefaultPages'.tr(), + body: [TextSpan(text: 'settings.DefaultPagesDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async => + SettingsConfigurationSABnzbdDefaultPagesRouter().navigateTo(context), ); } } diff --git a/lib/modules/settings/routes/configuration_search/pages/add_indexer.dart b/lib/modules/settings/routes/configuration_search/pages/add_indexer.dart index b9f7aa4e..f2a31b53 100644 --- a/lib/modules/settings/routes/configuration_search/pages/add_indexer.dart +++ b/lib/modules/settings/routes/configuration_search/pages/add_indexer.dart @@ -81,65 +81,67 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _displayName() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Display Name'), - subtitle: LunaText.subtitle( - text: _indexer.displayName == null || _indexer.displayName.isEmpty - ? 'Not Set' - : _indexer.displayName), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _name = _indexer.displayName ?? ''; + return LunaBlock( + title: 'Display Name', + body: [TextSpan(text: _name.isEmpty ? 'lunasea.NotSet'.tr() : _name)], + trailing: const LunaIconButton.arrow(), onTap: () async { - Tuple2 values = await LunaDialogs() - .editText(context, 'Display Name', prefill: _indexer.displayName); - if (values.item1 && mounted) + Tuple2 values = await LunaDialogs().editText( + context, + 'Display Name', + prefill: _name, + ); + if (values.item1 && mounted) { setState(() => _indexer.displayName = values.item2); + } }, ); } Widget _apiURL() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Indexer API Host'), - subtitle: LunaText.subtitle( - text: _indexer.host == null || _indexer.host.isEmpty - ? 'Not Set' - : _indexer.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _host = _indexer.host ?? ''; + return LunaBlock( + title: 'Indexer API Host', + body: [TextSpan(text: _host.isEmpty ? 'lunasea.NotSet'.tr() : _host)], + trailing: const LunaIconButton.arrow(), onTap: () async { - Tuple2 values = await LunaDialogs() - .editText(context, 'Indexer API Host', prefill: _indexer.host); - if (values.item1 && mounted) + Tuple2 values = await LunaDialogs().editText( + context, + 'Indexer API Host', + prefill: _host, + ); + if (values.item1 && mounted) { setState(() => _indexer.host = values.item2); + } }, ); } Widget _apiKey() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Indexer API Key'), - subtitle: LunaText.subtitle( - text: _indexer.apiKey == null || _indexer.apiKey.isEmpty - ? 'Not Set' - : _indexer.apiKey), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String _key = _indexer.apiKey ?? ''; + return LunaBlock( + title: 'Indexer API Key', + body: [TextSpan(text: _key.isEmpty ? 'lunasea.NotSet'.tr() : _key)], + trailing: const LunaIconButton.arrow(), onTap: () async { - Tuple2 values = await LunaDialogs() - .editText(context, 'Indexer API Key', prefill: _indexer.apiKey); - if (values.item1 && mounted) + Tuple2 values = await LunaDialogs().editText( + context, + 'Indexer API Key', + prefill: _key, + ); + if (values.item1 && mounted) { setState(() => _indexer.apiKey = values.item2); + } }, ); } Widget _headers() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSearchAddHeadersRouter() .navigateTo(context, indexer: _indexer), ); diff --git a/lib/modules/settings/routes/configuration_search/pages/add_indexer_headers.dart b/lib/modules/settings/routes/configuration_search/pages/add_indexer_headers.dart index d04f22c7..608c73fa 100644 --- a/lib/modules/settings/routes/configuration_search/pages/add_indexer_headers.dart +++ b/lib/modules/settings/routes/configuration_search/pages/add_indexer_headers.dart @@ -97,21 +97,23 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { (_arguments.indexer.headers ?? {}).cast(); List _sortedKeys = headers.keys.toList()..sort(); return _sortedKeys - .map((key) => _headerTile(key, headers[key])) + .map((key) => _headerTile(key, headers[key])) .toList(); } - LunaListTile _headerTile(String key, String value) { - return LunaListTile( - context: context, - title: LunaText.title(text: key.toString()), - subtitle: LunaText.subtitle(text: value.toString()), + LunaBlock _headerTile(String key, String value) { + return LunaBlock( + title: key.toString(), + body: [TextSpan(text: value.toString())], trailing: LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, onPressed: () async { - await HeaderUtility().deleteHeader(context, - headers: _arguments.indexer.headers, key: key); + await HeaderUtility().deleteHeader( + context, + headers: _arguments.indexer.headers, + key: key, + ); if (mounted) setState(() {}); }), ); diff --git a/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart b/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart index a1c76b40..0759d4ab 100644 --- a/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart +++ b/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart @@ -120,14 +120,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _displayName() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Display Name'), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'Display Name', + body: [ + TextSpan( text: _indexer.displayName == null || _indexer.displayName.isEmpty ? 'Not Set' - : _indexer.displayName), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + : _indexer.displayName, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await LunaDialogs() .editText(context, 'Display Name', prefill: _indexer.displayName); @@ -138,14 +140,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiURL() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Indexer API Host'), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'Indexer API Host', + body: [ + TextSpan( text: _indexer.host == null || _indexer.host.isEmpty ? 'Not Set' - : _indexer.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + : _indexer.host, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await LunaDialogs() .editText(context, 'Indexer API Host', prefill: _indexer.host); @@ -156,14 +160,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Indexer API Key'), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'Indexer API Key', + body: [ + TextSpan( text: _indexer.apiKey == null || _indexer.apiKey.isEmpty ? 'Not Set' - : _indexer.apiKey), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + : _indexer.apiKey, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 values = await LunaDialogs() .editText(context, 'Indexer API Key', prefill: _indexer.apiKey); @@ -174,11 +180,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _headers() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSearchEditHeadersRouter() .navigateTo(context, indexerId: widget.indexerId), ); diff --git a/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart b/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart index 57f067d1..b7b3fcba 100644 --- a/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart +++ b/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart @@ -113,20 +113,23 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { (_indexer.headers ?? {}).cast(); List _sortedKeys = headers.keys.toList()..sort(); return _sortedKeys - .map((key) => _headerTile(key, headers[key])) + .map((key) => _headerBlock(key, headers[key])) .toList(); } - LunaListTile _headerTile(String key, String value) { - return LunaListTile( - context: context, - title: LunaText.title(text: key.toString()), - subtitle: LunaText.subtitle(text: value.toString()), + LunaBlock _headerBlock(String key, String value) { + return LunaBlock( + title: key.toString(), + body: [TextSpan(text: value.toString())], trailing: LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, - onPressed: () async => HeaderUtility().deleteHeader(context, - headers: _indexer.headers, key: key, indexer: _indexer), + onPressed: () async => HeaderUtility().deleteHeader( + context, + headers: _indexer.headers, + key: key, + indexer: _indexer, + ), ), ); } diff --git a/lib/modules/settings/routes/configuration_search/route.dart b/lib/modules/settings/routes/configuration_search/route.dart index 5ddf96d8..81daf48f 100644 --- a/lib/modules/settings/routes/configuration_search/route.dart +++ b/lib/modules/settings/routes/configuration_search/route.dart @@ -76,7 +76,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { List indexers = Database.indexersBox.values.toList(); indexers.sort((a, b) => a.displayName.toLowerCase().compareTo(b.displayName.toLowerCase())); - List list = List.generate( + List list = List.generate( indexers.length, (index) => _indexerTile(indexers[index], indexers[index].key), ); @@ -84,11 +84,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _indexerTile(IndexerHiveObject indexer, int index) { - return LunaListTile( - context: context, - title: LunaText.title(text: indexer.displayName), - subtitle: LunaText.subtitle(text: indexer.host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: indexer.displayName, + body: [TextSpan(text: indexer.host)], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSearchEditRouter().navigateTo( context, indexerId: index, @@ -105,28 +104,28 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _hideAdultCategories() { - return SearchDatabaseValue.HIDE_XXX.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Hide Adult Categories'), - subtitle: LunaText.subtitle(text: 'Hide Adult Content'), + SearchDatabaseValue _db = SearchDatabaseValue.HIDE_XXX; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Hide Adult Categories', + body: const [TextSpan(text: 'Hide Adult Content')], trailing: LunaSwitch( - value: SearchDatabaseValue.HIDE_XXX.data, - onChanged: (value) => SearchDatabaseValue.HIDE_XXX.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); } Widget _showLinks() { - return SearchDatabaseValue.SHOW_LINKS.listen( - builder: (context, box, widget) => LunaListTile( - context: context, - title: LunaText.title(text: 'Show Links'), - subtitle: LunaText.subtitle(text: 'Show Download and Comments Links'), + SearchDatabaseValue _db = SearchDatabaseValue.SHOW_LINKS; + return _db.listen( + builder: (context, box, widget) => LunaBlock( + title: 'Show Links', + body: const [TextSpan(text: 'Show Download and Comments Links')], trailing: LunaSwitch( - value: SearchDatabaseValue.SHOW_LINKS.data, - onChanged: (value) => SearchDatabaseValue.SHOW_LINKS.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart index 2e26fc1f..a1cc1104 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart @@ -64,17 +64,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.sonarrHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: - LunaText.subtitle(text: (host ?? '').isEmpty ? 'Not Set' : host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.sonarrHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, - prefill: Database.currentProfileObject.sonarrHost ?? '', + prefill: host, ); if (_values.item1) { Database.currentProfileObject.sonarrHost = _values.item2; @@ -86,17 +84,23 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.sonarrKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'API Key'), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.sonarrKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( - context, 'Sonarr API Key', - prefill: Database.currentProfileObject.sonarrKey ?? ''); + context, + 'settings.ApiKey'.tr(), + prefill: apiKey, + ); if (_values.item1) { Database.currentProfileObject.sonarrKey = _values.item2; Database.currentProfileObject.save(); @@ -108,8 +112,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _testConnection() { return LunaButton.text( - text: 'Test Connection', - icon: Icons.wifi_tethering_rounded, + text: 'settings.TestConnection'.tr(), + icon: LunaIcons.CONNECTION_TEST, onTap: () async { ProfileHiveObject _profile = Database.currentProfileObject; if (_profile.sonarrHost == null || _profile.sonarrHost.isEmpty) { @@ -127,18 +131,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return; } Sonarr( - host: _profile.sonarrHost, - apiKey: _profile.sonarrKey, - headers: - Map.from(_profile.sonarrHeaders ?? {})) - .system - .getStatus() - .then((_) => showLunaSuccessSnackBar( - title: 'Connected Successfully', - message: 'Sonarr is ready to use with LunaSea', - )) - .catchError((error, trace) { - LunaLogger().error('Connection Test Failed', error, trace); + host: _profile.sonarrHost, + apiKey: _profile.sonarrKey, + headers: Map.from( + _profile.sonarrHeaders ?? {}, + ), + ).system.getStatus().then((_) { + showLunaSuccessSnackBar( + title: 'Connected Successfully', + message: 'Sonarr is ready to use with LunaSea', + ); + }).catchError((error, trace) { + LunaLogger().error( + 'Connection Test Failed', + error, + trace, + ); showLunaErrorSnackBar( title: 'Connection Test Failed', error: error, @@ -149,13 +157,13 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async => - SettingsConfigurationSonarrHeadersRouter().navigateTo(context), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), + onTap: () async => SettingsConfigurationSonarrHeadersRouter().navigateTo( + context, + ), ); } } diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart index 3c499651..fd125465 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart @@ -34,7 +34,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( - title: 'Default Pages', + title: 'settings.DefaultPages'.tr(), scrollControllers: [scrollController], ); } @@ -51,67 +51,73 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _homePage() { - return SonarrDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Home'), - subtitle: LunaText.subtitle( - text: SonarrNavigationBar - .titles[SonarrDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LunaIconButton( - icon: SonarrNavigationBar - .icons[SonarrDatabaseValue.NAVIGATION_INDEX.data]), - onTap: () async { - List values = await SonarrDialogs.setDefaultPage(context, + SonarrDatabaseValue _db = SonarrDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) { + return LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: SonarrNavigationBar.titles[_db.data])], + trailing: LunaIconButton(icon: SonarrNavigationBar.icons[_db.data]), + onTap: () async { + List values = await SonarrDialogs.setDefaultPage( + context, titles: SonarrNavigationBar.titles, - icons: SonarrNavigationBar.icons); - if (values[0]) SonarrDatabaseValue.NAVIGATION_INDEX.put(values[1]); - }, - ), + icons: SonarrNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); + }, + ); + }, ); } Widget _seriesDetailsPage() { - return SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Series Details'), - subtitle: LunaText.subtitle( - text: SonarrSeriesDetailsNavigationBar.titles[ - SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), - trailing: LunaIconButton( - icon: SonarrSeriesDetailsNavigationBar.icons[ - SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS.data]), - onTap: () async { - List values = await SonarrDialogs.setDefaultPage(context, + SonarrDatabaseValue _db = + SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS; + return _db.listen( + builder: (context, box, _) { + return LunaBlock( + title: 'sonarr.SeriesDetails'.tr(), + body: [ + TextSpan(text: SonarrSeriesDetailsNavigationBar.titles[_db.data]) + ], + trailing: LunaIconButton( + icon: SonarrSeriesDetailsNavigationBar.icons[_db.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]); - }, - ), + icons: SonarrSeriesDetailsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); + }, + ); + }, ); } Widget _seasonDetailsPage() { - return SonarrDatabaseValue.NAVIGATION_INDEX_SEASON_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Season Details'), - subtitle: LunaText.subtitle( - text: SonarrSeasonDetailsNavigationBar.titles[ - SonarrDatabaseValue.NAVIGATION_INDEX_SEASON_DETAILS.data]), - trailing: LunaIconButton( - icon: SonarrSeasonDetailsNavigationBar.icons[ - SonarrDatabaseValue.NAVIGATION_INDEX_SEASON_DETAILS.data]), - onTap: () async { - List values = await SonarrDialogs.setDefaultPage(context, + SonarrDatabaseValue _db = + SonarrDatabaseValue.NAVIGATION_INDEX_SEASON_DETAILS; + return _db.listen( + builder: (context, box, _) { + return LunaBlock( + title: 'sonarr.SeasonDetails'.tr(), + body: [ + TextSpan(text: SonarrSeasonDetailsNavigationBar.titles[_db.data]) + ], + trailing: LunaIconButton( + icon: SonarrSeasonDetailsNavigationBar.icons[_db.data]), + onTap: () async { + List values = await SonarrDialogs.setDefaultPage( + context, titles: SonarrSeasonDetailsNavigationBar.titles, - icons: SonarrSeasonDetailsNavigationBar.icons); - if (values[0]) - SonarrDatabaseValue.NAVIGATION_INDEX_SEASON_DETAILS.put(values[1]); - }, - ), + icons: SonarrSeasonDetailsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); + }, + ); + }, ); } } diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/default_sorting.dart b/lib/modules/settings/routes/configuration_sonarr/pages/default_sorting.dart index 1b225850..ce0a9b37 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/default_sorting.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/default_sorting.dart @@ -56,26 +56,21 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _sortingSeries() { - return SonarrDatabaseValue.DEFAULT_SORTING_SERIES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Series Sort Category'), - subtitle: LunaText.subtitle( - text: (SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data - as SonarrSeriesSorting) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + SonarrDatabaseValue _db = SonarrDatabaseValue.DEFAULT_SORTING_SERIES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Series Sort Category', + body: [TextSpan(text: (_db.data as SonarrSeriesSorting).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { List titles = SonarrSeriesSorting.values - .map((e) => e.readable) + .map((sorting) => sorting.readable) .toList(); Tuple2 values = await SonarrDialogs() .setDefaultSortingOrFiltering(context, titles: titles); if (values.item1) { - SonarrDatabaseValue.DEFAULT_SORTING_SERIES - .put(SonarrSeriesSorting.values[values.item2]); - context.read().seriesSortType = - SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; + _db.put(SonarrSeriesSorting.values[values.item2]); + context.read().seriesSortType = _db.data; context.read().seriesSortAscending = SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; } @@ -84,102 +79,62 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { ); } - Widget _filteringSeries() { - return SonarrDatabaseValue.DEFAULT_FILTERING_SERIES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Series Filter Category'), - subtitle: LunaText.subtitle( - text: (SonarrDatabaseValue.DEFAULT_FILTERING_SERIES.data - as SonarrSeriesFilter) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - List titles = - SonarrSeriesFilter.values.map((e) => e.readable).toList(); - Tuple2 values = await SonarrDialogs() - .setDefaultSortingOrFiltering(context, titles: titles); - if (values.item1) { - SonarrDatabaseValue.DEFAULT_FILTERING_SERIES - .put(SonarrSeriesFilter.values[values.item2]); - } - }, - ), - ); - } - - Widget _filteringReleases() { - return SonarrDatabaseValue.DEFAULT_FILTERING_RELEASES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Filter Category'), - subtitle: LunaText.subtitle( - text: (SonarrDatabaseValue.DEFAULT_FILTERING_RELEASES.data - as SonarrReleasesFilter) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), - onTap: () async { - List titles = SonarrReleasesFilter.values - .map((e) => e.readable) - .toList(); - Tuple2 values = await SonarrDialogs() - .setDefaultSortingOrFiltering(context, titles: titles); - if (values.item1) { - SonarrDatabaseValue.DEFAULT_FILTERING_RELEASES - .put(SonarrReleasesFilter.values[values.item2]); - context.read().seriesFilterType = - SonarrDatabaseValue.DEFAULT_FILTERING_RELEASES.data; - } - }, - ), - ); - } - Widget _sortingSeriesDirection() { - return SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Series Sort Direction'), - subtitle: LunaText.subtitle( - text: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data - ? 'Ascending' - : 'Descending'), + SonarrDatabaseValue _db = + SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Series Sort Direction', + body: [TextSpan(text: _db.data ? 'Ascending' : 'Descending')], trailing: LunaSwitch( - value: SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data, + value: _db.data, onChanged: (value) { - SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.put(value); + _db.put(value); context.read().seriesSortType = SonarrDatabaseValue.DEFAULT_SORTING_SERIES.data; - context.read().seriesSortAscending = - SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING.data; + context.read().seriesSortAscending = _db.data; }, ), ), ); } + Widget _filteringSeries() { + SonarrDatabaseValue _db = SonarrDatabaseValue.DEFAULT_FILTERING_SERIES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Series Filter Category', + body: [TextSpan(text: (_db.data as SonarrSeriesFilter).readable)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + List titles = SonarrSeriesFilter.values + .map((filter) => filter.readable) + .toList(); + Tuple2 values = await SonarrDialogs() + .setDefaultSortingOrFiltering(context, titles: titles); + if (values.item1) { + _db.put(SonarrSeriesFilter.values[values.item2]); + } + }, + ), + ); + } + Widget _sortingReleases() { - return SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Sort Category'), - subtitle: LunaText.subtitle( - text: (SonarrDatabaseValue.DEFAULT_SORTING_RELEASES.data - as SonarrReleasesSorting) - .readable), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + SonarrDatabaseValue _db = SonarrDatabaseValue.DEFAULT_SORTING_RELEASES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Sort Category', + body: [TextSpan(text: (_db.data as SonarrReleasesSorting).readable)], + trailing: const LunaIconButton.arrow(), onTap: () async { List titles = SonarrReleasesSorting.values - .map((e) => e.readable) + .map((sorting) => sorting.readable) .toList(); - Tuple2 values = - await SonarrDialogs().setDefaultSortingOrFiltering( - context, - titles: titles, - ); + Tuple2 values = await SonarrDialogs() + .setDefaultSortingOrFiltering(context, titles: titles); if (values.item1) { - SonarrDatabaseValue.DEFAULT_SORTING_RELEASES - .put(SonarrReleasesSorting.values[values.item2]); + _db.put(SonarrReleasesSorting.values[values.item2]); } }, ), @@ -187,20 +142,39 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _sortingReleasesDirection() { - return SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Releases Sort Direction'), - subtitle: LunaText.subtitle( - text: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data - ? 'Ascending' - : 'Descending'), + SonarrDatabaseValue _db = + SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Sort Direction', + body: [TextSpan(text: _db.data ? 'Ascending' : 'Descending')], trailing: LunaSwitch( - value: SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data, - onChanged: (value) => - SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.put(value), + value: _db.data, + onChanged: (value) => _db.put(value), ), ), ); } + + Widget _filteringReleases() { + SonarrDatabaseValue _db = SonarrDatabaseValue.DEFAULT_FILTERING_RELEASES; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Releases Filter Category', + body: [TextSpan(text: (_db.data as SonarrReleasesFilter).readable)], + trailing: const LunaIconButton.arrow(), + onTap: () async { + List titles = SonarrReleasesFilter.values + .map((filter) => filter.readable) + .toList(); + Tuple2 values = await SonarrDialogs() + .setDefaultSortingOrFiltering(context, titles: titles); + if (values.item1) { + _db.put(SonarrReleasesFilter.values[values.item2]); + context.read().seriesFilterType = _db.data; + } + }, + ), + ); + } } diff --git a/lib/modules/settings/routes/configuration_sonarr/route.dart b/lib/modules/settings/routes/configuration_sonarr/route.dart index f6db8615..76bfaa3e 100644 --- a/lib/modules/settings/routes/configuration_sonarr/route.dart +++ b/lib/modules/settings/routes/configuration_sonarr/route.dart @@ -56,9 +56,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.SONARR.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.SONARR.name}', trailing: LunaSwitch( value: Database.currentProfileObject.sonarrEnabled ?? false, onChanged: (value) { @@ -72,55 +71,54 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle(text: 'Connection Details for Sonarr'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.ConnectionDetails'.tr(), + body: [ + TextSpan( + text: 'settings.ConnectionDetailsDescription'.tr( + args: [LunaModule.SONARR.name], + ), + ) + ], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSonarrConnectionDetailsRouter() .navigateTo(context), ); } Widget _defaultPagesPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Pages'), - subtitle: LunaText.subtitle(text: 'Set Default Landing Pages'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.DefaultPages'.tr(), + body: [TextSpan(text: 'settings.DefaultPagesDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSonarrDefaultPagesRouter().navigateTo(context), ); } Widget _defaultSortingFilteringPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Sorting & Filtering'), - subtitle: - LunaText.subtitle(text: 'Set Default Sorting & Filtering Methods'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.DefaultSortingAndFiltering'.tr(), + body: [ + TextSpan(text: 'settings.DefaultSortingAndFilteringDescription'.tr()), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationSonarrDefaultSortingRouter().navigateTo(context), ); } Widget _queueSize() { - return SonarrDatabaseValue.QUEUE_PAGE_SIZE.listen( - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Queue Size'), - subtitle: LunaText.subtitle( - text: SonarrDatabaseValue.QUEUE_PAGE_SIZE.data == 1 - ? '1 Item' - : '${SonarrDatabaseValue.QUEUE_PAGE_SIZE.data} Items', - ), - trailing: LunaIconButton(icon: Icons.queue_rounded), + SonarrDatabaseValue _db = SonarrDatabaseValue.QUEUE_PAGE_SIZE; + return _db.listen( + builder: (context, _, __) => LunaBlock( + title: 'Queue Size', + body: [TextSpan(text: _db.data == 1 ? '1 Item' : '${_db.data} Items')], + trailing: const LunaIconButton(icon: Icons.queue_rounded), onTap: () async { Tuple2 result = await SonarrDialogs().setQueuePageSize(context); - if (result.item1) - SonarrDatabaseValue.QUEUE_PAGE_SIZE.put(result.item2); + if (result.item1) _db.put(result.item2); }, ), ); diff --git a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart index 551e8f95..c4a0bd77 100644 --- a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart @@ -64,13 +64,11 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _host() { - String host = Database.currentProfileObject.tautulliHost; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Host'), - subtitle: - LunaText.subtitle(text: (host ?? '').isEmpty ? 'Not Set' : host), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String host = Database.currentProfileObject.tautulliHost ?? ''; + return LunaBlock( + title: 'settings.Host'.tr(), + body: [TextSpan(text: host.isEmpty ? 'lunasea.NotSet'.tr() : host)], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editHost( context, @@ -86,17 +84,23 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _apiKey() { - String apiKey = Database.currentProfileObject.tautulliKey; - return LunaListTile( - context: context, - title: LunaText.title(text: 'API Key'), - subtitle: LunaText.subtitle( - text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + String apiKey = Database.currentProfileObject.tautulliKey ?? ''; + return LunaBlock( + title: 'settings.ApiKey'.tr(), + body: [ + TextSpan( + text: apiKey.isEmpty + ? 'lunasea.NotSet'.tr() + : LunaUI.TEXT_OBFUSCATED_PASSWORD, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await LunaDialogs().editText( - context, 'Tautulli API Key', - prefill: Database.currentProfileObject.tautulliKey ?? ''); + context, + 'settings.ApiKey'.tr(), + prefill: Database.currentProfileObject.tautulliKey ?? '', + ); if (_values.item1) { Database.currentProfileObject.tautulliKey = _values.item2; Database.currentProfileObject.save(); @@ -108,8 +112,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _testConnection() { return LunaButton.text( - text: 'Test Connection', - icon: Icons.wifi_tethering_rounded, + text: 'settings.TestConnection'.tr(), + icon: LunaIcons.CONNECTION_TEST, onTap: () async { ProfileHiveObject _profile = Database.currentProfileObject; if (_profile?.tautulliHost?.isEmpty ?? true) { @@ -149,11 +153,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _customHeaders() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Custom Headers'), - subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.CustomHeaders'.tr(), + body: [TextSpan(text: 'settings.CustomHeadersDescription'.tr())], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationTautulliHeadersRouter().navigateTo(context), ); diff --git a/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart b/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart index 2b7022bf..2cd1dda9 100644 --- a/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart @@ -54,110 +54,108 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _homePage() { - return TautulliDatabaseValue.NAVIGATION_INDEX.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Home'), - subtitle: LunaText.subtitle( - text: TautulliNavigationBar - .titles[TautulliDatabaseValue.NAVIGATION_INDEX.data]), - trailing: LunaIconButton( - icon: TautulliNavigationBar - .icons[TautulliDatabaseValue.NAVIGATION_INDEX.data]), + TautulliDatabaseValue _db = TautulliDatabaseValue.NAVIGATION_INDEX; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'lunasea.Home'.tr(), + body: [TextSpan(text: TautulliNavigationBar.titles[_db.data])], + trailing: LunaIconButton(icon: TautulliNavigationBar.icons[_db.data]), onTap: () async { - List values = await TautulliDialogs.setDefaultPage(context, - titles: TautulliNavigationBar.titles, - icons: TautulliNavigationBar.icons); - if (values[0]) TautulliDatabaseValue.NAVIGATION_INDEX.put(values[1]); + List values = await TautulliDialogs.setDefaultPage( + context, + titles: TautulliNavigationBar.titles, + icons: TautulliNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); }, ), ); } Widget _graphsPage() { - return TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Graphs'), - subtitle: LunaText.subtitle( - text: TautulliGraphsNavigationBar - .titles[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), - trailing: LunaIconButton( - icon: TautulliGraphsNavigationBar - .icons[TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS.data]), + TautulliDatabaseValue _db = TautulliDatabaseValue.NAVIGATION_INDEX_GRAPHS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Graphs', + body: [TextSpan(text: TautulliGraphsNavigationBar.titles[_db.data])], + trailing: + LunaIconButton(icon: TautulliGraphsNavigationBar.icons[_db.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]); + List values = await TautulliDialogs.setDefaultPage( + context, + titles: TautulliGraphsNavigationBar.titles, + icons: TautulliGraphsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); }, ), ); } Widget _libraryDetailsPage() { - return TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Library Details'), - subtitle: LunaText.subtitle( - text: TautulliLibrariesDetailsNavigationBar.titles[ - TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), + TautulliDatabaseValue _db = + TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Library Details', + body: [ + TextSpan(text: TautulliLibrariesDetailsNavigationBar.titles[_db.data]) + ], trailing: LunaIconButton( - icon: TautulliLibrariesDetailsNavigationBar.icons[ - TautulliDatabaseValue.NAVIGATION_INDEX_LIBRARIES_DETAILS.data]), + icon: TautulliLibrariesDetailsNavigationBar.icons[_db.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]); + List values = await TautulliDialogs.setDefaultPage( + context, + titles: TautulliLibrariesDetailsNavigationBar.titles, + icons: TautulliLibrariesDetailsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); }, ), ); } Widget _mediaDetailsPage() { - return TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Media Details'), - subtitle: LunaText.subtitle( - text: TautulliMediaDetailsNavigationBar.titles[ - TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), + TautulliDatabaseValue _db = + TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'Media Details', + body: [ + TextSpan(text: TautulliMediaDetailsNavigationBar.titles[_db.data]), + ], trailing: LunaIconButton( - icon: TautulliMediaDetailsNavigationBar.icons[ - TautulliDatabaseValue.NAVIGATION_INDEX_MEDIA_DETAILS.data]), + icon: TautulliMediaDetailsNavigationBar.icons[_db.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]); + List values = await TautulliDialogs.setDefaultPage( + context, + titles: TautulliMediaDetailsNavigationBar.titles, + icons: TautulliMediaDetailsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); }, ), ); } Widget _userDetailsPage() { - return TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'User Details'), - subtitle: LunaText.subtitle( - text: TautulliUserDetailsNavigationBar.titles[ - TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), + TautulliDatabaseValue _db = + TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS; + return _db.listen( + builder: (context, box, _) => LunaBlock( + title: 'User Details', + body: [ + TextSpan(text: TautulliUserDetailsNavigationBar.titles[_db.data]), + ], trailing: LunaIconButton( - icon: TautulliUserDetailsNavigationBar.icons[ - TautulliDatabaseValue.NAVIGATION_INDEX_USER_DETAILS.data]), + icon: TautulliUserDetailsNavigationBar.icons[_db.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]); + List values = await TautulliDialogs.setDefaultPage( + context, + titles: TautulliUserDetailsNavigationBar.titles, + icons: TautulliUserDetailsNavigationBar.icons, + ); + if (values[0]) _db.put(values[1]); }, ), ); diff --git a/lib/modules/settings/routes/configuration_tautulli/route.dart b/lib/modules/settings/routes/configuration_tautulli/route.dart index dfe22157..f5b9c251 100644 --- a/lib/modules/settings/routes/configuration_tautulli/route.dart +++ b/lib/modules/settings/routes/configuration_tautulli/route.dart @@ -58,9 +58,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _enabledToggle() { return ValueListenableBuilder( valueListenable: Database.profilesBox.listenable(), - builder: (context, _, __) => LunaListTile( - context: context, - title: LunaText.title(text: 'Enable ${LunaModule.TAUTULLI.name}'), + builder: (context, _, __) => LunaBlock( + title: 'Enable ${LunaModule.TAUTULLI.name}', trailing: LunaSwitch( value: Database.currentProfileObject.tautulliEnabled ?? false, onChanged: (value) { @@ -74,44 +73,40 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _connectionDetailsPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Connection Details'), - subtitle: LunaText.subtitle(text: 'Connection Details for Tautulli'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Connection Details', + body: const [ + TextSpan(text: 'Connection Details for Tautulli'), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationTautulliConnectionDetailsRouter() .navigateTo(context), ); } Widget _defaultPagesPage() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Pages'), - subtitle: LunaText.subtitle(text: 'Set Default Landing Pages'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'Default Pages', + body: const [TextSpan(text: 'Set Default Landing Pages')], + trailing: const LunaIconButton.arrow(), onTap: () async => SettingsConfigurationTautulliDefaultPagesRouter().navigateTo(context), ); } Widget _defaultTerminationMessage() { - return TautulliDatabaseValue.TERMINATION_MESSAGE.listen( + TautulliDatabaseValue _db = TautulliDatabaseValue.TERMINATION_MESSAGE; + return _db.listen( builder: (context, box, _) { - String terminationMessage = - TautulliDatabaseValue.TERMINATION_MESSAGE.data; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Default Termination Message'), - subtitle: LunaText.subtitle( - text: - terminationMessage.isEmpty ? 'Not Set' : terminationMessage), - trailing: LunaIconButton(icon: Icons.videocam_off_rounded), + String message = _db.data; + return LunaBlock( + title: 'Default Termination Message', + body: [TextSpan(text: message.isEmpty ? 'Not Set' : message)], + trailing: const LunaIconButton(icon: Icons.videocam_off_rounded), onTap: () async { Tuple2 result = await TautulliDialogs.setTerminationMessage(context); - if (result.item1) - TautulliDatabaseValue.TERMINATION_MESSAGE.put(result.item2); + if (result.item1) _db.put(result.item2); }, ); }, @@ -119,19 +114,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _activityRefreshRate() { - return TautulliDatabaseValue.REFRESH_RATE.listen( - builder: (context, box, _) { + TautulliDatabaseValue _db = TautulliDatabaseValue.REFRESH_RATE; + return _db.listen(builder: (context, box, _) { String refreshRate; - if (TautulliDatabaseValue.REFRESH_RATE.data == 1) - refreshRate = 'Every Second'; - if (TautulliDatabaseValue.REFRESH_RATE.data != 1) - refreshRate = - 'Every ${TautulliDatabaseValue.REFRESH_RATE.data} Seconds'; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Activity Refresh Rate'), - subtitle: LunaText.subtitle(text: refreshRate), - trailing: LunaIconButton(icon: Icons.refresh_rounded), + if (_db.data == 1) refreshRate = 'Every Second'; + if (_db.data != 1) refreshRate = 'Every ${_db.data} Seconds'; + return LunaBlock( + title: 'Activity Refresh Rate', + body: [TextSpan(text: refreshRate)], + trailing: const LunaIconButton(icon: LunaIcons.REFRESH), onTap: () async { List _values = await TautulliDialogs.setRefreshRate(context); if (_values[0]) TautulliDatabaseValue.REFRESH_RATE.put(_values[1]); @@ -141,26 +132,20 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _statisticsItemCount() { - return ValueListenableBuilder( - valueListenable: Database.lunaSeaBox - .listenable(keys: [TautulliDatabaseValue.STATISTICS_STATS_COUNT.key]), + TautulliDatabaseValue _db = TautulliDatabaseValue.STATISTICS_STATS_COUNT; + return _db.listen( builder: (context, box, _) { String statisticsItems; - if (TautulliDatabaseValue.STATISTICS_STATS_COUNT.data == 1) - statisticsItems = '1 Item'; - if (TautulliDatabaseValue.STATISTICS_STATS_COUNT.data != 1) - statisticsItems = - '${TautulliDatabaseValue.STATISTICS_STATS_COUNT.data} Items'; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Statistics Item Count'), - subtitle: LunaText.subtitle(text: statisticsItems), - trailing: LunaIconButton(icon: Icons.format_list_numbered), + if (_db.data == 1) statisticsItems = '1 Item'; + if (_db.data != 1) statisticsItems = '${_db.data} Items'; + return LunaBlock( + title: 'Statistics Item Count', + body: [TextSpan(text: statisticsItems)], + trailing: const LunaIconButton(icon: Icons.format_list_numbered), onTap: () async { List _values = await TautulliDialogs.setStatisticsItemCount(context); - if (_values[0]) - TautulliDatabaseValue.STATISTICS_STATS_COUNT.put(_values[1]); + if (_values[0]) _db.put(_values[1]); }, ); }, diff --git a/lib/modules/settings/routes/configuration_wakeonlan/route.dart b/lib/modules/settings/routes/configuration_wakeonlan/route.dart index 9f5556fa..24481af6 100644 --- a/lib/modules/settings/routes/configuration_wakeonlan/route.dart +++ b/lib/modules/settings/routes/configuration_wakeonlan/route.dart @@ -55,9 +55,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Enable Wake on LAN'), + return LunaBlock( + title: 'Enable Wake on LAN', trailing: LunaSwitch( value: Database.currentProfileObject.wakeOnLANEnabled ?? false, onChanged: (value) { @@ -71,15 +70,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _broadcastAddress() { String broadcastAddress = Database.currentProfileObject.wakeOnLANBroadcastAddress; - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.BroadcastAddress'.tr()), - subtitle: LunaText.subtitle( - text: broadcastAddress == null || broadcastAddress == '' - ? 'Not Set' - : broadcastAddress, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.BroadcastAddress'.tr(), + body: [ + TextSpan( + text: broadcastAddress == null || broadcastAddress == '' + ? 'Not Set' + : broadcastAddress, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editBroadcastAddress( @@ -97,13 +97,14 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _macAddress() { String macAddress = Database.currentProfileObject.wakeOnLANMACAddress; - return LunaListTile( - context: context, - title: LunaText.title(text: 'settings.MACAddress'.tr()), - subtitle: LunaText.subtitle( - text: macAddress == null || macAddress == '' ? 'Not Set' : macAddress, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'settings.MACAddress'.tr(), + body: [ + TextSpan( + text: macAddress == null || macAddress == '' ? 'Not Set' : macAddress, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async { Tuple2 _values = await SettingsDialogs().editMACAddress( context, diff --git a/lib/modules/settings/routes/debug_menu/route.dart b/lib/modules/settings/routes/debug_menu/route.dart index e00daeaf..d56dfaed 100644 --- a/lib/modules/settings/routes/debug_menu/route.dart +++ b/lib/modules/settings/routes/debug_menu/route.dart @@ -42,13 +42,14 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'Clear Alerts Box'), + LunaBlock( + title: 'Clear Alerts Box', onTap: () async { Database().clearAlertsBox(); showLunaSuccessSnackBar( - title: 'Cleared', message: 'Cleared Alerts Box'); + title: 'Cleared', + message: 'Cleared Alerts Box', + ); }, ), ], diff --git a/lib/modules/settings/routes/donations/route.dart b/lib/modules/settings/routes/donations/route.dart index e710d332..eba0d39b 100644 --- a/lib/modules/settings/routes/donations/route.dart +++ b/lib/modules/settings/routes/donations/route.dart @@ -95,10 +95,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _iapTile(ProductDetails product) { - return LunaListTile( - context: context, - title: LunaText.title(text: product.lunaName), - subtitle: LunaText.subtitle(text: product.price), + return LunaBlock( + title: product.lunaName, + body: [TextSpan(text: product.price)], trailing: LunaIconButton(icon: product.lunaIcon), onTap: () async { final PurchaseParam _parameters = PurchaseParam( diff --git a/lib/modules/settings/routes/notifications/route.dart b/lib/modules/settings/routes/notifications/route.dart index 3778d76d..9f2d3e30 100644 --- a/lib/modules/settings/routes/notifications/route.dart +++ b/lib/modules/settings/routes/notifications/route.dart @@ -56,11 +56,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { }, ), SettingsBanners.NOTIFICATIONS_MODULE_SUPPORT.banner(), - LunaListTile( - context: context, - title: LunaText.title(text: 'Getting Started'), - subtitle: LunaText.subtitle(text: 'Information & Setup Instructions'), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaBlock( + title: 'Getting Started', + body: const [TextSpan(text: 'Information & Setup Instructions')], + trailing: const LunaIconButton.arrow(), onTap: LunaLinks.NOTIFICATIONS_GETTING_STARTED.url.lunaOpenGenericLink, ), diff --git a/lib/modules/settings/routes/profiles.dart b/lib/modules/settings/routes/profiles.dart index d55c9aa7..2482d2a2 100644 --- a/lib/modules/settings/routes/profiles.dart +++ b/lib/modules/settings/routes/profiles.dart @@ -34,20 +34,6 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaAppBar( title: 'Profiles', scrollControllers: [scrollController], - actions: [ - LunaIconButton( - icon: Icons.help_outline_rounded, - onPressed: () async => LunaDialogs().textPreview( - context, - 'Profiles', - [ - 'Profiles allow you to add multiple instances of modules into LunaSea.', - 'Newznab indexer searching is enabled and shared across all profiles.', - 'You can switch between profiles in the main navigation drawer of LunaSea.', - ].join('\n\n'), - ), - ), - ], ); } @@ -55,26 +41,24 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ + SettingsBanners.PROFILES_SUPPORT.banner(), _enabledProfile(), - LunaListTile( - context: context, - title: LunaText.title(text: 'Add'), - subtitle: LunaText.subtitle(text: 'Add a New Profile'), - trailing: LunaIconButton(icon: Icons.add_rounded), + LunaBlock( + title: 'settings.AddProfile'.tr(), + body: [TextSpan(text: 'settings.AddProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.ADD), onTap: () async => LunaProfile().addProfile(), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Rename'), - subtitle: LunaText.subtitle(text: 'Rename an Existing Profile'), - trailing: LunaIconButton(icon: Icons.text_format_rounded), + LunaBlock( + title: 'settings.RenameProfile'.tr(), + body: [TextSpan(text: 'settings.RenameProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.RENAME), onTap: () async => LunaProfile().renameProfile(), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Delete'), - subtitle: LunaText.subtitle(text: 'Delete an Existing Profile'), - trailing: LunaIconButton(icon: Icons.delete_rounded), + LunaBlock( + title: 'settings.DeleteProfile'.tr(), + body: [TextSpan(text: 'settings.DeleteProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.DELETE), onTap: () async => LunaProfile().deleteProfile(), ), ], @@ -82,23 +66,21 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledProfile() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Enabled Profile'), - subtitle: LunaDatabaseValue.ENABLED_PROFILE.listen( - builder: (context, _, __) => - LunaText.subtitle(text: LunaDatabaseValue.ENABLED_PROFILE.data), + return LunaDatabaseValue.ENABLED_PROFILE.listen( + builder: (context, _, __) => LunaBlock( + title: 'Enabled Profile', + body: [TextSpan(text: LunaDatabaseValue.ENABLED_PROFILE.data)], + trailing: const LunaIconButton(icon: LunaIcons.USER), + onTap: () async { + Tuple2 results = await SettingsDialogs().enabledProfile( + LunaState.navigatorKey.currentContext, + LunaProfile().profilesList(), + ); + if (results.item1 && + results.item2 != LunaDatabaseValue.ENABLED_PROFILE.data) + LunaProfile().safelyChangeProfiles(results.item2); + }, ), - trailing: LunaIconButton(icon: Icons.switch_account_rounded), - onTap: () async { - Tuple2 results = await SettingsDialogs().enabledProfile( - LunaState.navigatorKey.currentContext, - LunaProfile().profilesList(), - ); - if (results.item1 && - results.item2 != LunaDatabaseValue.ENABLED_PROFILE.data) - LunaProfile().safelyChangeProfiles(results.item2); - }, ); } } diff --git a/lib/modules/settings/routes/resources/route.dart b/lib/modules/settings/routes/resources/route.dart index a40c00dd..8a04f052 100644 --- a/lib/modules/settings/routes/resources/route.dart +++ b/lib/modules/settings/routes/resources/route.dart @@ -42,67 +42,59 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'Discord'), - subtitle: LunaText.subtitle(text: 'Chat & Discussions'), - trailing: LunaIconButton(icon: LunaBrandIcons.discord), + LunaBlock( + title: 'Discord', + body: const [TextSpan(text: 'Chat & Discussions')], + trailing: const LunaIconButton(icon: LunaBrandIcons.discord), onTap: LunaLinks.DISCORD.launch, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Documentation'), - subtitle: LunaText.subtitle(text: 'View the Documentation'), - trailing: LunaIconButton(icon: Icons.auto_stories_rounded), + LunaBlock( + title: 'Documentation', + body: const [TextSpan(text: 'View the Documentation')], + trailing: const LunaIconButton(icon: Icons.auto_stories_rounded), onTap: LunaLinks.DOCUMENTATION.launch, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Feedback Board'), - subtitle: LunaText.subtitle(text: 'Request New Features'), - trailing: LunaIconButton(icon: Icons.speaker_notes_rounded), + LunaBlock( + title: 'Feedback Board', + body: const [TextSpan(text: 'Request New Features')], + trailing: const LunaIconButton(icon: Icons.speaker_notes_rounded), onTap: LunaLinks.FEEDBACK_BOARD.launch, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'GitHub'), - subtitle: LunaText.subtitle(text: 'View the Source Code'), - trailing: LunaIconButton(icon: LunaBrandIcons.github), + LunaBlock( + title: 'GitHub', + body: const [TextSpan(text: 'View the Source Code')], + trailing: const LunaIconButton(icon: LunaBrandIcons.github), onTap: LunaLinks.GITHUB.launch, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Reddit'), - subtitle: LunaText.subtitle(text: 'Ask Questions & Get Support'), - trailing: LunaIconButton(icon: LunaBrandIcons.reddit), + LunaBlock( + title: 'Reddit', + body: const [TextSpan(text: 'Ask Questions & Get Support')], + trailing: const LunaIconButton(icon: LunaBrandIcons.reddit), onTap: LunaLinks.REDDIT.launch, ), if (Platform.isIOS) - LunaListTile( - context: context, - title: LunaText.title(text: 'TestFlight'), - subtitle: LunaText.subtitle(text: 'Join the TestFlight Beta'), - trailing: LunaIconButton(icon: Icons.developer_board_rounded), - onTap: LunaLinks.TESTFLIGHT.launch), - LunaListTile( - context: context, - title: LunaText.title(text: 'System Status'), - subtitle: - LunaText.subtitle(text: 'Status Page for Hosted Services'), - trailing: LunaIconButton(icon: Icons.health_and_safety), - onTap: LunaLinks.SYSTEM_STATUS.launch), - LunaListTile( - context: context, - title: LunaText.title(text: 'Weblate'), - subtitle: LunaText.subtitle(text: 'Help Localize LunaSea'), - trailing: LunaIconButton(icon: Icons.translate_rounded), + LunaBlock( + title: 'TestFlight', + body: const [TextSpan(text: 'Join the TestFlight Beta')], + trailing: const LunaIconButton(icon: Icons.developer_board_rounded), + onTap: LunaLinks.TESTFLIGHT.launch, + ), + LunaBlock( + title: 'System Status', + body: const [TextSpan(text: 'Status Page for Hosted Services')], + trailing: const LunaIconButton(icon: Icons.health_and_safety), + onTap: LunaLinks.SYSTEM_STATUS.launch, + ), + LunaBlock( + title: 'Weblate', + body: const [TextSpan(text: 'Help Localize LunaSea')], + trailing: const LunaIconButton(icon: LunaIcons.TRANSLATE), onTap: LunaLinks.WEBLATE.launch, ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Website'), - subtitle: LunaText.subtitle(text: 'Visit LunaSea\'s Website'), - trailing: LunaIconButton(icon: Icons.home_rounded), + LunaBlock( + title: 'Website', + body: const [TextSpan(text: 'Visit LunaSea\'s Website')], + trailing: const LunaIconButton(icon: LunaIcons.HOME), onTap: LunaLinks.WEBSITE.launch, ), ], diff --git a/lib/modules/settings/routes/settings/route.dart b/lib/modules/settings/routes/settings/route.dart index 15f364a9..c61749bb 100644 --- a/lib/modules/settings/routes/settings/route.dart +++ b/lib/modules/settings/routes/settings/route.dart @@ -46,95 +46,55 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Account'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.AccountDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.person_rounded), + LunaBlock( + title: 'settings.Account'.tr(), + body: [TextSpan(text: 'settings.AccountDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.person_rounded), onTap: () async => SettingsAccountRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Configuration'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.ConfigurationDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.device_hub_rounded), + LunaBlock( + title: 'settings.Configuration'.tr(), + body: [TextSpan(text: 'settings.ConfigurationDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.device_hub_rounded), onTap: () async => SettingsConfigurationRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Notifications'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.NotificationsDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.notifications_rounded), + LunaBlock( + title: 'settings.Notifications'.tr(), + body: [TextSpan(text: 'settings.NotificationsDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.notifications_rounded), onTap: () async => SettingsNotificationsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Profiles'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.ProfilesDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.switch_account_rounded), + LunaBlock( + title: 'settings.Profiles'.tr(), + body: [TextSpan(text: 'settings.ProfilesDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.switch_account_rounded), onTap: () async => SettingsProfilesRouter().navigateTo(context), ), const LunaDivider(), if (LunaInAppPurchases.isPlatformCompatible) - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Donations'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.DonationsDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.attach_money_rounded), + LunaBlock( + title: 'settings.Donations'.tr(), + body: [TextSpan(text: 'settings.DonationsDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.attach_money_rounded), onTap: () async => SettingsDonationsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.Resources'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.ResourcesDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.help_outline_rounded), + LunaBlock( + title: 'settings.Resources'.tr(), + body: [TextSpan(text: 'settings.ResourcesDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.help_outline_rounded), onTap: () async => SettingsResourcesRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.System'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.SystemDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.settings_rounded), + LunaBlock( + title: 'settings.System'.tr(), + body: [TextSpan(text: 'settings.SystemDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.settings_rounded), onTap: () async => SettingsSystemRouter().navigateTo(context), ), if (kDebugMode) - LunaListTile( - context: context, - title: LunaText.title( - text: 'settings.DebugMenu'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'settings.DebugMenuDescription'.tr(), - ), - trailing: LunaIconButton(icon: Icons.bug_report_rounded), + LunaBlock( + title: 'settings.DebugMenu'.tr(), + body: [TextSpan(text: 'settings.DebugMenuDescription'.tr())], + trailing: const LunaIconButton(icon: Icons.bug_report_rounded), onTap: () async => SettingsDebugMenuRouter().navigateTo(context), ), ], diff --git a/lib/modules/settings/routes/system/route.dart b/lib/modules/settings/routes/system/route.dart index 6c84e52b..8703aa1a 100644 --- a/lib/modules/settings/routes/system/route.dart +++ b/lib/modules/settings/routes/system/route.dart @@ -49,11 +49,6 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { const SettingsSystemBackupRestoreBackupTile(), const SettingsSystemBackupRestoreRestoreTile(), const LunaDivider(), - if (LunaFirebaseAnalytics.isPlatformCompatible) _enableAnalytics(), - if (LunaFirebaseCrashlytics.isPlatformCompatible) _enableCrashlytics(), - if (LunaFirebaseCrashlytics.isPlatformCompatible || - LunaFirebaseAnalytics.isPlatformCompatible) - const LunaDivider(), if (kDebugMode) _hideTooltipBanners(), _clearConfiguration(), ], @@ -68,74 +63,29 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { if (snapshot.hasError) version = 'Unknown'; if (snapshot.hasData) version = '${snapshot.data.version} (${snapshot.data.buildNumber})'; - return LunaListTile( - context: context, - title: LunaText.title(text: 'Version: $version'), - subtitle: LunaText.subtitle(text: 'View Recent Changes'), - trailing: LunaIconButton(icon: Icons.system_update_rounded), - onTap: () async => LunaChangelog().showChangelog( - snapshot.data?.version, snapshot.data?.buildNumber), + return LunaBlock( + title: 'Version: $version', + body: const [TextSpan(text: 'View Recent Changes')], + trailing: const LunaIconButton(icon: Icons.system_update_rounded), + onTap: () async => LunaChangelogSheet().show(context: context), ); }); } Widget _logs() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Logs'), - subtitle: LunaText.subtitle(text: 'View, Export, and Clear Logs'), - trailing: LunaIconButton(icon: Icons.developer_mode_rounded), + return LunaBlock( + title: 'Logs', + body: const [TextSpan(text: 'View, Export, and Clear Logs')], + trailing: const LunaIconButton(icon: Icons.developer_mode_rounded), onTap: () async => SettingsSystemLogsRouter().navigateTo(context), ); } - Widget _enableAnalytics() { - return LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Firebase Analytics'), - subtitle: LunaText.subtitle(text: 'User Engagement Tracking'), - trailing: LunaSwitch( - value: LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.data, - onChanged: (value) async { - LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.put(value); - LunaFirebaseAnalytics().setEnabledState(); - }, - ), - ), - ); - } - - Widget _enableCrashlytics() { - return LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.listen( - builder: (context, box, _) => LunaListTile( - context: context, - title: LunaText.title(text: 'Firebase Crashlytics'), - subtitle: LunaText.subtitle(text: 'Crash and Error Tracking'), - trailing: LunaSwitch( - value: LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data, - onChanged: (value) async { - LunaDatabaseValue enabled = - LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS; - if (enabled.data) { - bool result = - await SettingsDialogs().disableCrashlyticsWarning(context); - if (result) enabled.put(value); - } else { - enabled.put(value); - } - LunaFirebaseCrashlytics().setEnabledState(); - }), - ), - ); - } - Widget _hideTooltipBanners() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Dismiss Tooltip Banners'), - subtitle: LunaText.subtitle(text: 'Hide Tooltips, Alerts, & Hints'), - trailing: LunaIconButton(icon: Icons.rule_rounded), + return LunaBlock( + title: 'Dismiss Tooltip Banners', + body: const [TextSpan(text: 'Hide Tooltips, Alerts, & Hints')], + trailing: const LunaIconButton(icon: Icons.rule_rounded), onTap: () async { bool result = await SettingsDialogs().dismissTooltipBanners(context); if (result) { @@ -152,11 +102,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _clearConfiguration() { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Clear Configuration'), - subtitle: LunaText.subtitle(text: 'Clean Slate'), - trailing: LunaIconButton(icon: Icons.delete_sweep_rounded), + return LunaBlock( + title: 'Clear Configuration', + body: const [TextSpan(text: 'Clean Slate')], + trailing: const LunaIconButton(icon: Icons.delete_sweep_rounded), onTap: () async { bool result = await SettingsDialogs().clearConfiguration(context); if (result) { diff --git a/lib/modules/settings/routes/system/widgets/backup_tile.dart b/lib/modules/settings/routes/system/widgets/backup_tile.dart index 4c2a3b67..5648c2a1 100644 --- a/lib/modules/settings/routes/system/widgets/backup_tile.dart +++ b/lib/modules/settings/routes/system/widgets/backup_tile.dart @@ -10,11 +10,10 @@ class SettingsSystemBackupRestoreBackupTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Backup to Device'), - subtitle: LunaText.subtitle(text: 'Backup Configuration Data'), - trailing: LunaIconButton(icon: Icons.upload_rounded), + return LunaBlock( + title: 'Backup to Device', + body: const [TextSpan(text: 'Backup Configuration Data')], + trailing: const LunaIconButton(icon: Icons.upload_rounded), onTap: () async => _backup(context), ); } diff --git a/lib/modules/settings/routes/system/widgets/restore_tile.dart b/lib/modules/settings/routes/system/widgets/restore_tile.dart index 42904c23..2bcd1792 100644 --- a/lib/modules/settings/routes/system/widgets/restore_tile.dart +++ b/lib/modules/settings/routes/system/widgets/restore_tile.dart @@ -10,11 +10,10 @@ class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Restore from Device'), - subtitle: LunaText.subtitle(text: 'Restore Configuration Data'), - trailing: LunaIconButton(icon: Icons.download_rounded), + return LunaBlock( + title: 'Restore from Device', + body: const [TextSpan(text: 'Restore Configuration Data')], + trailing: const LunaIconButton(icon: Icons.download_rounded), onTap: () async => _restore(context), ); } diff --git a/lib/modules/settings/routes/system_logs/route.dart b/lib/modules/settings/routes/system_logs/route.dart index 1dce4b02..5cc433b6 100644 --- a/lib/modules/settings/routes/system_logs/route.dart +++ b/lib/modules/settings/routes/system_logs/route.dart @@ -51,22 +51,19 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _body() { return LunaListView(controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'All Logs'), - subtitle: LunaText.subtitle(text: 'View Logs of All Types'), - trailing: LunaIconButton(icon: Icons.developer_mode_rounded), + LunaBlock( + title: 'All Logs', + body: const [TextSpan(text: 'View Logs of All Types')], + trailing: const LunaIconButton(icon: Icons.developer_mode_rounded), onTap: () async => _viewLogs('all'), ), ...List.generate( LunaLogType.values.length, (index) { if (LunaLogType.values[index].enabled) - return LunaListTile( - context: context, - title: LunaText.title(text: LunaLogType.values[index].name), - subtitle: LunaText.subtitle( - text: LunaLogType.values[index].description), + return LunaBlock( + title: LunaLogType.values[index].name, + body: [TextSpan(text: LunaLogType.values[index].description)], trailing: LunaIconButton(icon: LunaLogType.values[index].icon), onTap: () async => _viewLogs(LunaLogType.values[index].key), ); @@ -82,7 +79,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _clearLogs() { return LunaButton.text( text: 'Clear', - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, onTap: () async { bool result = await SettingsDialogs().clearLogs(context); @@ -101,7 +98,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return Builder( builder: (context) => LunaButton.text( text: 'Export', - icon: Icons.file_download_rounded, + icon: LunaIcons.DOWNLOAD, onTap: () async { showLunaInfoSnackBar( title: 'Exporting Logs', diff --git a/lib/modules/sonarr/core/api_controller.dart b/lib/modules/sonarr/core/api_controller.dart index 5a414316..8fc3f9ce 100644 --- a/lib/modules/sonarr/core/api_controller.dart +++ b/lib/modules/sonarr/core/api_controller.dart @@ -176,13 +176,14 @@ class SonarrAPIController { throw Exception('Series does not exist in catalogue'); } return series[seriesId].clone(); - }).then((series) { + }).then((series) async { series.seasons.forEach((seriesSeason) { if (seriesSeason.seasonNumber == season.seasonNumber) { seriesSeason.monitored = !seriesSeason.monitored; } }); - return context.read().api.series.update(series: series); + await context.read().api.series.update(series: series); + return series; }).then((series) { return context.read().setSingleSeries(series); }).then((series) { diff --git a/lib/modules/sonarr/core/database.dart b/lib/modules/sonarr/core/database.dart index 7b467eee..c68fe12d 100644 --- a/lib/modules/sonarr/core/database.dart +++ b/lib/modules/sonarr/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; @@ -121,7 +120,7 @@ class SonarrDatabase extends LunaModuleDatabase { extension SonarrDatabaseValueExtension on SonarrDatabaseValue { String get key { - return 'SONARR_${describeEnum(this)}'; + return 'SONARR_${this.name}'; } dynamic get data { diff --git a/lib/modules/sonarr/core/dialogs.dart b/lib/modules/sonarr/core/dialogs.dart index 4ceba7c3..ec71fb58 100644 --- a/lib/modules/sonarr/core/dialogs.dart +++ b/lib/modules/sonarr/core/dialogs.dart @@ -494,7 +494,9 @@ class SonarrDialogs { subtitle: LunaDialog.richText( children: [ LunaDialog.bolded( - text: folders[index].freeSpace.lunaBytesToString()) + text: folders[index].freeSpace.lunaBytesToString(), + fontSize: LunaDialog.BUTTON_SIZE, + ), ], ), icon: Icons.folder_rounded, @@ -785,7 +787,7 @@ class SonarrDialogs { ), Padding( padding: const EdgeInsets.symmetric( - vertical: LunaUI.DEFAULT_MARGIN_SIZE / 3, + vertical: LunaUI.DEFAULT_MARGIN_SIZE / 4, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -796,7 +798,7 @@ class SonarrDialogs { Expanded( child: Padding( padding: const EdgeInsets.only( - left: 56.0, // 32.0 + iconSize + left: 32.0 + LunaUI.ICON_SIZE, ), child: LunaDialog.richText( children: [ diff --git a/lib/modules/sonarr/core/extensions/sonarr_calendar.dart b/lib/modules/sonarr/core/extensions/sonarr_calendar.dart index 4ee19da3..6c9110fd 100644 --- a/lib/modules/sonarr/core/extensions/sonarr_calendar.dart +++ b/lib/modules/sonarr/core/extensions/sonarr_calendar.dart @@ -6,7 +6,7 @@ extension SonarrCalendarExtension on SonarrCalendar { 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()); + : DateFormat('hh:mm\na').format(this.airDateUtc.toLocal()); return LunaUI.TEXT_EMDASH; } diff --git a/lib/modules/sonarr/core/extensions/sonarr_series.dart b/lib/modules/sonarr/core/extensions/sonarr_series.dart index 10a2d9ae..db603ce5 100644 --- a/lib/modules/sonarr/core/extensions/sonarr_series.dart +++ b/lib/modules/sonarr/core/extensions/sonarr_series.dart @@ -56,6 +56,15 @@ extension SonarrSeriesExtension on SonarrSeries { ); } + String get lunaPreviousAiring { + if (this.previousAiring == null) return LunaUI.TEXT_EMDASH; + return this.previousAiring.lunaDateTimeReadable( + timeOnNewLine: false, + showSeconds: false, + sameLineDelimiter: '@', + ); + } + String get lunaDateAdded { if (this.added == null) { return 'lunasea.Unknown'.tr(); diff --git a/lib/modules/sonarr/core/state.dart b/lib/modules/sonarr/core/state.dart index 72857235..815e5ab6 100644 --- a/lib/modules/sonarr/core/state.dart +++ b/lib/modules/sonarr/core/state.dart @@ -125,7 +125,7 @@ class SonarrState extends LunaModuleState { Future> get series => _series; void fetchAllSeries() { if (_api != null) { - _series = _api.series.getAll().then((series) { + _series = _api.series.getAll(includeSeasonImages: true).then((series) { return { for (SonarrSeries s in series) s.id: s, }; @@ -137,7 +137,8 @@ class SonarrState extends LunaModuleState { Future fetchSeries(int seriesId) async { assert(seriesId != null); if (_api != null) { - SonarrSeries series = await _api.series.get(seriesId: seriesId); + SonarrSeries series = + await _api.series.get(seriesId: seriesId, includeSeasonImages: true); (await _series)[seriesId] = series; } notifyListeners(); diff --git a/lib/modules/sonarr/core/types/sorting_series.dart b/lib/modules/sonarr/core/types/sorting_series.dart index 5e669487..6457f917 100644 --- a/lib/modules/sonarr/core/types/sorting_series.dart +++ b/lib/modules/sonarr/core/types/sorting_series.dart @@ -15,6 +15,8 @@ enum SonarrSeriesSorting { NETWORK, @HiveField(4) NEXT_AIRING, + @HiveField(8) + PREVIOUS_AIRING, @HiveField(5) QUALITY, @HiveField(6) @@ -42,6 +44,8 @@ extension SonarrSeriesSortingExtension on SonarrSeriesSorting { return 'quality'; case SonarrSeriesSorting.NEXT_AIRING: return 'next_airing'; + case SonarrSeriesSorting.PREVIOUS_AIRING: + return 'previous_airing'; default: return null; } @@ -65,6 +69,8 @@ extension SonarrSeriesSortingExtension on SonarrSeriesSorting { return 'Quality Profile'; case SonarrSeriesSorting.NEXT_AIRING: return 'Next Airing'; + case SonarrSeriesSorting.PREVIOUS_AIRING: + return 'Previous Airing'; default: return null; } @@ -88,6 +94,8 @@ extension SonarrSeriesSortingExtension on SonarrSeriesSorting { return SonarrSeriesSorting.QUALITY; case 'next_airing': return SonarrSeriesSorting.NEXT_AIRING; + case 'previous_airing': + return SonarrSeriesSorting.PREVIOUS_AIRING; default: return null; } @@ -109,6 +117,8 @@ class _Sorter { return _network(data, ascending); case SonarrSeriesSorting.NEXT_AIRING: return _nextAiring(data, ascending); + case SonarrSeriesSorting.PREVIOUS_AIRING: + return _previousAiring(data, ascending); case SonarrSeriesSorting.SIZE: return _size(data, ascending); case SonarrSeriesSorting.TYPE: @@ -205,6 +215,33 @@ class _Sorter { return series; } + List _previousAiring( + List series, bool ascending) { + series.sort((a, b) { + if (a.previousAiring == null && b.previousAiring == null) { + if (a.status == 'ended' && b.status != 'ended') return 1; + if (b.status == 'ended' && a.status != 'ended') return -1; + return a.sortTitle.toLowerCase().compareTo(b.sortTitle.toLowerCase()); + } + if (ascending) { + if (a.previousAiring == null) return 1; + if (b.previousAiring == null) return -1; + int _comparison = a.previousAiring.compareTo(b.previousAiring); + return _comparison == 0 + ? a.sortTitle.toLowerCase().compareTo(b.sortTitle.toLowerCase()) + : _comparison; + } else { + if (b.previousAiring == null) return -1; + if (a.previousAiring == null) return 1; + int _comparison = b.previousAiring.compareTo(a.previousAiring); + return _comparison == 0 + ? a.sortTitle.toLowerCase().compareTo(b.sortTitle.toLowerCase()) + : _comparison; + } + }); + return series; + } + List _quality(List series, bool ascending) { series.sort((a, b) { int _comparison = ascending diff --git a/lib/modules/sonarr/core/types/sorting_series.g.dart b/lib/modules/sonarr/core/types/sorting_series.g.dart index 18730f32..b55af7ec 100644 --- a/lib/modules/sonarr/core/types/sorting_series.g.dart +++ b/lib/modules/sonarr/core/types/sorting_series.g.dart @@ -23,6 +23,8 @@ class SonarrSeriesSortingAdapter extends TypeAdapter { return SonarrSeriesSorting.NETWORK; case 4: return SonarrSeriesSorting.NEXT_AIRING; + case 8: + return SonarrSeriesSorting.PREVIOUS_AIRING; case 5: return SonarrSeriesSorting.QUALITY; case 6: @@ -52,6 +54,9 @@ class SonarrSeriesSortingAdapter extends TypeAdapter { case SonarrSeriesSorting.NEXT_AIRING: writer.writeByte(4); break; + case SonarrSeriesSorting.PREVIOUS_AIRING: + writer.writeByte(8); + break; case SonarrSeriesSorting.QUALITY: writer.writeByte(5); break; diff --git a/lib/modules/sonarr/routes/add_series/widgets/appbar.dart b/lib/modules/sonarr/routes/add_series/widgets/appbar.dart index 01bd49a4..fa656211 100644 --- a/lib/modules/sonarr/routes/add_series/widgets/appbar.dart +++ b/lib/modules/sonarr/routes/add_series/widgets/appbar.dart @@ -32,7 +32,7 @@ class _SearchBar extends StatefulWidget implements PreferredSizeWidget { @override Size get preferredSize => - const Size.fromHeight(LunaTextInputBar.appBarHeight); + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State<_SearchBar> createState() => _State(); @@ -70,7 +70,7 @@ class _State extends State<_SearchBar> { ), ], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ), ); } diff --git a/lib/modules/sonarr/routes/add_series/widgets/page_search.dart b/lib/modules/sonarr/routes/add_series/widgets/page_search.dart index 53084d48..b5a7a40e 100644 --- a/lib/modules/sonarr/routes/add_series/widgets/page_search.dart +++ b/lib/modules/sonarr/routes/add_series/widgets/page_search.dart @@ -92,6 +92,7 @@ class _State extends State ); return LunaListViewBuilder( controller: widget.scrollController, + itemExtent: SonarrSeriesAddSearchResultTile.extent, itemCount: results.length, itemBuilder: (context, index) { SonarrExclusion exclusion = exclusions?.firstWhere( diff --git a/lib/modules/sonarr/routes/add_series/widgets/search_results_tile.dart b/lib/modules/sonarr/routes/add_series/widgets/search_results_tile.dart index 472703b8..2210e875 100644 --- a/lib/modules/sonarr/routes/add_series/widgets/search_results_tile.dart +++ b/lib/modules/sonarr/routes/add_series/widgets/search_results_tile.dart @@ -3,6 +3,12 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; class SonarrSeriesAddSearchResultTile extends StatefulWidget { + static final double extent = LunaBlock.calculateItemExtent( + 1, + hasBottom: true, + bottomHeight: LunaBlock.SUBTITLE_HEIGHT * 2, + ); + final SonarrSeries series; final bool onTapShowOverview; final bool exists; @@ -23,17 +29,17 @@ class SonarrSeriesAddSearchResultTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( + return LunaBlock( backgroundUrl: widget.series.remotePoster, posterUrl: widget.series.remotePoster, posterHeaders: context.watch().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, title: widget.series.title, - darken: widget.exists, titleColor: widget.isExcluded ? LunaColours.red : Colors.white, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle2MaxLines: 2, + disabled: widget.exists, + body: [_subtitle1()], + bottom: _subtitle2(), + bottomHeight: LunaBlock.SUBTITLE_HEIGHT * 2, onTap: _onTap, onLongPress: _onLongPress, ); @@ -49,14 +55,25 @@ class _State extends State { ]); } - TextSpan _subtitle2() => TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - fontStyle: FontStyle.italic, - color: widget.exists ? Colors.white30 : Colors.white70, + Widget _subtitle2() { + return SizedBox( + height: LunaBlock.SUBTITLE_HEIGHT * 2, + child: RichText( + text: TextSpan( + style: const TextStyle( + fontStyle: FontStyle.italic, + fontSize: LunaUI.FONT_SIZE_H3, + color: LunaColours.grey, + ), + children: [ + LunaTextSpan.extended(text: widget.series.lunaOverview), + ], ), - text: '${widget.series.lunaOverview}\n', - ); + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ); + } Future _onTap() async { if (widget.onTapShowOverview) { diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_language_profile.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_language_profile.dart index 6f0997e5..7a9ac694 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_language_profile.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_language_profile.dart @@ -9,16 +9,18 @@ class SonarrSeriesAddDetailsLanguageProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.LanguageProfile'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.LanguageProfile'.tr(), + body: [ + TextSpan( text: context .watch() .languageProfile ?.name ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_monitor.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_monitor.dart index f626bf05..48adcd88 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_monitor.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_monitor.dart @@ -9,17 +9,18 @@ class SonarrSeriesAddDetailsMonitorTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Monitor'.tr()), - subtitle: LunaText.subtitle( - text: context - .watch() - .monitorType - ?.lunaName ?? - LunaUI.TEXT_EMDASH, - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'sonarr.Monitor'.tr(), + body: [ + TextSpan( + text: context + .watch() + .monitorType + ?.lunaName ?? + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_quality_profile.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_quality_profile.dart index 398514a5..7458746d 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_quality_profile.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_quality_profile.dart @@ -9,16 +9,18 @@ class SonarrSeriesAddDetailsQualityProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.QualityProfile'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.QualityProfile'.tr(), + body: [ + TextSpan( text: context .watch() .qualityProfile ?.name ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_root_folder.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_root_folder.dart index bdf2c265..5bf57ddb 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_root_folder.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_root_folder.dart @@ -9,13 +9,15 @@ class SonarrSeriesAddDetailsRootFolderTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.RootFolder'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.RootFolder'.tr(), + body: [ + TextSpan( text: context.watch().rootFolder?.path ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_series_type.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_series_type.dart index e112471c..00e139f2 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_series_type.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_series_type.dart @@ -9,17 +9,19 @@ class SonarrSeriesAddDetailsSeriesTypeTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.SeriesType'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.SeriesType'.tr(), + body: [ + TextSpan( text: context .watch() .seriesType ?.value ?.lunaCapitalizeFirstLetters() ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_tags.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_tags.dart index ae50c64e..02ff6a03 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_tags.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_tags.dart @@ -9,18 +9,20 @@ class SonarrSeriesAddDetailsTagsTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Tags'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.Tags'.tr(), + body: [ + TextSpan( text: context.watch().tags.isEmpty ? LunaUI.TEXT_EMDASH : context .watch() .tags .map((e) => e.label) - .join(', ')), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + .join(', '), + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => await SonarrDialogs().setAddTags(context), ); } diff --git a/lib/modules/sonarr/routes/add_series_details/widgets/tile_use_season_folders.dart b/lib/modules/sonarr/routes/add_series_details/widgets/tile_use_season_folders.dart index 546dcdc2..6e2fe296 100644 --- a/lib/modules/sonarr/routes/add_series_details/widgets/tile_use_season_folders.dart +++ b/lib/modules/sonarr/routes/add_series_details/widgets/tile_use_season_folders.dart @@ -9,14 +9,8 @@ class SonarrSeriesAddDetailsUseSeasonFoldersTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title( - text: 'sonarr.SeasonFolders'.tr(), - ), - subtitle: LunaText.subtitle( - text: 'sonarr.SeasonFoldersDescription'.tr(), - ), + return LunaBlock( + title: 'sonarr.SeasonFolders'.tr(), trailing: LunaSwitch( value: context.watch().useSeasonFolders, onChanged: (value) { diff --git a/lib/modules/sonarr/routes/catalogue/route.dart b/lib/modules/sonarr/routes/catalogue/route.dart index 05f9c676..cc4baa54 100644 --- a/lib/modules/sonarr/routes/catalogue/route.dart +++ b/lib/modules/sonarr/routes/catalogue/route.dart @@ -51,7 +51,7 @@ class _State extends State child: SonarrSeriesSearchBar( scrollController: SonarrNavigationBar.scrollControllers[0], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ); } diff --git a/lib/modules/sonarr/routes/catalogue/widgets/search_bar_filter_button.dart b/lib/modules/sonarr/routes/catalogue/widgets/search_bar_filter_button.dart index 0d67d3c1..3c1dc2cd 100644 --- a/lib/modules/sonarr/routes/catalogue/widgets/search_bar_filter_button.dart +++ b/lib/modules/sonarr/routes/catalogue/widgets/search_bar_filter_button.dart @@ -35,7 +35,7 @@ class _State extends State { child: Text( SonarrSeriesFilter.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.seriesFilterType == SonarrSeriesFilter.values[index] ? LunaColours.accent @@ -46,8 +46,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.symmetric(horizontal: 12.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/catalogue/widgets/search_bar_sort_button.dart b/lib/modules/sonarr/routes/catalogue/widgets/search_bar_sort_button.dart index 4f8d9398..3acab1a7 100644 --- a/lib/modules/sonarr/routes/catalogue/widgets/search_bar_sort_button.dart +++ b/lib/modules/sonarr/routes/catalogue/widgets/search_bar_sort_button.dart @@ -43,7 +43,7 @@ class _State extends State { Text( SonarrSeriesSorting.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.seriesSortType == SonarrSeriesSorting.values[index] ? LunaColours.accent @@ -56,7 +56,7 @@ class _State extends State { state.seriesSortAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], @@ -65,8 +65,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: EdgeInsets.zero, color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/catalogue/widgets/series_tile.dart b/lib/modules/sonarr/routes/catalogue/widgets/series_tile.dart index a9c765c3..6dfe4a6b 100644 --- a/lib/modules/sonarr/routes/catalogue/widgets/series_tile.dart +++ b/lib/modules/sonarr/routes/catalogue/widgets/series_tile.dart @@ -3,8 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; class SonarrSeriesTile extends StatefulWidget { - static final itemExtent = - LunaFourLineCardWithPoster.itemExtent + LunaUI.MARGIN_CARD.vertical; + static final itemExtent = LunaBlock.calculateItemExtent(3); final SonarrSeries series; final SonarrQualityProfile profile; @@ -24,17 +23,19 @@ class _State extends State { Widget build(BuildContext context) { return Selector>>( selector: (_, state) => state.series, - builder: (context, series, _) => LunaFourLineCardWithPoster( + builder: (context, series, _) => LunaBlock( backgroundUrl: - context.read().getPosterURL(widget.series.id), + context.read().getFanartURL(widget.series.id), posterUrl: context.read().getPosterURL(widget.series.id), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, - darken: !widget.series.monitored, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + disabled: !widget.series.monitored, title: widget.series.title, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle3: _subtitle3(), + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], onTap: _onTap, onLongPress: _onLongPress, ), @@ -43,13 +44,13 @@ class _State extends State { TextSpan _buildChildTextSpan(String text, SonarrSeriesSorting sorting) { TextStyle style; - if (context.read().seriesSortType == sorting) - style = TextStyle( - color: widget.series.monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30), + if (context.read().seriesSortType == sorting) { + style = const TextStyle( + color: LunaColours.accent, fontWeight: LunaUI.FONT_WEIGHT_BOLD, + fontSize: LunaUI.FONT_SIZE_H3, ); + } return TextSpan( text: text, style: style, @@ -91,6 +92,7 @@ class _State extends State { } TextSpan _subtitle3() { + SonarrSeriesSorting _sorting = context.read().seriesSortType; return TextSpan( children: [ _buildChildTextSpan( @@ -98,14 +100,18 @@ class _State extends State { SonarrSeriesSorting.NETWORK, ), TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), - if (context.read().seriesSortType == - SonarrSeriesSorting.DATE_ADDED) + if (_sorting == SonarrSeriesSorting.DATE_ADDED) _buildChildTextSpan( widget.series.lunaDateAdded, SonarrSeriesSorting.DATE_ADDED, ), - if (context.read().seriesSortType != - SonarrSeriesSorting.DATE_ADDED) + if (_sorting == SonarrSeriesSorting.PREVIOUS_AIRING) + _buildChildTextSpan( + widget.series.lunaPreviousAiring, + SonarrSeriesSorting.PREVIOUS_AIRING, + ), + if (_sorting != SonarrSeriesSorting.DATE_ADDED && + _sorting != SonarrSeriesSorting.PREVIOUS_AIRING) _buildChildTextSpan( widget.series.lunaNextAiringLine, SonarrSeriesSorting.NEXT_AIRING, diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_language_profile.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_language_profile.dart index 25935610..0e455bcf 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_language_profile.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_language_profile.dart @@ -12,12 +12,14 @@ class SonarrSeriesEditLanguageProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.LanguageProfile'.tr()), - subtitle: LunaText.subtitle( - text: context.watch().languageProfile.name), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'sonarr.LanguageProfile'.tr(), + body: [ + TextSpan( + text: context.watch().languageProfile.name, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_monitored.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_monitored.dart index 8402b811..9a8114dc 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_monitored.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_monitored.dart @@ -9,10 +9,8 @@ class SonarrSeriesEditMonitoredTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Monitored'.tr()), - subtitle: LunaText.subtitle(text: 'sonarr.MonitoredDescription'.tr()), + return LunaBlock( + title: 'sonarr.Monitored'.tr(), trailing: LunaSwitch( value: context.watch().monitored, onChanged: (value) => diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_quality_profile.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_quality_profile.dart index 760e748c..00ed00dc 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_quality_profile.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_quality_profile.dart @@ -12,13 +12,15 @@ class SonarrSeriesEditQualityProfileTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.QualityProfile'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.QualityProfile'.tr(), + body: [ + TextSpan( text: context.watch().qualityProfile?.name ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ) + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_series_path.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_series_path.dart index 8ac5d162..644317e1 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_series_path.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_series_path.dart @@ -9,13 +9,15 @@ class SonarrSeriesEditSeriesPathTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.SeriesPath'.tr()), - subtitle: LunaText.subtitle( + return LunaBlock( + title: 'sonarr.SeriesPath'.tr(), + body: [ + TextSpan( text: context.watch().seriesPath ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + LunaUI.TEXT_EMDASH, + ), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_series_type.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_series_type.dart index 672b1e29..5b40c610 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_series_type.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_series_type.dart @@ -3,21 +3,24 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; class SonarrSeriesEditSeriesTypeTile extends StatelessWidget { - const SonarrSeriesEditSeriesTypeTile({Key key,}) : super(key: key); + const SonarrSeriesEditSeriesTypeTile({ + Key key, + }) : super(key: key); @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.SeriesType'.tr()), - subtitle: LunaText.subtitle( - text: context - .watch() - .seriesType - .value - ?.lunaCapitalizeFirstLetters() ?? - LunaUI.TEXT_EMDASH), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'sonarr.SeriesType'.tr(), + body: [ + TextSpan( + text: context + .watch() + .seriesType + .value + ?.lunaCapitalizeFirstLetters() ?? + LunaUI.TEXT_EMDASH), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => _onTap(context), ); } diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_tags.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_tags.dart index 0615ddab..9732aa9c 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_tags.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_tags.dart @@ -9,19 +9,20 @@ class SonarrSeriesEditTagsTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Tags'.tr()), - subtitle: LunaText.subtitle( - text: (context.watch().tags?.isEmpty ?? true) - ? 'lunasea.NotSet'.tr() - : context - .watch() - .tags - .map((e) => e.label) - .join(', '), - ), - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'sonarr.Tags'.tr(), + body: [ + TextSpan( + text: (context.watch().tags?.isEmpty ?? true) + ? 'lunasea.NotSet'.tr() + : context + .watch() + .tags + .map((e) => e.label) + .join(', '), + ) + ], + trailing: const LunaIconButton.arrow(), onTap: () async => await SonarrDialogs().setEditTags(context), ); } diff --git a/lib/modules/sonarr/routes/edit_series/widgets/tile_use_season_folders.dart b/lib/modules/sonarr/routes/edit_series/widgets/tile_use_season_folders.dart index a3305541..48de388f 100644 --- a/lib/modules/sonarr/routes/edit_series/widgets/tile_use_season_folders.dart +++ b/lib/modules/sonarr/routes/edit_series/widgets/tile_use_season_folders.dart @@ -9,15 +9,13 @@ class SonarrSeriesEditSeasonFoldersTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.UseSeasonFolders'.tr()), - subtitle: - LunaText.subtitle(text: 'sonarr.UseSeasonFoldersDescription'.tr()), + return LunaBlock( + title: 'sonarr.UseSeasonFolders'.tr(), trailing: LunaSwitch( value: context.watch().useSeasonFolders, - onChanged: (value) => - context.read().useSeasonFolders = value, + onChanged: (value) { + context.read().useSeasonFolders = value; + }, ), ); } diff --git a/lib/modules/sonarr/routes/missing/widgets/missing_tile.dart b/lib/modules/sonarr/routes/missing/widgets/missing_tile.dart index ea21f451..412a3864 100644 --- a/lib/modules/sonarr/routes/missing/widgets/missing_tile.dart +++ b/lib/modules/sonarr/routes/missing/widgets/missing_tile.dart @@ -3,8 +3,7 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/sonarr.dart'; class SonarrMissingTile extends StatefulWidget { - static final itemExtent = - LunaFourLineCardWithPoster.itemExtent + LunaUI.MARGIN_CARD.vertical; + static final itemExtent = LunaBlock.calculateItemExtent(3); final SonarrMissingRecord record; final SonarrSeries series; @@ -22,20 +21,22 @@ class SonarrMissingTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( + return LunaBlock( backgroundUrl: - context.read().getBannerURL(widget.record.seriesId), + context.read().getFanartURL(widget.record.seriesId), posterUrl: context.read().getPosterURL(widget.record.seriesId), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, title: widget.record?.series?.title ?? widget.series?.title ?? LunaUI.TEXT_EMDASH, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle3: _subtitle3(), - darken: !widget.record.monitored, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + disabled: !widget.record.monitored, onTap: _onTap, onLongPress: _onLongPress, trailing: _trailing(), @@ -52,42 +53,30 @@ class _State extends State { TextSpan _subtitle1() { return TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: widget.record.monitored ? Colors.white70 : Colors.white30, - ), children: [ TextSpan( text: widget.record.seasonNumber == 0 - ? 'Specials ' - : 'Season ${widget.record.seasonNumber} '), - const TextSpan(text: LunaUI.TEXT_EMDASH), - TextSpan(text: ' Episode ${widget.record.episodeNumber}'), + ? 'Specials' + : 'Season ${widget.record.seasonNumber}'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: 'Episode ${widget.record.episodeNumber}'), ], ); } TextSpan _subtitle2() { return TextSpan( - style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - color: widget.record.monitored ? Colors.white70 : Colors.white30, + style: const TextStyle( + fontStyle: FontStyle.italic, ), - children: [ - TextSpan( - style: const TextStyle( - fontStyle: FontStyle.italic, - ), - text: widget.record.title ?? 'Unknown Title', - ), - ], + text: widget.record.title ?? 'lunasea.Unknown'.tr(), ); } TextSpan _subtitle3() { return TextSpan( style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: LunaColours.red, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), diff --git a/lib/modules/sonarr/routes/more/route.dart b/lib/modules/sonarr/routes/more/route.dart index eca1377c..0a0b4864 100644 --- a/lib/modules/sonarr/routes/more/route.dart +++ b/lib/modules/sonarr/routes/more/route.dart @@ -26,6 +26,7 @@ class _State extends State with AutomaticKeepAliveClientMixin { ); } + // ignore: unused_element Future _showComingSoonMessage() async { showLunaInfoSnackBar( title: 'lunasea.ComingSoon'.tr(), @@ -37,55 +38,48 @@ class _State extends State with AutomaticKeepAliveClientMixin { return LunaListView( controller: SonarrNavigationBar.scrollControllers[3], children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.History'.tr()), - subtitle: LunaText.subtitle(text: 'sonarr.HistoryDescription'.tr()), + LunaBlock( + title: 'sonarr.History'.tr(), + body: [TextSpan(text: 'sonarr.HistoryDescription'.tr())], trailing: LunaIconButton( icon: Icons.history_rounded, color: LunaColours().byListIndex(0), ), onTap: () async => SonarrHistoryRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.ManualImport'.tr()), - subtitle: - LunaText.subtitle(text: 'sonarr.ManualImportDescription'.tr()), - trailing: LunaIconButton( - icon: Icons.download_done_rounded, - color: LunaColours().byListIndex(1), - ), - onTap: () async => _showComingSoonMessage(), - ), - LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Queue'.tr()), - subtitle: LunaText.subtitle(text: 'sonarr.QueueDescription'.tr()), + // LunaBlock( + // title: 'sonarr.ManualImport'.tr(), + // body: [TextSpan(text: 'sonarr.ManualImportDescription'.tr())], + // trailing: LunaIconButton( + // icon: Icons.download_done_rounded, + // color: LunaColours().byListIndex(1), + // ), + // onTap: () async => _showComingSoonMessage(), + // ), + LunaBlock( + title: 'sonarr.Queue'.tr(), + body: [TextSpan(text: 'sonarr.QueueDescription'.tr())], trailing: LunaIconButton( icon: Icons.queue_rounded, - color: LunaColours().byListIndex(2), + color: LunaColours().byListIndex(1), ), onTap: () async => SonarrQueueRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.SystemStatus'.tr()), - subtitle: - LunaText.subtitle(text: 'sonarr.SystemStatusDescription'.tr()), - trailing: LunaIconButton( - icon: Icons.computer_rounded, - color: LunaColours().byListIndex(3), - ), - onTap: () async => _showComingSoonMessage(), - ), - LunaListTile( - context: context, - title: LunaText.title(text: 'sonarr.Tags'.tr()), - subtitle: LunaText.subtitle(text: 'sonarr.TagsDescription'.tr()), + // LunaBlock( + // title: 'sonarr.SystemStatus'.tr(), + // body: [TextSpan(text: 'sonarr.SystemStatusDescription'.tr())], + // trailing: LunaIconButton( + // icon: Icons.computer_rounded, + // color: LunaColours().byListIndex(3), + // ), + // onTap: () async => _showComingSoonMessage(), + // ), + LunaBlock( + title: 'sonarr.Tags'.tr(), + body: [TextSpan(text: 'sonarr.TagsDescription'.tr())], trailing: LunaIconButton( icon: Icons.style_rounded, - color: LunaColours().byListIndex(4), + color: LunaColours().byListIndex(2), ), onTap: () async => SonarrTagsRouter().navigateTo(context), ), diff --git a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart index 3375481b..762ea21f 100644 --- a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart +++ b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart @@ -155,7 +155,7 @@ class _State extends State { body: widget.queueRecord.episode?.title ?? LunaUI.TEXT_EMDASH, ), if (widget.type == SonarrQueueTileType.ALL) - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent( title: 'sonarr.Quality'.tr(), body: widget.queueRecord.quality?.quality?.name ?? LunaUI.TEXT_EMDASH, diff --git a/lib/modules/sonarr/routes/releases/widgets/search_bar.dart b/lib/modules/sonarr/routes/releases/widgets/search_bar.dart index e2881eb6..258ae7fe 100644 --- a/lib/modules/sonarr/routes/releases/widgets/search_bar.dart +++ b/lib/modules/sonarr/routes/releases/widgets/search_bar.dart @@ -12,7 +12,8 @@ class SonarrReleasesSearchBar extends StatefulWidget }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State createState() => _State(); diff --git a/lib/modules/sonarr/routes/releases/widgets/search_bar_filter_button.dart b/lib/modules/sonarr/routes/releases/widgets/search_bar_filter_button.dart index 91848676..b6913ccd 100644 --- a/lib/modules/sonarr/routes/releases/widgets/search_bar_filter_button.dart +++ b/lib/modules/sonarr/routes/releases/widgets/search_bar_filter_button.dart @@ -36,7 +36,7 @@ class _State extends State { child: Text( SonarrReleasesFilter.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.filterType == SonarrReleasesFilter.values[index] ? LunaColours.accent : Colors.white, @@ -46,8 +46,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 14.0), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/releases/widgets/search_bar_sort_button.dart b/lib/modules/sonarr/routes/releases/widgets/search_bar_sort_button.dart index c4db9efa..42735950 100644 --- a/lib/modules/sonarr/routes/releases/widgets/search_bar_sort_button.dart +++ b/lib/modules/sonarr/routes/releases/widgets/search_bar_sort_button.dart @@ -44,7 +44,7 @@ class _State extends State { Text( SonarrReleasesSorting.values[index].readable, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: state.sortType == SonarrReleasesSorting.values[index] ? LunaColours.accent @@ -56,7 +56,7 @@ class _State extends State { state.sortAscending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE + 2.0, + size: LunaUI.FONT_SIZE_H2, color: LunaColours.accent, ), ], @@ -65,8 +65,8 @@ class _State extends State { ), ), ), - height: LunaTextInputBar.appBarInnerHeight, - width: LunaTextInputBar.appBarInnerHeight, + height: LunaTextInputBar.defaultHeight, + width: LunaTextInputBar.defaultHeight, margin: const EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 13.5), color: Theme.of(context).canvasColor, ); diff --git a/lib/modules/sonarr/routes/season_details/sheets/episode_details.dart b/lib/modules/sonarr/routes/season_details/sheets/episode_details.dart index f654b462..9db5f1cc 100644 --- a/lib/modules/sonarr/routes/season_details/sheets/episode_details.dart +++ b/lib/modules/sonarr/routes/season_details/sheets/episode_details.dart @@ -68,16 +68,15 @@ class SonarrEpisodeDetailsSheet extends LunaBottomModalSheet { text: 'sonarr.Missing'.tr(), ), ]; + if (_nodes.isEmpty) return const SizedBox(height: 0, width: 0); return Padding( child: Wrap( direction: Axis.horizontal, - spacing: 6.0, - runSpacing: 6.0, + spacing: LunaUI.DEFAULT_MARGIN_SIZE / 2, + runSpacing: LunaUI.DEFAULT_MARGIN_SIZE / 2, children: _nodes, ), - padding: const EdgeInsets.symmetric( - horizontal: LunaUI.DEFAULT_MARGIN_SIZE, - ), + padding: LunaUI.MARGIN_H_DEFAULT_V_HALF.copyWith(top: 0), ); } @@ -103,9 +102,7 @@ class SonarrEpisodeDetailsSheet extends LunaBottomModalSheet { ), _highlightedNodes(context), Padding( - padding: LunaUI.MARGIN_CARD.copyWith( - top: LunaUI.DEFAULT_MARGIN_SIZE, - ), + padding: LunaUI.MARGIN_DEFAULT_HORIZONTAL, child: LunaText.subtitle( text: episode.overview ?? 'sonarr.NoSummaryAvailable'.tr(), maxLines: 0, @@ -153,7 +150,7 @@ class SonarrEpisodeDetailsSheet extends LunaBottomModalSheet { icon: Icons.info_outline_rounded, onTap: () async => SonarrMediaInfoSheet(mediaInfo: episodeFile.mediaInfo) - .showModal(context: context), + .show(context: context), ), LunaButton( type: LunaButtonType.TEXT, diff --git a/lib/modules/sonarr/routes/season_details/widgets/episode_tile.dart b/lib/modules/sonarr/routes/season_details/widgets/episode_tile.dart index 25e7de93..9f81881b 100644 --- a/lib/modules/sonarr/routes/season_details/widgets/episode_tile.dart +++ b/lib/modules/sonarr/routes/season_details/widgets/episode_tile.dart @@ -23,16 +23,12 @@ class _State extends State { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title( - text: widget.episode.title, - darken: !widget.episode.monitored, - ), - subtitle: _collapsedSubtitle(), - leading: _leading(context), - trailing: _trailing(context), - contentPadding: true, + return LunaBlock( + disabled: !widget.episode.monitored, + title: widget.episode.title, + body: _body(), + leading: _leading(), + trailing: _trailing(), onTap: _onTap, onLongPress: _onLongPress, ); @@ -44,7 +40,7 @@ class _State extends State { episode: widget.episode, episodeFile: widget.episodeFile, queueRecords: widget.queueRecords, - ).showModal(context: context); + ).show(context: context); } Future _onLongPress() async { @@ -59,66 +55,43 @@ class _State extends State { } } - Widget _collapsedSubtitle() { - return RichText( - text: TextSpan( - style: TextStyle( - color: widget.episode.monitored ? Colors.white70 : Colors.white30, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + List _body() { + return [ + TextSpan(text: widget.episode.lunaAirDate()), + TextSpan( + text: widget.episode.lunaDownloadedQuality( + widget.episodeFile, + widget.queueRecords?.isNotEmpty ?? false + ? widget.queueRecords.first + : null, ), - children: [ - TextSpan(text: widget.episode.lunaAirDate()), - const TextSpan(text: '\n'), - TextSpan( - text: widget.episode.lunaDownloadedQuality( - widget.episodeFile, - widget.queueRecords?.isNotEmpty ?? false - ? widget.queueRecords.first - : null, - ), - style: TextStyle( - color: widget.episode.monitored - ? widget.episode.lunaDownloadedQualityColor( - widget.episodeFile, - widget.queueRecords?.isNotEmpty ?? false - ? widget.queueRecords.first - : null, - ) - : widget.episode - .lunaDownloadedQualityColor( - widget.episodeFile, - widget.queueRecords?.isNotEmpty ?? false - ? widget.queueRecords.first - : null, - ) - .withOpacity(0.30), - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), + style: TextStyle( + color: widget.episode.lunaDownloadedQualityColor( + widget.episodeFile, + widget.queueRecords?.isNotEmpty ?? false + ? widget.queueRecords.first + : null, ), - ], + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), ), - maxLines: 2, - softWrap: false, - overflow: TextOverflow.fade, - ); + ]; } - Widget _leading(BuildContext context) { + Widget _leading() { return LunaIconButton( text: widget.episode.episodeNumber.toString(), - textSize: LunaUI.FONT_SIZE_BUTTON, - color: widget.episode.monitored ? Colors.white : Colors.white30, + textSize: LunaUI.FONT_SIZE_H4, ); } - Widget _trailing(BuildContext context) { + Widget _trailing() { Future setLoadingState(LunaLoadingState state) async { if (this.mounted) setState(() => _loadingState = state); } return LunaIconButton( icon: Icons.search_rounded, - color: widget.episode.monitored ? Colors.white : Colors.white30, loadingState: _loadingState, onPressed: () async { setLoadingState(LunaLoadingState.ACTIVE); diff --git a/lib/modules/sonarr/routes/series_details/widgets/overview_series_description_tile.dart b/lib/modules/sonarr/routes/series_details/widgets/overview_series_description_tile.dart index 73156545..1f500738 100644 --- a/lib/modules/sonarr/routes/series_details/widgets/overview_series_description_tile.dart +++ b/lib/modules/sonarr/routes/series_details/widgets/overview_series_description_tile.dart @@ -12,18 +12,20 @@ class SonarrSeriesDetailsOverviewDescriptionTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( - posterPlaceholder: LunaAssets.blankVideo, + return LunaBlock( + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, backgroundUrl: context.read().getFanartURL(series.id), posterUrl: context.read().getPosterURL(series.id), posterHeaders: context.read().headers, title: series.title, - subtitle1: TextSpan( - text: series.overview == null || series.overview.isEmpty - ? 'No summary is available.\n\n\n' - : '${series.overview}\n\n\n', - ), - subtitle1MaxLines: 3, + body: [ + LunaTextSpan.extended( + text: series.overview == null || series.overview.isEmpty + ? 'sonarr.NoSummaryAvailable'.tr() + : series.overview, + ), + ], + customBodyMaxLines: 3, onTap: () async => LunaDialogs().textPreview( context, series.title, diff --git a/lib/modules/sonarr/routes/series_details/widgets/overview_series_information_block.dart.dart b/lib/modules/sonarr/routes/series_details/widgets/overview_series_information_block.dart.dart index b2b231be..4c1eb5bc 100644 --- a/lib/modules/sonarr/routes/series_details/widgets/overview_series_information_block.dart.dart +++ b/lib/modules/sonarr/routes/series_details/widgets/overview_series_information_block.dart.dart @@ -44,7 +44,7 @@ class SonarrSeriesDetailsOverviewInformationBlock extends StatelessWidget { title: 'tags', body: series?.lunaTags(tags), ), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent( title: 'status', body: series?.status?.lunaCapitalizeFirstLetters(), @@ -57,7 +57,7 @@ class SonarrSeriesDetailsOverviewInformationBlock extends StatelessWidget { title: 'added on', body: series?.lunaDateAdded, ), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent( title: 'year', body: series?.lunaYear, diff --git a/lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart b/lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart index a1379070..5cc6b1d9 100644 --- a/lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart +++ b/lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart @@ -12,27 +12,14 @@ class SonarrSeriesDetailsSeasonAllTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title( - text: 'sonarr.AllSeasons'.tr(), - darken: !series.monitored, - ), - subtitle: RichText( - text: TextSpan( - style: TextStyle( - color: series.monitored ? Colors.white70 : Colors.white30, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - _subtitle1(), - const TextSpan(text: '\n'), - _subtitle2(), - ], - ), - ), - contentPadding: true, - trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), + return LunaBlock( + title: 'sonarr.AllSeasons'.tr(), + disabled: !series.monitored, + body: [ + _subtitle1(), + _subtitle2(), + ], + trailing: const LunaIconButton.arrow(), onTap: () async => SonarrSeasonDetailsRouter().navigateTo( context, seriesId: series.id, @@ -52,12 +39,8 @@ class SonarrSeriesDetailsSeasonAllTile extends StatelessWidget { return TextSpan( style: TextStyle( color: series.lunaPercentageComplete == 100 - ? series.monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30) - : series.monitored - ? LunaColours.red - : LunaColours.red.withOpacity(0.30), + ? LunaColours.accent + : LunaColours.red, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), text: [ diff --git a/lib/modules/sonarr/routes/series_details/widgets/season_tile.dart b/lib/modules/sonarr/routes/series_details/widgets/season_tile.dart index 4ff3b1ee..4132be4a 100644 --- a/lib/modules/sonarr/routes/series_details/widgets/season_tile.dart +++ b/lib/modules/sonarr/routes/series_details/widgets/season_tile.dart @@ -24,46 +24,55 @@ class _State extends State { @override Widget build(BuildContext context) { if (widget.season == null) return const SizedBox(height: 0.0); - return LunaListTile( - context: context, - title: LunaText.title( - text: widget.season.lunaTitle, - darken: !widget.season.monitored, - ), - subtitle: RichText( - text: TextSpan( - style: TextStyle( - color: widget.season.monitored ? Colors.white70 : Colors.white30, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - _subtitle1(), - const TextSpan(text: '\n'), - _subtitle2(), - ], - ), - ), - trailing: _trailing(context), - onTap: () async => SonarrSeasonDetailsRouter().navigateTo( - context, - seriesId: widget.seriesId, - seasonNumber: widget.season.seasonNumber, - ), - onLongPress: () async { - Tuple2 result = await SonarrDialogs() - .seasonSettings(context, widget.season.seasonNumber); - if (result.item1) - result.item2.execute( - context, - widget.seriesId, - widget.season.seasonNumber, - ); - }, - contentPadding: true, + return LunaBlock( + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + posterUrl: widget.season?.images + ?.firstWhere((e) => e.coverType == 'poster', orElse: () => null) + ?.url ?? + '', + posterHeaders: context.read().headers, + title: widget.season.lunaTitle, + disabled: !widget.season.monitored, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + trailing: _trailing(), + onTap: _onTap, + onLongPress: _onLongPress, ); } + Future _onTap() async => SonarrSeasonDetailsRouter().navigateTo( + context, + seriesId: widget.seriesId, + seasonNumber: widget.season.seasonNumber, + ); + + Future _onLongPress() async { + Tuple2 result = await SonarrDialogs() + .seasonSettings(context, widget.season.seasonNumber); + if (result.item1) + result.item2.execute( + context, + widget.seriesId, + widget.season.seasonNumber, + ); + } + TextSpan _subtitle1() { + return TextSpan( + text: widget.season.statistics?.previousAiring?.lunaDateTimeReadable( + timeOnNewLine: false, + showSeconds: false, + sameLineDelimiter: '@', + ) ?? + LunaUI.TEXT_EMDASH, + ); + } + + TextSpan _subtitle2() { return TextSpan( text: widget.season?.statistics?.sizeOnDisk ?.lunaBytesToString(decimals: 1) ?? @@ -71,16 +80,12 @@ class _State extends State { ); } - TextSpan _subtitle2() { + TextSpan _subtitle3() { return TextSpan( style: TextStyle( color: widget.season.lunaPercentageComplete == 100 - ? widget.season.monitored - ? LunaColours.accent - : LunaColours.accent.withOpacity(0.30) - : widget.season.monitored - ? LunaColours.red - : LunaColours.red.withOpacity(0.30), + ? LunaColours.accent + : LunaColours.red, fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), text: [ @@ -92,7 +97,7 @@ class _State extends State { ); } - Widget _trailing(BuildContext context) { + Widget _trailing() { Future setLoadingState(LunaLoadingState state) async { if (this.mounted) setState(() => _loadingState = state); } @@ -101,7 +106,7 @@ class _State extends State { icon: widget.season.monitored ? Icons.turned_in_rounded : Icons.turned_in_not_rounded, - color: widget.season.monitored ? Colors.white : Colors.white30, + color: LunaColours.white, loadingState: _loadingState, onPressed: () async { setLoadingState(LunaLoadingState.ACTIVE); diff --git a/lib/modules/sonarr/routes/tags/widgets/tag_tile.dart b/lib/modules/sonarr/routes/tags/widgets/tag_tile.dart index aac4b7b4..66e7eb9b 100644 --- a/lib/modules/sonarr/routes/tags/widgets/tag_tile.dart +++ b/lib/modules/sonarr/routes/tags/widgets/tag_tile.dart @@ -39,14 +39,13 @@ class _State extends State with LunaLoadCallbackMixin { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: widget.tag.label), - subtitle: LunaText.subtitle(text: subtitle()), + return LunaBlock( + title: widget.tag.label, + body: [TextSpan(text: subtitle())], trailing: (seriesList?.isNotEmpty ?? true) ? null : LunaIconButton( - icon: Icons.delete_rounded, + icon: LunaIcons.DELETE, color: LunaColours.red, onPressed: _handleDelete, ), diff --git a/lib/modules/sonarr/routes/upcoming/route.dart b/lib/modules/sonarr/routes/upcoming/route.dart index 6868f9ae..39dc8776 100644 --- a/lib/modules/sonarr/routes/upcoming/route.dart +++ b/lib/modules/sonarr/routes/upcoming/route.dart @@ -117,7 +117,10 @@ class _State extends State Map series, ) => [ - LunaHeader(text: date), + LunaHeader( + text: date, + // subtitle: 'This is a test', + ), ...List.generate( upcoming.length, (index) => SonarrUpcomingTile( diff --git a/lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart b/lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart index 7af36ea3..f0b8c2d7 100644 --- a/lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart +++ b/lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart @@ -19,20 +19,22 @@ class SonarrUpcomingTile extends StatefulWidget { class _State extends State { @override Widget build(BuildContext context) { - return LunaFourLineCardWithPoster( + return LunaBlock( backgroundUrl: - context.read().getBannerURL(widget.record.seriesId), + context.read().getFanartURL(widget.record.seriesId), posterUrl: context.read().getPosterURL(widget.record.seriesId), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, title: widget.record?.series?.title ?? widget.series?.title ?? LunaUI.TEXT_EMDASH, - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - subtitle3: _subtitle3(), - darken: !widget.record.monitored, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + disabled: !widget.record.monitored, onTap: _onTap, onLongPress: _onLongPress, trailing: _trailing(), @@ -50,10 +52,10 @@ class _State extends State { children: [ TextSpan( text: widget.record.seasonNumber == 0 - ? 'Specials ' - : 'Season ${widget.record.seasonNumber} '), - const TextSpan(text: LunaUI.TEXT_EMDASH), - TextSpan(text: ' Episode ${widget.record.episodeNumber}'), + ? 'Specials' + : 'Season ${widget.record.seasonNumber}'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: 'Episode ${widget.record.episodeNumber}'), ], ); } diff --git a/lib/modules/tautulli/core/database.dart b/lib/modules/tautulli/core/database.dart index 6cfa9cc2..9d6f53d3 100644 --- a/lib/modules/tautulli/core/database.dart +++ b/lib/modules/tautulli/core/database.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; @@ -60,7 +59,7 @@ class TautulliDatabase extends LunaModuleDatabase { extension TautulliDatabaseValueExtension on TautulliDatabaseValue { String get key { - return 'TAUTULLI_${describeEnum(this)}'; + return 'TAUTULLI_${this.name}'; } dynamic get data { diff --git a/lib/modules/tautulli/core/extensions/tautulli_history_record.dart b/lib/modules/tautulli/core/extensions/tautulli_history_record.dart index e7f4327d..423edbd9 100644 --- a/lib/modules/tautulli/core/extensions/tautulli_history_record.dart +++ b/lib/modules/tautulli/core/extensions/tautulli_history_record.dart @@ -20,7 +20,7 @@ extension TautulliHistoryRecordExtension on TautulliHistoryRecord { : this.parentTitle : this.grandparentTitle; - IconData get lsWatchStatusIcon { + IconData get lunaWatchStatusIcon { switch (this.watchedStatus) { case TautulliWatchedStatus.PARTIALLY_WATCHED: return Icons.radio_button_checked_rounded; diff --git a/lib/modules/tautulli/core/graphs/bar_graph.dart b/lib/modules/tautulli/core/graphs/bar_graph.dart index 128a4904..3c5c58cd 100644 --- a/lib/modules/tautulli/core/graphs/bar_graph.dart +++ b/lib/modules/tautulli/core/graphs/bar_graph.dart @@ -66,7 +66,7 @@ class TautulliBarGraphHelper { return BarTooltipItem( (_header + _body).trim(), const TextStyle( - color: Colors.white70, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_SUBHEADER, ), ); diff --git a/lib/modules/tautulli/core/graphs/graph.dart b/lib/modules/tautulli/core/graphs/graph.dart index 80b46290..804b3d79 100644 --- a/lib/modules/tautulli/core/graphs/graph.dart +++ b/lib/modules/tautulli/core/graphs/graph.dart @@ -15,7 +15,7 @@ class TautulliGraphHelper { FlBorderData borderData() => FlBorderData( show: true, - border: Border.all(color: Colors.white12), + border: Border.all(color: LunaColours.white10), ); FlTitlesData titlesData( @@ -44,7 +44,7 @@ class TautulliGraphHelper { ].join() : data.categories[value.truncate()].toUpperCase(), getTextStyles: (_, __) => const TextStyle( - color: Colors.white30, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_GRAPH_LEGEND, ), ), @@ -106,10 +106,10 @@ class TautulliGraphHelper { child: Container( height: GRAPH_HEIGHT + LEGEND_HEIGHT, alignment: Alignment.center, - child: LunaIconButton( - icon: Icons.error_rounded, - iconSize: 60.0, - color: Colors.white12, + child: const LunaIconButton( + icon: LunaIcons.ERROR, + iconSize: LunaUI.ICON_SIZE * 2, + color: LunaColours.red, ), ), ); diff --git a/lib/modules/tautulli/core/graphs/line_graph.dart b/lib/modules/tautulli/core/graphs/line_graph.dart index 39282e1f..e0c03a3b 100644 --- a/lib/modules/tautulli/core/graphs/line_graph.dart +++ b/lib/modules/tautulli/core/graphs/line_graph.dart @@ -22,7 +22,7 @@ class TautulliLineGraphHelper { ?.toString() : '??', getTextStyles: (_, __) => const TextStyle( - color: Colors.white30, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_GRAPH_LEGEND, ), ), @@ -43,7 +43,11 @@ class TautulliLineGraphHelper { ), belowBarData: BarAreaData( show: true, - colors: [LunaColours().byGraphLayer(sIndex).withOpacity(0.20)], + colors: [ + LunaColours() + .byGraphLayer(sIndex) + .withOpacity(LunaUI.OPACITY_SPLASH) + ], ), dotData: FlDotData( show: true, @@ -87,7 +91,7 @@ class TautulliLineGraphHelper { : Duration(seconds: value ?? 0).lunaTimestampWords, ].join().trim(), const TextStyle( - color: Colors.white70, + color: LunaColours.grey, fontSize: LunaUI.FONT_SIZE_SUBHEADER, ), ); @@ -101,7 +105,7 @@ class TautulliLineGraphHelper { (index) => TouchedSpotIndicatorData( FlLine( strokeWidth: 3.0, - color: bar.colors[0].withOpacity(0.50), + color: bar.colors[0].withOpacity(LunaUI.OPACITY_DISABLED), ), FlDotData( show: true, diff --git a/lib/modules/tautulli/core/state.dart b/lib/modules/tautulli/core/state.dart index 236259b1..c7356ed5 100644 --- a/lib/modules/tautulli/core/state.dart +++ b/lib/modules/tautulli/core/state.dart @@ -651,6 +651,7 @@ class TautulliState extends LunaModuleState { /// Get the direct URL to an image via `pms_image_proxy` using a rating key. String getImageURLFromRatingKey(int ratingKey, {int width = 300}) { + if (ratingKey == null) return null; if (host.endsWith('/')) return [ host, @@ -670,6 +671,7 @@ class TautulliState extends LunaModuleState { /// Get the direct URL to an image via `pms_image_proxy` using an image path. String getImageURLFromPath(String path, {int width = 300}) { + if (path == null || path.isEmpty) return null; if (host.endsWith('/')) return [ host, diff --git a/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart b/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart index 1a274a2c..74a2e483 100644 --- a/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart +++ b/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -import 'package:percent_indicator/linear_percent_indicator.dart'; class TautulliActivityTile extends StatelessWidget { final TautulliSession session; @@ -15,19 +14,23 @@ class TautulliActivityTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaFiveLineCardWithPoster( + return LunaBlock( title: session.lunaTitle, posterUrl: session.lunaArtworkPath(context), posterHeaders: context.read().headers, - posterPlaceholder: LunaAssets.blankVideo, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, backgroundUrl: context.watch().getImageURLFromPath( session.art, width: MediaQuery.of(context).size.width.truncate(), ), - subtitle1: _subtitle1(), - subtitle2: _subtitle2(), - customSubtitle3: _subtitle3(), - customSubtitle4: _subtitle4(), + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + bottom: _bottomWidget(), + bottomHeight: LunaLinearPercentIndicator.height, + trailing: LunaIconButton(icon: session.lunaSessionStateIcon), onTap: disableOnTap ? null : () async => _enterDetails(context), ); } @@ -37,7 +40,7 @@ class TautulliActivityTile extends StatelessWidget { return TextSpan( children: [ TextSpan(text: session.parentTitle), - TextSpan(text: LunaUI.TEXT_EMDASH.lunaPad()), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), TextSpan( text: 'tautulli.Episode'.tr(args: [ session.mediaIndex?.toString() ?? LunaUI.TEXT_EMDASH @@ -76,49 +79,41 @@ class TautulliActivityTile extends StatelessWidget { } TextSpan _subtitle2() { - return TextSpan(text: session.lunaTranscodeDecision); + return TextSpan(text: session.lunaFriendlyName); } - Widget _subtitle3() { - return Row( - children: [ - Container( - child: Icon( - session.lunaSessionStateIcon, - size: LunaUI.FONT_SIZE_SUBTITLE, - color: Colors.white70, - ), - alignment: Alignment.centerLeft, - width: 16.0 + session.lunaSessionStateIconOffset, - transform: Matrix4.translationValues( - session.lunaSessionStateIconOffset, 0.0, 0.0), - ), - LunaText.subtitle(text: session.lunaFriendlyName), - ], + TextSpan _subtitle3() { + return TextSpan( + text: session.lunaTranscodeDecision, + style: const TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.accent, + ), ); } - Widget _subtitle4() { - return Padding( + Widget _bottomWidget() { + return SizedBox( + height: LunaLinearPercentIndicator.height, child: Stack( + alignment: Alignment.center, children: [ - LinearPercentIndicator( + LunaLinearPercentIndicator( percent: session.lunaTranscodeProgress, - padding: const EdgeInsets.symmetric(horizontal: 2.0), - progressColor: LunaColours.splash.withOpacity(0.30), + progressColor: LunaColours.accent.withOpacity( + LunaUI.OPACITY_SPLASH, + ), backgroundColor: Colors.transparent, - lineHeight: 4.0, ), - LinearPercentIndicator( + LunaLinearPercentIndicator( percent: session.lunaProgressPercent, - padding: const EdgeInsets.symmetric(horizontal: 2.0), progressColor: LunaColours.accent, - backgroundColor: LunaColours.accent.withOpacity(0.15), - lineHeight: 4.0, + backgroundColor: LunaColours.grey.withOpacity( + LunaUI.OPACITY_SPLASH, + ), ), ], ), - padding: const EdgeInsets.only(top: 4.0), ); } diff --git a/lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart b/lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart index 7c7111bb..86f2bf12 100644 --- a/lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart +++ b/lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart @@ -12,12 +12,10 @@ class TautulliCheckForUpdatesPMSTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Plex Media Server'), - subtitle: _subtitle(), + return LunaBlock( + title: 'Plex Media Server', + body: _subtitle(), trailing: _trailing(), - contentPadding: true, ); } @@ -34,38 +32,28 @@ class TautulliCheckForUpdatesPMSTile extends StatelessWidget { ); } - Widget _subtitle() { - return RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + List _subtitle() { + return [ + if (!update.updateAvailable) + const TextSpan( + text: 'No Updates Available', + style: TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), ), - children: [ - if (!update.updateAvailable) - const TextSpan( - text: 'No Updates Available\n', - style: TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - if (!update.updateAvailable) - TextSpan(text: 'Current Version: ${update.version}'), - if (update.updateAvailable) - const TextSpan( - text: 'Update Available\n', - style: TextStyle( - color: LunaColours.orange, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - if (update.updateAvailable) - TextSpan(text: 'Latest Version: ${update.version}'), - ], - ), - overflow: TextOverflow.fade, - maxLines: 3, - ); + if (!update.updateAvailable) + TextSpan(text: 'Current Version: ${update.version}'), + if (update.updateAvailable) + const TextSpan( + text: 'Update Available', + style: TextStyle( + color: LunaColours.orange, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ), + if (update.updateAvailable) + TextSpan(text: 'Latest Version: ${update.version}'), + ]; } } diff --git a/lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart b/lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart index 45de4a54..4daaae05 100644 --- a/lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart +++ b/lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart @@ -13,12 +13,10 @@ class TautulliCheckForUpdatesTautulliTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: 'Tautulli'), - subtitle: _subtitle(), + return LunaBlock( + title: 'Tautulli', + body: _subtitle(), trailing: _trailing(), - contentPadding: true, ); } @@ -35,45 +33,53 @@ class TautulliCheckForUpdatesTautulliTile extends StatelessWidget { ); } - Widget _subtitle() { - return RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + List _subtitle() { + return [ + if (!update.update) + const TextSpan( + text: 'No Updates Available', + style: TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), ), - children: [ - if (!update.update) - const TextSpan( - text: 'No Updates Available\n', - style: TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - if (update.update) - const TextSpan( - text: 'Update Available\n', - style: TextStyle( - color: LunaColours.orange, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - if (update.update) + if (update.update) + const TextSpan( + text: 'Update Available', + style: TextStyle( + color: LunaColours.orange, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ), + if (update.update) + TextSpan( + children: [ + const TextSpan(text: 'Current Version: '), TextSpan( - text: - 'Current Version: ${update.currentRelease ?? update.currentVersion?.substring(0, min(7, update.currentVersion?.length ?? 0)) ?? 'Unknown'}\n', + text: update.currentRelease ?? + update.currentVersion?.substring( + 0, + min(7, update.currentVersion?.length ?? 0), + ) ?? + 'lunasea.Unknown'.tr(), ), - if (update.update) + ], + ), + if (update.update) + TextSpan( + children: [ + const TextSpan(text: 'Latest Version: '), TextSpan( - text: - 'Latest Version: ${update.latestRelease ?? update.latestVersion?.substring(0, min(7, update.latestVersion?.length ?? 0)) ?? 'Unknown'}\n', + text: update.latestRelease ?? + update.latestVersion?.substring( + 0, + min(7, update.latestVersion?.length ?? 0), + ) ?? + 'lunasea.Unknown'.tr(), ), - TextSpan(text: 'Install Type: ${update.installType}'), - ], - ), - overflow: TextOverflow.fade, - maxLines: 4, - ); + ], + ), + TextSpan(text: 'Install Type: ${update.installType}'), + ]; } } diff --git a/lib/modules/tautulli/routes/graphs/widgets/type_button.dart b/lib/modules/tautulli/routes/graphs/widgets/type_button.dart index 27e760d9..66c2e64d 100644 --- a/lib/modules/tautulli/routes/graphs/widgets/type_button.dart +++ b/lib/modules/tautulli/routes/graphs/widgets/type_button.dart @@ -28,7 +28,7 @@ class TautulliGraphsTypeButton extends StatelessWidget { TautulliStatsType.values[index].value .lunaCapitalizeFirstLetters(), style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: type == TautulliGraphYAxis.values[index] ? LunaColours.accent : Colors.white, diff --git a/lib/modules/tautulli/routes/history/route.dart b/lib/modules/tautulli/routes/history/route.dart index 50552a55..6bd47bbf 100644 --- a/lib/modules/tautulli/routes/history/route.dart +++ b/lib/modules/tautulli/routes/history/route.dart @@ -72,6 +72,7 @@ class _State extends State return LunaListViewBuilder( controller: TautulliNavigationBar.scrollControllers[2], itemCount: history.records.length, + itemExtent: LunaBlock.calculateItemExtent(3), itemBuilder: (context, index) => TautulliHistoryTile(history: history.records[index]), ); diff --git a/lib/modules/tautulli/routes/history/widgets/history_tile.dart b/lib/modules/tautulli/routes/history/widgets/history_tile.dart index c8a29bf6..ae496ba7 100644 --- a/lib/modules/tautulli/routes/history/widgets/history_tile.dart +++ b/lib/modules/tautulli/routes/history/widgets/history_tile.dart @@ -4,8 +4,6 @@ import 'package:lunasea/modules/tautulli.dart'; class TautulliHistoryTile extends StatelessWidget { final TautulliHistoryRecord history; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliHistoryTile({ Key key, @@ -14,114 +12,74 @@ class TautulliHistoryTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _poster(context), - _details, - ], - ), - onTap: () async => _onTap(context), - ), - decoration: LunaCardDecoration( - uri: context.watch().getImageURLFromRatingKey( - history.grandparentRatingKey ?? - history.parentRatingKey ?? - history.ratingKey ?? - '', - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context.watch().headers.cast(), - ), - ); - } - - Widget _poster(BuildContext context) => LunaNetworkImage( - url: context.watch().getImageURLFromPath(history.thumb), - headers: context.watch().headers.cast(), - height: _imageDimension, - width: _imageDimension / 1.5, - placeholderAsset: LunaAssets.blankVideo, - ); - - Widget get _details => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - LunaText.title(text: history.lsTitle, maxLines: 1), - _subtitle, - LunaText.subtitle(text: history.lsDate), - _userInfo, - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), + return LunaBlock( + title: history.lsTitle, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + bodyLeadingIcons: [ + null, + null, + history.lunaWatchStatusIcon, + ], + posterUrl: + context.watch().getImageURLFromPath(history.thumb), + posterHeaders: context.watch().headers, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + backgroundHeaders: context.watch().headers, + backgroundUrl: context.watch().getImageURLFromRatingKey( + history.grandparentRatingKey ?? + history.parentRatingKey ?? + history.ratingKey ?? + '', ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _subtitle => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - // Television - if (history.mediaType == TautulliMediaType.EPISODE) - TextSpan( - text: - 'S${history.parentMediaIndex}\t•\tE${history.mediaIndex}'), - if (history.mediaType == TautulliMediaType.EPISODE) - TextSpan(text: '\t—\t${history.title}'), - // Movie - if (history.mediaType == TautulliMediaType.MOVIE) - TextSpan(text: history.year.toString()), - // Music - if (history.mediaType == TautulliMediaType.TRACK) - TextSpan(text: history.title), - if (history.mediaType == TautulliMediaType.TRACK) - TextSpan(text: '\t—\t${history.parentTitle}'), - // Live - if (history.mediaType == TautulliMediaType.LIVE) - TextSpan(text: history.title), - ], - ), - softWrap: false, - maxLines: 1, - overflow: TextOverflow.fade, - ); - - Widget get _userInfo => Row( - children: [ - Padding( - child: Icon( - history.lsWatchStatusIcon, - size: LunaUI.FONT_SIZE_SUBHEADER, - ), - padding: const EdgeInsets.only(right: 6.0), - ), - Expanded( - child: LunaText.subtitle( - text: history.friendlyName ?? 'Unknown User', - maxLines: 1, - ), - ) - ], - ); - - Future _onTap(BuildContext context) async => - TautulliHistoryDetailsRouter().navigateTo( + onTap: () async => TautulliHistoryDetailsRouter().navigateTo( context, ratingKey: history.ratingKey, sessionKey: history.sessionKey, referenceId: history.referenceId, - ); + ), + ); + } + + TextSpan _subtitle1() { + return TextSpan( + children: [ + if (history.mediaType == TautulliMediaType.EPISODE) + TextSpan( + children: [ + TextSpan(text: 'Season ${history.parentMediaIndex}'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: 'Episode ${history.mediaIndex}: '), + TextSpan( + text: history.title, + style: const TextStyle(fontStyle: FontStyle.italic), + ), + ], + ), + if (history.mediaType == TautulliMediaType.MOVIE) + TextSpan(text: history.year.toString()), + if (history.mediaType == TautulliMediaType.TRACK) + TextSpan( + children: [ + TextSpan(text: history.title), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: history.parentTitle), + ], + ), + if (history.mediaType == TautulliMediaType.LIVE) + TextSpan(text: history.title), + ], + ); + } + + TextSpan _subtitle2() { + return TextSpan(text: history.lsDate); + } + + TextSpan _subtitle3() { + return TextSpan(text: history.friendlyName ?? 'Unknown User'); + } } diff --git a/lib/modules/tautulli/routes/libraries/widgets/library_tile.dart b/lib/modules/tautulli/routes/libraries/widgets/library_tile.dart index 5a0fe98e..6d1c4f69 100644 --- a/lib/modules/tautulli/routes/libraries/widgets/library_tile.dart +++ b/lib/modules/tautulli/routes/libraries/widgets/library_tile.dart @@ -12,44 +12,33 @@ class TautulliLibrariesLibraryTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: library.sectionName), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan(text: '${library.readableCount}\n'), - TextSpan( - text: library.plays == 1 - ? '1 Play ${LunaUI.TEXT_EMDASH} ' - : '${library.plays} Plays ${LunaUI.TEXT_EMDASH} ', - ), - TextSpan(text: '${library.duration.lunaTimestampWords}\n'), - TextSpan( - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - text: library.lastAccessed?.lunaAge ?? 'Unknown', - ), + int _plays = library.plays; + return LunaBlock( + title: library.sectionName, + body: [ + TextSpan(text: library.readableCount), + TextSpan( + children: [ + TextSpan(text: _plays == 1 ? '1 Play' : '$_plays Plays'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: library.duration.lunaTimestampWords), ], ), - overflow: TextOverflow.fade, - maxLines: 3, + TextSpan( + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + text: library.lastAccessed?.lunaAge ?? 'Unknown', + ), + ], + backgroundUrl: + context.watch().getImageURLFromPath(library.thumb), + backgroundHeaders: context.watch().headers, + onTap: () async => TautulliLibrariesDetailsRouter().navigateTo( + context, + sectionId: library.sectionId, ), - contentPadding: true, - decoration: LunaCardDecoration( - uri: context.watch().getImageURLFromPath(library.thumb), - headers: context.watch().headers, - ), - onTap: () async => _onTap(context), ); } - - Future _onTap(BuildContext context) => TautulliLibrariesDetailsRouter() - .navigateTo(context, sectionId: library.sectionId); } diff --git a/lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart b/lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart index 27da18f1..4bc07446 100644 --- a/lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart +++ b/lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart @@ -12,56 +12,24 @@ class TautulliLibrariesDetailsUserStatsTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaTwoLineCardWithPoster( + return LunaBlock( posterUrl: context.watch().getImageURLFromPath(user.userThumb), posterHeaders: context.watch().headers.cast(), - posterPlaceholder: LunaAssets.blankUser, + posterPlaceholderIcon: LunaIcons.USER, title: user.friendlyName, - customSubtitle1: _subtitle1(), - customSubtitle2: _subtitle2(), + body: [ + TextSpan( + text: user.totalPlays == 1 ? '1 Play' : '${user.totalPlays} Plays'), + TextSpan(text: user.userId.toString()), + ], + bodyLeadingIcons: const [ + LunaIcons.PLAY, + LunaIcons.USER, + ], onTap: () async => _onTap(context), - ); - } - - Widget _subtitle1() { - return Row( - children: [ - const Padding( - child: Icon( - Icons.play_arrow_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE, - ), - padding: EdgeInsets.only(right: 6.0), - ), - Expanded( - child: LunaText.subtitle( - text: user.totalPlays == 1 ? '1 Play' : '${user.totalPlays} Plays', - maxLines: 1, - ), - ), - ], - ); - } - - Widget _subtitle2() { - return Row( - children: [ - const Padding( - child: Icon( - Icons.person_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE, - ), - padding: EdgeInsets.only(right: 6.0), - ), - Expanded( - child: LunaText.subtitle( - text: user.userId.toString(), - maxLines: 1, - ), - ), - ], + posterIsSquare: true, ); } diff --git a/lib/modules/tautulli/routes/logs/route.dart b/lib/modules/tautulli/routes/logs/route.dart index 5a12ca68..7188b9e9 100644 --- a/lib/modules/tautulli/routes/logs/route.dart +++ b/lib/modules/tautulli/routes/logs/route.dart @@ -41,20 +41,18 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaListView( controller: scrollController, children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'Logins'), - subtitle: LunaText.subtitle(text: 'Tautulli Login Logs'), + LunaBlock( + title: 'Logins', + body: const [TextSpan(text: 'Tautulli Login Logs')], trailing: LunaIconButton( icon: Icons.vpn_key_rounded, color: LunaColours().byListIndex(0), ), onTap: () async => TautulliLogsLoginsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Newsletters'), - subtitle: LunaText.subtitle(text: 'Tautulli Newsletter Logs'), + LunaBlock( + title: 'Newsletters', + body: const [TextSpan(text: 'Tautulli Newsletter Logs')], trailing: LunaIconButton( icon: Icons.email_rounded, color: LunaColours().byListIndex(1), @@ -62,10 +60,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { onTap: () async => TautulliLogsNewslettersRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Notifications'), - subtitle: LunaText.subtitle(text: 'Tautulli Notification Logs'), + LunaBlock( + title: 'Notifications', + body: const [TextSpan(text: 'Tautulli Notification Logs')], trailing: LunaIconButton( icon: Icons.notifications_rounded, color: LunaColours().byListIndex(2), @@ -73,10 +70,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { onTap: () async => TautulliLogsNotificationsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Plex Media Scanner'), - subtitle: LunaText.subtitle(text: 'Plex Media Scanner Logs'), + LunaBlock( + title: 'Plex Media Scanner', + body: const [TextSpan(text: 'Plex Media Scanner Logs')], trailing: LunaIconButton( icon: Icons.scanner_rounded, color: LunaColours().byListIndex(3), @@ -84,10 +80,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { onTap: () async => TautulliLogsPlexMediaScannerRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Plex Media Server'), - subtitle: LunaText.subtitle(text: 'Plex Media Server Logs'), + LunaBlock( + title: 'Plex Media Server', + body: const [TextSpan(text: 'Plex Media Server Logs')], trailing: LunaIconButton( icon: LunaBrandIcons.plex, color: LunaColours().byListIndex(4), @@ -95,10 +90,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { onTap: () async => TautulliLogsPlexMediaServerRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Tautulli'), - subtitle: LunaText.subtitle(text: 'Tautulli Logs'), + LunaBlock( + title: 'Tautulli', + body: const [TextSpan(text: 'Tautulli Logs')], trailing: LunaIconButton( icon: LunaBrandIcons.tautulli, color: LunaColours().byListIndex(5), diff --git a/lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart index 00c9e1a9..e443d1f6 100644 --- a/lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart +++ b/lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart @@ -12,43 +12,26 @@ class TautulliLogsLoginsLogTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: login.friendlyName), - subtitle: _subtitle(), + return LunaBlock( + title: login.friendlyName, + body: _body(), trailing: _trailing(), - contentPadding: true, ); } - Widget _subtitle() { - return RichText( - text: TextSpan( + List _body() { + return [ + TextSpan(text: '${login.ipAddress}\n'), + TextSpan(text: '${login.os}\n'), + TextSpan(text: '${login.host}\n'), + TextSpan( + text: login.timestamp.lunaDateTimeReadable(), style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), - children: [ - TextSpan(text: '${login.ipAddress}\n'), - TextSpan(text: '${login.os}\n'), - TextSpan(text: '${login.host}\n'), - TextSpan( - text: LunaDatabaseValue.USE_24_HOUR_TIME.data - ? DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} HH:mm') - .format(login.timestamp) - : DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} hh:mm a') - .format(login.timestamp), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - ], ), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 5, - ); + ]; } Widget _trailing() { diff --git a/lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart index 9f4c26dc..d43f152b 100644 --- a/lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart +++ b/lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart @@ -12,42 +12,27 @@ class TautulliLogsNewsletterLogTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: newsletter.agentName), - subtitle: _subtitle(), + return LunaBlock( + title: newsletter.agentName, + body: _body(), trailing: _trailing(), - contentPadding: true, ); } - Widget _subtitle() => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan(text: '${newsletter.notifyAction}\n'), - TextSpan(text: '${newsletter.subjectText}\n'), - TextSpan(text: '${newsletter.bodyText}\n'), - TextSpan( - text: LunaDatabaseValue.USE_24_HOUR_TIME.data - ? DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} HH:mm') - .format(newsletter.timestamp) - : DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} hh:mm a') - .format(newsletter.timestamp), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - ], + List _body() { + return [ + TextSpan(text: newsletter.notifyAction), + TextSpan(text: newsletter.subjectText), + TextSpan(text: newsletter.bodyText), + TextSpan( + text: newsletter.timestamp.lunaDateTimeReadable(), + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 4, - ); + ), + ]; + } Widget _trailing() => Column( children: [ @@ -55,7 +40,7 @@ class TautulliLogsNewsletterLogTile extends StatelessWidget { icon: newsletter.success ? Icons.check_circle_rounded : Icons.cancel_rounded, - color: newsletter.success ? Colors.white : LunaColours.red, + color: newsletter.success ? LunaColours.white : LunaColours.red, ), ], crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart index f982118b..2bd0c0ed 100644 --- a/lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart +++ b/lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart @@ -12,42 +12,27 @@ class TautulliLogsNotificationLogTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: notification.agentName), - subtitle: _subtitle(), + return LunaBlock( + title: notification.agentName, + body: _body(), trailing: _trailing(), - contentPadding: true, ); } - Widget _subtitle() => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan(text: '${notification.notifyAction}\n'), - TextSpan(text: '${notification.subjectText}\n'), - TextSpan(text: '${notification.bodyText}\n'), - TextSpan( - text: LunaDatabaseValue.USE_24_HOUR_TIME.data - ? DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} HH:mm') - .format(notification.timestamp) - : DateFormat('MMMM dd, yyyy ${LunaUI.TEXT_EMDASH} hh:mm a') - .format(notification.timestamp), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - ], + List _body() { + return [ + TextSpan(text: notification.notifyAction), + TextSpan(text: notification.subjectText), + TextSpan(text: notification.bodyText), + TextSpan( + text: notification.timestamp.lunaDateTimeReadable(), + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 4, - ); + ), + ]; + } Widget _trailing() => Column( children: [ @@ -55,7 +40,7 @@ class TautulliLogsNotificationLogTile extends StatelessWidget { icon: notification.success ? Icons.check_circle_rounded : Icons.cancel_rounded, - color: notification.success ? Colors.white : LunaColours.red, + color: notification.success ? LunaColours.white : LunaColours.red, ), ], crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart index 495eb999..592f4310 100644 --- a/lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart +++ b/lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart @@ -4,9 +4,6 @@ import 'package:lunasea/modules/tautulli.dart'; class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { final TautulliMetadata metadata; - final double _height = 105.0; - final double _width = 70.0; - final double _padding = 8.0; const TautulliMediaDetailsMetadataHeaderTile({ Key key, @@ -15,31 +12,31 @@ class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: _body(context), - decoration: metadata.art != null && metadata.art.isNotEmpty - ? LunaCardDecoration( - uri: context.watch().getImageURLFromPath( - metadata.art, - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context.watch().headers, - ) - : null, + return LunaBlock( + title: _title, + body: _body, + backgroundHeaders: context.watch().headers, + backgroundUrl: context.watch().getImageURLFromPath( + metadata.art, + width: MediaQuery.of(context).size.width.truncate(), + ), + bottom: const SizedBox(), + bottomHeight: _bottomHeight, + posterUrl: + context.watch().getImageURLFromPath(_posterLink), + posterHeaders: context.watch().headers, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, ); } - Widget _body(BuildContext context) { - return Row( - children: [ - _poster(context), - Expanded(child: _mediaInfo), - ], - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - ); - } + String get _title => + metadata.grandparentTitle == null || metadata.grandparentTitle.isEmpty + ? metadata.parentTitle == null || metadata.parentTitle.isEmpty + ? metadata.title == null || metadata.title.isEmpty + ? 'Unknown Title' + : metadata.title + : metadata.parentTitle + : metadata.grandparentTitle; String get _posterLink { switch (metadata.mediaType) { @@ -61,52 +58,16 @@ class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { } } - Widget _poster(BuildContext context) { - return LunaNetworkImage( - url: context.watch().getImageURLFromPath(_posterLink), - placeholderAsset: LunaAssets.blankVideo, - height: _height, - width: _width, - headers: context.watch().headers.cast(), - ); - } - - Widget get _mediaInfo => Padding( - child: SizedBox( - child: Column( - children: [ - LunaText.title(text: _title, maxLines: 1), - const SizedBox(height: 4.0), - ..._subtitles, - ], - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_height - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ); - - String get _title => - metadata.grandparentTitle == null || metadata.grandparentTitle.isEmpty - ? metadata.parentTitle == null || metadata.parentTitle.isEmpty - ? metadata.title == null || metadata.title.isEmpty - ? 'Unknown Title' - : metadata.title - : metadata.parentTitle - : metadata.grandparentTitle; - - List get _subtitles { + List get _body { switch (metadata.mediaType) { case TautulliMediaType.MOVIE: case TautulliMediaType.SHOW: case TautulliMediaType.ALBUM: case TautulliMediaType.SEASON: - return [_subtitleOne]; + return [_subtitle1]; case TautulliMediaType.EPISODE: case TautulliMediaType.TRACK: - return [_subtitleOne, _subtitleTwo]; + return [_subtitle1, _subtitle2]; case TautulliMediaType.ARTIST: case TautulliMediaType.COLLECTION: case TautulliMediaType.LIVE: @@ -116,13 +77,32 @@ class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { } } - Widget get _subtitleOne { + double get _bottomHeight { + switch (metadata.mediaType) { + case TautulliMediaType.MOVIE: + case TautulliMediaType.SHOW: + case TautulliMediaType.ALBUM: + case TautulliMediaType.SEASON: + return LunaBlock.SUBTITLE_HEIGHT * 2; + case TautulliMediaType.EPISODE: + case TautulliMediaType.TRACK: + return LunaBlock.SUBTITLE_HEIGHT; + case TautulliMediaType.ARTIST: + case TautulliMediaType.COLLECTION: + case TautulliMediaType.LIVE: + case TautulliMediaType.NULL: + default: + return LunaBlock.SUBTITLE_HEIGHT * 3; + } + } + + TextSpan get _subtitle1 { String _text = ''; switch (metadata.mediaType) { case TautulliMediaType.MOVIE: case TautulliMediaType.ARTIST: case TautulliMediaType.SHOW: - _text = metadata.year?.toString() ?? 'Unknown'; + _text = metadata.year?.toString() ?? 'lunasea.Unknown'.tr(); break; case TautulliMediaType.ALBUM: case TautulliMediaType.SEASON: @@ -140,19 +120,19 @@ class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { break; case TautulliMediaType.NULL: default: - _text = 'Unknown'; + _text = 'lunasea.Unknown'.tr(); break; } - return LunaText.subtitle(text: _text); + return TextSpan(text: _text); } - Widget get _subtitleTwo { + TextSpan get _subtitle2 { String _text = ''; switch (metadata.mediaType) { case TautulliMediaType.MOVIE: case TautulliMediaType.ARTIST: case TautulliMediaType.SHOW: - _text = metadata.year?.toString() ?? 'Unknown'; + _text = metadata.year?.toString() ?? 'lunasea.Unknown'.tr(); break; case TautulliMediaType.ALBUM: case TautulliMediaType.SEASON: @@ -164,16 +144,9 @@ class TautulliMediaDetailsMetadataHeaderTile extends StatelessWidget { break; case TautulliMediaType.NULL: default: - _text = 'Unknown'; + _text = 'lunasea.Unknown'.tr(); break; } - return Text( - _text, - maxLines: 3, - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - ); + return TextSpan(text: _text); } } diff --git a/lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart index 1da1fa0d..aca2f2d8 100644 --- a/lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart +++ b/lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart @@ -40,7 +40,8 @@ class _State extends State { @override Widget build(BuildContext context) { - if (_summary == null && _buttons == null) return const SizedBox(height: 0.0); + if (_summary == null && _buttons == null) + return const SizedBox(height: 0.0); return LunaCard( context: context, child: InkWell( @@ -55,7 +56,7 @@ class _State extends State { child: RichText( text: TextSpan( style: const TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, ), text: _summary, ), diff --git a/lib/modules/tautulli/routes/more/route.dart b/lib/modules/tautulli/routes/more/route.dart index f92f29ea..fe90b568 100644 --- a/lib/modules/tautulli/routes/more/route.dart +++ b/lib/modules/tautulli/routes/more/route.dart @@ -31,10 +31,9 @@ class _State extends State return LunaListView( controller: TautulliNavigationBar.scrollControllers[3], children: [ - LunaListTile( - context: context, - title: LunaText.title(text: 'Check for Updates'), - subtitle: LunaText.subtitle(text: 'Tautulli & Plex Updates'), + LunaBlock( + title: 'Check for Updates', + body: const [TextSpan(text: 'Tautulli & Plex Updates')], trailing: LunaIconButton( icon: Icons.system_update_rounded, color: LunaColours().byListIndex(0), @@ -42,70 +41,63 @@ class _State extends State onTap: () async => TautulliCheckForUpdatesRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Graphs'), - subtitle: LunaText.subtitle(text: 'Play Count & Duration Graphs'), + LunaBlock( + title: 'Graphs', + body: const [TextSpan(text: 'Play Count & Duration Graphs')], trailing: LunaIconButton( icon: Icons.insert_chart_rounded, color: LunaColours().byListIndex(1), ), onTap: () async => TautulliGraphsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Libraries'), - subtitle: LunaText.subtitle(text: 'Plex Library Information'), + LunaBlock( + title: 'Libraries', + body: const [TextSpan(text: 'Plex Library Information')], trailing: LunaIconButton( icon: Icons.video_library_rounded, color: LunaColours().byListIndex(2), ), onTap: () async => TautulliLibrariesRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Logs'), - subtitle: LunaText.subtitle(text: 'Tautulli & Plex Logs'), + LunaBlock( + title: 'Logs', + body: const [TextSpan(text: 'Tautulli & Plex Logs')], trailing: LunaIconButton( icon: Icons.developer_mode_rounded, color: LunaColours().byListIndex(3), ), onTap: () async => TautulliLogsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Recently Added'), - subtitle: LunaText.subtitle(text: 'Recently Added Content to Plex'), + LunaBlock( + title: 'Recently Added', + body: const [TextSpan(text: 'Recently Added Content to Plex')], trailing: LunaIconButton( icon: Icons.recent_actors_rounded, color: LunaColours().byListIndex(4), ), onTap: () async => TautulliRecentlyAddedRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Search'), - subtitle: LunaText.subtitle(text: 'Search Your Libraries'), + LunaBlock( + title: 'Search', + body: const [TextSpan(text: 'Search Your Libraries')], trailing: LunaIconButton( icon: Icons.search_rounded, color: LunaColours().byListIndex(5), ), onTap: () async => TautulliSearchRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Statistics'), - subtitle: LunaText.subtitle(text: 'User & Library Statistics'), + LunaBlock( + title: 'Statistics', + body: const [TextSpan(text: 'User & Library Statistics')], trailing: LunaIconButton( icon: Icons.format_list_numbered_rounded, color: LunaColours().byListIndex(6), ), onTap: () async => TautulliStatisticsRouter().navigateTo(context), ), - LunaListTile( - context: context, - title: LunaText.title(text: 'Synced Items'), - subtitle: LunaText.subtitle(text: 'Synced Content on Devices'), + LunaBlock( + title: 'Synced Items', + body: const [TextSpan(text: 'Synced Content on Devices')], trailing: LunaIconButton( icon: Icons.sync_rounded, color: LunaColours().byListIndex(7), diff --git a/lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart b/lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart index 5cfc8e8c..24b1e0a4 100644 --- a/lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart +++ b/lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart @@ -2,45 +2,38 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliRecentlyAddedContentTile extends StatelessWidget { +class TautulliRecentlyAddedContentTile extends StatefulWidget { final TautulliRecentlyAdded recentlyAdded; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliRecentlyAddedContentTile({ Key key, @required this.recentlyAdded, }) : super(key: key); + @override + State createState() => _State(); +} + +class _State extends State { @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _poster(context), - _details(context), - ], - ), - onTap: () async => _onTap(context), - ), - decoration: recentlyAdded.art != null && recentlyAdded.art.isNotEmpty - ? LunaCardDecoration( - uri: context.watch().getImageURLFromPath( - recentlyAdded.art, - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: - context.watch().headers.cast(), - ) - : null, + return LunaBlock( + title: _title, + body: _body, + posterHeaders: context.watch().headers, + posterUrl: + context.watch().getImageURLFromPath(_posterLink), + backgroundHeaders: context.watch().headers, + backgroundUrl: context + .watch() + .getImageURLFromPath(widget.recentlyAdded.art), + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + onTap: _onTap, ); } String get _posterLink { - switch (recentlyAdded.mediaType) { + switch (widget.recentlyAdded.mediaType) { case TautulliMediaType.MOVIE: case TautulliMediaType.SHOW: case TautulliMediaType.SEASON: @@ -48,112 +41,82 @@ class TautulliRecentlyAddedContentTile extends StatelessWidget { case TautulliMediaType.ALBUM: case TautulliMediaType.LIVE: case TautulliMediaType.COLLECTION: - return recentlyAdded.thumb; + return widget.recentlyAdded.thumb; case TautulliMediaType.EPISODE: case TautulliMediaType.TRACK: - return recentlyAdded.grandparentThumb; + return widget.recentlyAdded.grandparentThumb; case TautulliMediaType.NULL: default: return ''; } } - Widget _poster(BuildContext context) { - return LunaNetworkImage( - url: context.watch().getImageURLFromPath(_posterLink), - headers: context.watch().headers.cast(), - placeholderAsset: LunaAssets.blankVideo, - height: _imageDimension, - width: _imageDimension / 1.5, - ); + String get _title { + String title = 'lunasea.Unknown'.tr(); + if (widget.recentlyAdded.title != null && + widget.recentlyAdded.title.isNotEmpty) + title = widget.recentlyAdded.title; + if (widget.recentlyAdded.parentTitle != null && + widget.recentlyAdded.parentTitle.isNotEmpty) + title = widget.recentlyAdded.parentTitle; + if (widget.recentlyAdded.grandparentTitle != null && + widget.recentlyAdded.grandparentTitle.isNotEmpty) + title = widget.recentlyAdded.grandparentTitle; + return title; } - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle, - _library, - _addedAt, - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, + List get _body { + return [ + // Television + if (widget.recentlyAdded.mediaType == TautulliMediaType.EPISODE) + TextSpan( + children: [ + TextSpan(text: 'S${widget.recentlyAdded.parentMediaIndex}'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: 'E${widget.recentlyAdded.mediaIndex}: '), + TextSpan( + text: widget.recentlyAdded.title, + style: const TextStyle( + fontStyle: FontStyle.italic, + ), ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _title { - String title = 'Unknown Title'; - if (recentlyAdded.title != null && recentlyAdded.title.isNotEmpty) - title = recentlyAdded.title; - if (recentlyAdded.parentTitle != null && - recentlyAdded.parentTitle.isNotEmpty) title = recentlyAdded.parentTitle; - if (recentlyAdded.grandparentTitle != null && - recentlyAdded.grandparentTitle.isNotEmpty) - title = recentlyAdded.grandparentTitle; - return LunaText.title( - text: title, - maxLines: 1, - ); - } - - Widget get _subtitle => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - // Television - if (recentlyAdded.mediaType == TautulliMediaType.EPISODE) - TextSpan( - text: - 'S${recentlyAdded.parentMediaIndex}\t${LunaUI.TEXT_BULLET}\tE${recentlyAdded.mediaIndex}'), - if (recentlyAdded.mediaType == TautulliMediaType.EPISODE) - TextSpan(text: '\t${LunaUI.TEXT_EMDASH}\t${recentlyAdded.title}'), - if (recentlyAdded.mediaType == TautulliMediaType.SHOW) - TextSpan(text: recentlyAdded.year.toString()), - if (recentlyAdded.mediaType == TautulliMediaType.SEASON) - TextSpan(text: recentlyAdded.fullTitle), - // Movie - if (recentlyAdded.mediaType == TautulliMediaType.MOVIE) - TextSpan(text: recentlyAdded.year.toString()), - // Music - if (recentlyAdded.mediaType == TautulliMediaType.ARTIST) - const TextSpan(text: LunaUI.TEXT_EMDASH), - if (recentlyAdded.mediaType == TautulliMediaType.ALBUM) - TextSpan(text: recentlyAdded.title), - if (recentlyAdded.mediaType == TautulliMediaType.TRACK) - TextSpan(text: recentlyAdded.title), - if (recentlyAdded.mediaType == TautulliMediaType.TRACK) - TextSpan( - text: - '\t${LunaUI.TEXT_EMDASH}\t${recentlyAdded.parentTitle}'), - // Other - if (recentlyAdded.mediaType == TautulliMediaType.LIVE) - const TextSpan(text: LunaUI.TEXT_EMDASH), - if (recentlyAdded.mediaType == TautulliMediaType.COLLECTION) - const TextSpan(text: LunaUI.TEXT_EMDASH), ], ), - softWrap: false, - maxLines: 1, - overflow: TextOverflow.fade, + if (widget.recentlyAdded.mediaType == TautulliMediaType.SHOW) + TextSpan(text: widget.recentlyAdded.year.toString()), + if (widget.recentlyAdded.mediaType == TautulliMediaType.SEASON) + TextSpan(text: widget.recentlyAdded.fullTitle), + // Movie + if (widget.recentlyAdded.mediaType == TautulliMediaType.MOVIE) + TextSpan(text: widget.recentlyAdded.year.toString()), + // Music + if (widget.recentlyAdded.mediaType == TautulliMediaType.ARTIST) + const TextSpan(text: LunaUI.TEXT_EMDASH), + if (widget.recentlyAdded.mediaType == TautulliMediaType.ALBUM) + TextSpan(text: widget.recentlyAdded.title), + if (widget.recentlyAdded.mediaType == TautulliMediaType.TRACK) + TextSpan( + children: [ + TextSpan(text: widget.recentlyAdded.title), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: widget.recentlyAdded.parentTitle), + ], + ), + // Other + if (widget.recentlyAdded.mediaType == TautulliMediaType.LIVE) + const TextSpan(text: LunaUI.TEXT_EMDASH), + if (widget.recentlyAdded.mediaType == TautulliMediaType.COLLECTION) + const TextSpan(text: LunaUI.TEXT_EMDASH), + TextSpan(text: widget.recentlyAdded.libraryName), + TextSpan( + text: widget.recentlyAdded.addedAt?.lunaAge ?? 'lunasea.Unknown'.tr(), + ), + ]; + } + + Future _onTap() async => TautulliMediaDetailsRouter().navigateTo( + context, + ratingKey: widget.recentlyAdded.ratingKey, + mediaType: widget.recentlyAdded.mediaType, ); - - Widget get _addedAt => - LunaText.subtitle(text: recentlyAdded.addedAt?.lunaAge ?? 'Unknown'); - - Widget get _library => LunaText.subtitle(text: recentlyAdded.libraryName); - - Future _onTap(BuildContext context) async => - TautulliMediaDetailsRouter().navigateTo(context, - ratingKey: recentlyAdded.ratingKey, - mediaType: recentlyAdded.mediaType); } diff --git a/lib/modules/tautulli/routes/search/widgets/search_appbar.dart b/lib/modules/tautulli/routes/search/widgets/search_appbar.dart index 7242650c..dbba785e 100644 --- a/lib/modules/tautulli/routes/search/widgets/search_appbar.dart +++ b/lib/modules/tautulli/routes/search/widgets/search_appbar.dart @@ -21,7 +21,8 @@ class _SearchBar extends StatefulWidget implements PreferredSizeWidget { }) : super(key: key); @override - Size get preferredSize => const Size.fromHeight(62.0); + Size get preferredSize => + const Size.fromHeight(LunaTextInputBar.defaultAppBarHeight); @override State<_SearchBar> createState() => _State(); @@ -59,7 +60,7 @@ class _State extends State<_SearchBar> { ), ], ), - height: LunaTextInputBar.appBarHeight, + height: LunaTextInputBar.defaultAppBarHeight, ), ); } diff --git a/lib/modules/tautulli/routes/search/widgets/search_result_tile.dart b/lib/modules/tautulli/routes/search/widgets/search_result_tile.dart index 395abcb1..18f9eae7 100644 --- a/lib/modules/tautulli/routes/search/widgets/search_result_tile.dart +++ b/lib/modules/tautulli/routes/search/widgets/search_result_tile.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliSearchResultTile extends StatelessWidget { +class TautulliSearchResultTile extends StatefulWidget { final TautulliSearchResult result; final TautulliMediaType mediaType; - final double _imageDimension = 84.0; - final double _padding = 8.0; const TautulliSearchResultTile({ Key key, @@ -14,68 +12,38 @@ class TautulliSearchResultTile extends StatelessWidget { @required this.mediaType, }) : super(key: key); + @override + State createState() => _State(); +} + +class _State extends State { @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - child: Row( - children: [ - _poster(context), - _details, - ], - ), - onTap: () async => _onTap(context), - ), - decoration: result.art != null && result.art.isNotEmpty - ? LunaCardDecoration( - uri: context - .watch() - .getImageURLFromPath(result.art), - headers: context.watch().headers, - ) - : null, + return LunaBlock( + title: widget.result.title, + body: [ + TextSpan(text: _body1), + TextSpan(text: widget.result.grandparentTitle), + _library(), + ], + posterUrl: context + .watch() + .getImageURLFromPath(widget.result.thumb), + posterHeaders: context.watch().headers, + backgroundHeaders: context.watch().headers, + backgroundUrl: + context.watch().getImageURLFromPath(widget.result.art), + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + onTap: _onTap, ); } - Widget _poster(BuildContext context) => LunaNetworkImage( - url: context.watch().getImageURLFromPath(result.thumb), - headers: context.watch().headers.cast(), - height: _imageDimension, - width: _imageDimension / 1.5, - placeholderAsset: LunaAssets.blankVideo, - ); - - Widget get _details => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitleOne, - _subtitleTwo, - _library, - // _date, - // _userInfo, - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)) + 1, - ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _title => LunaText.title(text: result.title, maxLines: 1); - - Widget get _subtitleOne { + String get _body1 { String _text = ''; - switch (result.mediaType) { + switch (widget.result.mediaType) { case TautulliMediaType.MOVIE: case TautulliMediaType.SHOW: - _text = result.year?.toString() ?? 'Unknown'; + _text = widget.result.year?.toString() ?? 'Unknown'; break; case TautulliMediaType.ARTIST: break; @@ -83,36 +51,31 @@ class TautulliSearchResultTile extends StatelessWidget { case TautulliMediaType.EPISODE: case TautulliMediaType.ALBUM: case TautulliMediaType.TRACK: - _text = result?.parentTitle ?? ''; + _text = widget.result?.parentTitle ?? ''; break; case TautulliMediaType.COLLECTION: _text = - '${result?.minYear ?? 0} ${LunaUI.TEXT_EMDASH} ${result?.maxYear ?? 0}'; + '${widget.result?.minYear ?? 0} ${LunaUI.TEXT_EMDASH} ${widget.result?.maxYear ?? 0}'; break; default: break; } - return LunaText.subtitle(text: _text, maxLines: 1); + return _text; } - Widget get _subtitleTwo => - LunaText.subtitle(text: result.grandparentTitle, maxLines: 1); + TextSpan _library() { + return TextSpan( + text: widget.result.libraryName, + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ); + } - Widget get _library => Text( - result.libraryName, - maxLines: 1, - overflow: TextOverflow.fade, - softWrap: false, - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ); - - Future _onTap(BuildContext context) async => - TautulliMediaDetailsRouter().navigateTo( + Future _onTap() async => TautulliMediaDetailsRouter().navigateTo( context, - ratingKey: result.ratingKey, - mediaType: mediaType, + ratingKey: widget.result.ratingKey, + mediaType: widget.mediaType, ); } diff --git a/lib/modules/tautulli/routes/statistics/widgets/media_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/media_tile.dart index 7ea22d60..78aa4609 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/media_tile.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/media_tile.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliStatisticsMediaTile extends StatelessWidget { +class TautulliStatisticsMediaTile extends StatefulWidget { final Map data; final TautulliMediaType mediaType; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliStatisticsMediaTile({ Key key, @@ -14,118 +12,72 @@ class TautulliStatisticsMediaTile extends StatelessWidget { @required this.mediaType, }) : super(key: key); + @override + State createState() => _State(); +} + +class _State extends State { @override Widget build(BuildContext context) { - return LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _poster(context), - _details(context), - ], - ), - onTap: () async => _onTap(context), - ), - decoration: data['art'] != null && (data['art'] as String).isNotEmpty - ? LunaCardDecoration( - uri: context.watch().getImageURLFromPath( - data['art'], - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: - context.watch().headers.cast(), - ) - : null, + return LunaBlock( + title: widget.data['title'] ?? 'lunasea.Unknown'.tr(), + body: _body(), + onTap: _onTap, + posterUrl: context + .read() + .getImageURLFromPath(widget.data['thumb']), + posterHeaders: context.watch().headers, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + backgroundUrl: + context.read().getImageURLFromPath(widget.data['art']), + backgroundHeaders: context.watch().headers, ); } - Widget _poster(BuildContext context) => LunaNetworkImage( - url: context.watch().getImageURLFromPath(data['thumb']), - headers: context.watch().headers.cast(), - placeholderAsset: LunaAssets.blankVideo, - height: _imageDimension, - width: _imageDimension / 1.5, - ); - - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle(context), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), + List _body() { + return [ + TextSpan( + text: widget.data['total_plays'].toString() + + (widget.data['total_plays'] == 1 ? ' Play' : ' Plays'), + style: TextStyle( + color: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaColours.accent + : null, + fontWeight: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaUI.FONT_WEIGHT_BOLD + : null, ), - ); - - Widget get _title => LunaText.title( - text: data['title'], - maxLines: 1, - ); - - Widget _subtitle(BuildContext context) => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: data['total_plays'].toString() + - (data['total_plays'] == 1 ? ' Play' : ' Plays'), + ), + widget.data['total_duration'] != null + ? TextSpan( + text: Duration(seconds: widget.data['total_duration']) + .lunaTimestampWords, style: TextStyle( color: context.watch().statisticsType == - TautulliStatsType.PLAYS + TautulliStatsType.DURATION ? LunaColours.accent : null, fontWeight: context.watch().statisticsType == - TautulliStatsType.PLAYS + TautulliStatsType.DURATION ? LunaUI.FONT_WEIGHT_BOLD : null, ), - ), - const TextSpan(text: '\n'), - data['total_duration'] != null - ? TextSpan( - text: Duration(seconds: data['total_duration']) - .lunaTimestampWords, - style: TextStyle( - color: context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaColours.accent - : null, - fontWeight: - context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaUI.FONT_WEIGHT_BOLD - : null, - ), - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH), - const TextSpan(text: '\n'), - data['last_play'] != null - ? TextSpan( - text: - 'Last Played ${DateTime.fromMillisecondsSinceEpoch(data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH) - ], - ), - softWrap: false, - maxLines: 3, - overflow: TextOverflow.fade, - ); + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH), + widget.data['last_play'] != null + ? TextSpan( + text: + 'Last Played ${DateTime.fromMillisecondsSinceEpoch(widget.data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH) + ]; + } - Future _onTap(BuildContext context) async => - TautulliMediaDetailsRouter().navigateTo(context, - ratingKey: data['rating_key'], mediaType: mediaType); + Future _onTap() async => TautulliMediaDetailsRouter().navigateTo( + context, + ratingKey: widget.data['rating_key'], + mediaType: widget.mediaType, + ); } diff --git a/lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart index 1923b8c0..f16992ae 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliStatisticsPlatformTile extends StatelessWidget { +class TautulliStatisticsPlatformTile extends StatefulWidget { final Map data; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliStatisticsPlatformTile({ Key key, @@ -13,101 +11,52 @@ class TautulliStatisticsPlatformTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: Row( - children: [ - _placeholder, - _details(context), - ], + State createState() => _State(); +} + +class _State extends State { + @override + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data['platform'] ?? 'Unknown Platform', + body: _body(), + posterPlaceholderIcon: LunaIcons.DEVICES, + posterIsSquare: true, + ); + } + + List _body() { + return [ + TextSpan( + text: widget.data['total_plays'].toString() + + (widget.data['total_plays'] == 1 ? ' Play' : ' Plays'), + style: TextStyle( + color: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaColours.accent + : null, + fontWeight: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaUI.FONT_WEIGHT_BOLD + : null, ), - ); - - Widget get _placeholder => Container( - height: _imageDimension, - width: _imageDimension / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - image: const DecorationImage( - image: AssetImage(LunaAssets.blankDevice), - fit: BoxFit.cover, - ), - ), - ); - - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle(context), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _title => LunaText.title( - text: data['platform'] ?? 'Unknown Platform', - maxLines: 1, - ); - - Widget _subtitle(BuildContext context) => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: data['total_plays'].toString() + - (data['total_plays'] == 1 ? ' Play' : ' Plays'), + ), + widget.data['total_duration'] != null + ? TextSpan( + text: Duration(seconds: widget.data['total_duration']) + .lunaTimestampWords, style: TextStyle( color: context.watch().statisticsType == - TautulliStatsType.PLAYS + TautulliStatsType.DURATION ? LunaColours.accent : null, fontWeight: context.watch().statisticsType == - TautulliStatsType.PLAYS + TautulliStatsType.DURATION ? LunaUI.FONT_WEIGHT_BOLD : null, ), - ), - const TextSpan(text: '\n'), - data['total_duration'] != null - ? TextSpan( - text: Duration(seconds: data['total_duration']) - .lunaTimestampWords, - style: TextStyle( - color: context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaColours.accent - : null, - fontWeight: - context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaUI.FONT_WEIGHT_BOLD - : null, - ), - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH), - const TextSpan(text: '\n'), - data['last_play'] != null - ? TextSpan( - text: - 'Last Used ${DateTime.fromMillisecondsSinceEpoch(data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH) - ], - ), - softWrap: false, - maxLines: 3, - overflow: TextOverflow.fade, - ); + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH), + ]; + } } diff --git a/lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart index 3938e916..70318ceb 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliStatisticsRecentlyWatchedTile extends StatelessWidget { +class TautulliStatisticsRecentlyWatchedTile extends StatefulWidget { final Map data; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliStatisticsRecentlyWatchedTile({ Key key, @@ -13,91 +11,45 @@ class TautulliStatisticsRecentlyWatchedTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _poster(context), - _details(context), - ], - ), - onTap: () async => _onTap(context), - ), - decoration: data['art'] != null && (data['art'] as String).isNotEmpty - ? LunaCardDecoration( - uri: context.watch().getImageURLFromPath( - data['art'], - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context - .watch() - .headers - .cast(), - ) - : null, - ); - - Widget _poster(BuildContext context) => LunaNetworkImage( - url: context.watch().getImageURLFromPath(data['thumb']), - headers: context.watch().headers.cast(), - placeholderAsset: LunaAssets.blankVideo, - height: _imageDimension, - width: _imageDimension / 1.5, - ); + State createState() => _State(); +} - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle(context), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ), - ); +class _State extends State { + @override + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data['title'] ?? 'lunasea.Unknown'.tr(), + body: _body(), + onTap: _onTap, + posterUrl: context + .read() + .getImageURLFromPath(widget.data['thumb']), + posterHeaders: context.watch().headers, + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, + backgroundUrl: + context.read().getImageURLFromPath(widget.data['art']), + backgroundHeaders: context.watch().headers, + ); + } - Widget get _title => LunaText.title( - text: data['title'], - maxLines: 1, - ); + List _body() { + return [ + TextSpan(text: widget.data['friendly_name'] ?? 'Unknown User'), + widget.data['player'] != null + ? TextSpan(text: widget.data['player']) + : const TextSpan(text: LunaUI.TEXT_EMDASH), + widget.data['last_watch'] != null + ? TextSpan( + text: + 'Watched ${DateTime.fromMillisecondsSinceEpoch(widget.data['last_watch'] * 1000)?.lunaAge ?? 'Unknown'}', + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH) + ]; + } - Widget _subtitle(BuildContext context) => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan(text: data['friendly_name'] ?? 'Unknown User'), - const TextSpan(text: '\n'), - data['player'] != null - ? TextSpan(text: data['player']) - : const TextSpan(text: LunaUI.TEXT_EMDASH), - const TextSpan(text: '\n'), - data['last_watch'] != null - ? TextSpan( - text: - 'Watched ${DateTime.fromMillisecondsSinceEpoch(data['last_watch'] * 1000)?.lunaAge ?? 'Unknown'}', - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH) - ], - ), - softWrap: false, - maxLines: 3, - overflow: TextOverflow.fade, + Future _onTap() async => TautulliMediaDetailsRouter().navigateTo( + context, + ratingKey: widget.data['rating_key'], + mediaType: TautulliMediaType.NULL.from(widget.data['media_type']), ); - - Future _onTap(BuildContext context) async => - TautulliMediaDetailsRouter().navigateTo(context, - ratingKey: data['rating_key'], - mediaType: TautulliMediaType.NULL.from(data['media_type'])); } diff --git a/lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart index 91d92bd0..b661d5e8 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class TautulliStatisticsStreamTile extends StatelessWidget { +class TautulliStatisticsStreamTile extends StatefulWidget { final Map data; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliStatisticsStreamTile({ Key key, @@ -12,89 +10,41 @@ class TautulliStatisticsStreamTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: Row( - children: [ - _placeholder, - _details(context), - ], - ), - ); - - Widget get _placeholder => Container( - height: _imageDimension, - width: _imageDimension / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - image: const DecorationImage( - image: AssetImage(LunaAssets.blankStream), - fit: BoxFit.cover, - ), - ), - ); - - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle(context), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ), - ); + State createState() => _State(); +} - Widget get _title => LunaText.title( - text: data['title'] ?? 'Unknown Title', - maxLines: 1, - ); +class _State extends State { + @override + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data['title'] ?? 'Unknown Title', + body: _body(), + posterIsSquare: true, + posterPlaceholderIcon: LunaIcons.SHUFFLE, + ); + } - Widget _subtitle(BuildContext context) => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: data['count'].toString() + - (data['count'] == 1 ? ' Play' : ' Plays'), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - const TextSpan(text: '\n'), - int.tryParse(data['started']) != null - ? TextSpan( - 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)), - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH), - const TextSpan(text: '\n'), - data['last_play'] != null - ? TextSpan( - text: - 'Last Used ${DateTime.fromMillisecondsSinceEpoch(data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH) - ], + List _body() { + return [ + TextSpan( + text: widget.data['count'].toString() + + (widget.data['count'] == 1 ? ' Play' : ' Plays'), + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, ), - softWrap: false, - maxLines: 3, - overflow: TextOverflow.fade, - ); + ), + int.tryParse(widget.data['started']) != null + ? TextSpan( + text: LunaDatabaseValue.USE_24_HOUR_TIME.data + ? DateFormat('yyyy-MM-dd HH:mm').format( + DateTime.fromMillisecondsSinceEpoch( + int.tryParse(widget.data['started']) * 1000)) + : DateFormat('yyyy-MM-dd hh:mm a').format( + DateTime.fromMillisecondsSinceEpoch( + int.tryParse(widget.data['started']) * 1000)), + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH), + ]; + } } diff --git a/lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart b/lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart index a7bd889a..83872b96 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart @@ -27,7 +27,7 @@ class TautulliStatisticsTimeRangeButton extends StatelessWidget { child: Text( TautulliStatisticsTimeRange.values[index].name, style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: range == TautulliStatisticsTimeRange.values[index] ? LunaColours.accent diff --git a/lib/modules/tautulli/routes/statistics/widgets/type_button.dart b/lib/modules/tautulli/routes/statistics/widgets/type_button.dart index 6b831a36..7e23f7fa 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/type_button.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/type_button.dart @@ -27,7 +27,7 @@ class TautulliStatisticsTypeButton extends StatelessWidget { TautulliStatsType.values[index].value .lunaCapitalizeFirstLetters(), style: TextStyle( - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + fontSize: LunaUI.FONT_SIZE_H3, color: type == TautulliStatsType.values[index] ? LunaColours.accent : Colors.white, diff --git a/lib/modules/tautulli/routes/statistics/widgets/user_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/user_tile.dart index 255f25f3..f45f335a 100644 --- a/lib/modules/tautulli/routes/statistics/widgets/user_tile.dart +++ b/lib/modules/tautulli/routes/statistics/widgets/user_tile.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/tautulli.dart'; -class TautulliStatisticsUserTile extends StatelessWidget { +class TautulliStatisticsUserTile extends StatefulWidget { final Map data; - final double _imageDimension = 83.0; - final double _padding = 8.0; const TautulliStatisticsUserTile({ Key key, @@ -13,120 +11,73 @@ class TautulliStatisticsUserTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _poster(context), - _details(context), - ], - ), - onTap: () async => _onTap(context), - ), - ); + State createState() => _State(); +} - Widget _poster(BuildContext context) => LunaNetworkImage( - url: context - .watch() - .getImageURLFromPath(data['user_thumb']), - headers: context.watch().headers.cast(), - placeholderAsset: LunaAssets.blankUser, - height: _imageDimension, - width: _imageDimension / 1.5, - ); +class _State extends State { + @override + Widget build(BuildContext context) { + return LunaBlock( + title: widget.data['friendly_name'] ?? 'Unknown User', + body: _body(), + posterUrl: context + .watch() + .getImageURLFromPath(widget.data['user_thumb']), + posterHeaders: context.watch().headers, + posterIsSquare: true, + posterPlaceholderIcon: LunaIcons.USER, + onTap: _onTap, + ); + } - Widget _details(BuildContext context) => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _subtitle(context), - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, + List _body() { + return [ + TextSpan( + children: [ + TextSpan( + text: widget.data['total_plays'].toString() + + (widget.data['total_plays'] == 1 ? ' Play' : ' Plays'), + style: TextStyle( + color: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaColours.accent + : null, + fontWeight: context.watch().statisticsType == + TautulliStatsType.PLAYS + ? LunaUI.FONT_WEIGHT_BOLD + : null, ), - height: (_imageDimension - (_padding * 2)), ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _title => LunaText.title( - text: data['friendly_name'] ?? 'Unknown User', - maxLines: 1, - ); + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + widget.data['total_duration'] != null + ? TextSpan( + text: Duration(seconds: widget.data['total_duration']) + .lunaTimestampWords, + style: TextStyle( + color: context.watch().statisticsType == + TautulliStatsType.DURATION + ? LunaColours.accent + : null, + fontWeight: context.watch().statisticsType == + TautulliStatsType.DURATION + ? LunaUI.FONT_WEIGHT_BOLD + : null, + ), + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH), + ], + ), + widget.data['last_play'] != null + ? TextSpan( + text: + 'Last Streamed ${DateTime.fromMillisecondsSinceEpoch(widget.data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', + ) + : const TextSpan(text: LunaUI.TEXT_EMDASH) + ]; + } - Widget _subtitle(BuildContext context) => RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - children: [ - TextSpan( - text: data['total_plays'].toString() + - (data['total_plays'] == 1 ? ' Play' : ' Plays'), - style: TextStyle( - color: context.watch().statisticsType == - TautulliStatsType.PLAYS - ? LunaColours.accent - : null, - fontWeight: context.watch().statisticsType == - TautulliStatsType.PLAYS - ? LunaUI.FONT_WEIGHT_BOLD - : null, - ), - ), - const TextSpan(text: '\n'), - data['total_duration'] != null - ? TextSpan( - text: Duration(seconds: data['total_duration']) - .lunaTimestampWords, - style: TextStyle( - color: context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaColours.accent - : null, - fontWeight: - context.watch().statisticsType == - TautulliStatsType.DURATION - ? LunaUI.FONT_WEIGHT_BOLD - : null, - ), - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH), - const TextSpan(text: '\n'), - data['last_play'] != null - ? TextSpan( - text: - 'Last Streamed ${DateTime.fromMillisecondsSinceEpoch(data['last_play'] * 1000)?.lunaAge ?? 'Unknown'}', - ) - : const TextSpan(text: LunaUI.TEXT_EMDASH) - ], - ), - softWrap: false, - maxLines: 3, - overflow: TextOverflow.fade, + Future _onTap() async => TautulliUserDetailsRouter().navigateTo( + context, + userId: widget.data['user_id'], ); - - Future _onTap(BuildContext context) async { - TautulliTableUser _user = await context.watch().users.then( - (users) => users.users.firstWhere( - (user) => user.userId == data['user_id'] ?? -1, - orElse: () => null, - ), - ); - if (_user == null) { - showLunaErrorSnackBar( - title: 'User Not Found', - message: 'Unable to find the Tautulli user', - ); - } else { - TautulliUserDetailsRouter().navigateTo(context, userId: _user.userId); - } - } } diff --git a/lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart b/lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart index 5438a2ff..6e768df7 100644 --- a/lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart +++ b/lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart @@ -12,63 +12,72 @@ class TautulliSyncedItemTile extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaListTile( - context: context, - title: LunaText.title(text: syncedItem.syncTitle), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, + return LunaBlock( + title: syncedItem.syncTitle, + body: [ + _subtitle1(), + _subtitle2(), + _subtitle3(), + ], + backgroundHeaders: context.watch().headers, + backgroundUrl: context.watch().getImageURLFromRatingKey( + syncedItem?.ratingKey, + width: MediaQuery.of(context).size.width.truncate(), ), - children: [ - TextSpan( - text: - (syncedItem.state ?? 'Unknown').lunaCapitalizeFirstLetters(), - style: const TextStyle( - color: LunaColours.accent, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - ), - ), - const TextSpan(text: '\t${LunaUI.TEXT_EMDASH}\t'), - TextSpan( - text: (syncedItem.metadataType ?? 'Unknown') - .lunaCapitalizeFirstLetters()), - const TextSpan(text: '\t${LunaUI.TEXT_BULLET}\t'), - TextSpan( - text: (syncedItem.itemCompleteCount ?? 0) == 1 - ? '1 Item' - : '${(syncedItem.itemCompleteCount ?? 0)} Items', - ), - const TextSpan(text: '\t${LunaUI.TEXT_BULLET}\t'), - TextSpan( - text: - (syncedItem.totalSize ?? 0).lunaBytesToString(decimals: 1)), - const TextSpan(text: '\n'), - TextSpan(text: syncedItem.user ?? 'Unknown User'), - const TextSpan(text: '\n'), - TextSpan(text: syncedItem.platform ?? 'Unknown Platform'), - const TextSpan(text: '\t${LunaUI.TEXT_BULLET}\t'), - TextSpan(text: syncedItem.deviceName ?? 'Unknown Device'), - ], - ), - ), - decoration: syncedItem.ratingKey != null - ? LunaCardDecoration( - uri: context.watch().getImageURLFromRatingKey( - syncedItem.ratingKey, - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context.watch().headers, - ) - : null, + posterHeaders: context.watch().headers, + posterUrl: context.watch().getImageURLFromRatingKey( + syncedItem?.ratingKey, + width: MediaQuery.of(context).size.width.truncate(), + ), + posterPlaceholderIcon: LunaIcons.VIDEO_CAM, onTap: () async => _onTap(context), - contentPadding: true, ); } - Future _onTap(BuildContext context) async => - TautulliMediaDetailsRouter().navigateTo(context, - ratingKey: syncedItem.ratingKey, - mediaType: TautulliMediaType.NULL.from(syncedItem.metadataType)); + TextSpan _subtitle1() { + int _count = syncedItem.itemCompleteCount ?? 0; + int _size = syncedItem.totalSize ?? 0; + String _type = syncedItem.metadataType ?? 'lunasea.Unknown'.tr(); + + return TextSpan( + children: [ + TextSpan(text: _type.lunaCapitalizeFirstLetters()), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: _count == 1 ? '1 Item' : '$_count Items'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: _size.lunaBytesToString(decimals: 1)), + ], + ); + } + + TextSpan _subtitle2() { + return TextSpan( + children: [ + TextSpan(text: syncedItem.user ?? 'Unknown User'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: syncedItem.deviceName ?? 'Unknown Device'), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: syncedItem.platform ?? 'Unknown Platform'), + ], + ); + } + + TextSpan _subtitle3() { + String _state = syncedItem.state ?? 'lunasea.Unknown'.tr(); + return TextSpan( + text: _state.lunaCapitalizeFirstLetters(), + style: const TextStyle( + color: LunaColours.accent, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + ), + ); + } + + Future _onTap(BuildContext context) async { + TautulliMediaDetailsRouter().navigateTo( + context, + ratingKey: syncedItem.ratingKey, + mediaType: TautulliMediaType.NULL.from(syncedItem.metadataType), + ); + } } diff --git a/lib/modules/tautulli/routes/users/widgets/user_tile.dart b/lib/modules/tautulli/routes/users/widgets/user_tile.dart index 8997f5e0..2f7939c2 100644 --- a/lib/modules/tautulli/routes/users/widgets/user_tile.dart +++ b/lib/modules/tautulli/routes/users/widgets/user_tile.dart @@ -4,8 +4,6 @@ import 'package:lunasea/modules/tautulli.dart'; class TautulliUserTile extends StatelessWidget { final TautulliTableUser user; - final double _imageDimension = 70.0; - final double _padding = 8.0; const TautulliUserTile({ Key key, @@ -13,98 +11,30 @@ class TautulliUserTile extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => LunaCard( - context: context, - child: InkWell( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - child: Row( - children: [ - _userThumb(context), - _details, - ], - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + Widget build(BuildContext context) { + return LunaBlock( + title: user.friendlyName, + posterUrl: user.userThumb, + posterHeaders: context.read().headers, + posterPlaceholderIcon: LunaIcons.USER, + posterIsSquare: true, + backgroundUrl: context.watch().getImageURLFromPath( + user.thumb, + width: MediaQuery.of(context).size.width.truncate(), ), - onTap: () async => _enterDetails(context), - ), - decoration: user.thumb != null && user.thumb.isNotEmpty - ? LunaCardDecoration( - uri: context.watch().getImageURLFromPath( - user.thumb, - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context - .watch() - .headers - .cast(), - ) - : null, - ); - - Widget _userThumb(BuildContext context) => LunaNetworkImage( - url: user.userThumb, - placeholderAsset: LunaAssets.blankUser, - height: _imageDimension, - width: _imageDimension, - ); - - Widget get _details => Expanded( - child: Padding( - child: SizedBox( - child: Column( - children: [ - _title, - _lastPlayedTime, - _lastPlayedContent, - ], - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - ), - height: (_imageDimension - (_padding * 2)), - ), - padding: EdgeInsets.all(_padding), - ), - ); - - Widget get _title => LunaText.title(text: user.friendlyName); - - Widget get _lastPlayedTime => Row( - children: [ - const Padding( - child: Icon( - Icons.visibility_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE, - ), - padding: EdgeInsets.only(right: 6.0), - ), - Expanded( - child: LunaText.subtitle( - text: user.lastSeen?.lunaAge ?? 'Never', - maxLines: 1, - ), - ), - ], - ); - - Widget get _lastPlayedContent => Row( - children: [ - const Padding( - child: Icon( - Icons.play_arrow_rounded, - size: LunaUI.FONT_SIZE_SUBTITLE, - ), - padding: EdgeInsets.only(right: 6.0), - ), - Expanded( - child: LunaText.subtitle( - text: user.lastPlayed ?? 'Never', - maxLines: 1, - ), - ), - ], - ); - - Future _enterDetails(BuildContext context) async => - TautulliUserDetailsRouter().navigateTo(context, userId: user.userId); + backgroundHeaders: context.read().headers, + body: [ + TextSpan(text: user.lastSeen?.lunaAge ?? 'Never'), + TextSpan(text: user.lastPlayed ?? 'Never'), + ], + bodyLeadingIcons: const [ + LunaIcons.WATCHED, + LunaIcons.PLAY, + ], + onTap: () async => TautulliUserDetailsRouter().navigateTo( + context, + userId: user.userId, + ), + ); + } } diff --git a/lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart b/lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart index 5b25f4b8..201f22f8 100644 --- a/lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart +++ b/lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart @@ -84,43 +84,28 @@ class _State extends State } Widget _tile(TautulliUserIPRecord record) { - return LunaListTile( - context: context, - title: LunaText.title(text: record.ipAddress), - subtitle: RichText( - text: TextSpan( - style: const TextStyle( - color: Colors.white70, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), + int _count = record.playCount; + return LunaBlock( + title: record.ipAddress, + body: [ + TextSpan( children: [ - TextSpan(text: record.lastSeen?.lunaAge ?? 'Unknown'), - const TextSpan(text: '\t${LunaUI.TEXT_EMDASH}\t'), - TextSpan( - text: record.playCount == 1 - ? '1 Play' - : '${record.playCount} Plays', - ), - const TextSpan(text: '\n'), - TextSpan(text: record.lastPlayed), + TextSpan(text: record.lastSeen?.lunaAge ?? 'lunasea.Unknown'.tr()), + TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()), + TextSpan(text: _count == 1 ? '1 Play' : '$_count Plays'), ], ), - maxLines: 2, - softWrap: false, - overflow: TextOverflow.fade, + TextSpan(text: record.lastPlayed), + ], + backgroundUrl: context.read().getImageURLFromPath( + record.thumb ?? '', + width: MediaQuery.of(context).size.width.truncate(), + ), + backgroundHeaders: context.read().headers, + onTap: () async => TautulliIPAddressDetailsRouter().navigateTo( + context, + ipAddress: record.ipAddress, ), - decoration: record.thumb != null && record.thumb.isNotEmpty - ? LunaCardDecoration( - uri: context.read().getImageURLFromPath( - record.thumb ?? '', - width: MediaQuery.of(context).size.width.truncate(), - ), - headers: context.read().headers, - ) - : null, - contentPadding: true, - onTap: () async => TautulliIPAddressDetailsRouter() - .navigateTo(context, ipAddress: record.ipAddress), ); } } diff --git a/lib/modules/tautulli/routes/users_details/widgets/profile.dart b/lib/modules/tautulli/routes/users_details/widgets/profile.dart index 8e5e8a41..fe273e4b 100644 --- a/lib/modules/tautulli/routes/users_details/widgets/profile.dart +++ b/lib/modules/tautulli/routes/users_details/widgets/profile.dart @@ -131,7 +131,7 @@ class _State extends State ? widget.user.lastSeen?.lunaAge ?? 'Unknown' : 'Never', ), - const LunaTableContent(title: '', body: ''), + LunaTableContent(title: '', body: ''), LunaTableContent( title: 'title', body: widget.user.lastPlayed ?? 'None'), LunaTableContent( diff --git a/localization/lunasea/en.json b/localization/lunasea/en.json index 138a20c6..a6deac31 100644 --- a/localization/lunasea/en.json +++ b/localization/lunasea/en.json @@ -1,4 +1,5 @@ { + "lunasea.Ascending": "Ascending", "lunasea.Add": "Add", "lunasea.AnErrorHasOccurred": "An Error Has Occurred", "lunasea.BackUp": "Back Up", @@ -10,12 +11,14 @@ "lunasea.ComingSoon": "Coming Soon", "lunasea.Dashboard": "Dashboard", "lunasea.Delete": "Delete", + "lunasea.Descending": "Descending", "lunasea.Disable": "Disable", "lunasea.Disabled": "Disabled", "lunasea.Dismiss": "Dismiss", "lunasea.ExternalModules": "External Modules", "lunasea.GoBack": "Go Back", "lunasea.GoToSettings": "Go to Settings", + "lunasea.Home": "Home", "lunasea.IncorrectEncryptionKey": "Incorrect encryption key", "lunasea.Module": "Module", "lunasea.ModuleIsNotEnabled": "{} Is Not Enabled", diff --git a/localization/radarr/en.json b/localization/radarr/en.json index a8d12c89..8bbe7406 100644 --- a/localization/radarr/en.json +++ b/localization/radarr/en.json @@ -41,6 +41,7 @@ "radarr.ManualImportDescription": "Import Movies from the Filesystem", "radarr.MinimumAvailability": "Minimum Availability", "radarr.Missing": "Missing", + "radarr.Monitor": "Monitor", "radarr.Monitored": "Monitored", "radarr.MonitoredDescription": "Monitor for new releases", "radarr.MonitorMovie": "Monitor Movie", @@ -85,6 +86,8 @@ "radarr.SelectQuality": "Select Quality", "radarr.Size": "Size", "radarr.SortCatalogue": "Sort Catalogue", + "radarr.StartSearchFor": "Start Search For…", + "radarr.StartSearchForMissingMovie": "Start search for missing movie", "radarr.Studio": "Studio", "radarr.SystemStatus": "System Status", "radarr.SystemStatusDescription": "System Status & Disk Space", diff --git a/localization/settings/en.json b/localization/settings/en.json index 94a072e5..728a730e 100644 --- a/localization/settings/en.json +++ b/localization/settings/en.json @@ -6,11 +6,13 @@ "settings.AccountHelp": "LunaSea Account", "settings.AccountHelpHint1": "LunaSea offers a free account to backup your configuration to the cloud, with additional features coming in the future!", "settings.AccountSettings": "Account Settings", + "settings.Add": "Add", "settings.AddHeader": "Add Header", "settings.AddModule": "Add Module", "settings.AddModuleFailed": "Failed to Add Module", "settings.AddModuleSuccess": "Module Added", "settings.AddProfile": "Add Profile", + "settings.AddProfileDescription": "Add a New Profile", "settings.AllFieldsAreRequired": "All fields are required", "settings.AmoledTheme": "AMOLED Theme", "settings.AmoledThemeBorders": "AMOLED Theme Borders", @@ -62,6 +64,10 @@ "settings.CustomHeaders": "Custom Headers", "settings.CustomHeadersDescription": "Add Custom Headers to Requests", "settings.DefaultPage": "Default Page", + "settings.DefaultPages": "Default Pages", + "settings.DefaultPagesDescription": "Set Default Landing Pages", + "settings.DefaultSortingAndFiltering": "Default Sorting & Filtering", + "settings.DefaultSortingAndFilteringDescription": "Set Default Sorting & Filtering Methods", "settings.DecryptBackup": "Decrypt Backup", "settings.DecryptBackupHint1": "Please enter the encryption key for this backup.", "settings.DebugMenu": "Debug Menu", @@ -83,6 +89,7 @@ "settings.DeleteModuleHint1": "Are you sure you want to delete this external module?", "settings.DeleteModuleSuccess": "Module Deleted", "settings.DeleteProfile": "Delete Profile", + "settings.DeleteProfileDescription": "Delete an Existing Profile", "settings.DismissBanners": "Dismiss Banners", "settings.DismissBannersHint1": "Are you sure you want to dismiss all tooltip banners?", "settings.DismissBannersHint2": "The tooltip banners will give you tips and hints for features available within LunaSea.", @@ -158,6 +165,7 @@ "settings.RegisteredFailure": "Failed to Register", "settings.RegisteredSuccess": "Successfully Registered", "settings.RenameProfile": "Rename Profile", + "settings.RenameProfileDescription": "Rename an Existing Profile", "settings.ResetPassword": "Reset Password", "settings.Resources": "Resources", "settings.ResourcesDescription": "Useful Resources & Links", diff --git a/localization/sonarr/en.json b/localization/sonarr/en.json index 8399449c..121b9a77 100644 --- a/localization/sonarr/en.json +++ b/localization/sonarr/en.json @@ -106,7 +106,7 @@ "sonarr.MonitorSeries": "Monitor Series", "sonarr.Monitored": "Monitored", "sonarr.Monitoring": "Monitoring", - "sonarr.MonitoredDescription": "Monitor series for new releases", + "sonarr.MonitoredDescription": "Download monitored episodes in this series", "sonarr.More": "More", "sonarr.Name": "Name", "sonarr.NoEpisodesFound": "No Episodes Found", diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 89bcb015..a2b998b1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,11 +5,11 @@ import FlutterMacOS import Foundation +import bitsdojo_window_macos import cloud_firestore import file_selector_macos import firebase_auth import firebase_core -import firebase_crashlytics import firebase_messaging import firebase_storage import package_info_plus_macos @@ -17,14 +17,13 @@ import path_provider_macos import share_plus_macos import shared_preferences_macos import url_launcher_macos -import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) - FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) @@ -32,5 +31,4 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index f05e4c66..9a2cc16e 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,5 +1,7 @@ PODS: - - cloud_firestore (3.1.0): + - bitsdojo_window_macos (0.0.1): + - FlutterMacOS + - cloud_firestore (3.1.5): - Firebase/CoreOnly (~> 8.9.0) - Firebase/Firestore (~> 8.9.0) - firebase_core @@ -11,9 +13,6 @@ PODS: - FirebaseAuth (~> 8.9.0) - Firebase/CoreOnly (8.9.0): - FirebaseCore (= 8.9.0) - - Firebase/Crashlytics (8.9.0): - - Firebase/CoreOnly - - FirebaseCrashlytics (~> 8.9.0) - Firebase/Firestore (8.9.0): - Firebase/CoreOnly - FirebaseFirestore (~> 8.9.0) @@ -23,25 +22,20 @@ PODS: - Firebase/Storage (8.9.0): - Firebase/CoreOnly - FirebaseStorage (~> 8.9.0) - - firebase_auth (3.2.0): + - firebase_auth (3.3.4): - Firebase/Auth (~> 8.9.0) - Firebase/CoreOnly (~> 8.9.0) - firebase_core - FlutterMacOS - - firebase_core (1.10.0): + - firebase_core (1.10.6): - Firebase/CoreOnly (~> 8.9.0) - FlutterMacOS - - firebase_crashlytics (2.3.0): - - Firebase/CoreOnly (~> 8.9.0) - - Firebase/Crashlytics (~> 8.9.0) - - firebase_core - - FlutterMacOS - - firebase_messaging (11.1.0): + - firebase_messaging (11.2.4): - Firebase/CoreOnly (~> 8.9.0) - Firebase/Messaging (~> 8.9.0) - firebase_core - FlutterMacOS - - firebase_storage (10.1.0): + - firebase_storage (10.2.4): - Firebase/CoreOnly (~> 8.9.0) - Firebase/Storage (~> 8.9.0) - firebase_core @@ -55,18 +49,11 @@ PODS: - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - - FirebaseCoreDiagnostics (8.9.0): + - FirebaseCoreDiagnostics (8.10.0): - GoogleDataTransport (~> 9.1) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - nanopb (~> 2.30908.0) - - FirebaseCrashlytics (8.9.0): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleDataTransport (~> 9.1) - - GoogleUtilities/Environment (~> 7.6) - - nanopb (~> 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - FirebaseFirestore (8.9.0): - FirebaseFirestore/AutodetectLeveldb (= 8.9.0) - FirebaseFirestore/AutodetectLeveldb (8.9.0): @@ -75,7 +62,7 @@ PODS: - FirebaseFirestore/Base (8.9.0) - FirebaseFirestore/WithLeveldb (8.9.0): - FirebaseFirestore/Base - - FirebaseInstallations (8.9.0): + - FirebaseInstallations (8.10.0): - FirebaseCore (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/UserDefaults (~> 7.6) @@ -131,15 +118,13 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - - window_size (0.0.2): - - FlutterMacOS DEPENDENCIES: + - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) - cloud_firestore (from `Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - firebase_auth (from `Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos`) - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) - - firebase_crashlytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos`) - firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`) - firebase_storage (from `Flutter/ephemeral/.symlinks/plugins/firebase_storage/macos`) - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.9.0`) @@ -149,7 +134,6 @@ DEPENDENCIES: - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) SPEC REPOS: trunk: @@ -157,7 +141,6 @@ SPEC REPOS: - FirebaseAuth - FirebaseCore - FirebaseCoreDiagnostics - - FirebaseCrashlytics - FirebaseInstallations - FirebaseMessaging - FirebaseStorage @@ -168,6 +151,8 @@ SPEC REPOS: - PromisesObjC EXTERNAL SOURCES: + bitsdojo_window_macos: + :path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos cloud_firestore: :path: Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos file_selector_macos: @@ -176,8 +161,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos firebase_core: :path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos - firebase_crashlytics: - :path: Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos firebase_messaging: :path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos firebase_storage: @@ -197,8 +180,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - window_size: - :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos CHECKOUT OPTIONS: FirebaseFirestore: @@ -206,20 +187,19 @@ CHECKOUT OPTIONS: :tag: 8.9.0 SPEC CHECKSUMS: - cloud_firestore: 635c4f823019b5d905149856e9242806787dc973 + bitsdojo_window_macos: 7e9b1bbb09bdce418d9657ead7fc9d824203ff0d + cloud_firestore: 3081be06968400a92d26f1143e794eb391c553c8 file_selector_macos: ff6dc948d4ddd34e8602a1f60b7d0b4cc6051a47 Firebase: 13d8d96499e2635428d5bf0ec675df21f95d9a95 - firebase_auth: 04bf9ff2c4c793d6c2cb4070bed10b8c693ad8bc - firebase_core: f7e0e04eb75da503e953da4fe54d02697e5ec2d8 - firebase_crashlytics: da8a3127373fcdd9f261e65a11cc5ff7cd0445da - firebase_messaging: a6db59d37bbc35db20d7e3f37430035cb97ad6f4 - firebase_storage: ca5b600aff78ee5cc01dfbee5a3d42ad05d2d25d + firebase_auth: 93152664a5331c5363e2f5528a9bbaadcf4859ed + firebase_core: e0159870e86f57998fc9853a50d6309a3ab2eee6 + firebase_messaging: 5757ecfd9318eb3b14593dbfa0f153d40a021a9f + firebase_storage: 5d0a09f47ef925f20ca3bfa0e837aad77d72778b FirebaseAuth: 2b78b2a32c07b3ecfa4970bdf1d3632b8304099b FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b - FirebaseCoreDiagnostics: 5daa63f1c1409d981a2d5007daa100b36eac6a34 - FirebaseCrashlytics: 40efbd81157dae307ec95612fa1328347284d2c2 + FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18 FirebaseFirestore: 7c22bff6c0e1f222920cd110e9da13a117b61fea - FirebaseInstallations: caa7c8e0d3e2345b8829d2fa9ca1b4dfbf2fcc85 + FirebaseInstallations: 830327b45345ffc859eaa9c17bcd5ae893fd5425 FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316 FirebaseStorage: 452c98c31ccb40b819764bf3039426c4388d9939 FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 @@ -233,8 +213,7 @@ SPEC CHECKSUMS: share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 shared_preferences_macos: 480ce071d0666e37cef23fe6c702293a3d21799e url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4 - window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 PODFILE CHECKSUM: b26bb765a568f6d4f2c0d9767ef2eee43a3f07ed -COCOAPODS: 1.10.2 +COCOAPODS: 1.11.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 7d2d11cd..a38bebff 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -27,7 +27,7 @@ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 9CB665D62619F8E400932F8C /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9CB665D52619F8E400932F8C /* GoogleService-Info.plist */; }; - E2906A573490D10D8845334F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E7DE776FC1CBDC26656AD48 /* Pods_Runner.framework */; }; + CA61A3521F573D6644DB14A9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 618FDC36C766439F24D06F8C /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,7 +54,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 1B29EF870B5C8E869083EFAE /* 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 = ""; }; + 2D911EEB517390915940B299 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* LunaSea.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LunaSea.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -69,12 +69,12 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 4E7DE776FC1CBDC26656AD48 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4743A8E42A87B4AD69F5E777 /* 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 = ""; }; + 618FDC36C766439F24D06F8C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 9CB665D52619F8E400932F8C /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - E5A31BE4917BC75E55DDA859 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - FC093CD24AC4AC3A54B59B19 /* 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 = ""; }; + C23D530B760B9F49181D59A9 /* 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -82,13 +82,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E2906A573490D10D8845334F /* Pods_Runner.framework in Frameworks */, + CA61A3521F573D6644DB14A9 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0D33575850A81F169F37BFE6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 618FDC36C766439F24D06F8C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 33BA886A226E78AF003329D5 /* Configs */ = { isa = PBXGroup; children = ( @@ -108,7 +116,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, C16DBC6D5885D252E3ED5BAD /* Pods */, - 3B26DF832180B5080A22E2D1 /* Frameworks */, + 0D33575850A81F169F37BFE6 /* Frameworks */, ); sourceTree = ""; }; @@ -155,20 +163,12 @@ path = Runner; sourceTree = ""; }; - 3B26DF832180B5080A22E2D1 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4E7DE776FC1CBDC26656AD48 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; C16DBC6D5885D252E3ED5BAD /* Pods */ = { isa = PBXGroup; children = ( - FC093CD24AC4AC3A54B59B19 /* Pods-Runner.debug.xcconfig */, - E5A31BE4917BC75E55DDA859 /* Pods-Runner.release.xcconfig */, - 1B29EF870B5C8E869083EFAE /* Pods-Runner.profile.xcconfig */, + 4743A8E42A87B4AD69F5E777 /* Pods-Runner.debug.xcconfig */, + 2D911EEB517390915940B299 /* Pods-Runner.release.xcconfig */, + C23D530B760B9F49181D59A9 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -180,14 +180,14 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 4D266BF8BB8608230366B56F /* [CP] Check Pods Manifest.lock */, + 3CBBBFA7C9E7216004B3FD0A /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 5639AAACFE2C6B94B773B49D /* [CP] Embed Pods Frameworks */, - D8462304FDC18F87B9E491E7 /* [CP] Copy Pods Resources */, + F996385B971FCEF98CA6257D /* [CP] Embed Pods Frameworks */, + 7DE54AC427A284D3594DB252 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -295,7 +295,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 4D266BF8BB8608230366B56F /* [CP] Check Pods Manifest.lock */ = { + 3CBBBFA7C9E7216004B3FD0A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -317,24 +317,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 5639AAACFE2C6B94B773B49D /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - D8462304FDC18F87B9E491E7 /* [CP] Copy Pods Resources */ = { + 7DE54AC427A284D3594DB252 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -351,6 +334,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; + F996385B971FCEF98CA6257D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 2722837e..65438bdd 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -1,7 +1,12 @@ import Cocoa import FlutterMacOS +import bitsdojo_window_macos + +class MainFlutterWindow: BitsdojoWindow { + override func bitsdojo_window_configure() -> UInt { + return 0 + } -class MainFlutterWindow: NSWindow { override func awakeFromNib() { let flutterViewController = FlutterViewController.init() let windowFrame = self.frame diff --git a/packages/sonarr/lib/src/controllers/series.dart b/packages/sonarr/lib/src/controllers/series.dart index 54d88f6c..4645efe1 100644 --- a/packages/sonarr/lib/src/controllers/series.dart +++ b/packages/sonarr/lib/src/controllers/series.dart @@ -23,6 +23,7 @@ class SonarrController_Series { List tags = const [], bool searchForMissingEpisodes = false, bool searchForCutoffUnmetEpisodes = false, + bool includeSeasonImages = false, }) async => _commandAddSeries( _client, @@ -58,16 +59,24 @@ class SonarrController_Series { /// Returns the series with the matching ID. Future get({ required int seriesId, + bool includeSeasonImages = false, }) async => _commandGetSeries( _client, seriesId: seriesId, + includeSeasonImages: includeSeasonImages, ); /// Handler for [series](https://github.com/Sonarr/Sonarr/wiki/Series#get). /// /// Returns a list of all series. - Future> getAll() async => _commandGetAllSeries(_client); + Future> getAll({ + bool includeSeasonImages = false, + }) async => + _commandGetAllSeries( + _client, + includeSeasonImages: includeSeasonImages, + ); /// Handler for [series]https://github.com/Sonarr/Sonarr/wiki/Series#put). /// diff --git a/packages/sonarr/lib/src/controllers/series/get_all_series.dart b/packages/sonarr/lib/src/controllers/series/get_all_series.dart index 24291f29..cb97aaac 100644 --- a/packages/sonarr/lib/src/controllers/series/get_all_series.dart +++ b/packages/sonarr/lib/src/controllers/series/get_all_series.dart @@ -1,7 +1,12 @@ part of sonarr_commands; -Future> _commandGetAllSeries(Dio client) async { - Response response = await client.get('series'); +Future> _commandGetAllSeries( + Dio client, { + bool includeSeasonImages = false, +}) async { + Response response = await client.get('series', queryParameters: { + 'includeSeasonImages': includeSeasonImages, + }); return (response.data as List) .map((series) => SonarrSeries.fromJson(series)) .toList(); diff --git a/packages/sonarr/lib/src/controllers/series/get_series.dart b/packages/sonarr/lib/src/controllers/series/get_series.dart index a696fd2b..c2f4d327 100644 --- a/packages/sonarr/lib/src/controllers/series/get_series.dart +++ b/packages/sonarr/lib/src/controllers/series/get_series.dart @@ -3,7 +3,10 @@ part of sonarr_commands; Future _commandGetSeries( Dio client, { required int seriesId, + bool includeSeasonImages = false, }) async { - Response response = await client.get('series/$seriesId'); + Response response = await client.get('series/$seriesId', queryParameters: { + 'includeSeasonImages': includeSeasonImages, + }); return SonarrSeries.fromJson(response.data); } diff --git a/packages/sonarr/lib/src/models/series/season.dart b/packages/sonarr/lib/src/models/series/season.dart index baf9a9e0..64438095 100644 --- a/packages/sonarr/lib/src/models/series/season.dart +++ b/packages/sonarr/lib/src/models/series/season.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; -import 'season_statistics.dart'; +import 'package:sonarr/models.dart'; part 'season.g.dart'; @@ -15,10 +15,14 @@ class SonarrSeriesSeason { @JsonKey(name: 'statistics') SonarrSeriesSeasonStatistics? statistics; + @JsonKey(name: 'images') + List? images; + SonarrSeriesSeason({ this.seasonNumber, this.monitored, this.statistics, + this.images, }); @override diff --git a/packages/sonarr/lib/src/models/series/season.g.dart b/packages/sonarr/lib/src/models/series/season.g.dart index 63a23121..d93d647d 100644 --- a/packages/sonarr/lib/src/models/series/season.g.dart +++ b/packages/sonarr/lib/src/models/series/season.g.dart @@ -14,6 +14,9 @@ SonarrSeriesSeason _$SonarrSeriesSeasonFromJson(Map json) => ? null : SonarrSeriesSeasonStatistics.fromJson( json['statistics'] as Map), + images: (json['images'] as List?) + ?.map((e) => SonarrImage.fromJson(e as Map)) + .toList(), ); Map _$SonarrSeriesSeasonToJson(SonarrSeriesSeason instance) { @@ -28,5 +31,6 @@ Map _$SonarrSeriesSeasonToJson(SonarrSeriesSeason instance) { writeNotNull('seasonNumber', instance.seasonNumber); writeNotNull('monitored', instance.monitored); writeNotNull('statistics', instance.statistics?.toJson()); + writeNotNull('images', instance.images?.map((e) => e.toJson()).toList()); return val; } diff --git a/pubspec.lock b/pubspec.lock index 58ef324a..8b0417fb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "30.0.0" + version: "31.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.7.0" + version: "2.8.0" archive: dependency: transitive description: @@ -35,7 +35,7 @@ packages: name: asn1lib url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" async: dependency: transitive description: @@ -49,7 +49,42 @@ packages: name: badges url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" + bitsdojo_window: + dependency: "direct main" + description: + name: bitsdojo_window + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+1" + bitsdojo_window_linux: + dependency: transitive + description: + name: bitsdojo_window_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + bitsdojo_window_macos: + dependency: transitive + description: + name: bitsdojo_window_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + bitsdojo_window_platform_interface: + dependency: transitive + description: + name: bitsdojo_window_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + bitsdojo_window_windows: + dependency: transitive + description: + name: bitsdojo_window_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" boolean_selector: dependency: transitive description: @@ -63,7 +98,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.2.0" build_config: dependency: transitive description: @@ -84,21 +119,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.6" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.7" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "7.2.2" + version: "7.2.3" built_collection: dependency: transitive description: @@ -154,21 +189,21 @@ packages: name: cloud_firestore url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.5" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "5.4.5" + version: "5.4.10" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.5" code_builder: dependency: transitive description: @@ -217,14 +252,14 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.1" dio: dependency: "direct main" description: name: dio url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.0.4" easy_localization: dependency: "direct main" description: @@ -260,6 +295,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.1" + fading_edge_scrollview: + dependency: "direct main" + description: + name: fading_edge_scrollview + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" ffi: dependency: transitive description: @@ -280,7 +322,7 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "4.2.2+1" + version: "4.2.8" file_selector_macos: dependency: "direct main" description: @@ -295,132 +337,90 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.2" - firebase: - dependency: transitive - description: - name: firebase - url: "https://pub.dartlang.org" - source: hosted - version: "9.0.2" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - url: "https://pub.dartlang.org" - source: hosted - version: "8.3.4" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0+1" firebase_auth: dependency: "direct main" description: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "3.3.4" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "6.1.4" + version: "6.1.9" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "3.3.5" firebase_core: dependency: "direct main" description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "1.10.0" + version: "1.10.6" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.2.3" firebase_core_web: dependency: transitive description: name: firebase_core_web url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - firebase_crashlytics: - dependency: "direct main" - description: - name: firebase_crashlytics - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.0" - firebase_crashlytics_platform_interface: - dependency: transitive - description: - name: firebase_crashlytics_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.6" + version: "1.5.3" firebase_messaging: dependency: "direct main" description: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "11.1.0" + version: "11.2.4" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.0.9" + version: "3.1.4" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.5" firebase_storage: dependency: "direct main" description: name: firebase_storage url: "https://pub.dartlang.org" source: hosted - version: "10.1.0" + version: "10.2.4" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.6" + version: "4.0.11" firebase_storage_web: dependency: transitive description: name: firebase_storage_web url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.2.5" fixnum: dependency: transitive description: @@ -434,14 +434,14 @@ packages: name: fl_chart url: "https://pub.dartlang.org" source: hosted - version: "0.40.2" + version: "0.40.6" flash: dependency: "direct main" description: name: flash url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.3+1" fluro: dependency: "direct main" description: @@ -479,14 +479,14 @@ packages: name: flutter_native_splash url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" + version: "1.3.2" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" flutter_spinkit: dependency: "direct main" description: @@ -533,7 +533,7 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" hive_flutter: dependency: "direct main" description: @@ -575,7 +575,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.0.8" + version: "3.1.0" in_app_purchase: dependency: "direct main" description: @@ -638,7 +638,7 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.3.0" + version: "4.4.0" lints: dependency: transitive description: @@ -660,6 +660,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" meta: dependency: transitive description: @@ -771,21 +778,35 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.8" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.4" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.4" path_provider_platform_interface: dependency: transitive description: @@ -820,7 +841,7 @@ packages: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -869,14 +890,14 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" quick_actions: dependency: "direct main" description: name: quick_actions url: "https://pub.dartlang.org" source: hosted - version: "0.6.0+7" + version: "0.6.0+8" quick_actions_platform_interface: dependency: transitive description: @@ -946,6 +967,20 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted + version: "2.0.11" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted version: "2.0.8" shared_preferences_linux: dependency: transitive @@ -1042,14 +1077,14 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.2.1" source_helper: dependency: transitive description: name: source_helper url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" source_map_stack_trace: dependency: transitive description: @@ -1154,21 +1189,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.3" + version: "1.20.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.7" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.10" timing: dependency: transitive description: @@ -1176,6 +1211,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + transparent_image: + dependency: "direct main" + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" tuple: dependency: "direct main" description: @@ -1203,7 +1245,21 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.12" + version: "6.0.17" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.13" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.13" url_launcher_linux: dependency: transitive description: @@ -1231,7 +1287,7 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" url_launcher_windows: dependency: transitive description: @@ -1259,7 +1315,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "7.4.0" + version: "7.5.0" wake_on_lan: dependency: "direct main" description: @@ -1294,16 +1350,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.10" - window_size: - dependency: "direct main" - description: - path: "plugins/window_size" - ref: "57a2cd88486105d3d813583339eb0e23c9bfd6d2" - resolved-ref: "57a2cd88486105d3d813583339eb0e23c9bfd6d2" - url: "git://github.com/google/flutter-desktop-embedding.git" - source: git - version: "0.1.0" + version: "2.3.1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index eb91d662..cecbbc3e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,28 +8,27 @@ environment: dependencies: flutter: sdk: flutter - badges: ^2.0.1 - cloud_firestore: ^3.1.0 + badges: ^2.0.2 + cloud_firestore: ^3.1.5 convert: ^3.0.1 - dio: ^4.0.1 + dio: ^4.0.4 easy_localization: ^3.0.0 encrypt: ^5.0.1 expandable: ^5.0.1 - file_picker: ^4.2.2+1 + fading_edge_scrollview: ^2.0.1 + file_picker: ^4.2.8 file_selector_macos: ^0.0.4 file_selector_platform_interface: ^2.0.2 - firebase_analytics: ^8.3.4 - firebase_auth: ^3.2.0 - firebase_core: ^1.10.0 - firebase_crashlytics: ^2.3.0 - firebase_messaging: ^11.1.0 - firebase_storage: ^10.1.0 - fl_chart: ^0.40.2 - flash: ^2.0.3 + firebase_auth: ^3.3.4 + firebase_core: ^1.10.6 + firebase_messaging: ^11.2.4 + firebase_storage: ^10.2.4 + fl_chart: ^0.40.6 + flash: ^2.0.3+1 fluro: ^2.0.3 flutter_spinkit: ^5.1.0 google_nav_bar: ^5.0.5 - hive: ^2.0.4 + hive: ^2.0.5 hive_flutter: ^1.1.0 in_app_purchase: ^1.0.9 infinite_scroll_pagination: ^3.1.0 @@ -38,10 +37,10 @@ dependencies: overseerr: path: packages/overseerr package_info_plus: ^1.3.0 - path_provider: ^2.0.6 + path_provider: ^2.0.8 percent_indicator: ^3.4.0 provider: ^6.0.1 - quick_actions: ^0.6.0+7 + quick_actions: ^0.6.0+8 radarr: path: packages/radarr share_plus: ^3.0.4 @@ -53,27 +52,24 @@ dependencies: table_calendar: ^3.0.3 tautulli: path: packages/tautulli + transparent_image: ^2.0.0 tuple: ^2.0.0 - url_launcher: ^6.0.12 + url_launcher: ^6.0.17 uuid: ^3.0.5 wake_on_lan: path: packages/wake_on_lan - window_size: - git: - url: git://github.com/google/flutter-desktop-embedding.git - path: plugins/window_size - ref: 57a2cd88486105d3d813583339eb0e23c9bfd6d2 xml: ^5.3.1 + bitsdojo_window: ^0.1.1+1 dev_dependencies: - build_runner: ^2.1.5 + build_runner: ^2.1.7 coverage: ^1.0.3 flutter_launcher_icons: ^0.9.2 flutter_lints: ^1.0.4 - flutter_native_splash: ^1.3.1 + flutter_native_splash: ^1.3.2 hive_generator: ^1.1.1 mockito: ^5.0.16 - test: ^1.19.3 + test: ^1.20.0 flutter: uses-material-design: true @@ -90,12 +86,12 @@ flutter: flutter_icons: android: true ios: true - image_path: "assets/images/branding/icon.png" + image_path: "assets/icon/icon.png" adaptive_icon_background: "#32323E" - adaptive_icon_foreground: "assets/images/branding/icon_adaptive.png" + adaptive_icon_foreground: "assets/icon/icon_adaptive.png" flutter_native_splash: - image: "assets/images/branding/splash.png" + image: "assets/images/branding_full.png" color: "#32323E" scripts: derry.yaml