mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-30 20:24:25 +00:00
[Release] v4.2.0+40200001 (#287)
NEW - [Settings/Modules] Add an information/help button with module descriptions and links TWEAKS - [Radarr] Always show the amount of days content will be available instead of limiting it to only content in the next 30 days - [Settings] Moved "Backup & Restore" and "Logs" to "System" section - [Settings] Merged "Customization" and "Modules" sections into "Configuration" - [Settings/Sonarr] Removed the need to enable Sonarr to test the connection - [Settings/Tautulli] Removed the need to enable Tautulli to test the connection FIXES - [IAPs] Ensure all IAPs are marked as "consumed" - [Flutter] Updated packages - [Logging] Updated Sentry to v4 framework to improve capturing fatal/crashing bugs - [Settings/Logs] Hide exception and stack trace buttons when an error is not available - [Sonarr/History] Fixed history fetching the oldest entries, not the newest - [State] Correctly clear state when clearing LunaSea's configuration - [Tautulli/Activity] Fixed consistency of hardware transcoding indicator compared to the web UI - [UI/Divider] Fix consistency of divider width across regular and AMOLED dark theme
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -49,3 +49,8 @@ _changelog.txt
|
||||
# Build Configurations
|
||||
ExportOptions.plist
|
||||
key.properties
|
||||
|
||||
# Firebase
|
||||
android/app/google-services.json
|
||||
ios/GoogleService-Info.plist
|
||||
output/
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,5 +1,29 @@
|
||||
# LunaSea Changelog
|
||||
|
||||
## [Beta/TestFlight] v4.2.0 (40200001)
|
||||
|
||||
#### NEW
|
||||
- `[Settings/Modules]` Add an information/help button with module descriptions and links
|
||||
|
||||
#### TWEAKS
|
||||
- `[Radarr]` Always show the amount of days content will be available instead of limiting it to only content in the next 30 days
|
||||
- `[Settings]` Moved "Backup & Restore" and "Logs" to "System" section
|
||||
- `[Settings]` Merged "Customization" and "Modules" sections into "Configuration"
|
||||
- `[Settings/Sonarr]` Removed the need to enable Sonarr to test the connection
|
||||
- `[Settings/Tautulli]` Removed the need to enable Tautulli to test the connection
|
||||
|
||||
#### FIXES
|
||||
- `[IAPs]` Ensure all IAPs are marked as "consumed"
|
||||
- `[Flutter]` Updated packages
|
||||
- `[Logging]` Updated Sentry to v4 framework to improve capturing fatal/crashing bugs
|
||||
- `[Settings/Logs]` Hide exception and stack trace buttons when an error is not available
|
||||
- `[Sonarr/History]` Fixed history fetching the oldest entries, not the newest
|
||||
- `[State]` Correctly clear state when clearing LunaSea's configuration
|
||||
- `[Tautulli/Activity]` Fixed consistency of hardware transcoding indicator compared to the web UI
|
||||
- `[UI/Divider]` Fix consistency of divider width across regular and AMOLED dark theme
|
||||
|
||||
---
|
||||
|
||||
## [Stable/Production] v4.1.1 (40101001)
|
||||
|
||||
#### NEW
|
||||
|
||||
14
README.md
14
README.md
@@ -2,21 +2,19 @@
|
||||
[](https://play.google.com/store/apps/details?id=app.lunasea.lunasea)
|
||||
[](https://www.reddit.com/r/LunaSeaApp)
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
LunaSea is a fully featured, open source self-hosted software controller! Focused on giving you a seamless experience between all of your self-hosted media software, LunaSea supports:
|
||||
LunaSea is a fully featured, open source self-hosted controller! Focused on giving you a seamless experience between all of your self-hosted media software, LunaSea supports:
|
||||
|
||||
- Lidarr
|
||||
- Radarr
|
||||
- Sonarr
|
||||
- SABnzbd
|
||||
- NZBGet
|
||||
- NZBHydra2
|
||||
- SABnzbd
|
||||
- Newznab Indexer Searching
|
||||
- Wake on LAN
|
||||
- NZBHydra2
|
||||
- Tautulli
|
||||
- Wake on LAN
|
||||
|
||||
LunaSea even comes with support for multiple instances of applications using profiles, backing up and restoring your configuration to your filesystem, an AMOLED black theme, and more!
|
||||
|
||||
@@ -33,6 +31,7 @@ LunaSea even comes with support for multiple instances of applications using pro
|
||||
3. [Flutter Framework (Stable Channel)](https://flutter.dev/)
|
||||
4. [XCode](https://apps.apple.com/ca/app/xcode/id497799835?mt=12)
|
||||
5. [Developer Certificate Configured](https://github.com/LunaSeaApp/LunaSea/wiki/Setup-of-Development-Certificate)
|
||||
6. [FlutterFire for IOS Configured](https://firebase.flutter.dev/docs/installation/ios/)
|
||||
|
||||
#### Developing
|
||||
|
||||
@@ -74,6 +73,7 @@ LunaSea even comes with support for multiple instances of applications using pro
|
||||
1. [Android SDK/Android Studio Installed & Configured](https://developer.android.com/studio)
|
||||
2. [Flutter Framework (Stable Channel)](https://flutter.dev/)
|
||||
3. [Keystore Configured](https://github.com/LunaSeaApp/LunaSea/wiki/Configure-Keystore)
|
||||
4. [FlutterFire for Android Configured](https://firebase.flutter.dev/docs/installation/android/)
|
||||
|
||||
#### Developing
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ if (flutterVersionName == null) {
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
|
||||
@@ -8,6 +8,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.4'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
17
ios/Podfile
17
ios/Podfile
@@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
platform :ios, '11.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
@@ -30,25 +30,12 @@ flutter_ios_podfile_setup
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '6.33.0'
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ENABLE_BITCODE'] = 'NO'
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
'PERMISSION_SPEECH_RECOGNIZER=0',
|
||||
'PERMISSION_SENSORS=0',
|
||||
'PERMISSION_MEDIA_LIBRARY=0',
|
||||
'PERMISSION_EVENTS=0',
|
||||
'PERMISSION_CONTACTS=0',
|
||||
'PERMISSION_REMINDERS=0',
|
||||
'PERMISSION_MICROPHONE=0'
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
131
ios/Podfile.lock
131
ios/Podfile.lock
@@ -1,4 +1,9 @@
|
||||
PODS:
|
||||
- cloud_firestore (0.14.4):
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- Firebase/Firestore (~> 6.33.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- DKImagePickerController/Core (4.3.2):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
@@ -33,20 +38,92 @@ PODS:
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Firebase/Auth (6.33.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseAuth (~> 6.9.2)
|
||||
- Firebase/CoreOnly (6.33.0):
|
||||
- FirebaseCore (= 6.10.3)
|
||||
- Firebase/Firestore (6.33.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseFirestore (~> 1.18.0)
|
||||
- Firebase/Storage (6.33.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseStorage (~> 3.9.0)
|
||||
- firebase_auth (0.18.4-1):
|
||||
- Firebase/Auth (~> 6.33.0)
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- firebase_core (0.5.3):
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- Flutter
|
||||
- firebase_storage (5.2.0):
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- Firebase/Storage (~> 6.33.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseAuth (6.9.2):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- FirebaseCore (6.10.3):
|
||||
- FirebaseCoreDiagnostics (~> 1.6)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- FirebaseCoreDiagnostics (1.7.0):
|
||||
- GoogleDataTransport (~> 7.4)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- nanopb (~> 1.30906.0)
|
||||
- FirebaseFirestore (1.18.0)
|
||||
- FirebaseStorage (3.9.1):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- Flutter (1.0.0)
|
||||
- GoogleDataTransport (7.5.1):
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (6.7.2):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Environment (6.7.2):
|
||||
- PromisesObjC (~> 1.2)
|
||||
- GoogleUtilities/Logger (6.7.2):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Network (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (6.7.2)"
|
||||
- GoogleUtilities/Reachability (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMSessionFetcher/Core (1.5.0)
|
||||
- in_app_purchase (0.0.1):
|
||||
- Flutter
|
||||
- nanopb (1.30906.0):
|
||||
- nanopb/decode (= 1.30906.0)
|
||||
- nanopb/encode (= 1.30906.0)
|
||||
- nanopb/decode (1.30906.0)
|
||||
- nanopb/encode (1.30906.0)
|
||||
- package_info (0.0.1):
|
||||
- Flutter
|
||||
- path_provider (0.0.1):
|
||||
- Flutter
|
||||
- "permission_handler (5.0.1+1)":
|
||||
- Flutter
|
||||
- PromisesObjC (1.2.11)
|
||||
- quick_actions (0.0.1):
|
||||
- Flutter
|
||||
- SDWebImage (5.10.0):
|
||||
- SDWebImage/Core (= 5.10.0)
|
||||
- SDWebImage/Core (5.10.0)
|
||||
- Sentry (6.0.12):
|
||||
- Sentry/Core (= 6.0.12)
|
||||
- Sentry/Core (6.0.12)
|
||||
- sentry_flutter (0.0.1):
|
||||
- Flutter
|
||||
- Sentry (~> 6.0.10)
|
||||
- share (0.0.1):
|
||||
- Flutter
|
||||
- SwiftyGif (5.3.0)
|
||||
@@ -54,13 +131,19 @@ PODS:
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
|
||||
- FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `6.33.0`)
|
||||
- Flutter (from `Flutter`)
|
||||
- in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
|
||||
- quick_actions (from `.symlinks/plugins/quick_actions/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- share (from `.symlinks/plugins/share/ios`)
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
|
||||
@@ -68,12 +151,34 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- Firebase
|
||||
- FirebaseAuth
|
||||
- FirebaseCore
|
||||
- FirebaseCoreDiagnostics
|
||||
- FirebaseStorage
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- GTMSessionFetcher
|
||||
- nanopb
|
||||
- PromisesObjC
|
||||
- SDWebImage
|
||||
- Sentry
|
||||
- SwiftyGif
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
cloud_firestore:
|
||||
:path: ".symlinks/plugins/cloud_firestore/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
firebase_auth:
|
||||
:path: ".symlinks/plugins/firebase_auth/ios"
|
||||
firebase_core:
|
||||
:path: ".symlinks/plugins/firebase_core/ios"
|
||||
firebase_storage:
|
||||
:path: ".symlinks/plugins/firebase_storage/ios"
|
||||
FirebaseFirestore:
|
||||
:git: https://github.com/invertase/firestore-ios-sdk-frameworks.git
|
||||
:tag: 6.33.0
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
in_app_purchase:
|
||||
@@ -86,26 +191,50 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/permission_handler/ios"
|
||||
quick_actions:
|
||||
:path: ".symlinks/plugins/quick_actions/ios"
|
||||
sentry_flutter:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
share:
|
||||
:path: ".symlinks/plugins/share/ios"
|
||||
url_launcher:
|
||||
:path: ".symlinks/plugins/url_launcher/ios"
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
FirebaseFirestore:
|
||||
:git: https://github.com/invertase/firestore-ios-sdk-frameworks.git
|
||||
:tag: 6.33.0
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
cloud_firestore: b8c0e15fa49dfff87c2817d288b577e5dca2df13
|
||||
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
|
||||
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
|
||||
firebase_auth: d5159db3873478d1ac839af7b10d2f831516136a
|
||||
firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659
|
||||
firebase_storage: a023e199edb807d8481c8aa722c8516f462ffab2
|
||||
FirebaseAuth: c92d49ada7948d1a23466e3db17bc4c2039dddc3
|
||||
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
|
||||
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
|
||||
FirebaseFirestore: 4e00bcfe64c4cb493124b87e249abe6c580403c7
|
||||
FirebaseStorage: 15e0f15ef3c7fec3d1899d68623e47d4447066b4
|
||||
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
||||
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
|
||||
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
|
||||
GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
|
||||
in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
|
||||
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6
|
||||
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
|
||||
quick_actions: 6cb2390c4dab0e737c94573c27e18d9666710720
|
||||
SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee
|
||||
Sentry: 8ffcfe3416e0a58b90bb1ae319e8e8a13022a03e
|
||||
sentry_flutter: e57e33f56d5791e4effa48e85e44505367686b27
|
||||
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
|
||||
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
|
||||
PODFILE CHECKSUM: d583357f6c19d22241654320250048fb085838b3
|
||||
PODFILE CHECKSUM: 1b77370c3c4c6c5f98e1d8ea21a13d3289f277f1
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
9C2991242453FE3100B93AB2 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C2991232453FE3100B93AB2 /* StoreKit.framework */; };
|
||||
9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -47,6 +48,7 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9C0A396F23E0F00900EC8127 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
A046209115F73D5EC348E242 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
D5A8F6AE14D184A58D8A7331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E2F8912DE8B8A6F72277E96C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -79,6 +81,7 @@
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */,
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
@@ -153,6 +156,7 @@
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
FB187DB152E6AC8A61142056 /* [CP] Embed Pods Frameworks */,
|
||||
5BA92B8D22335C6E222F251E /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -205,6 +209,7 @@
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
@@ -227,6 +232,24 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
5BA92B8D22335C6E222F251E /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
|
||||
"${PODS_ROOT}/FirebaseFirestore/FirebaseFirestore/Resources/gRPCCertificates-Cpp.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -273,13 +296,19 @@
|
||||
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
|
||||
"${PODS_ROOT}/../Flutter/Flutter.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/in_app_purchase/in_app_purchase.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/package_info/package_info.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share/share.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
|
||||
);
|
||||
@@ -288,13 +317,19 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
|
||||
);
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
export 'core/constants.dart';
|
||||
export 'core/adapters.dart';
|
||||
export 'core/configuration.dart';
|
||||
export 'core/constants.dart';
|
||||
export 'core/database.dart';
|
||||
export 'core/dialogs.dart';
|
||||
export 'core/encryption.dart';
|
||||
export 'core/extensions.dart';
|
||||
export 'core/filesystem.dart';
|
||||
export 'core/image_cache.dart';
|
||||
export 'core/filesystem.dart';
|
||||
export 'core/firebase.dart';
|
||||
export 'core/in_app_purchases.dart';
|
||||
export 'core/logger.dart';
|
||||
export 'core/luna_ui.dart';
|
||||
export 'core/module_map.dart';
|
||||
export 'core/module_metadata.dart';
|
||||
export 'core/networking.dart';
|
||||
export 'core/profile.dart';
|
||||
export 'core/providers.dart';
|
||||
export 'core/quick_actions.dart';
|
||||
export 'core/router.dart';
|
||||
export 'core/state.dart';
|
||||
export 'core/theme.dart';
|
||||
export 'core/types.dart';
|
||||
export 'core/ui.dart';
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export 'adapters/indexer.dart';
|
||||
export 'adapters/lunasea.dart';
|
||||
export 'adapters/profile.dart';
|
||||
61
lib/core/adapters/indexer.dart
Normal file
61
lib/core/adapters/indexer.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'indexer.g.dart';
|
||||
|
||||
/// Hive database object containing all information on an indexer
|
||||
@HiveType(typeId: 1, adapterName: 'IndexerHiveObjectAdapter')
|
||||
class IndexerHiveObject extends HiveObject {
|
||||
/// Create a new [IndexerHiveObject] object with all fields set to empty values('', false, 0, {}, etc.)
|
||||
factory IndexerHiveObject.empty() => IndexerHiveObject(
|
||||
displayName: '',
|
||||
host: '',
|
||||
key: '',
|
||||
headers: {},
|
||||
);
|
||||
|
||||
/// Create a new [IndexerHiveObject] from another [IndexerHiveObject] (deep-copy).
|
||||
factory IndexerHiveObject.fromIndexerHiveObject(IndexerHiveObject indexer) => IndexerHiveObject(
|
||||
displayName: indexer.displayName,
|
||||
host: indexer.host,
|
||||
key: indexer.key,
|
||||
headers: indexer.headers,
|
||||
);
|
||||
|
||||
/// Create a new [IndexerHiveObject] from a map where the keys map 1-to-1.
|
||||
///
|
||||
/// - Does _not_ do type checking, and will throw an error if the type is invalid.
|
||||
/// - If the key is null, sets to the "empty" value
|
||||
factory IndexerHiveObject.fromMap(Map indexer) => IndexerHiveObject(
|
||||
displayName: indexer['displayName'] ?? '',
|
||||
host: indexer['host'] ?? '',
|
||||
key: indexer['key'] ?? '',
|
||||
headers: indexer['headers'] ?? {},
|
||||
);
|
||||
|
||||
IndexerHiveObject({
|
||||
@required this.displayName,
|
||||
@required this.host,
|
||||
@required this.key,
|
||||
@required this.headers,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => toMap().toString();
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
"displayName": displayName ?? '',
|
||||
"host": host ?? '',
|
||||
"key": key ?? '',
|
||||
"headers": headers ?? {},
|
||||
};
|
||||
|
||||
@HiveField(0)
|
||||
String displayName;
|
||||
@HiveField(1)
|
||||
String host;
|
||||
@HiveField(2)
|
||||
String key;
|
||||
@HiveField(3)
|
||||
Map headers;
|
||||
}
|
||||
415
lib/core/adapters/profile.dart
Normal file
415
lib/core/adapters/profile.dart
Normal file
@@ -0,0 +1,415 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules.dart';
|
||||
|
||||
part 'profile.g.dart';
|
||||
|
||||
/// Hive database object containing all profile fields
|
||||
@HiveType(typeId: 0, adapterName: 'ProfileHiveObjectAdapter')
|
||||
class ProfileHiveObject extends HiveObject {
|
||||
/// Create a new [ProfileHiveObject] object with all fields set to empty values('', false, 0, {}, etc.)
|
||||
factory ProfileHiveObject.empty() => ProfileHiveObject(
|
||||
//Lidarr
|
||||
lidarrEnabled: false,
|
||||
lidarrHost: '',
|
||||
lidarrKey: '',
|
||||
lidarrHeaders: {},
|
||||
//Radarr
|
||||
radarrEnabled: false,
|
||||
radarrHost: '',
|
||||
radarrKey: '',
|
||||
radarrHeaders: {},
|
||||
//Sonarr
|
||||
sonarrEnabled: false,
|
||||
sonarrHost: '',
|
||||
sonarrKey: '',
|
||||
sonarrVersion3: false,
|
||||
sonarrHeaders: {},
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: false,
|
||||
sabnzbdHost: '',
|
||||
sabnzbdKey: '',
|
||||
sabnzbdHeaders: {},
|
||||
//NZBGet
|
||||
nzbgetEnabled: false,
|
||||
nzbgetHost: '',
|
||||
nzbgetUser: '',
|
||||
nzbgetPass: '',
|
||||
nzbgetBasicAuth: false,
|
||||
nzbgetHeaders: {},
|
||||
//Wake on LAN
|
||||
wakeOnLANEnabled: false,
|
||||
wakeOnLANBroadcastAddress: '',
|
||||
wakeOnLANMACAddress: '',
|
||||
//Tautulli
|
||||
tautulliEnabled: false,
|
||||
tautulliHost: '',
|
||||
tautulliKey: '',
|
||||
tautulliHeaders: {},
|
||||
//Ombi
|
||||
ombiEnabled: false,
|
||||
ombiHost: '',
|
||||
ombiKey: '',
|
||||
ombiHeaders: {},
|
||||
);
|
||||
|
||||
/// Create a new [ProfileHiveObject] from another [ProfileHiveObject] (deep-copy).
|
||||
factory ProfileHiveObject.fromProfileHiveObject(ProfileHiveObject profile) => ProfileHiveObject(
|
||||
//Lidarr
|
||||
lidarrEnabled: profile.lidarrEnabled,
|
||||
lidarrHost: profile.lidarrHost,
|
||||
lidarrKey: profile.lidarrKey,
|
||||
lidarrHeaders: profile.lidarrHeaders,
|
||||
//Radarr
|
||||
radarrEnabled: profile.radarrEnabled,
|
||||
radarrHost: profile.radarrHost,
|
||||
radarrKey: profile.radarrKey,
|
||||
radarrHeaders: profile.radarrHeaders,
|
||||
//Sonarr
|
||||
sonarrEnabled: profile.sonarrEnabled,
|
||||
sonarrHost: profile.sonarrHost,
|
||||
sonarrKey: profile.sonarrKey,
|
||||
sonarrVersion3: profile.sonarrVersion3,
|
||||
sonarrHeaders: profile.sonarrHeaders,
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: profile.sabnzbdEnabled,
|
||||
sabnzbdHost: profile.sabnzbdHost,
|
||||
sabnzbdKey: profile.sabnzbdKey,
|
||||
sabnzbdHeaders: profile.sabnzbdHeaders,
|
||||
//NZBGet
|
||||
nzbgetEnabled: profile.nzbgetEnabled,
|
||||
nzbgetHost: profile.nzbgetHost,
|
||||
nzbgetUser: profile.nzbgetUser,
|
||||
nzbgetPass: profile.nzbgetPass,
|
||||
nzbgetBasicAuth: profile.nzbgetBasicAuth,
|
||||
nzbgetHeaders: profile.nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
wakeOnLANEnabled: profile.wakeOnLANEnabled,
|
||||
wakeOnLANBroadcastAddress: profile.wakeOnLANBroadcastAddress,
|
||||
wakeOnLANMACAddress: profile.wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
tautulliEnabled: profile.tautulliEnabled,
|
||||
tautulliHost: profile.tautulliHost,
|
||||
tautulliKey: profile.tautulliKey,
|
||||
tautulliHeaders: profile.tautulliHeaders,
|
||||
//Ombi
|
||||
ombiEnabled: profile.ombiEnabled,
|
||||
ombiHost: profile.ombiHost,
|
||||
ombiKey: profile.ombiKey,
|
||||
ombiHeaders: profile.ombiHeaders,
|
||||
);
|
||||
|
||||
/// Create a new [ProfileHiveObject] from a map where the keys map 1-to-1.
|
||||
///
|
||||
/// - Does _not_ do type checking, and will throw an error if the type is invalid.
|
||||
/// - If the key is null, sets to the "empty" value
|
||||
factory ProfileHiveObject.fromMap(Map profile) => ProfileHiveObject(
|
||||
//Sonarr
|
||||
sonarrEnabled: profile['sonarrEnabled'] ?? false,
|
||||
sonarrHost: profile['sonarrHost'] ?? '',
|
||||
sonarrKey: profile['sonarrKey'] ?? '',
|
||||
sonarrVersion3: profile['sonarrVersion3'] ?? false,
|
||||
sonarrHeaders: profile['sonarrHeaders'] ?? {},
|
||||
//Radarr
|
||||
radarrEnabled: profile['radarrEnabled'] ?? false,
|
||||
radarrHost: profile['radarrHost'] ?? '',
|
||||
radarrKey: profile['radarrKey'] ?? '',
|
||||
radarrHeaders: profile['radarrHeaders'] ?? {},
|
||||
//Lidarr
|
||||
lidarrEnabled: profile['lidarrEnabled'] ?? false,
|
||||
lidarrHost: profile['lidarrHost'] ?? '',
|
||||
lidarrKey: profile['lidarrKey'] ?? '',
|
||||
lidarrHeaders: profile['lidarrHeaders'] ?? {},
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: profile['sabnzbdEnabled'] ?? false,
|
||||
sabnzbdHost: profile['sabnzbdHost'] ?? '',
|
||||
sabnzbdKey: profile['sabnzbdKey'] ?? '',
|
||||
sabnzbdHeaders: profile['sabnzbdHeaders'] ?? {},
|
||||
//NZBGet
|
||||
nzbgetEnabled: profile['nzbgetEnabled'] ?? false,
|
||||
nzbgetHost: profile['nzbgetHost'] ?? '',
|
||||
nzbgetUser: profile['nzbgetUser'] ?? '',
|
||||
nzbgetPass: profile['nzbgetPass'] ?? '',
|
||||
nzbgetBasicAuth: profile['nzbgetBasicAuth'] ?? false,
|
||||
nzbgetHeaders: profile['nzbgetHeaders'] ?? {},
|
||||
//Wake on LAN
|
||||
wakeOnLANEnabled: profile['wakeOnLANEnabled'] ?? false,
|
||||
wakeOnLANBroadcastAddress: profile['wakeOnLANBroadcastAddress'] ?? '',
|
||||
wakeOnLANMACAddress: profile['wakeOnLANMACAddress'] ?? '',
|
||||
//Tautulli
|
||||
tautulliEnabled: profile['tautulliEnabled'] ?? false,
|
||||
tautulliHost: profile['tautulliHost'] ?? '',
|
||||
tautulliKey: profile['tautulliKey'] ?? '',
|
||||
tautulliHeaders: profile['tautulliHeaders'] ?? {},
|
||||
//Ombi
|
||||
ombiEnabled: profile['ombiEnabled'] ?? false,
|
||||
ombiHost: profile['ombiHost'] ?? '',
|
||||
ombiKey: profile['ombiKey'] ?? '',
|
||||
ombiHeaders: profile['ombiHeaders'] ?? {},
|
||||
);
|
||||
|
||||
|
||||
ProfileHiveObject({
|
||||
//Lidarr
|
||||
@required this.lidarrEnabled,
|
||||
@required this.lidarrHost,
|
||||
@required this.lidarrKey,
|
||||
@required this.lidarrHeaders,
|
||||
//Radarr
|
||||
@required this.radarrEnabled,
|
||||
@required this.radarrHost,
|
||||
@required this.radarrKey,
|
||||
@required this.radarrHeaders,
|
||||
//Sonarr
|
||||
@required this.sonarrEnabled,
|
||||
@required this.sonarrHost,
|
||||
@required this.sonarrKey,
|
||||
@required this.sonarrVersion3,
|
||||
@required this.sonarrHeaders,
|
||||
//SABnzbd
|
||||
@required this.sabnzbdEnabled,
|
||||
@required this.sabnzbdHost,
|
||||
@required this.sabnzbdKey,
|
||||
@required this.sabnzbdHeaders,
|
||||
//NZBGet
|
||||
@required this.nzbgetEnabled,
|
||||
@required this.nzbgetHost,
|
||||
@required this.nzbgetUser,
|
||||
@required this.nzbgetPass,
|
||||
@required this.nzbgetBasicAuth,
|
||||
@required this.nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
@required this.wakeOnLANEnabled,
|
||||
@required this.wakeOnLANBroadcastAddress,
|
||||
@required this.wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
@required this.tautulliEnabled,
|
||||
@required this.tautulliHost,
|
||||
@required this.tautulliKey,
|
||||
@required this.tautulliHeaders,
|
||||
//Ombi
|
||||
@required this.ombiEnabled,
|
||||
@required this.ombiHost,
|
||||
@required this.ombiKey,
|
||||
@required this.ombiHeaders,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => toMap().toString();
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
"key": key,
|
||||
//Sonarr
|
||||
"sonarrEnabled": sonarrEnabled,
|
||||
"sonarrHost": sonarrHost,
|
||||
"sonarrKey": sonarrKey,
|
||||
"sonarrVersion3": sonarrVersion3,
|
||||
"sonarrHeaders": sonarrHeaders,
|
||||
//Radarr
|
||||
"radarrEnabled": radarrEnabled,
|
||||
"radarrHost": radarrHost,
|
||||
"radarrKey": radarrKey,
|
||||
"radarrHeaders": radarrHeaders,
|
||||
//Lidarr
|
||||
"lidarrEnabled": lidarrEnabled,
|
||||
"lidarrHost": lidarrHost,
|
||||
"lidarrKey": lidarrKey,
|
||||
"lidarrHeaders": lidarrHeaders,
|
||||
//SABnzbd
|
||||
"sabnzbdEnabled": sabnzbdEnabled,
|
||||
"sabnzbdHost": sabnzbdHost,
|
||||
"sabnzbdKey": sabnzbdKey,
|
||||
"sabnzbdHeaders": sabnzbdHeaders,
|
||||
//NZBGet
|
||||
"nzbgetEnabled": nzbgetEnabled,
|
||||
"nzbgetHost": nzbgetHost,
|
||||
"nzbgetUser": nzbgetUser,
|
||||
"nzbgetPass": nzbgetPass,
|
||||
"nzbgetBasicAuth": nzbgetBasicAuth,
|
||||
"nzbgetHeaders": nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
"wakeOnLANEnabled": wakeOnLANEnabled,
|
||||
"wakeOnLANBroadcastAddress": wakeOnLANBroadcastAddress,
|
||||
"wakeOnLANMACAddress": wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
"tautulliEnabled": tautulliEnabled,
|
||||
"tautulliHost": tautulliHost,
|
||||
"tautulliKey": tautulliKey,
|
||||
"tautulliHeaders": tautulliHeaders,
|
||||
//Ombi
|
||||
"ombiEnabled": ombiEnabled,
|
||||
"ombiHost": ombiHost,
|
||||
"ombiKey": ombiKey,
|
||||
"ombiHeaders": ombiHeaders,
|
||||
};
|
||||
|
||||
//Lidarr
|
||||
@HiveField(0)
|
||||
bool lidarrEnabled;
|
||||
@HiveField(1)
|
||||
String lidarrHost;
|
||||
@HiveField(2)
|
||||
String lidarrKey;
|
||||
@HiveField(26)
|
||||
Map lidarrHeaders;
|
||||
|
||||
Map<String, dynamic> getLidarr() => {
|
||||
'enabled': lidarrEnabled ?? false,
|
||||
'host': lidarrHost ?? '',
|
||||
'key': lidarrKey ?? '',
|
||||
'headers': lidarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//Radarr
|
||||
@HiveField(3)
|
||||
bool radarrEnabled;
|
||||
@HiveField(4)
|
||||
String radarrHost;
|
||||
@HiveField(5)
|
||||
String radarrKey;
|
||||
@HiveField(27)
|
||||
Map radarrHeaders;
|
||||
|
||||
Map<String, dynamic> getRadarr() => {
|
||||
'enabled': radarrEnabled ?? false,
|
||||
'host': radarrHost ?? '',
|
||||
'key': radarrKey ?? '',
|
||||
'headers': radarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//Sonarr
|
||||
@HiveField(6)
|
||||
bool sonarrEnabled;
|
||||
@HiveField(7)
|
||||
String sonarrHost;
|
||||
@HiveField(8)
|
||||
String sonarrKey;
|
||||
@HiveField(21)
|
||||
bool sonarrVersion3;
|
||||
@HiveField(28)
|
||||
Map sonarrHeaders;
|
||||
|
||||
Map<String, dynamic> getSonarr() => {
|
||||
'enabled': sonarrEnabled ?? false,
|
||||
'host': sonarrHost ?? '',
|
||||
'key': sonarrKey ?? '',
|
||||
'v3': sonarrVersion3 ?? false,
|
||||
'headers': sonarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//SABnzbd
|
||||
@HiveField(9)
|
||||
bool sabnzbdEnabled;
|
||||
@HiveField(10)
|
||||
String sabnzbdHost;
|
||||
@HiveField(11)
|
||||
String sabnzbdKey;
|
||||
@HiveField(29)
|
||||
Map sabnzbdHeaders;
|
||||
|
||||
Map<String, dynamic> getSABnzbd() => {
|
||||
'enabled': sabnzbdEnabled ?? false,
|
||||
'host': sabnzbdHost ?? '',
|
||||
'key': sabnzbdKey ?? '',
|
||||
'headers': sabnzbdHeaders ?? {},
|
||||
};
|
||||
|
||||
//NZBGet
|
||||
@HiveField(12)
|
||||
bool nzbgetEnabled;
|
||||
@HiveField(13)
|
||||
String nzbgetHost;
|
||||
@HiveField(14)
|
||||
String nzbgetUser;
|
||||
@HiveField(15)
|
||||
String nzbgetPass;
|
||||
@HiveField(22)
|
||||
bool nzbgetBasicAuth;
|
||||
@HiveField(30)
|
||||
Map nzbgetHeaders;
|
||||
|
||||
Map<String, dynamic> getNZBGet() => {
|
||||
'enabled': nzbgetEnabled ?? false,
|
||||
'host': nzbgetHost ?? '',
|
||||
'user': nzbgetUser ?? '',
|
||||
'pass': nzbgetPass ?? '',
|
||||
'basic_auth': nzbgetBasicAuth ?? false,
|
||||
'headers': nzbgetHeaders ?? {},
|
||||
};
|
||||
|
||||
//Wake On LAN
|
||||
@HiveField(23)
|
||||
bool wakeOnLANEnabled;
|
||||
@HiveField(24)
|
||||
String wakeOnLANBroadcastAddress;
|
||||
@HiveField(25)
|
||||
String wakeOnLANMACAddress;
|
||||
|
||||
Map<String, dynamic> getWakeOnLAN() => {
|
||||
'enabled': wakeOnLANEnabled ?? false,
|
||||
'broadcastAddress': wakeOnLANBroadcastAddress ?? '',
|
||||
'MACAddress': wakeOnLANMACAddress ?? '',
|
||||
};
|
||||
|
||||
//Tautulli
|
||||
@HiveField(31)
|
||||
bool tautulliEnabled;
|
||||
@HiveField(32)
|
||||
String tautulliHost;
|
||||
@HiveField(33)
|
||||
String tautulliKey;
|
||||
@HiveField(35)
|
||||
Map tautulliHeaders;
|
||||
|
||||
Map<String, dynamic> getTautulli() => {
|
||||
'enabled': tautulliEnabled ?? false,
|
||||
'host': tautulliHost ?? '',
|
||||
'key': tautulliKey ?? '',
|
||||
'headers': tautulliHeaders ?? {},
|
||||
};
|
||||
|
||||
//Ombi
|
||||
@HiveField(36)
|
||||
bool ombiEnabled;
|
||||
@HiveField(37)
|
||||
String ombiHost;
|
||||
@HiveField(38)
|
||||
String ombiKey;
|
||||
@HiveField(39)
|
||||
Map ombiHeaders;
|
||||
|
||||
Map<String, dynamic> getOmbi() => {
|
||||
'enabled': ombiEnabled ?? false,
|
||||
'host': ombiHost ?? '',
|
||||
'key': ombiKey ?? '',
|
||||
'headers': ombiHeaders ?? {},
|
||||
};
|
||||
|
||||
List<String> get enabledModules => [
|
||||
...enabledAutomationModules,
|
||||
...enabledClientModules,
|
||||
...enabledMonitoringModules,
|
||||
];
|
||||
|
||||
List<String> get enabledAutomationModules => [
|
||||
if(lidarrEnabled ?? false) LidarrConstants.MODULE_KEY,
|
||||
if(radarrEnabled ?? false) RadarrConstants.MODULE_KEY,
|
||||
if(sonarrEnabled ?? false) SonarrConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
List<String> get enabledClientModules => [
|
||||
if(nzbgetEnabled ?? false) NZBGetConstants.MODULE_KEY,
|
||||
if(sabnzbdEnabled ?? false) SABnzbdConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
List<String> get enabledMonitoringModules => [
|
||||
if(tautulliEnabled ?? false) TautulliConstants.MODULE_KEY,
|
||||
if(ombiEnabled ?? false) OmbiConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
bool get anyAutomationEnabled => enabledAutomationModules.isNotEmpty;
|
||||
bool get anyClientsEnabled => enabledClientModules.isNotEmpty;
|
||||
bool get anyMonitoringEnabled => enabledMonitoringModules.isNotEmpty;
|
||||
bool get anythingEnabled => anyAutomationEnabled || anyClientsEnabled || anyMonitoringEnabled;
|
||||
}
|
||||
@@ -1,2 +1,103 @@
|
||||
export 'configuration/export.dart';
|
||||
export 'configuration/import.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/settings/core.dart' show SettingsDatabase, SettingsConstants;
|
||||
import 'package:lunasea/modules/home/core.dart' show HomeDatabase, HomeConstants;
|
||||
import 'package:lunasea/modules/search/core.dart' show SearchDatabase, SearchConstants;
|
||||
import 'package:lunasea/modules/lidarr/core.dart' show LidarrDatabase, LidarrConstants;
|
||||
import 'package:lunasea/modules/radarr/core.dart' show RadarrDatabase, RadarrConstants;
|
||||
import 'package:lunasea/modules/sonarr/core.dart' show SonarrDatabase, SonarrConstants;
|
||||
import 'package:lunasea/modules/nzbget/core.dart' show NZBGetDatabase, NZBGetConstants;
|
||||
import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdDatabase, SABnzbdConstants;
|
||||
import 'package:lunasea/modules/ombi/core.dart' show OmbiDatabase, OmbiConstants;
|
||||
import 'package:lunasea/modules/tautulli/core.dart' show TautulliDatabase, TautulliConstants;
|
||||
|
||||
class LunaConfiguration {
|
||||
/// Returns a list of all profiles converted to a map.
|
||||
List<Map<String, dynamic>> _getProfiles() {
|
||||
List<Map<String, dynamic>> _data = [];
|
||||
for(var key in Database.profilesBox.keys) _data.add(Database.profilesBox.get(key).toMap());
|
||||
return _data;
|
||||
}
|
||||
|
||||
/// Given a list of map objects, creates or updates profiles for each object.
|
||||
void _setProfiles(List data) {
|
||||
Box<dynamic> box = Database.profilesBox;
|
||||
for(Map profile in data) box.put(profile['key'], ProfileHiveObject.fromMap(profile));
|
||||
}
|
||||
|
||||
/// Returns a list of all indexers converted to a map.
|
||||
List<Map<String, dynamic>> _getIndexers() {
|
||||
List<Map<String, dynamic>> _data = [];
|
||||
for(var key in Database.indexersBox.keys) _data.add(Database.indexersBox.get(key).toMap());
|
||||
return _data;
|
||||
}
|
||||
|
||||
/// Given a list of map objects, creates or updates indexers for each object.
|
||||
void _setIndexers(List data) {
|
||||
Box<dynamic> box = Database.indexersBox;
|
||||
for(Map indexer in data) box.add(IndexerHiveObject.fromMap(indexer));
|
||||
}
|
||||
|
||||
/// Import the entire configuration from a JSON-encoded string (typically read through a `.lunasea` backup file).
|
||||
///
|
||||
/// - Clears all boxes
|
||||
/// - Calls `_setProfiles()` and `_setIndexers()`
|
||||
/// - Calls `import()` on all module databases, which implement [LunaDatabase].
|
||||
/// - Resets the application state
|
||||
///
|
||||
/// On a failed import, resets LunaSea back to the default/base state
|
||||
Future<void> import(BuildContext context, String data) async {
|
||||
Map config = json.decode(data);
|
||||
Database.clearAllBoxes();
|
||||
try {
|
||||
// Set the profilers, indexer, and global LunaSea boxes
|
||||
if(config['profiles'] != null) _setProfiles(config['profiles']);
|
||||
if(config['indexers'] != null) _setIndexers(config['indexers']);
|
||||
if(config['lunasea'] != null) LunaDatabase().import(config['lunasea']);
|
||||
// General
|
||||
if(config[SettingsConstants.MODULE_KEY] != null) SettingsDatabase().import(config[SettingsConstants.MODULE_KEY]);
|
||||
if(config[HomeConstants.MODULE_KEY] != null) HomeDatabase().import(config[HomeConstants.MODULE_KEY]);
|
||||
if(config[SearchConstants.MODULE_KEY] != null) SearchDatabase().import(config[SearchConstants.MODULE_KEY]);
|
||||
// Automation
|
||||
if(config[LidarrConstants.MODULE_KEY] != null) LidarrDatabase().import(config[LidarrConstants.MODULE_KEY]);
|
||||
if(config[RadarrConstants.MODULE_KEY] != null) RadarrDatabase().import(config[RadarrConstants.MODULE_KEY]);
|
||||
if(config[SonarrConstants.MODULE_KEY] != null) SonarrDatabase().import(config[SonarrConstants.MODULE_KEY]);
|
||||
// Clients
|
||||
if(config[NZBGetConstants.MODULE_KEY] != null) NZBGetDatabase().import(config[NZBGetConstants.MODULE_KEY]);
|
||||
if(config[SABnzbdConstants.MODULE_KEY] != null) SABnzbdDatabase().import(config[SABnzbdConstants.MODULE_KEY]);
|
||||
//Monitoring
|
||||
if(config[OmbiConstants.MODULE_KEY] != null) OmbiDatabase().import(config[OmbiConstants.MODULE_KEY]);
|
||||
if(config[TautulliConstants.MODULE_KEY] != null) TautulliDatabase().import(config[TautulliConstants.MODULE_KEY]);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to import configuration, resetting to default', error, stack);
|
||||
Database.setDefaults();
|
||||
}
|
||||
// Reset the entire app's state
|
||||
LunaState.reset(context);
|
||||
}
|
||||
|
||||
/// Converts the entire application configuration/database into a JSON string.
|
||||
///
|
||||
/// - Calls `_getProfiles()` and `_getIndexers()`
|
||||
/// - Calls `export()` on all module databases, which implement [LunaModuleDatabase].
|
||||
String export() => json.encode({
|
||||
"profiles": _getProfiles(),
|
||||
"indexers": _getIndexers(),
|
||||
"lunasea": LunaDatabase().export(),
|
||||
// General
|
||||
SettingsConstants.MODULE_KEY: SettingsDatabase().export(),
|
||||
HomeConstants.MODULE_KEY: HomeDatabase().export(),
|
||||
SearchConstants.MODULE_KEY: SearchDatabase().export(),
|
||||
// Automation
|
||||
LidarrConstants.MODULE_KEY: LidarrDatabase().export(),
|
||||
RadarrConstants.MODULE_KEY: RadarrDatabase().export(),
|
||||
SonarrConstants.MODULE_KEY: SonarrDatabase().export(),
|
||||
// Clients
|
||||
NZBGetConstants.MODULE_KEY: NZBGetDatabase().export(),
|
||||
SABnzbdConstants.MODULE_KEY: SABnzbdDatabase().export(),
|
||||
// Monitoring
|
||||
OmbiConstants.MODULE_KEY: OmbiDatabase().export(),
|
||||
TautulliConstants.MODULE_KEY: TautulliDatabase().export(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:lunasea/core/database.dart';
|
||||
|
||||
class Export {
|
||||
Export._();
|
||||
|
||||
static bool validateType(dynamic value) {
|
||||
Type _type = value.runtimeType;
|
||||
if(
|
||||
_type == bool ||
|
||||
_type == String ||
|
||||
_type == int ||
|
||||
_type == double
|
||||
) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static Map get _lunasea {
|
||||
Map<String, dynamic> _data = {};
|
||||
_data['profile'] = LunaSeaDatabaseValue.ENABLED_PROFILE.data;
|
||||
return _data;
|
||||
}
|
||||
|
||||
static List get _profiles {
|
||||
Box<dynamic> _box = Database.profilesBox;
|
||||
List _data = [];
|
||||
for(var key in _box.keys) {
|
||||
ProfileHiveObject profile = _box.get(key);
|
||||
_data.add(profile.toMap());
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
static List get _indexers {
|
||||
Box<dynamic> _box = Database.indexersBox;
|
||||
List _data = [];
|
||||
for(var key in _box.keys) {
|
||||
IndexerHiveObject indexer = _box.get(key);
|
||||
_data.add(indexer.toMap());
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
static String export() {
|
||||
return json.encode({
|
||||
"lunasea": _lunasea,
|
||||
"profiles": _profiles,
|
||||
"indexers": _indexers,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/core/database.dart';
|
||||
|
||||
class Import {
|
||||
Import._();
|
||||
|
||||
static void _setLunaSea(BuildContext context, Map data) {
|
||||
String _name = data['lunasea']['profile'];
|
||||
Map _profile = (data['profiles'] as List).firstWhere((element) => element['key'] == _name, orElse: () => null);
|
||||
if(_profile == null) _name = (data['profiles'] as List)[0]['key'];
|
||||
LunaSeaDatabaseValue.ENABLED_PROFILE.put(_name);
|
||||
}
|
||||
|
||||
static void _setProfiles(List data) {
|
||||
Box<dynamic> _box = Database.profilesBox;
|
||||
for(Map profile in data) {
|
||||
_box.put(profile['key'], ProfileHiveObject(
|
||||
//Sonarr
|
||||
sonarrEnabled: profile['sonarrEnabled'] ?? false,
|
||||
sonarrHost: profile['sonarrHost'] ?? '',
|
||||
sonarrKey: profile['sonarrKey'] ?? '',
|
||||
sonarrVersion3: profile['sonarrVersion3'] ?? false,
|
||||
sonarrHeaders: profile['sonarrHeaders'] ?? {},
|
||||
//Radarr
|
||||
radarrEnabled: profile['radarrEnabled'] ?? false,
|
||||
radarrHost: profile['radarrHost'] ?? '',
|
||||
radarrKey: profile['radarrKey'] ?? '',
|
||||
radarrHeaders: profile['radarrHeaders'] ?? {},
|
||||
//Lidarr
|
||||
lidarrEnabled: profile['lidarrEnabled'] ?? false,
|
||||
lidarrHost: profile['lidarrHost'] ?? '',
|
||||
lidarrKey: profile['lidarrKey'] ?? '',
|
||||
lidarrHeaders: profile['lidarrHeaders'] ?? {},
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: profile['sabnzbdEnabled'] ?? false,
|
||||
sabnzbdHost: profile['sabnzbdHost'] ?? '',
|
||||
sabnzbdKey: profile['sabnzbdKey'] ?? '',
|
||||
sabnzbdHeaders: profile['sabnzbdHeaders'] ?? {},
|
||||
//NZBGet
|
||||
nzbgetEnabled: profile['nzbgetEnabled'] ?? false,
|
||||
nzbgetHost: profile['nzbgetHost'] ?? '',
|
||||
nzbgetUser: profile['nzbgetUser'] ?? '',
|
||||
nzbgetPass: profile['nzbgetPass'] ?? '',
|
||||
nzbgetBasicAuth: profile['nzbgetBasicAuth'] ?? false,
|
||||
nzbgetHeaders: profile['nzbgetHeaders'] ?? {},
|
||||
//Wake on LAN
|
||||
wakeOnLANEnabled: profile['wakeOnLANEnabled'] ?? false,
|
||||
wakeOnLANBroadcastAddress: profile['wakeOnLANBroadcastAddress'] ?? '',
|
||||
wakeOnLANMACAddress: profile['wakeOnLANMACAddress'] ?? '',
|
||||
//Tautulli
|
||||
tautulliEnabled: profile['tautulliEnabled'] ?? false,
|
||||
tautulliHost: profile['tautulliHost'] ?? '',
|
||||
tautulliKey: profile['tautulliKey'] ?? '',
|
||||
tautulliHeaders: profile['tautulliHeaders'] ?? {},
|
||||
//Ombi
|
||||
ombiEnabled: profile['ombiEnabled'] ?? false,
|
||||
ombiHost: profile['ombiHost'] ?? '',
|
||||
ombiKey: profile['ombiKey'] ?? '',
|
||||
ombiHeaders: profile['ombiHeaders'] ?? {},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
static void _setIndexers(List data) {
|
||||
Box<dynamic> _box = Database.indexersBox;
|
||||
for(Map indexer in data) {
|
||||
_box.add(IndexerHiveObject(
|
||||
displayName: indexer['displayName'] ?? '',
|
||||
host: indexer['host'] ?? '',
|
||||
key: indexer['key'] ?? '',
|
||||
headers: indexer['headers'] ?? {},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
static bool _validate(Map config) {
|
||||
if(
|
||||
config['profiles'] != null &&
|
||||
config['indexers'] != null &&
|
||||
config['lunasea'] != null
|
||||
) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _clearBoxes() {
|
||||
Database.clearIndexersBox();
|
||||
Database.clearLunaSeaBox();
|
||||
Database.clearProfilesBox();
|
||||
}
|
||||
|
||||
static Future<bool> import(BuildContext context, String data) async {
|
||||
Map _config = json.decode(data);
|
||||
if(_validate(_config)) {
|
||||
_clearBoxes();
|
||||
_setProfiles(_config['profiles']);
|
||||
_setIndexers(_config['indexers']);
|
||||
_setLunaSea(context, _config);
|
||||
LunaProvider.reset(context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/module_map.dart';
|
||||
import 'package:lunasea/modules.dart' show
|
||||
LidarrConstants,
|
||||
RadarrConstants,
|
||||
SonarrConstants,
|
||||
NZBGetConstants,
|
||||
SABnzbdConstants,
|
||||
SearchConstants,
|
||||
SettingsConstants,
|
||||
WakeOnLANConstants,
|
||||
TautulliConstants;
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/lidarr/core.dart' show LidarrConstants;
|
||||
import 'package:lunasea/modules/radarr/core.dart' show RadarrConstants;
|
||||
import 'package:lunasea/modules/sonarr/core.dart' show SonarrConstants;
|
||||
import 'package:lunasea/modules/nzbget/core.dart' show NZBGetConstants;
|
||||
import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdConstants;
|
||||
import 'package:lunasea/modules/search/core.dart' show SearchConstants;
|
||||
import 'package:lunasea/modules/settings/core.dart' show SettingsConstants;
|
||||
import 'package:lunasea/modules/wake_on_lan/core.dart' show WakeOnLANConstants;
|
||||
import 'package:lunasea/modules/tautulli/core.dart' show TautulliConstants;
|
||||
|
||||
class Constants {
|
||||
Constants._();
|
||||
|
||||
static const APPLICATION_NAME = 'LunaSea';
|
||||
static const SENTRY_DSN = 'https://511f76efcf714ecfb5ed6b26b5819bd6@o426090.ingest.sentry.io/5367513';
|
||||
|
||||
static const Map<String, LunaModuleMap> MODULE_MAP = {
|
||||
LidarrConstants.MODULE_KEY: LidarrConstants.MODULE_MAP,
|
||||
RadarrConstants.MODULE_KEY: RadarrConstants.MODULE_MAP,
|
||||
SonarrConstants.MODULE_KEY: SonarrConstants.MODULE_MAP,
|
||||
NZBGetConstants.MODULE_KEY: NZBGetConstants.MODULE_MAP,
|
||||
SABnzbdConstants.MODULE_KEY: SABnzbdConstants.MODULE_MAP,
|
||||
SearchConstants.MODULE_KEY: SearchConstants.MODULE_MAP,
|
||||
SettingsConstants.MODULE_KEY: SettingsConstants.MODULE_MAP,
|
||||
WakeOnLANConstants.MODULE_KEY: WakeOnLANConstants.MODULE_MAP,
|
||||
TautulliConstants.MODULE_KEY: TautulliConstants.MODULE_MAP,
|
||||
static const Map<String, LunaModuleMetadata> MODULE_METADATA = {
|
||||
LidarrConstants.MODULE_KEY: LidarrConstants.MODULE_METADATA,
|
||||
RadarrConstants.MODULE_KEY: RadarrConstants.MODULE_METADATA,
|
||||
SonarrConstants.MODULE_KEY: SonarrConstants.MODULE_METADATA,
|
||||
NZBGetConstants.MODULE_KEY: NZBGetConstants.MODULE_METADATA,
|
||||
SABnzbdConstants.MODULE_KEY: SABnzbdConstants.MODULE_METADATA,
|
||||
SearchConstants.MODULE_KEY: SearchConstants.MODULE_METADATA,
|
||||
SettingsConstants.MODULE_KEY: SettingsConstants.MODULE_METADATA,
|
||||
WakeOnLANConstants.MODULE_KEY: WakeOnLANConstants.MODULE_METADATA,
|
||||
TautulliConstants.MODULE_KEY: TautulliConstants.MODULE_METADATA,
|
||||
};
|
||||
|
||||
static const EMPTY_MAP = {};
|
||||
@@ -51,21 +48,14 @@ class Constants {
|
||||
static const UI_FONT_SIZE_SUBTITLE = 13.0;
|
||||
static const UI_FONT_SIZE_GRAPH_LEGEND = 10.0;
|
||||
|
||||
static const CONFIGURATION_INVALID = '<<INVALID_CONFIGURATION>>';
|
||||
static const ENCRYPTION_FAILURE = '<<INVALID_ENCRYPTION>>';
|
||||
static const NO_SERVICES_ENABLED = '<<NO_SERVICES_ENABLED>>';
|
||||
static const CHECK_LOGS_MESSAGE = 'Check the logs for more details';
|
||||
|
||||
static const BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb'];
|
||||
static const BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
|
||||
static const URL_DOCUMENTATION = 'https://docs.lunasea.app';
|
||||
static const URL_GITHUB = 'https://github.com/JagandeepBrar/LunaSea';
|
||||
static const URL_GITHUB = 'https://github.com/CometTools/LunaSea';
|
||||
static const URL_REDDIT = 'https://www.reddit.com/r/LunaSeaApp';
|
||||
static const URL_WEBSITE = 'https://www.lunasea.app';
|
||||
static const URL_FEEDBACK = 'https://feedback.lunasea.app';
|
||||
static const URL_LICENSES = 'https://docs.lunasea.app/other-resources/licenses';
|
||||
static const URL_CHANGELOG = 'https://docs.lunasea.app/development/changelog';
|
||||
static const URL_CHANGELOG = 'https://github.com/CometTools/LunaSea/blob/master/CHANGELOG.md';
|
||||
static const URL_DISCORD = 'https://discord.com/invite/8MH2N3h';
|
||||
static const URL_SENTRY = 'https://sentry.io';
|
||||
static const URL_TESTFLIGHT = 'https://testflight.apple.com/join/WWXaybra';
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export 'database/database.dart';
|
||||
export 'database/adapters.dart';
|
||||
export 'database/luna_database.dart';
|
||||
export 'database/module_database.dart';
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'indexer.g.dart';
|
||||
|
||||
@HiveType(typeId: 1, adapterName: 'IndexerHiveObjectAdapter')
|
||||
class IndexerHiveObject extends HiveObject {
|
||||
factory IndexerHiveObject.empty() {
|
||||
return IndexerHiveObject(
|
||||
displayName: '',
|
||||
host: '',
|
||||
key: '',
|
||||
headers: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory IndexerHiveObject.from(IndexerHiveObject obj) {
|
||||
return IndexerHiveObject(
|
||||
displayName: obj.displayName,
|
||||
host: obj.host,
|
||||
key: obj.key,
|
||||
headers: obj.headers,
|
||||
);
|
||||
}
|
||||
|
||||
IndexerHiveObject({
|
||||
@required this.displayName,
|
||||
@required this.host,
|
||||
@required this.key,
|
||||
@required this.headers,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"displayName": displayName ?? '',
|
||||
"host": host ?? '',
|
||||
"key": key ?? '',
|
||||
"headers": headers ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
@HiveField(0)
|
||||
String displayName;
|
||||
@HiveField(1)
|
||||
String host;
|
||||
@HiveField(2)
|
||||
String key;
|
||||
@HiveField(3)
|
||||
Map headers;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'package:lunasea/core/database/database.dart';
|
||||
import 'package:lunasea/core/extensions.dart';
|
||||
|
||||
class LunaSeaDatabase {
|
||||
LunaSeaDatabase._();
|
||||
|
||||
static void registerAdapters() {
|
||||
Hive.registerAdapter(LSBrowsersAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
enum LunaSeaDatabaseValue {
|
||||
ENABLED_PROFILE,
|
||||
ENABLED_SENTRY,
|
||||
CLIENT_IDENTIFIER,
|
||||
THEME_AMOLED,
|
||||
THEME_AMOLED_BORDER,
|
||||
THEME_IMAGE_BACKGROUND_OPACITY,
|
||||
SELECTED_BROWSER,
|
||||
DRAWER_GROUP_MODULES,
|
||||
DRAWER_EXPAND_AUTOMATION,
|
||||
DRAWER_EXPAND_CLIENTS,
|
||||
DRAWER_EXPAND_MONITORING,
|
||||
QUICK_ACTIONS_LIDARR,
|
||||
QUICK_ACTIONS_RADARR,
|
||||
QUICK_ACTIONS_SONARR,
|
||||
QUICK_ACTIONS_NZBGET,
|
||||
QUICK_ACTIONS_SABNZBD,
|
||||
QUICK_ACTIONS_TAUTULLI,
|
||||
QUICK_ACTIONS_SEARCH,
|
||||
USE_24_HOUR_TIME,
|
||||
}
|
||||
|
||||
extension LunaSeaDatabaseValueExtension on LunaSeaDatabaseValue {
|
||||
String get key {
|
||||
switch(this) {
|
||||
case LunaSeaDatabaseValue.ENABLED_PROFILE: return 'profile';
|
||||
case LunaSeaDatabaseValue.ENABLED_SENTRY: return 'LUNASEA_ENABLED_SENTRY';
|
||||
case LunaSeaDatabaseValue.THEME_AMOLED: return 'LUNASEA_THEME_AMOLED';
|
||||
case LunaSeaDatabaseValue.THEME_AMOLED_BORDER: return 'LUNASEA_THEME_AMOLED_BORDER';
|
||||
case LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY';
|
||||
case LunaSeaDatabaseValue.SELECTED_BROWSER: return 'LUNASEA_SELECTED_BROWSER';
|
||||
case LunaSeaDatabaseValue.CLIENT_IDENTIFIER: return 'LUNASEA_CLIENT_IDENTIFIER';
|
||||
case LunaSeaDatabaseValue.DRAWER_GROUP_MODULES: return 'LUNASEA_DRAWER_GROUP_MODULES';
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return 'LUNASEA_DRAWER_EXPAND_AUTOMATION';
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS: return 'LUNASEA_DRAWER_EXPAND_CLIENTS';
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING: return 'LUNASEA_DRAWER_EXPAND_MONITORING';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR: return 'LUNASEA_QUICK_ACTIONS_LIDARR';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR: return 'LUNASEA_QUICK_ACTIONS_RADARR';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR: return 'LUNASEA_QUICK_ACTIONS_SONARR';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET: return 'LUNASEA_QUICK_ACTIONS_NZBGET';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD: return 'LUNASEA_QUICK_ACTIONS_SABNZBD';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return 'LUNASEA_QUICK_ACTIONS_TAUTULLI';
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH: return 'LUNASEA_QUICK_ACTIONS_SEARCH';
|
||||
case LunaSeaDatabaseValue.USE_24_HOUR_TIME: return 'LUNASEA_USE_24_HOUR_TIME';
|
||||
}
|
||||
throw Exception('key not found');
|
||||
}
|
||||
|
||||
dynamic get data {
|
||||
final _box = Database.lunaSeaBox;
|
||||
switch(this) {
|
||||
case LunaSeaDatabaseValue.ENABLED_PROFILE: return _box.get(this.key, defaultValue: 'default');
|
||||
case LunaSeaDatabaseValue.SELECTED_BROWSER: return _box.get(this.key, defaultValue: LSBrowsers.APPLE_SAFARI);
|
||||
case LunaSeaDatabaseValue.THEME_AMOLED: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.THEME_AMOLED_BORDER: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return _box.get(this.key, defaultValue: 10);
|
||||
case LunaSeaDatabaseValue.ENABLED_SENTRY: return _box.get(this.key, defaultValue: true);
|
||||
case LunaSeaDatabaseValue.CLIENT_IDENTIFIER: return _box.get(this.key, defaultValue: null);
|
||||
case LunaSeaDatabaseValue.DRAWER_GROUP_MODULES: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return _box.get(this.key, defaultValue: true);
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS: return _box.get(this.key, defaultValue: true);
|
||||
case LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING: return _box.get(this.key, defaultValue: true);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH: return _box.get(this.key, defaultValue: false);
|
||||
case LunaSeaDatabaseValue.USE_24_HOUR_TIME: return _box.get(this.key, defaultValue: false);
|
||||
}
|
||||
throw Exception('data not found');
|
||||
}
|
||||
|
||||
void put(dynamic value) => Database.lunaSeaBox.put(this.key, value);
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules.dart';
|
||||
|
||||
part 'profile.g.dart';
|
||||
|
||||
//Next HiveField ID: 40
|
||||
|
||||
/**
|
||||
* Dead Fields
|
||||
* 16, 17, 18, 19, 20, 34
|
||||
*/
|
||||
|
||||
/// Hive database object containing all profile fields
|
||||
@HiveType(typeId: 0, adapterName: 'ProfileHiveObjectAdapter')
|
||||
class ProfileHiveObject extends HiveObject {
|
||||
factory ProfileHiveObject.empty() {
|
||||
return ProfileHiveObject(
|
||||
//Lidarr
|
||||
lidarrEnabled: false,
|
||||
lidarrHost: '',
|
||||
lidarrKey: '',
|
||||
lidarrHeaders: {},
|
||||
//Radarr
|
||||
radarrEnabled: false,
|
||||
radarrHost: '',
|
||||
radarrKey: '',
|
||||
radarrHeaders: {},
|
||||
//Sonarr
|
||||
sonarrEnabled: false,
|
||||
sonarrHost: '',
|
||||
sonarrKey: '',
|
||||
sonarrVersion3: false,
|
||||
sonarrHeaders: {},
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: false,
|
||||
sabnzbdHost: '',
|
||||
sabnzbdKey: '',
|
||||
sabnzbdHeaders: {},
|
||||
//NZBGet
|
||||
nzbgetEnabled: false,
|
||||
nzbgetHost: '',
|
||||
nzbgetUser: '',
|
||||
nzbgetPass: '',
|
||||
nzbgetBasicAuth: false,
|
||||
nzbgetHeaders: {},
|
||||
//Wake on LAN
|
||||
wakeOnLANEnabled: false,
|
||||
wakeOnLANBroadcastAddress: '',
|
||||
wakeOnLANMACAddress: '',
|
||||
//Tautulli
|
||||
tautulliEnabled: false,
|
||||
tautulliHost: '',
|
||||
tautulliKey: '',
|
||||
tautulliHeaders: {},
|
||||
//Ombi
|
||||
ombiEnabled: false,
|
||||
ombiHost: '',
|
||||
ombiKey: '',
|
||||
ombiHeaders: {},
|
||||
);
|
||||
}
|
||||
|
||||
factory ProfileHiveObject.from(ProfileHiveObject obj) {
|
||||
return ProfileHiveObject(
|
||||
//Lidarr
|
||||
lidarrEnabled: obj.lidarrEnabled,
|
||||
lidarrHost: obj.lidarrHost,
|
||||
lidarrKey: obj.lidarrKey,
|
||||
lidarrHeaders: obj.lidarrHeaders,
|
||||
//Radarr
|
||||
radarrEnabled: obj.radarrEnabled,
|
||||
radarrHost: obj.radarrHost,
|
||||
radarrKey: obj.radarrKey,
|
||||
radarrHeaders: obj.radarrHeaders,
|
||||
//Sonarr
|
||||
sonarrEnabled: obj.sonarrEnabled,
|
||||
sonarrHost: obj.sonarrHost,
|
||||
sonarrKey: obj.sonarrKey,
|
||||
sonarrVersion3: obj.sonarrVersion3,
|
||||
sonarrHeaders: obj.sonarrHeaders,
|
||||
//SABnzbd
|
||||
sabnzbdEnabled: obj.sabnzbdEnabled,
|
||||
sabnzbdHost: obj.sabnzbdHost,
|
||||
sabnzbdKey: obj.sabnzbdKey,
|
||||
sabnzbdHeaders: obj.sabnzbdHeaders,
|
||||
//NZBGet
|
||||
nzbgetEnabled: obj.nzbgetEnabled,
|
||||
nzbgetHost: obj.nzbgetHost,
|
||||
nzbgetUser: obj.nzbgetUser,
|
||||
nzbgetPass: obj.nzbgetPass,
|
||||
nzbgetBasicAuth: obj.nzbgetBasicAuth,
|
||||
nzbgetHeaders: obj.nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
wakeOnLANEnabled: obj.wakeOnLANEnabled,
|
||||
wakeOnLANBroadcastAddress: obj.wakeOnLANBroadcastAddress,
|
||||
wakeOnLANMACAddress: obj.wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
tautulliEnabled: obj.tautulliEnabled,
|
||||
tautulliHost: obj.tautulliHost,
|
||||
tautulliKey: obj.tautulliKey,
|
||||
tautulliHeaders: obj.tautulliHeaders,
|
||||
//Ombi
|
||||
ombiEnabled: obj.ombiEnabled,
|
||||
ombiHost: obj.ombiHost,
|
||||
ombiKey: obj.ombiKey,
|
||||
ombiHeaders: obj.ombiHeaders,
|
||||
);
|
||||
}
|
||||
|
||||
ProfileHiveObject({
|
||||
//Lidarr
|
||||
@required this.lidarrEnabled,
|
||||
@required this.lidarrHost,
|
||||
@required this.lidarrKey,
|
||||
@required this.lidarrHeaders,
|
||||
//Radarr
|
||||
@required this.radarrEnabled,
|
||||
@required this.radarrHost,
|
||||
@required this.radarrKey,
|
||||
@required this.radarrHeaders,
|
||||
//Sonarr
|
||||
@required this.sonarrEnabled,
|
||||
@required this.sonarrHost,
|
||||
@required this.sonarrKey,
|
||||
@required this.sonarrVersion3,
|
||||
@required this.sonarrHeaders,
|
||||
//SABnzbd
|
||||
@required this.sabnzbdEnabled,
|
||||
@required this.sabnzbdHost,
|
||||
@required this.sabnzbdKey,
|
||||
@required this.sabnzbdHeaders,
|
||||
//NZBGet
|
||||
@required this.nzbgetEnabled,
|
||||
@required this.nzbgetHost,
|
||||
@required this.nzbgetUser,
|
||||
@required this.nzbgetPass,
|
||||
@required this.nzbgetBasicAuth,
|
||||
@required this.nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
@required this.wakeOnLANEnabled,
|
||||
@required this.wakeOnLANBroadcastAddress,
|
||||
@required this.wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
@required this.tautulliEnabled,
|
||||
@required this.tautulliHost,
|
||||
@required this.tautulliKey,
|
||||
@required this.tautulliHeaders,
|
||||
//Ombi
|
||||
@required this.ombiEnabled,
|
||||
@required this.ombiHost,
|
||||
@required this.ombiKey,
|
||||
@required this.ombiHeaders,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => toMap().toString();
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"key": key,
|
||||
//Sonarr
|
||||
"sonarrEnabled": sonarrEnabled,
|
||||
"sonarrHost": sonarrHost,
|
||||
"sonarrKey": sonarrKey,
|
||||
"sonarrVersion3": sonarrVersion3,
|
||||
"sonarrHeaders": sonarrHeaders,
|
||||
//Radarr
|
||||
"radarrEnabled": radarrEnabled,
|
||||
"radarrHost": radarrHost,
|
||||
"radarrKey": radarrKey,
|
||||
"radarrHeaders": radarrHeaders,
|
||||
//Lidarr
|
||||
"lidarrEnabled": lidarrEnabled,
|
||||
"lidarrHost": lidarrHost,
|
||||
"lidarrKey": lidarrKey,
|
||||
"lidarrHeaders": lidarrHeaders,
|
||||
//SABnzbd
|
||||
"sabnzbdEnabled": sabnzbdEnabled,
|
||||
"sabnzbdHost": sabnzbdHost,
|
||||
"sabnzbdKey": sabnzbdKey,
|
||||
"sabnzbdHeaders": sabnzbdHeaders,
|
||||
//NZBGet
|
||||
"nzbgetEnabled": nzbgetEnabled,
|
||||
"nzbgetHost": nzbgetHost,
|
||||
"nzbgetUser": nzbgetUser,
|
||||
"nzbgetPass": nzbgetPass,
|
||||
"nzbgetBasicAuth": nzbgetBasicAuth,
|
||||
"nzbgetHeaders": nzbgetHeaders,
|
||||
//Wake On LAN
|
||||
"wakeOnLANEnabled": wakeOnLANEnabled,
|
||||
"wakeOnLANBroadcastAddress": wakeOnLANBroadcastAddress,
|
||||
"wakeOnLANMACAddress": wakeOnLANMACAddress,
|
||||
//Tautulli
|
||||
"tautulliEnabled": tautulliEnabled,
|
||||
"tautulliHost": tautulliHost,
|
||||
"tautulliKey": tautulliKey,
|
||||
"tautulliHeaders": tautulliHeaders,
|
||||
//Ombi
|
||||
"ombiEnabled": ombiEnabled,
|
||||
"ombiHost": ombiHost,
|
||||
"ombiKey": ombiKey,
|
||||
"ombiHeaders": ombiHeaders,
|
||||
};
|
||||
}
|
||||
|
||||
//Lidarr
|
||||
@HiveField(0)
|
||||
bool lidarrEnabled;
|
||||
@HiveField(1)
|
||||
String lidarrHost;
|
||||
@HiveField(2)
|
||||
String lidarrKey;
|
||||
@HiveField(26)
|
||||
Map lidarrHeaders;
|
||||
|
||||
Map<String, dynamic> getLidarr() => {
|
||||
'enabled': lidarrEnabled ?? false,
|
||||
'host': lidarrHost ?? '',
|
||||
'key': lidarrKey ?? '',
|
||||
'headers': lidarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//Radarr
|
||||
@HiveField(3)
|
||||
bool radarrEnabled;
|
||||
@HiveField(4)
|
||||
String radarrHost;
|
||||
@HiveField(5)
|
||||
String radarrKey;
|
||||
@HiveField(27)
|
||||
Map radarrHeaders;
|
||||
|
||||
Map<String, dynamic> getRadarr() => {
|
||||
'enabled': radarrEnabled ?? false,
|
||||
'host': radarrHost ?? '',
|
||||
'key': radarrKey ?? '',
|
||||
'headers': radarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//Sonarr
|
||||
@HiveField(6)
|
||||
bool sonarrEnabled;
|
||||
@HiveField(7)
|
||||
String sonarrHost;
|
||||
@HiveField(8)
|
||||
String sonarrKey;
|
||||
@HiveField(21)
|
||||
bool sonarrVersion3;
|
||||
@HiveField(28)
|
||||
Map sonarrHeaders;
|
||||
|
||||
Map<String, dynamic> getSonarr() => {
|
||||
'enabled': sonarrEnabled ?? false,
|
||||
'host': sonarrHost ?? '',
|
||||
'key': sonarrKey ?? '',
|
||||
'v3': sonarrVersion3 ?? false,
|
||||
'headers': sonarrHeaders ?? {},
|
||||
};
|
||||
|
||||
//SABnzbd
|
||||
@HiveField(9)
|
||||
bool sabnzbdEnabled;
|
||||
@HiveField(10)
|
||||
String sabnzbdHost;
|
||||
@HiveField(11)
|
||||
String sabnzbdKey;
|
||||
@HiveField(29)
|
||||
Map sabnzbdHeaders;
|
||||
|
||||
Map<String, dynamic> getSABnzbd() => {
|
||||
'enabled': sabnzbdEnabled ?? false,
|
||||
'host': sabnzbdHost ?? '',
|
||||
'key': sabnzbdKey ?? '',
|
||||
'headers': sabnzbdHeaders ?? {},
|
||||
};
|
||||
|
||||
//NZBGet
|
||||
@HiveField(12)
|
||||
bool nzbgetEnabled;
|
||||
@HiveField(13)
|
||||
String nzbgetHost;
|
||||
@HiveField(14)
|
||||
String nzbgetUser;
|
||||
@HiveField(15)
|
||||
String nzbgetPass;
|
||||
@HiveField(22)
|
||||
bool nzbgetBasicAuth;
|
||||
@HiveField(30)
|
||||
Map nzbgetHeaders;
|
||||
|
||||
Map<String, dynamic> getNZBGet() => {
|
||||
'enabled': nzbgetEnabled ?? false,
|
||||
'host': nzbgetHost ?? '',
|
||||
'user': nzbgetUser ?? '',
|
||||
'pass': nzbgetPass ?? '',
|
||||
'basic_auth': nzbgetBasicAuth ?? false,
|
||||
'headers': nzbgetHeaders ?? {},
|
||||
};
|
||||
|
||||
//Wake On LAN
|
||||
@HiveField(23)
|
||||
bool wakeOnLANEnabled;
|
||||
@HiveField(24)
|
||||
String wakeOnLANBroadcastAddress;
|
||||
@HiveField(25)
|
||||
String wakeOnLANMACAddress;
|
||||
|
||||
Map<String, dynamic> getWakeOnLAN() => {
|
||||
'enabled': wakeOnLANEnabled ?? false,
|
||||
'broadcastAddress': wakeOnLANBroadcastAddress ?? '',
|
||||
'MACAddress': wakeOnLANMACAddress ?? '',
|
||||
};
|
||||
|
||||
//Tautulli
|
||||
@HiveField(31)
|
||||
bool tautulliEnabled;
|
||||
@HiveField(32)
|
||||
String tautulliHost;
|
||||
@HiveField(33)
|
||||
String tautulliKey;
|
||||
@HiveField(35)
|
||||
Map tautulliHeaders;
|
||||
|
||||
Map<String, dynamic> getTautulli() => {
|
||||
'enabled': tautulliEnabled ?? false,
|
||||
'host': tautulliHost ?? '',
|
||||
'key': tautulliKey ?? '',
|
||||
'headers': tautulliHeaders ?? {},
|
||||
};
|
||||
|
||||
//Ombi
|
||||
@HiveField(36)
|
||||
bool ombiEnabled;
|
||||
@HiveField(37)
|
||||
String ombiHost;
|
||||
@HiveField(38)
|
||||
String ombiKey;
|
||||
@HiveField(39)
|
||||
Map ombiHeaders;
|
||||
|
||||
Map<String, dynamic> getOmbi() => {
|
||||
'enabled': ombiEnabled ?? false,
|
||||
'host': ombiHost ?? '',
|
||||
'key': ombiKey ?? '',
|
||||
'headers': ombiHeaders ?? {},
|
||||
};
|
||||
|
||||
List<String> get enabledModules => [
|
||||
...enabledAutomationModules,
|
||||
...enabledClientModules,
|
||||
...enabledMonitoringModules,
|
||||
];
|
||||
|
||||
List<String> get enabledAutomationModules => [
|
||||
if(lidarrEnabled ?? false) LidarrConstants.MODULE_KEY,
|
||||
if(radarrEnabled ?? false) RadarrConstants.MODULE_KEY,
|
||||
if(sonarrEnabled ?? false) SonarrConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
List<String> get enabledClientModules => [
|
||||
if(nzbgetEnabled ?? false) NZBGetConstants.MODULE_KEY,
|
||||
if(sabnzbdEnabled ?? false) SABnzbdConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
List<String> get enabledMonitoringModules => [
|
||||
if(tautulliEnabled ?? false) TautulliConstants.MODULE_KEY,
|
||||
if(ombiEnabled ?? false) OmbiConstants.MODULE_KEY,
|
||||
];
|
||||
|
||||
bool get anyAutomationEnabled => enabledAutomationModules.isNotEmpty;
|
||||
bool get anyClientsEnabled => enabledClientModules.isNotEmpty;
|
||||
bool get anyMonitoringEnabled => enabledMonitoringModules.isNotEmpty;
|
||||
bool get anythingEnabled => anyAutomationEnabled || anyClientsEnabled || anyMonitoringEnabled;
|
||||
}
|
||||
@@ -1,68 +1,76 @@
|
||||
// Imports
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import './adapters.dart';
|
||||
import 'package:lunasea/modules.dart' show
|
||||
HomeDatabase,
|
||||
SearchDatabase,
|
||||
SettingsDatabase,
|
||||
LidarrDatabase,
|
||||
RadarrDatabase,
|
||||
SonarrDatabase,
|
||||
NZBGetDatabase,
|
||||
SABnzbdDatabase;
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/home/core.dart' show HomeDatabase;
|
||||
import 'package:lunasea/modules/search/core.dart' show SearchDatabase;
|
||||
import 'package:lunasea/modules/settings/core.dart' show SettingsDatabase;
|
||||
import 'package:lunasea/modules/lidarr/core.dart' show LidarrDatabase;
|
||||
import 'package:lunasea/modules/radarr/core.dart' show RadarrDatabase;
|
||||
import 'package:lunasea/modules/sonarr/core.dart' show SonarrDatabase;
|
||||
import 'package:lunasea/modules/nzbget/core.dart' show NZBGetDatabase;
|
||||
import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdDatabase;
|
||||
import 'package:lunasea/modules/ombi/core.dart' show OmbiDatabase;
|
||||
import 'package:lunasea/modules/tautulli/core.dart' show TautulliDatabase;
|
||||
// Exports
|
||||
export 'package:hive/hive.dart';
|
||||
export 'package:hive_flutter/hive_flutter.dart';
|
||||
|
||||
/// Next HiveType: 16
|
||||
///
|
||||
/// Dead Fields:
|
||||
|
||||
class Database {
|
||||
Database._();
|
||||
class Database {
|
||||
static const String _DATABASE_PATH = 'database';
|
||||
|
||||
/// Initialize the database.
|
||||
///
|
||||
/// - Initializes Hive database path
|
||||
/// - Register all adapters
|
||||
/// - Open all boxes
|
||||
/// - Set default database state of necessary
|
||||
static Future<void> initialize() async {
|
||||
await Hive.initFlutter('database');
|
||||
await Hive.initFlutter(_DATABASE_PATH);
|
||||
_registerAdapters();
|
||||
await _openBoxes();
|
||||
if(profilesBox.keys.length == 0) setDefaults();
|
||||
}
|
||||
|
||||
/// Deinitialize the database by closing all open hive boxes.
|
||||
static Future<void> deinitialize() async => await Hive.close();
|
||||
|
||||
/// Registers all necessary object adapters for Hive.
|
||||
static void _registerAdapters() {
|
||||
//Core
|
||||
Hive.registerAdapter(IndexerHiveObjectAdapter());
|
||||
Hive.registerAdapter(ProfileHiveObjectAdapter());
|
||||
//General
|
||||
LunaSeaDatabase.registerAdapters();
|
||||
HomeDatabase.registerAdapters();
|
||||
SearchDatabase.registerAdapters();
|
||||
SettingsDatabase.registerAdapters();
|
||||
LunaDatabase().registerAdapters();
|
||||
HomeDatabase().registerAdapters();
|
||||
SearchDatabase().registerAdapters();
|
||||
SettingsDatabase().registerAdapters();
|
||||
//Automation
|
||||
LidarrDatabase.registerAdapters();
|
||||
RadarrDatabase.registerAdapters();
|
||||
SonarrDatabase.registerAdapters();
|
||||
LidarrDatabase().registerAdapters();
|
||||
RadarrDatabase().registerAdapters();
|
||||
SonarrDatabase().registerAdapters();
|
||||
//Clients
|
||||
NZBGetDatabase.registerAdapters();
|
||||
SABnzbdDatabase.registerAdapters();
|
||||
NZBGetDatabase().registerAdapters();
|
||||
SABnzbdDatabase().registerAdapters();
|
||||
//Monitoring
|
||||
OmbiDatabase().registerAdapters();
|
||||
TautulliDatabase().registerAdapters();
|
||||
}
|
||||
|
||||
/// Open all Hive boxes for reading.
|
||||
static Future<void> _openBoxes() async {
|
||||
await Hive.openBox('lunasea');
|
||||
await Hive.openBox<IndexerHiveObject>('indexers');
|
||||
await Hive.openBox<ProfileHiveObject>('profiles');
|
||||
}
|
||||
|
||||
/// Set the default state for all hive boxes.
|
||||
static void setDefaults() {
|
||||
//Clear all the boxes
|
||||
clearLunaSeaBox();
|
||||
clearProfilesBox();
|
||||
clearIndexersBox();
|
||||
clearAllBoxes();
|
||||
//Set default profile & enabled profile
|
||||
profilesBox.put('default', ProfileHiveObject.empty());
|
||||
lunaSeaBox.put(LunaSeaDatabaseValue.ENABLED_PROFILE.key, 'default');
|
||||
lunaSeaBox.put(LunaDatabaseValue.ENABLED_PROFILE.key, 'default');
|
||||
}
|
||||
|
||||
//Get boxes
|
||||
@@ -74,6 +82,11 @@ class Database {
|
||||
static void clearLunaSeaBox() => lunaSeaBox.deleteAll(lunaSeaBox.keys);
|
||||
static void clearProfilesBox() => profilesBox.deleteAll(profilesBox.keys);
|
||||
static void clearIndexersBox() => indexersBox.deleteAll(indexersBox.keys);
|
||||
static void clearAllBoxes() {
|
||||
clearLunaSeaBox();
|
||||
clearProfilesBox();
|
||||
clearIndexersBox();
|
||||
}
|
||||
|
||||
//Profile values
|
||||
static String get currentProfile => lunaSeaBox.get('profile');
|
||||
|
||||
194
lib/core/database/luna_database.dart
Normal file
194
lib/core/database/luna_database.dart
Normal file
@@ -0,0 +1,194 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaDatabase extends LunaModuleDatabase {
|
||||
@override
|
||||
void registerAdapters() {
|
||||
Hive.registerAdapter(LunaBrowserAdapter());
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> export() {
|
||||
Map<String, dynamic> data = {};
|
||||
for(LunaDatabaseValue value in LunaDatabaseValue.values) {
|
||||
switch(value) {
|
||||
// Non-primitive values
|
||||
case LunaDatabaseValue.SELECTED_BROWSER: data[value.key] = (LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).key; break;
|
||||
// Primitive values
|
||||
case LunaDatabaseValue.ENABLED_PROFILE:
|
||||
case LunaDatabaseValue.ENABLED_SENTRY:
|
||||
case LunaDatabaseValue.THEME_AMOLED:
|
||||
case LunaDatabaseValue.THEME_AMOLED_BORDER:
|
||||
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY:
|
||||
case LunaDatabaseValue.DRAWER_GROUP_MODULES:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_MONITORING:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_RADARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SONARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH:
|
||||
case LunaDatabaseValue.USE_24_HOUR_TIME: data[value.key] = value.data; break;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void import(Map<String, dynamic> config) {
|
||||
for(String key in config.keys) {
|
||||
LunaDatabaseValue value = valueFromKey(key);
|
||||
if(value != null) switch(value) {
|
||||
// Non-primitive values
|
||||
case LunaDatabaseValue.SELECTED_BROWSER: value.put(LunaBrowser.APPLE_SAFARI.fromKey(config[key])); break;
|
||||
// Primitive values
|
||||
case LunaDatabaseValue.ENABLED_PROFILE:
|
||||
case LunaDatabaseValue.ENABLED_SENTRY:
|
||||
case LunaDatabaseValue.THEME_AMOLED:
|
||||
case LunaDatabaseValue.THEME_AMOLED_BORDER:
|
||||
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY:
|
||||
case LunaDatabaseValue.DRAWER_GROUP_MODULES:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS:
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_MONITORING:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_RADARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SONARR:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI:
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH:
|
||||
case LunaDatabaseValue.USE_24_HOUR_TIME: value.put(config[key]); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LunaDatabaseValue valueFromKey(String key) {
|
||||
switch(key) {
|
||||
case 'profile': return LunaDatabaseValue.ENABLED_PROFILE;
|
||||
case 'LUNASEA_ENABLED_SENTRY': return LunaDatabaseValue.ENABLED_SENTRY;
|
||||
case 'LUNASEA_THEME_AMOLED': return LunaDatabaseValue.THEME_AMOLED;
|
||||
case 'LUNASEA_THEME_AMOLED_BORDER': return LunaDatabaseValue.THEME_AMOLED_BORDER;
|
||||
case 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY': return LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY;
|
||||
case 'LUNASEA_SELECTED_BROWSER': return LunaDatabaseValue.SELECTED_BROWSER;
|
||||
case 'LUNASEA_DRAWER_GROUP_MODULES': return LunaDatabaseValue.DRAWER_GROUP_MODULES;
|
||||
case 'LUNASEA_DRAWER_EXPAND_AUTOMATION': return LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION;
|
||||
case 'LUNASEA_DRAWER_EXPAND_CLIENTS': return LunaDatabaseValue.DRAWER_EXPAND_CLIENTS;
|
||||
case 'LUNASEA_DRAWER_EXPAND_MONITORING': return LunaDatabaseValue.DRAWER_EXPAND_MONITORING;
|
||||
case 'LUNASEA_QUICK_ACTIONS_LIDARR': return LunaDatabaseValue.QUICK_ACTIONS_LIDARR;
|
||||
case 'LUNASEA_QUICK_ACTIONS_RADARR': return LunaDatabaseValue.QUICK_ACTIONS_RADARR;
|
||||
case 'LUNASEA_QUICK_ACTIONS_SONARR': return LunaDatabaseValue.QUICK_ACTIONS_SONARR;
|
||||
case 'LUNASEA_QUICK_ACTIONS_NZBGET': return LunaDatabaseValue.QUICK_ACTIONS_NZBGET;
|
||||
case 'LUNASEA_QUICK_ACTIONS_SABNZBD': return LunaDatabaseValue.QUICK_ACTIONS_SABNZBD;
|
||||
case 'LUNASEA_QUICK_ACTIONS_TAUTULLI': return LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI;
|
||||
case 'LUNASEA_QUICK_ACTIONS_SEARCH': return LunaDatabaseValue.QUICK_ACTIONS_SEARCH;
|
||||
case 'LUNASEA_USE_24_HOUR_TIME': return LunaDatabaseValue.USE_24_HOUR_TIME;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LunaDatabaseValue {
|
||||
ENABLED_PROFILE,
|
||||
ENABLED_SENTRY,
|
||||
THEME_AMOLED,
|
||||
THEME_AMOLED_BORDER,
|
||||
THEME_IMAGE_BACKGROUND_OPACITY,
|
||||
SELECTED_BROWSER,
|
||||
DRAWER_GROUP_MODULES,
|
||||
DRAWER_EXPAND_AUTOMATION,
|
||||
DRAWER_EXPAND_CLIENTS,
|
||||
DRAWER_EXPAND_MONITORING,
|
||||
QUICK_ACTIONS_LIDARR,
|
||||
QUICK_ACTIONS_RADARR,
|
||||
QUICK_ACTIONS_SONARR,
|
||||
QUICK_ACTIONS_NZBGET,
|
||||
QUICK_ACTIONS_SABNZBD,
|
||||
QUICK_ACTIONS_TAUTULLI,
|
||||
QUICK_ACTIONS_SEARCH,
|
||||
USE_24_HOUR_TIME,
|
||||
}
|
||||
|
||||
extension LunaDatabaseValueExtension on LunaDatabaseValue {
|
||||
String get key {
|
||||
switch(this) {
|
||||
case LunaDatabaseValue.ENABLED_PROFILE: return 'profile';
|
||||
case LunaDatabaseValue.ENABLED_SENTRY: return 'LUNASEA_ENABLED_SENTRY';
|
||||
case LunaDatabaseValue.THEME_AMOLED: return 'LUNASEA_THEME_AMOLED';
|
||||
case LunaDatabaseValue.THEME_AMOLED_BORDER: return 'LUNASEA_THEME_AMOLED_BORDER';
|
||||
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return 'LUNASEA_THEME_IMAGE_BACKGROUND_OPACITY';
|
||||
case LunaDatabaseValue.SELECTED_BROWSER: return 'LUNASEA_SELECTED_BROWSER';
|
||||
case LunaDatabaseValue.DRAWER_GROUP_MODULES: return 'LUNASEA_DRAWER_GROUP_MODULES';
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return 'LUNASEA_DRAWER_EXPAND_AUTOMATION';
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: return 'LUNASEA_DRAWER_EXPAND_CLIENTS';
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: return 'LUNASEA_DRAWER_EXPAND_MONITORING';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: return 'LUNASEA_QUICK_ACTIONS_LIDARR';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_RADARR: return 'LUNASEA_QUICK_ACTIONS_RADARR';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return 'LUNASEA_QUICK_ACTIONS_SONARR';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return 'LUNASEA_QUICK_ACTIONS_NZBGET';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return 'LUNASEA_QUICK_ACTIONS_SABNZBD';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return 'LUNASEA_QUICK_ACTIONS_TAUTULLI';
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return 'LUNASEA_QUICK_ACTIONS_SEARCH';
|
||||
case LunaDatabaseValue.USE_24_HOUR_TIME: return 'LUNASEA_USE_24_HOUR_TIME';
|
||||
}
|
||||
throw Exception('key not found');
|
||||
}
|
||||
|
||||
dynamic get data {
|
||||
final box = Database.lunaSeaBox;
|
||||
switch(this) {
|
||||
case LunaDatabaseValue.ENABLED_PROFILE: return box.get(this.key, defaultValue: 'default');
|
||||
case LunaDatabaseValue.SELECTED_BROWSER: return box.get(this.key, defaultValue: LunaBrowser.APPLE_SAFARI);
|
||||
case LunaDatabaseValue.THEME_AMOLED: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.THEME_AMOLED_BORDER: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: return box.get(this.key, defaultValue: 10);
|
||||
case LunaDatabaseValue.ENABLED_SENTRY: return box.get(this.key, defaultValue: true);
|
||||
case LunaDatabaseValue.DRAWER_GROUP_MODULES: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: return box.get(this.key, defaultValue: true);
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: return box.get(this.key, defaultValue: true);
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: return box.get(this.key, defaultValue: true);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_RADARR: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return box.get(this.key, defaultValue: false);
|
||||
case LunaDatabaseValue.USE_24_HOUR_TIME: return box.get(this.key, defaultValue: false);
|
||||
}
|
||||
throw Exception('data not found');
|
||||
}
|
||||
|
||||
void put(dynamic value) {
|
||||
final box = Database.lunaSeaBox;
|
||||
switch(this) {
|
||||
case LunaDatabaseValue.ENABLED_PROFILE: if(value.runtimeType == String) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.ENABLED_SENTRY: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.THEME_AMOLED: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.THEME_AMOLED_BORDER: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY: if(value.runtimeType == int) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.SELECTED_BROWSER: if(value.runtimeType == LunaBrowser) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.DRAWER_GROUP_MODULES: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_CLIENTS: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.DRAWER_EXPAND_MONITORING: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_RADARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SONARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case LunaDatabaseValue.USE_24_HOUR_TIME: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
}
|
||||
LunaLogger().warning('LunaDatabaseValueExtension', 'put', 'Attempted to enter data for invalid LunaDatabaseValue: ${this?.toString() ?? 'null'}');
|
||||
}
|
||||
|
||||
void listen(Widget Function(BuildContext, Box<dynamic>, Widget) builder) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]),
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
13
lib/core/database/module_database.dart
Normal file
13
lib/core/database/module_database.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
abstract class LunaModuleDatabase {
|
||||
/// Register all adapters necessary for this module
|
||||
void registerAdapters();
|
||||
|
||||
/// Export the current module's database into a [Map] object
|
||||
Map<String, dynamic> export();
|
||||
|
||||
/// Receiving a map object, import a configuration
|
||||
void import(Map<String, dynamic> config);
|
||||
|
||||
/// Return the Database enumerator value given the key
|
||||
dynamic valueFromKey(String value);
|
||||
}
|
||||
@@ -3,9 +3,14 @@ import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaDialogs {
|
||||
LunaDialogs._();
|
||||
|
||||
static Future<List<dynamic>> editText(BuildContext context, String title, { String prefill = '', List<TextSpan> extraText }) async {
|
||||
/// Show an an edit text prompt.
|
||||
///
|
||||
/// Can pass in [prefill] String to prefill the [TextFormField]. Can also pass in a list of [TextSpan] tp show text above the field.
|
||||
///
|
||||
/// Returns list containing:
|
||||
/// - 0: Flag (true if they hit save, false if they cancelled the prompt)
|
||||
/// - 1: Value from the [TextEditingController].
|
||||
Future<List<dynamic>> editText(BuildContext context, String dialogTitle, { String prefill = '', List<TextSpan> extraText }) async {
|
||||
bool _flag = false;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _textController = TextEditingController()..text = prefill;
|
||||
@@ -19,7 +24,7 @@ class LunaDialogs {
|
||||
|
||||
await LSDialog.dialog(
|
||||
context: context,
|
||||
title: title,
|
||||
title: dialogTitle,
|
||||
buttons: [
|
||||
LSDialog.button(
|
||||
text: 'Save',
|
||||
@@ -32,7 +37,7 @@ class LunaDialogs {
|
||||
key: _formKey,
|
||||
child: LSDialog.textFormInput(
|
||||
controller: _textController,
|
||||
title: title,
|
||||
title: dialogTitle,
|
||||
onSubmitted: (_) => _setValues(true),
|
||||
validator: (_) => null,
|
||||
),
|
||||
@@ -43,7 +48,10 @@ class LunaDialogs {
|
||||
return [_flag, _textController.text];
|
||||
}
|
||||
|
||||
static Future<void> textPreview(BuildContext context, String title, String text, { bool alignLeft = false }) async {
|
||||
/// Show a text preview dialog.
|
||||
///
|
||||
/// Can pass in boolean [alignLeft] to left align the text in the dialog (useful for bulleted lists)
|
||||
Future<void> textPreview(BuildContext context, String dialogTitle, String text, { bool alignLeft = false }) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
@@ -63,7 +71,7 @@ class LunaDialogs {
|
||||
},
|
||||
),
|
||||
],
|
||||
title: LSDialog.title(text: title),
|
||||
title: LSDialog.title(text: dialogTitle),
|
||||
content: LSDialog.content(
|
||||
children: [
|
||||
LSDialog.textContent(
|
||||
@@ -75,14 +83,17 @@ class LunaDialogs {
|
||||
],
|
||||
),
|
||||
contentPadding: LSDialog.textDialogContentPadding(),
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<List<dynamic>> deleteCatalogueWithFiles(BuildContext context, String title) async {
|
||||
/// **Will be removed in future**
|
||||
///
|
||||
/// Show a delete catalogue with all files warning dialog.
|
||||
Future<List<dynamic>> deleteCatalogueWithFiles(BuildContext context, String moduleTitle) async {
|
||||
bool _flag = false;
|
||||
|
||||
void _setValues(bool flag) {
|
||||
@@ -101,7 +112,7 @@ class LunaDialogs {
|
||||
),
|
||||
],
|
||||
content: [
|
||||
LSDialog.textContent(text: 'Are you sure you want to delete all the files and folders for $title?'),
|
||||
LSDialog.textContent(text: 'Are you sure you want to delete all the files and folders for $moduleTitle?'),
|
||||
],
|
||||
contentPadding: LSDialog.textDialogContentPadding(),
|
||||
);
|
||||
|
||||
@@ -2,32 +2,42 @@ import 'package:encrypt/encrypt.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaEncryption {
|
||||
LunaEncryption._();
|
||||
static const int _IV_LENGTH = 16;
|
||||
static const int _KEY_LENGTH = 32;
|
||||
static const String _PAD_VALUE = '0';
|
||||
static const String ENCRYPTION_FAILURE = '<<INVALID_ENCRYPTION>>';
|
||||
|
||||
static String encrypt(String encryptionKey, String data) {
|
||||
/// Encrypt a given string using a given encryption key, and return the encrypted string.
|
||||
///
|
||||
/// The key is duped, and either trimmed or padded (with _PAD_VALUE) to 32 characters.
|
||||
String encrypt(String encryptionKey, String data) {
|
||||
try {
|
||||
String _key = (encryptionKey+encryptionKey).padRight(32, '0').substring(0, 32);
|
||||
Key key = Key.fromUtf8(_key);
|
||||
IV iv = IV.fromLength(16);
|
||||
String morphedKey = (encryptionKey+encryptionKey).padRight(_KEY_LENGTH, _PAD_VALUE).substring(0, _KEY_LENGTH);
|
||||
Key key = Key.fromUtf8(morphedKey);
|
||||
IV iv = IV.fromLength(_IV_LENGTH);
|
||||
final _encrypter = Encrypter(AES(key));
|
||||
final _encrypted = _encrypter.encrypt(data, iv: iv).base64;
|
||||
return _encrypted;
|
||||
} catch (e) {
|
||||
LunaLogger.error('package:lunasea/core/configuration/encryption.dart', 'encrypt', 'Encryption error', e, StackTrace.current);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to decrypted "$data" with using key "$encryptionKey"', error, stack);
|
||||
}
|
||||
return Constants.ENCRYPTION_FAILURE;
|
||||
return ENCRYPTION_FAILURE;
|
||||
}
|
||||
|
||||
static String decrypt(String decryptionKey, String data) {
|
||||
/// Decrypt a given string using a given encryption key, and return the decrypted string.
|
||||
///
|
||||
/// The key is duped, and either trimmed or padded (with _PAD_VALUE) to 32 characters.
|
||||
String decrypt(String decryptionKey, String data) {
|
||||
try {
|
||||
String _key = (decryptionKey+decryptionKey).padRight(32, '0').substring(0, 32);
|
||||
Key key = Key.fromUtf8(_key);
|
||||
IV iv = IV.fromLength(16);
|
||||
String morphedKey = (decryptionKey+decryptionKey).padRight(_KEY_LENGTH, _PAD_VALUE).substring(0, _KEY_LENGTH);
|
||||
Key key = Key.fromUtf8(morphedKey);
|
||||
IV iv = IV.fromLength(_IV_LENGTH);
|
||||
final _encrypter = Encrypter(AES(key));
|
||||
return _encrypter.decrypt64(data, iv: iv);
|
||||
} catch (e) {
|
||||
LunaLogger.error('package:lunasea/core/configuration/encryption.dart', 'decrypt', 'Decryption error', e, StackTrace.current);
|
||||
} catch (error) {
|
||||
/// Do not log as error (to Sentry), since a decrpytion error is very likely a user problem
|
||||
LunaLogger().warning('LunaEncryption', 'decrypt', 'Failed to decrypted "$data" with using key "$decryptionKey"');
|
||||
}
|
||||
return Constants.ENCRYPTION_FAILURE;
|
||||
return ENCRYPTION_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export 'extensions/integer.dart';
|
||||
export 'extensions/datetime.dart';
|
||||
export 'extensions/double.dart';
|
||||
export 'extensions/duration.dart';
|
||||
export 'extensions/in_app_purchases.dart';
|
||||
export 'extensions/flog.dart';
|
||||
export 'extensions/flog_log_level.dart';
|
||||
export 'extensions/iap_product_details.dart';
|
||||
export 'extensions/int.dart';
|
||||
export 'extensions/string.dart';
|
||||
|
||||
@@ -2,39 +2,42 @@ import 'package:intl/intl.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
extension DateTimeExtension on DateTime {
|
||||
//ignore: non_constant_identifier_names
|
||||
DateTime lsDateTime_floor() => DateTime(this.year, this.month, this.day);
|
||||
/// Get the floor of a date.
|
||||
///
|
||||
/// The floor is a new [DateTime] object with only the year, month, and day copied from the original source.
|
||||
DateTime get lunaFloor => DateTime(this.year, this.month, this.day);
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
String lsDateTime_ageString(DateTime date) {
|
||||
if(this == null || date == null) return 'Unknown';
|
||||
Duration diff = this.difference(date);
|
||||
if(diff.inDays >= 1) {
|
||||
return '${diff.inDays} ${diff.inDays == 1 ? 'Day' : 'Days'} Ago';
|
||||
} else if(diff.inHours >= 1) {
|
||||
return '${diff.inHours} ${diff.inHours == 1 ? 'Hour' : 'Hours'} Ago';
|
||||
} else if(diff.inMinutes >= 1) {
|
||||
return '${diff.inMinutes} ${diff.inMinutes == 1 ? 'Minute' : 'Minutes'} Ago';
|
||||
}
|
||||
/// Returns a string representation of the "age" of the [DateTime] object.
|
||||
///
|
||||
/// Compares to [DateTime.now().toLocal()] to calculate the age.
|
||||
String get lunaAge {
|
||||
if(this == null) return 'Unknown';
|
||||
Duration diff = DateTime.now().toLocal().difference(this);
|
||||
if(diff.inDays >= 1) return '${diff.inDays} ${diff.inDays == 1 ? 'Day' : 'Days'} Ago';
|
||||
if(diff.inHours >= 1) return '${diff.inHours} ${diff.inHours == 1 ? 'Hour' : 'Hours'} Ago';
|
||||
if(diff.inMinutes >= 1) return '${diff.inMinutes} ${diff.inMinutes == 1 ? 'Minute' : 'Minutes'} Ago';
|
||||
return '${diff.inSeconds} ${diff.inSeconds == 1 ? 'Second' : 'Seconds'} Ago';
|
||||
}
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
String lsDateTime_upcomingString(DateTime date, { bool shouldLimit = true, int limit = 30 }) {
|
||||
if(this == null || date == null) return 'Unknown';
|
||||
Duration diff = date.difference(this);
|
||||
/// Returns a string representation of how far in the future the day is.
|
||||
///
|
||||
/// Compares to [DateTime.now().toLocal()] to calculate the upcoming time.
|
||||
String get lunaUpcomingDays {
|
||||
if(this == null) return 'Unknown';
|
||||
Duration diff = this.difference(DateTime.now().toLocal());
|
||||
if(diff.inDays == 0) return 'Today';
|
||||
if(shouldLimit && diff.inDays > limit) return '';
|
||||
return 'In ${diff.inDays} ${diff.inDays == 1 ? "Day" : "Days"}';
|
||||
}
|
||||
|
||||
/// Returns just the time as a String.
|
||||
/// 3 PM will return either as 15:00 (24 hour style) or 3:00 PM depending on the configured database option
|
||||
//ignore: non_constant_identifier_names
|
||||
String get lsDateTime_time => LunaSeaDatabaseValue.USE_24_HOUR_TIME.data
|
||||
? DateFormat.Hm().format(this)
|
||||
: DateFormat.jm().format(this);
|
||||
/// Returns just the time as a string.
|
||||
///
|
||||
/// 3 PM will return either 15:00 (24 hour style) or 3:00 PM depending on the configured database option.
|
||||
String get lunaTime => LunaDatabaseValue.USE_24_HOUR_TIME.data ? DateFormat.Hm().format(this) : DateFormat.jm().format(this);
|
||||
|
||||
/// Returns just the date as a string.
|
||||
///
|
||||
/// Formatted as YYYY-MM-DD
|
||||
String get lunaDate => '${this.year.toString().padLeft(4, '0')}-${this.month.toString().padLeft(2, '0')}-${this.day.toString().padLeft(2, '0')}';
|
||||
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
String get lsDateTime_date => '${this.year.toString().padLeft(4, '0')}-${this.month.toString().padLeft(2, '0')}-${this.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
export 'double/time.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
extension DoubleExtension on double {
|
||||
/// Given a double (of hours), return a string signifying how long ago it occurred.
|
||||
///
|
||||
/// Time greater than 48 hours => "x Days Ago" else "x Hours Ago"
|
||||
String lunaHoursToAge() {
|
||||
if(this == null) return Constants.TEXT_EMDASH;
|
||||
int days = (this/24).floor();
|
||||
if(this > 48) return '$days Days Ago';
|
||||
return this == 1 ? '1 Hour Ago' : '${this.toStringAsFixed(1)} Hours Ago';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
extension DoubleTimeExtension on double {
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsTime_releaseAgeString() {
|
||||
if(this == null) return '';
|
||||
int days = (this/24).floor();
|
||||
if(this > 48) return days == 1 ? '1 Day Ago' : '$days Days Ago';
|
||||
return this == 1 ? '1 Hour Ago' : '${this.toStringAsFixed(1)} Hours Ago';
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,31 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
extension DurationExtension on Duration {
|
||||
/// Creates a HH:MM:SS timestamp
|
||||
//ignore: non_constant_identifier_names
|
||||
String lsDuration_timestamp() {
|
||||
String hours = this.inHours.toString().padLeft(2, '0');
|
||||
String minutes = (this.inMinutes%60).toString().padLeft(2, '0');
|
||||
String seconds = (this.inSeconds%60).toString().padLeft(2, '0');
|
||||
return [
|
||||
if(hours != '00') '$hours:',
|
||||
'$minutes:',
|
||||
'$seconds',
|
||||
].join();
|
||||
}
|
||||
/// Return a string representation of the timestamp.
|
||||
///
|
||||
/// Format example: HH:MM:SS or MM:SS if the duration is less than one hour
|
||||
String get lunaTimestamp => [
|
||||
if(this.inHours != 0) this.inHours.toString().padLeft(2, '0'),
|
||||
(this.inMinutes%60).toString().padLeft(2, '0'),
|
||||
(this.inSeconds%60).toString().padLeft(2, '0'),
|
||||
].join(':');
|
||||
|
||||
|
||||
/// Creates a full timestamp, akin to 1 Day, 23 Hours, 10 Minutes
|
||||
//ignore: non_constant_identifier_names
|
||||
String lsDuration_fullTimestamp() {
|
||||
/// Returns a string representation of the timestamp as words.
|
||||
///
|
||||
/// Format example: 1 Day, 23 Hours, 10 Minutes
|
||||
String get lunaTimestampWords {
|
||||
String days, hours, minutes;
|
||||
if(this.inDays == 1) days = '1 Day ';
|
||||
if(this.inDays > 1) days = '${this.inDays.toString()} Days ';
|
||||
if(this.inHours == 1) hours = '1 Hour ';
|
||||
if(this.inHours > 1) hours = '${(this.inHours%24).toString()} Hours ';
|
||||
if(this.inMinutes == 1) minutes = '1 Minute ';
|
||||
if(this.inMinutes > 1) minutes = '${(this.inMinutes%60).toString()} Minutes ';
|
||||
if(this.inDays == 1) days = '1 Day';
|
||||
if(this.inDays > 1) days = '${this.inDays.toString()} Days';
|
||||
if(this.inHours == 1) hours = '1 Hour';
|
||||
if(this.inHours > 1) hours = '${(this.inHours%24).toString()} Hours';
|
||||
if(this.inMinutes == 1) minutes = '1 Minute';
|
||||
if(this.inMinutes > 1) minutes = '${(this.inMinutes%60).toString()} Minutes';
|
||||
return [
|
||||
if(days != null) days,
|
||||
if(hours != null) hours,
|
||||
if(minutes != null) minutes,
|
||||
if(minutes == null) Constants.TEXT_EMDASH,
|
||||
].join();
|
||||
].join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export 'flog/log_level.dart';
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:f_logs/f_logs.dart' as FLog;
|
||||
|
||||
extension FLogLogLevelExtension on FLog.LogLevel {
|
||||
//ignore: non_constant_identifier_names
|
||||
String lsFLogLogLevel_name() => this.toString().substring(9);
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
Color lsFLogLogLevel_color() {
|
||||
switch(this.toString()) {
|
||||
case 'LogLevel.WARNING': return Colors.orange;
|
||||
case 'LogLevel.ERROR': return Colors.red;
|
||||
case 'LogLevel.FATAL': return LunaColours.accent;
|
||||
default: return Colors.blueGrey;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
lib/core/extensions/flog_log_level.dart
Normal file
18
lib/core/extensions/flog_log_level.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:f_logs/f_logs.dart';
|
||||
|
||||
extension FLogLogLevelExtension on LogLevel {
|
||||
/// Return a readable string version of the log level.
|
||||
String get lunaName => this.toString().substring(9);
|
||||
|
||||
/// Return a [Color] corresponding to the log level.
|
||||
Color get lunaColour {
|
||||
switch(this) {
|
||||
case LogLevel.WARNING: return LunaColours.orange;
|
||||
case LogLevel.ERROR: return LunaColours.red;
|
||||
case LogLevel.FATAL: return LunaColours.accent;
|
||||
default: return LunaColours.blueGrey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,28 +3,27 @@ import 'package:in_app_purchase/in_app_purchase.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
extension ProductDetailsExtension on ProductDetails {
|
||||
//ignore: non_constant_identifier_names
|
||||
IconData get ls_Icon {
|
||||
/// Returns [IconData] icon corresponding to the IAP
|
||||
IconData get lunaIcon {
|
||||
switch(this.id) {
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_01: return Icons.local_drink;
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_03: return Icons.local_cafe;
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_05: return Icons.local_bar;
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_10: return Icons.fastfood;
|
||||
case LunaInAppPurchases.IAP_UPGRADE_PRO: return Icons.upgrade;
|
||||
default: return Icons.attach_money;
|
||||
}
|
||||
}
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
String get ls_Name {
|
||||
/// Returns a readable name for the corresponding IAP
|
||||
String get lunaName {
|
||||
switch(this.id) {
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_01: return 'Buy Me A Soda';
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_03: return 'Buy Me A Coffee';
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_05: return 'Buy Me A Beer';
|
||||
case LunaInAppPurchases.IAP_ID_DONATION_10: return 'Buy Me A Burger';
|
||||
case LunaInAppPurchases.IAP_UPGRADE_PRO: return 'Upgrade to LunaSea Pro';
|
||||
default: return 'Unknown In-App Purchase';
|
||||
}
|
||||
}
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
String get ls_Description => '${this.price}';
|
||||
}
|
||||
}
|
||||
76
lib/core/extensions/int.dart
Normal file
76
lib/core/extensions/int.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
extension IntegerBytesExtension on int {
|
||||
static const _BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb'];
|
||||
static const _BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
|
||||
String _bytesToString({ int decimals = 2, position = 0, bool bytes = true }) {
|
||||
if(this == null || this <= 0) return '${0.toStringAsFixed(decimals)} B';
|
||||
int chunk = bytes ? 1024 : 1000;
|
||||
double size = this.toDouble();
|
||||
while(size > chunk) {
|
||||
size /= chunk;
|
||||
position++;
|
||||
}
|
||||
return '${size.toStringAsFixed(decimals)} ${bytes ? _BYTE_SIZES[position] : _BIT_SIZES[position]}';
|
||||
}
|
||||
|
||||
/// Given an integer of the number of bytes/bits, return a string representation.
|
||||
String lunaBytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 0, bytes: bytes);
|
||||
|
||||
/// Given an integer of the number of kilobytes/kilobits, return a string representation.
|
||||
String lunaKilobytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 1, bytes: bytes);
|
||||
|
||||
/// Given an integer of the number of megabytes/megabits, return a string representation.
|
||||
String lunaMegabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 2, bytes: bytes);
|
||||
|
||||
/// Given an integer of the number of gigabytes/gigabits, return a string representation.
|
||||
String lunaGigabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 3, bytes: bytes);
|
||||
}
|
||||
|
||||
extension IntegerTimeExtension on int {
|
||||
static const int _MINUTE_IN_SECONDS = 60;
|
||||
static const int _HOUR_IN_SECONDS = 60*60;
|
||||
static const int _DAY_IN_SECONDS = 60*60*24;
|
||||
|
||||
/// Given an integer duration, converts it to a readable string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input.
|
||||
///
|
||||
/// Only contains the highest factor (2.5 Days would return 2 Days).
|
||||
String lunaDuration({ int divisor = 1, int multiplier = 1 }) {
|
||||
if(this == null) return '';
|
||||
int duration = ((this*multiplier)/divisor).floor();
|
||||
int days = (duration/_DAY_IN_SECONDS).floor();
|
||||
if(days > 0) return '$days ${days == 1 ? 'Day' : 'Days'}';
|
||||
int hours = (duration/_HOUR_IN_SECONDS).floor();
|
||||
if(hours > 0) return '$hours ${hours == 1 ? 'Hour' : 'Hours'}';
|
||||
int minutes = (duration/_MINUTE_IN_SECONDS).floor();
|
||||
if(minutes > 0) return '$minutes ${minutes == 1 ? 'Minute' : 'Minutes'}';
|
||||
return '$duration ${duration == 1 ? 'Second' : 'Seconds'}';
|
||||
}
|
||||
|
||||
/// Given an integer duration, converts it to a readable runtime string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input.
|
||||
///
|
||||
/// Format example: 1h 23m
|
||||
String lunaRuntime({ int divisor = 1, int multiplier = 1 }) {
|
||||
if(this == null || this == 0) return '';
|
||||
double runtime = ((this*multiplier)/divisor);
|
||||
if(runtime < 60) return '${runtime.floor()}m';
|
||||
return '${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m';
|
||||
}
|
||||
|
||||
/// Given an integer duration, converts it to a readable timestamp string. Assumes the duration is in seconds, use the `divisor` and `multiplier` variables to easily modify the input.
|
||||
///
|
||||
/// Format example: 1:23:45 (1 hour, 23 minutes, and 45 seconds)
|
||||
String lunaTimestamp({ int divisor = 1, int multiplier = 1 }) {
|
||||
if(this == null) return '';
|
||||
int duration = ((this*multiplier)/divisor).floor();
|
||||
int hours = 0, minutes = 0;
|
||||
while(duration >= _HOUR_IN_SECONDS) {
|
||||
duration -= _HOUR_IN_SECONDS;
|
||||
hours++;
|
||||
}
|
||||
while(duration >= _MINUTE_IN_SECONDS) {
|
||||
duration -= _MINUTE_IN_SECONDS;
|
||||
minutes++;
|
||||
}
|
||||
return '${hours.toString().padLeft(1, '0')}:${minutes.toString().padLeft(2, '0')}:${duration.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export 'integer/bytes.dart';
|
||||
export 'integer/time.dart';
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'package:lunasea/core/constants.dart';
|
||||
|
||||
extension IntegerBytesExtension on int {
|
||||
String _bytesToString({ int decimals = 2, position = 0, bool bytes = true }) {
|
||||
if(this == null || this <= 0) return '${0.toStringAsFixed(decimals)} B';
|
||||
int chunk = bytes ? 1024 : 1000;
|
||||
double size = this.toDouble();
|
||||
while(size > chunk) {
|
||||
size /= chunk;
|
||||
position++;
|
||||
}
|
||||
return '${size.toStringAsFixed(decimals)} ${bytes ? Constants.BYTE_SIZES[position] : Constants.BIT_SIZES[position]}';
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsBytes_BytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 0, bytes: bytes);
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsBytes_KilobytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 1, bytes: bytes);
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsBytes_MegabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 2, bytes: bytes);
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsBytes_GigabytesToString({ int decimals = 2, bool bytes = true }) => _bytesToString(decimals: decimals, position: 3, bytes: bytes);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
const DAY_IN_SECONDS = 60*60*24;
|
||||
const HOUR_IN_SECONDS = 60*60;
|
||||
const MINUTE_IN_SECONDS = 60;
|
||||
|
||||
extension IntegerTimeExtension on int {
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsTime_runtimeString({ bool dot = false, int divisor = 1, int multipler = 1 }) {
|
||||
if(this == null || this == 0) return '';
|
||||
double runtime = ((this*multipler)/divisor);
|
||||
if(runtime < 60) return dot
|
||||
? '\t•\t${runtime.floor()}m'
|
||||
: '${runtime.floor()}m';
|
||||
return dot
|
||||
? '\t•\t${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m'
|
||||
: '${(runtime/60).floor()}h ${(runtime-((runtime/60).floor()*60)).floor()}m';
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsTime_timestampString({ int divisor = 1, int multipler = 1 }) {
|
||||
if(this == null) return '';
|
||||
int seconds = ((this*multipler)/divisor).floor();
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
while(seconds >= 3600) {
|
||||
seconds -= 3600;
|
||||
hours++;
|
||||
}
|
||||
while(seconds >= 60) {
|
||||
seconds -= 60;
|
||||
minutes++;
|
||||
}
|
||||
return '${hours.toString().padLeft(1, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsTime_durationString({ int divisor = 1, int multiplier = 1 }) {
|
||||
if(this == null) return '';
|
||||
int duration = ((this*multiplier)/divisor).floor();
|
||||
int days = (duration/DAY_IN_SECONDS).floor();
|
||||
if(days > 0) return days == 1 ? '1 Day' : '$days Days';
|
||||
int hours = (duration/HOUR_IN_SECONDS).floor();
|
||||
if(hours > 0) return hours == 1 ? '1 Hour' : '$hours Hours';
|
||||
int minutes = (duration/MINUTE_IN_SECONDS).floor();
|
||||
if(minutes > 0) return minutes == 1 ? '1 Minute' : '$minutes Minutes';
|
||||
return duration == 1 ? '1 Second' : '$duration Seconds';
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,58 @@
|
||||
export 'string/language.dart';
|
||||
export 'string/links.dart';
|
||||
export 'string/slugs.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
extension StringExtension on String {
|
||||
/// Returns a string with all first letters of each word capitalized
|
||||
///
|
||||
/// Default word splitting pattern is by a space.
|
||||
String lunaCapitalizeFirstLetters({ Pattern pattern = ' ' }) {
|
||||
List<String> split = this.split(pattern);
|
||||
for(var i=0; i<split.length; i++) {
|
||||
split[i] = split[i].substring(0, 1).toUpperCase()+split[i].substring(1);
|
||||
}
|
||||
return split.join(pattern);
|
||||
}
|
||||
|
||||
/// Convert a string into a slug format.
|
||||
///
|
||||
/// Example "LunaSea Flutter" => 'lunasea-flutter';
|
||||
String lunaConvertToSlug() => this.toLowerCase().replaceAll(RegExp(r'[\ \.]'), '-').replaceAll(RegExp(r'[^a-zA-Z0-9\-]'), '').trim();
|
||||
}
|
||||
|
||||
extension StringLinksExtension on String {
|
||||
Future<void> _openLink(String url) async {
|
||||
try {
|
||||
//Attempt to launch the link forced as a universal link
|
||||
if(await _launchUniversalLink(url)) return;
|
||||
//If that fails and we're using a custom browser, format the link for the correct browser and attempt to launch
|
||||
LunaBrowser _browser = LunaDatabaseValue.SELECTED_BROWSER.data;
|
||||
if(_browser != LunaBrowser.APPLE_SAFARI && await _launchCustomBrowser(_browser.formatted(url))) return;
|
||||
//If all else fails, just launch it in Safari/stock browser
|
||||
await _launch(url);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Unable to open link: $url', error, stack);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _launchUniversalLink(String url) async => await launch(url, forceSafariVC: false, universalLinksOnly: true);
|
||||
Future<bool> _launchCustomBrowser(String url) async => await launch(url, forceSafariVC: false);
|
||||
Future<bool> _launch(String url) async => await launch(url);
|
||||
|
||||
/// Attempt to open this string as a URL in a browser.
|
||||
Future<void> lunaOpenGenericLink() async => await _openLink(this);
|
||||
|
||||
/// Attach this string as an ID/title to IMDB and attempt to launch it as a URL.
|
||||
Future<void> lunaOpenIMDB() async => await _openLink('https://www.imdb.com/title/$this');
|
||||
|
||||
/// Attach this string as a video ID to YouTube and attempt to launch it as a URL.
|
||||
Future<void> lunaOpenYouTube() async => await _openLink('https://www.youtube.com/watch?v=$this');
|
||||
|
||||
/// Attach this string as a movie ID to TheMovieDB and attempt to launch it as a URL.
|
||||
Future<void> lunaOpenTheMovieDB() async => await _openLink('https://www.themoviedb.org/movie/$this');
|
||||
|
||||
/// Attach this string as a series ID to TheTVDB and attempt to launch it as a URL.
|
||||
Future<void> lunaOpenTheTVDB() async => await _openLink('https://www.thetvdb.com/?id=$this&tab=series');
|
||||
|
||||
/// Attach this string as a series ID to TVMaze and attempt to launch it as a URL.
|
||||
Future<void> lunaOpenTVMaze() async => await _openLink('https://www.tvmaze.com/shows/$this');
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
extension StringLanguageExtension on String {
|
||||
// ignore: non_constant_identifier_names
|
||||
lsLanguage_Capitalize() {
|
||||
List<String> split = this.split(' ');
|
||||
for(var i=0; i<split.length; i++) {
|
||||
split[i] = split[i].substring(0, 1).toUpperCase()+split[i].substring(1);
|
||||
}
|
||||
return split.join(' ');
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/database.dart';
|
||||
import 'package:lunasea/core/ui.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
part 'links.g.dart';
|
||||
|
||||
@HiveType(typeId: 11, adapterName: 'LSBrowsersAdapter')
|
||||
enum LSBrowsers {
|
||||
@HiveField(0)
|
||||
APPLE_SAFARI,
|
||||
@HiveField(1)
|
||||
BRAVE_BROWSER,
|
||||
@HiveField(2)
|
||||
GOOGLE_CHROME,
|
||||
@HiveField(3)
|
||||
MICROSOFT_EDGE,
|
||||
@HiveField(4)
|
||||
MOZILLA_FIREFOX,
|
||||
}
|
||||
|
||||
extension LSBrowsersExtension on LSBrowsers {
|
||||
String get name {
|
||||
switch(this) {
|
||||
case LSBrowsers.APPLE_SAFARI: return 'Apple Safari';
|
||||
case LSBrowsers.BRAVE_BROWSER: return 'Brave Browser';
|
||||
case LSBrowsers.GOOGLE_CHROME: return 'Google Chrome';
|
||||
case LSBrowsers.MICROSOFT_EDGE: return 'Microsoft Edge';
|
||||
case LSBrowsers.MOZILLA_FIREFOX: return 'Mozilla Firefox';
|
||||
default: return 'Unknown Browser';
|
||||
}
|
||||
}
|
||||
|
||||
String get key {
|
||||
switch(this) {
|
||||
case LSBrowsers.APPLE_SAFARI: return 'applesafari';
|
||||
case LSBrowsers.BRAVE_BROWSER: return 'bravebrowser';
|
||||
case LSBrowsers.GOOGLE_CHROME: return 'googlechrome';
|
||||
case LSBrowsers.MICROSOFT_EDGE: return 'microsoftedge';
|
||||
case LSBrowsers.MOZILLA_FIREFOX: return 'mozillafirefox';
|
||||
default: return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch(this) {
|
||||
case LSBrowsers.APPLE_SAFARI: return CustomIcons.safari;
|
||||
case LSBrowsers.BRAVE_BROWSER: return CustomIcons.bravebrowser;
|
||||
case LSBrowsers.GOOGLE_CHROME: return CustomIcons.chrome;
|
||||
case LSBrowsers.MICROSOFT_EDGE: return CustomIcons.microsoftedge;
|
||||
case LSBrowsers.MOZILLA_FIREFOX: return CustomIcons.firefox;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String formatted(String url) {
|
||||
bool isHttps = url.substring(0, 8) == 'https://';
|
||||
switch(this) {
|
||||
case LSBrowsers.BRAVE_BROWSER: return 'brave://open-url?url=$url';
|
||||
case LSBrowsers.GOOGLE_CHROME: return isHttps
|
||||
? url.replaceFirst('https://', 'googlechromes://')
|
||||
: url.replaceFirst('http://', 'googlechrome://');
|
||||
case LSBrowsers.MICROSOFT_EDGE: return isHttps
|
||||
? url.replaceFirst('https://', 'microsoft-edge-https://')
|
||||
: url.replaceFirst('http://', 'microsoft-edge-http://');
|
||||
case LSBrowsers.MOZILLA_FIREFOX: return 'firefox://open-url?url=$url';
|
||||
case LSBrowsers.APPLE_SAFARI:
|
||||
default: return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension StringLinksExtension on String {
|
||||
Future<void> _openLink(String url) async {
|
||||
try {
|
||||
//Attempt to launch the link forced as a universal link
|
||||
if(await _launchUniversalLink(url)) return;
|
||||
//If that fails and we're using a custom browser, format the link for the correct browser and attempt to launch
|
||||
LSBrowsers _browser = LunaSeaDatabaseValue.SELECTED_BROWSER.data;
|
||||
if(
|
||||
_browser != LSBrowsers.APPLE_SAFARI &&
|
||||
await _launchCustomBrowser(_browser.formatted(url))
|
||||
) return;
|
||||
//If all else fails, just launch it in Safari
|
||||
await _launch(url);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Future<bool> _launchUniversalLink(String url) async => await launch(
|
||||
url,
|
||||
forceSafariVC: false,
|
||||
universalLinksOnly: true,
|
||||
);
|
||||
|
||||
Future<bool> _launchCustomBrowser(String url) async => await launch(
|
||||
url,
|
||||
forceSafariVC: false,
|
||||
);
|
||||
|
||||
Future<bool> _launch(String url) async => await launch(url);
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenLink() async => await _openLink(this);
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenIMDB() async => await _openLink('https://www.imdb.com/title/$this');
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenYoutube() async => await _openLink('https://www.youtube.com/watch?v=$this');
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenMovieDB() async => await _openLink('https://www.themoviedb.org/movie/$this');
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenTVDB() async => await _openLink('https://www.thetvdb.com/?id=$this&tab=series');
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> lsLinks_OpenTVMaze() async => await _openLink('https://www.tvmaze.com/shows/$this');
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
extension StringSlugsExtension on String {
|
||||
// ignore: non_constant_identifier_names
|
||||
String lsSlugs_ConvertToSlug() {
|
||||
return this
|
||||
.toLowerCase()
|
||||
.replaceAll(RegExp(r'[\ \.]'), '-')
|
||||
.replaceAll(RegExp(r'[^a-zA-Z0-9\-]'), '')
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,17 @@ import 'package:share/share.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class LunaFileSystem {
|
||||
LunaFileSystem._();
|
||||
|
||||
static Future<String> _getTempFilePath(String filename) async {
|
||||
Directory tempDir = await getTemporaryDirectory();
|
||||
return '${tempDir.path}/$filename';
|
||||
}
|
||||
|
||||
static Future<void> exportFileToTemporaryStorage(String name, String data) async {
|
||||
String _file = await _getTempFilePath(name);
|
||||
/// Export a given string to the OS-level share sheet with the given name.
|
||||
///
|
||||
/// Temporarily writes the string as a [File] to the temporary storage directory on the OS.
|
||||
///
|
||||
/// Temporary storage is eventually cleared by the OS, but is more than enough time to save/send via the share sheet.
|
||||
Future<void> exportStringToShareSheet(String name, String data) async {
|
||||
Directory tempDirectory = await getTemporaryDirectory();
|
||||
String path = '${tempDirectory.path}/$name';
|
||||
//Write the NZB to the filesystem
|
||||
File file = File(_file);
|
||||
File file = File(path);
|
||||
await file?.writeAsString(data);
|
||||
await Share.shareFiles([_file]);
|
||||
await Share.shareFiles([path]);
|
||||
}
|
||||
}
|
||||
|
||||
13
lib/core/firebase.dart
Normal file
13
lib/core/firebase.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
|
||||
class LunaFirebase {
|
||||
/// Return an instance of [FirebaseApp].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
FirebaseApp get app => Firebase.app();
|
||||
|
||||
/// Initialize Firebase: Calls [Firebase.initializeApp()].
|
||||
///
|
||||
/// This must be called before anything accesses Firebase services, or an exception will be thrown.
|
||||
static Future<FirebaseApp> initialize({ String name }) async => Firebase.initializeApp();
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
class LunaImageCache {
|
||||
LunaImageCache._();
|
||||
|
||||
/// Initialize the image cache by setting [ImageCache]'s maximumSize and maximumSizeBytes.
|
||||
static void initialize() {
|
||||
ImageCache().maximumSize = 1000;
|
||||
ImageCache().maximumSizeBytes = 128 << 20;
|
||||
|
||||
@@ -2,40 +2,75 @@ import 'dart:async';
|
||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||
|
||||
class LunaInAppPurchases {
|
||||
// Donation IAP IDs
|
||||
static const IAP_ID_DONATION_01 = 'donation_01';
|
||||
static const IAP_ID_DONATION_03 = 'donation_03';
|
||||
static const IAP_ID_DONATION_05 = 'donation_05';
|
||||
static const IAP_ID_DONATION_10 = 'donation_10';
|
||||
|
||||
static const IAP_IDS = [
|
||||
static const IAP_DONATION_IDS = [
|
||||
IAP_ID_DONATION_01,
|
||||
IAP_ID_DONATION_03,
|
||||
IAP_ID_DONATION_05,
|
||||
IAP_ID_DONATION_10,
|
||||
];
|
||||
|
||||
static final InAppPurchaseConnection connection = InAppPurchaseConnection.instance;
|
||||
static StreamSubscription<List<PurchaseDetails>> purchaseStream;
|
||||
static List<ProductDetails> products = [];
|
||||
static bool available = true;
|
||||
// Upgrade IAP IDs
|
||||
static const IAP_UPGRADE_PRO = 'upgrade_01';
|
||||
static const IAP_UPGRADE_IDS = [
|
||||
IAP_UPGRADE_PRO,
|
||||
];
|
||||
|
||||
static InAppPurchaseConnection get connection => InAppPurchaseConnection.instance;
|
||||
static StreamSubscription<List<PurchaseDetails>> _purchaseStream;
|
||||
static List<ProductDetails> donationIAPs = [];
|
||||
static List<ProductDetails> upgradeIAPs = [];
|
||||
static bool isAvailable = false;
|
||||
|
||||
/// Initialize the in-app purchases connection.
|
||||
///
|
||||
/// - Enables pending purchase
|
||||
/// - Attach [InAppPurchaseConnection.instance.purchaseUpdateStream] listener for automatically completing IAP purchases
|
||||
/// - Fetch and store donation IAPs
|
||||
static Future<void> initialize() async {
|
||||
InAppPurchaseConnection.enablePendingPurchases();
|
||||
purchaseStream = connection.purchaseUpdatedStream.listen((data) => LunaInAppPurchases._purchasedCallback(data));
|
||||
available = await connection.isAvailable();
|
||||
ProductDetailsResponse _resp = await connection.queryProductDetails(Set.from(IAP_IDS));
|
||||
products = _resp.productDetails;
|
||||
products.sort((a, b) => a.id.compareTo(b.id));
|
||||
isAvailable = await connection.isAvailable();
|
||||
if(isAvailable) {
|
||||
// Open listener
|
||||
_purchaseStream = connection.purchaseUpdatedStream.listen((data) => LunaInAppPurchases._purchasedCallback(data));
|
||||
// Load donation IAPs
|
||||
ProductDetailsResponse donation = await connection.queryProductDetails(Set.from(IAP_DONATION_IDS));
|
||||
donationIAPs = donation?.productDetails ?? [];
|
||||
donationIAPs.sort((a, b) => a?.id?.compareTo(b?.id) ?? 0);
|
||||
// Load upgrade IAPs
|
||||
ProductDetailsResponse upgrade = await connection.queryProductDetails(Set.from(IAP_UPGRADE_IDS));
|
||||
upgradeIAPs = upgrade?.productDetails ?? [];
|
||||
upgradeIAPs.sort((a, b) => a?.id?.compareTo(b?.id) ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> deinitialize() async {
|
||||
purchaseStream?.cancel();
|
||||
}
|
||||
/// Deinitialize the in-app purchases connection.
|
||||
///
|
||||
/// - Cleanly cancel/close the [InAppPurchaseConnection.instance.purchaseUpdateStream] listener
|
||||
static Future<void> deinitialize() async => _purchaseStream?.cancel();
|
||||
|
||||
/// Callback function for [purchaseUpdatedStream].
|
||||
///
|
||||
/// **Donations**:
|
||||
/// - Automatically completes the purchase, as there is no actual product to deliver
|
||||
///
|
||||
/// **Account Upgrade**:
|
||||
/// - Create a listener to the user's account in Firestore
|
||||
/// - Upgrade their account
|
||||
/// - Validate upgrade
|
||||
/// - Mark as completed, close listener
|
||||
static Future<void> _purchasedCallback(List<PurchaseDetails> purchases) async {
|
||||
for(var purchase in purchases) {
|
||||
if(purchase.pendingCompletePurchase) await connection.completePurchase(purchase);
|
||||
/// Handle donations by always completing the purchase
|
||||
if(IAP_DONATION_IDS.contains(purchase.productID)) {
|
||||
if(purchase.pendingCompletePurchase) await connection.completePurchase(purchase);
|
||||
}
|
||||
/// Handle account upgrades
|
||||
if(IAP_UPGRADE_IDS.contains(purchase.productID)) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,88 +1,79 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/constants.dart';
|
||||
import 'package:lunasea/core/database.dart';
|
||||
import 'package:sentry/sentry.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:f_logs/f_logs.dart' show FLog, DataLogType, FormatType, LogsConfig;
|
||||
export 'package:dio/dio.dart' show DioError;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
class LunaLogger {
|
||||
LunaLogger._();
|
||||
static final SentryClient _sentry = SentryClient(dsn: Constants.SENTRY_DSN);
|
||||
static const SENTRY_DSN = 'https://511f76efcf714ecfb5ed6b26b5819bd6@o426090.ingest.sentry.io/5367513';
|
||||
static const CHECK_LOGS_MESSAGE = 'Check the logs for more details';
|
||||
|
||||
/// Initialize the logger by setting the timestamp format and capturing errors on [FlutterError.onError].
|
||||
static void initialize() {
|
||||
LogsConfig config = FLog.getDefaultConfigurations()
|
||||
..formatType = FormatType.FORMAT_SQUARE
|
||||
..timestampFormat = 'MMMM dd, y - hh:mm:ss a';
|
||||
FLog.applyConfigurations(config);
|
||||
FlutterError.onError = (FlutterErrorDetails details, { bool forceReport = false }) async {
|
||||
bool inDebugMode = false;
|
||||
assert(inDebugMode = true);
|
||||
if (inDebugMode) {
|
||||
FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
|
||||
}
|
||||
if (kDebugMode) FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
|
||||
Zone.current.handleUncaughtError(details.exception, details.stack);
|
||||
};
|
||||
}
|
||||
|
||||
static void debug(String className, String methodName, String text, {DataLogType type = DataLogType.DEFAULT}) {
|
||||
FLog.debug(
|
||||
className: className,
|
||||
methodName: methodName,
|
||||
text: text,
|
||||
dataLogType: type.toString(),
|
||||
);
|
||||
}
|
||||
/// Log a new debug-level log.
|
||||
void debug(String text) => FLog.debug(
|
||||
text: text,
|
||||
dataLogType: DataLogType.DEFAULT.toString(),
|
||||
);
|
||||
|
||||
static void warning(String className, String methodName, String text, {DataLogType type = DataLogType.DEFAULT}) {
|
||||
FLog.warning(
|
||||
className: className,
|
||||
methodName: methodName,
|
||||
text: text,
|
||||
dataLogType: type.toString(),
|
||||
);
|
||||
}
|
||||
/// Log a new warning-level log.
|
||||
void warning(String className, String methodName, String text) => FLog.warning(
|
||||
className: className,
|
||||
methodName: methodName,
|
||||
text: text,
|
||||
dataLogType: DataLogType.DEFAULT.toString(),
|
||||
);
|
||||
|
||||
static void error(String className, String methodName, String text, dynamic error, StackTrace stack, {
|
||||
DataLogType type = DataLogType.DEFAULT,
|
||||
bool uploadToSentry = true,
|
||||
}) {
|
||||
/// Log a new error-level log.
|
||||
void error(String text, dynamic error, StackTrace stack) {
|
||||
Trace _trace = stack == null ? null : Trace.from(stack);
|
||||
FLog.error(
|
||||
className: className,
|
||||
methodName: methodName,
|
||||
className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown',
|
||||
methodName: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown',
|
||||
text: text,
|
||||
exception: error,
|
||||
stacktrace: stack,
|
||||
dataLogType: type.toString(),
|
||||
dataLogType: DataLogType.DEFAULT.toString(),
|
||||
);
|
||||
if(uploadToSentry && LunaSeaDatabaseValue.ENABLED_SENTRY.data) _sentry.captureException(
|
||||
exception: error,
|
||||
if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException(
|
||||
error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
|
||||
static void fatal(dynamic error, StackTrace stack, {
|
||||
DataLogType type = DataLogType.DEFAULT,
|
||||
bool uploadToSentry = true,
|
||||
}) {
|
||||
Trace _trace = Trace.from(stack);
|
||||
/// Log a new fatal-level log.
|
||||
void fatal(dynamic error, StackTrace stack) {
|
||||
Trace _trace = stack == null ? null : Trace.from(stack);
|
||||
FLog.fatal(
|
||||
className: _trace.frames.length >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown',
|
||||
methodName: _trace.frames.length >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown',
|
||||
className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown',
|
||||
methodName: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].member.toString() ?? 'Unknown' : 'Unknown',
|
||||
text: error.toString(),
|
||||
exception: error,
|
||||
stacktrace: stack,
|
||||
dataLogType: type.toString(),
|
||||
dataLogType: DataLogType.DEFAULT.toString(),
|
||||
);
|
||||
if(LunaSeaDatabaseValue.ENABLED_SENTRY.data) _sentry.captureException(
|
||||
exception: error,
|
||||
if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException(
|
||||
error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<File> exportLogs() async => FLog.exportLogs();
|
||||
|
||||
static Future<void> clearLogs() async => FLog.clearLogs();
|
||||
/// Export all logs, and return the [File] object containing the log file.
|
||||
Future<File> exportLogs() async => FLog.exportLogs();
|
||||
/// Clear all logs currently saved to the database.
|
||||
Future<void> clearLogs() async => FLog.clearLogs();
|
||||
}
|
||||
|
||||
@@ -3,20 +3,19 @@ import 'package:lunasea/core.dart';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Decoration LunaCardDecoration({ @required String uri, @required Map headers }) {
|
||||
if(LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null;
|
||||
double _opacity = (LunaSeaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int)/100;
|
||||
if(LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null;
|
||||
return BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(
|
||||
uri,
|
||||
headers: Map<String, String>.from(headers),
|
||||
),
|
||||
onError: (error, stack) => LunaLogger.warning(
|
||||
'LunaCardDecoration',
|
||||
'DecorationImage',
|
||||
'Failed to fetch background image: $uri',
|
||||
// To prevent excessive logs, just handle an error loading the image with an empty callback
|
||||
onError: (error, stack) {},
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withOpacity((LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int)/100),
|
||||
BlendMode.dstATop,
|
||||
),
|
||||
colorFilter: ColorFilter.mode(Colors.black.withOpacity(_opacity), BlendMode.dstATop),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
|
||||
@@ -52,7 +52,7 @@ Future<void> showLunaSnackBar({
|
||||
horizontalDismissDirection: HorizontalDismissDirection.horizontal,
|
||||
margin: EdgeInsets.all(_PADDING),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
borderColor: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? Colors.white12
|
||||
: Colors.transparent,
|
||||
child: FlashBar(
|
||||
|
||||
@@ -6,12 +6,12 @@ Future<void> showLunaErrorSnackBar({
|
||||
@required BuildContext context,
|
||||
@required String title,
|
||||
dynamic error,
|
||||
String message = Constants.CHECK_LOGS_MESSAGE,
|
||||
String message = LunaLogger.CHECK_LOGS_MESSAGE,
|
||||
}) async => showLunaSnackBar(
|
||||
context: context,
|
||||
title: title,
|
||||
message: message,
|
||||
type: LunaSnackbarType.ERROR,
|
||||
showButton: error != null,
|
||||
buttonOnPressed: () async => LunaDialogs.textPreview(context, 'Error', error.toString()),
|
||||
buttonOnPressed: () async => LunaDialogs().textPreview(context, 'Error', error.toString()),
|
||||
);
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LunaModuleMap {
|
||||
final String name;
|
||||
final String description;
|
||||
final String settingsDescription;
|
||||
final String route;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
|
||||
const LunaModuleMap({
|
||||
@required this.name,
|
||||
@required this.description,
|
||||
@required this.settingsDescription,
|
||||
@required this.route,
|
||||
@required this.icon,
|
||||
@required this.color,
|
||||
});
|
||||
}
|
||||
33
lib/core/module_metadata.dart
Normal file
33
lib/core/module_metadata.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// [LunaModuleMetadata] stores general information about a module, including:
|
||||
/// - Name
|
||||
/// - Descrption
|
||||
/// - Base/home route
|
||||
/// - Links
|
||||
/// - Icon
|
||||
/// - Brand colour
|
||||
/// - etc.
|
||||
class LunaModuleMetadata {
|
||||
final String name;
|
||||
final String description;
|
||||
final String settingsDescription;
|
||||
final String helpMessage;
|
||||
final String route;
|
||||
final String website;
|
||||
final String github;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
|
||||
const LunaModuleMetadata({
|
||||
@required this.name,
|
||||
@required this.description,
|
||||
@required this.settingsDescription,
|
||||
@required this.helpMessage,
|
||||
@required this.route,
|
||||
@required this.website,
|
||||
@required this.github,
|
||||
@required this.icon,
|
||||
@required this.color,
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
class LunaNetworking extends HttpOverrides {
|
||||
static void initialize() => HttpOverrides.global = LunaNetworking();
|
||||
/// Initialize the default HTTP client by setting [HttpOverrides]'s global HTTP client to [LunaNetworking].
|
||||
static void initialize() {
|
||||
HttpOverrides.global = LunaNetworking();
|
||||
}
|
||||
|
||||
/// Overrides the default [createHttpClient] function and ensures all HTTPS connections allow bad certificate connections.
|
||||
///
|
||||
/// Returns a new [HttpClient] using the given [context].
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext context) {
|
||||
final HttpClient client = super.createHttpClient(context);
|
||||
|
||||
@@ -2,17 +2,22 @@ import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaProfile {
|
||||
LunaProfile._();
|
||||
LunaProfile();
|
||||
|
||||
static Future<bool> changeProfile(BuildContext context, String profile) async {
|
||||
if(LunaSeaDatabaseValue.ENABLED_PROFILE.data != profile) {
|
||||
/// Safely change profiles.
|
||||
///
|
||||
/// Does this safely by:
|
||||
/// - Ensures that the passed in profile isn't already enabled
|
||||
/// - Ensures that the profile exists
|
||||
Future<bool> safelyChangeProfiles(BuildContext context, String profile) async {
|
||||
if(LunaDatabaseValue.ENABLED_PROFILE.data != profile) {
|
||||
if(Database.profilesBox.containsKey(profile)) {
|
||||
LunaSeaDatabaseValue.ENABLED_PROFILE.put(profile);
|
||||
LunaProvider.reset(context);
|
||||
LunaDatabaseValue.ENABLED_PROFILE.put(profile);
|
||||
LunaState.reset(context);
|
||||
LSSnackBar(context: context, title: 'Changed Profile', message: profile);
|
||||
return true;
|
||||
} else {
|
||||
LunaLogger.warning('LunaProfile', 'changeProfile', 'Attempted to change profile to unknown profile: $profile');
|
||||
LunaLogger().warning('LunaProfile', 'changeProfile', 'Attempted to change profile to unknown profile: $profile');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,48 +6,63 @@ import 'package:lunasea/main.dart';
|
||||
export 'package:quick_actions/quick_actions.dart' show ShortcutItem;
|
||||
|
||||
class LunaQuickActions {
|
||||
LunaQuickActions._();
|
||||
|
||||
static final QuickActions _quickActions = QuickActions();
|
||||
|
||||
LunaQuickActions._();
|
||||
|
||||
/// Initialize the quick actions by setting the action handler.
|
||||
///
|
||||
/// Required before the handler can actually receive any intents.
|
||||
static void initialize(BuildContext context) {
|
||||
_quickActions.initialize((action) => _handler(context, action));
|
||||
setShortcutItems();
|
||||
}
|
||||
|
||||
/// Sets the shortcut items by checking the database and enabling the respective action if enabled.
|
||||
static void setShortcutItems() {
|
||||
_quickActions.setShortcutItems(<ShortcutItem>[
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_SEARCH.data) SearchConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LidarrConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_RADARR.data) RadarrConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_SONARR.data) SonarrConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_NZBGET.data) NZBGetConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) SABnzbdConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaSeaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) TautulliConstants.MODULE_QUICK_ACTION,
|
||||
// General
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_SEARCH.data) SearchConstants.MODULE_QUICK_ACTION,
|
||||
// Automation
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LidarrConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_RADARR.data) RadarrConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_SONARR.data) SonarrConstants.MODULE_QUICK_ACTION,
|
||||
// Clients
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_NZBGET.data) NZBGetConstants.MODULE_QUICK_ACTION,
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) SABnzbdConstants.MODULE_QUICK_ACTION,
|
||||
// Monitoring
|
||||
if(LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) TautulliConstants.MODULE_QUICK_ACTION,
|
||||
]);
|
||||
}
|
||||
|
||||
/// The actual shortcut command handler
|
||||
static void _handler(BuildContext context, String action) {
|
||||
if(action != null) {
|
||||
switch(action) {
|
||||
// General
|
||||
case SearchConstants.MODULE_KEY: _pushSearch(); break;
|
||||
// Automation
|
||||
case LidarrConstants.MODULE_KEY: _pushLidarr(); break;
|
||||
case RadarrConstants.MODULE_KEY: _pushRadarr(); break;
|
||||
case SonarrConstants.MODULE_KEY: _pushSonarr(); break;
|
||||
// Clients
|
||||
case NZBGetConstants.MODULE_KEY: _pushNZBGet(); break;
|
||||
case SABnzbdConstants.MODULE_KEY: _pushSABnzbd(); break;
|
||||
// Monitoring
|
||||
case TautulliConstants.MODULE_KEY: _pushTautulli(); break;
|
||||
case OmbiConstants.MODULE_KEY: _pushOmbi(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// General
|
||||
static void _pushSearch() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Search.ROUTE_NAME, (Route<dynamic> route) => false);
|
||||
// Automation
|
||||
static void _pushLidarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Lidarr.ROUTE_NAME, (Route<dynamic> route) => false);
|
||||
static void _pushRadarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(Radarr.ROUTE_NAME, (Route<dynamic> route) => false);
|
||||
static void _pushSonarr() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(SonarrHomeRouter.route(), (Route<dynamic> route) => false);
|
||||
// Clients
|
||||
static void _pushNZBGet() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(NZBGet.ROUTE_NAME, (Route<dynamic> route) => false);
|
||||
static void _pushSABnzbd() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(SABnzbd.ROUTE_NAME, (Route<dynamic> route) => false);
|
||||
static void _pushOmbi() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(OmbiHomeRouter.route(), (Route<dynamic> route) => false);
|
||||
// Monitoring
|
||||
static void _pushTautulli() => LunaBIOS.navigatorKey.currentState.pushNamedAndRemoveUntil(TautulliHomeRouter.route(), (Route<dynamic> route) => false);
|
||||
}
|
||||
|
||||
@@ -1,110 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluro/fluro.dart';
|
||||
import 'package:lunasea/modules/home/routes.dart';
|
||||
import 'package:lunasea/modules/search/routes.dart';
|
||||
import 'package:lunasea/modules/lidarr/routes.dart';
|
||||
import 'package:lunasea/modules/radarr/routes.dart';
|
||||
import 'package:lunasea/modules/nzbget/routes.dart';
|
||||
import 'package:lunasea/modules/sabnzbd/routes.dart';
|
||||
import 'package:lunasea/modules/settings.dart' show SettingsRouter;
|
||||
import 'package:lunasea/modules/sonarr.dart' show SonarrRouter;
|
||||
import 'package:lunasea/modules/ombi.dart' show OmbiRouter;
|
||||
import 'package:lunasea/modules/tautulli.dart' show TautulliRouter;
|
||||
|
||||
class LunaRouter {
|
||||
static FluroRouter router = FluroRouter();
|
||||
|
||||
LunaRouter._();
|
||||
|
||||
static void intialize() {
|
||||
SettingsRouter.initialize(router);
|
||||
SonarrRouter.initialize(router);
|
||||
OmbiRouter.initialize(router);
|
||||
TautulliRouter.initialize(router);
|
||||
}
|
||||
|
||||
static TransitionType get transitionType => TransitionType.native;
|
||||
|
||||
static Map<String, WidgetBuilder> get routes => <String, WidgetBuilder> {
|
||||
..._home,
|
||||
..._search,
|
||||
..._lidarr,
|
||||
..._radarr,
|
||||
..._sabnzbd,
|
||||
..._nzbget,
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _home => <String, WidgetBuilder> {
|
||||
// /
|
||||
Home.ROUTE_NAME: (context) => Home(),
|
||||
// /home/*
|
||||
HomeCalendar.ROUTE_NAME: (context) => HomeCalendar(refreshIndicatorKey: null),
|
||||
HomeQuickAccess.ROUTE_NAME: (context) => HomeQuickAccess(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _search => <String, WidgetBuilder> {
|
||||
// /search
|
||||
Search.ROUTE_NAME: (context) => Search(),
|
||||
// /search/*
|
||||
SearchSearch.ROUTE_NAME: (context) => SearchSearch(),
|
||||
SearchCategories.ROUTE_NAME: (context) => SearchCategories(),
|
||||
SearchSubcategories.ROUTE_NAME: (context) => SearchSubcategories(),
|
||||
SearchResults.ROUTE_NAME: (context) => SearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _lidarr => <String, WidgetBuilder> {
|
||||
// /lidarr
|
||||
Lidarr.ROUTE_NAME: (context) => Lidarr(),
|
||||
// /lidarr/*
|
||||
LidarrCatalogue.ROUTE_NAME: (context) => LidarrCatalogue(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
LidarrMissing.ROUTE_NAME: (context) => LidarrMissing(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
LidarrHistory.ROUTE_NAME: (context) => LidarrHistory(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
// /lidarr/add/*
|
||||
LidarrAddSearch.ROUTE_NAME: (context) => LidarrAddSearch(),
|
||||
LidarrAddDetails.ROUTE_NAME: (context) => LidarrAddDetails(),
|
||||
// /lidarr/edit/*
|
||||
LidarrEditArtist.ROUTE_NAME: (context) => LidarrEditArtist(),
|
||||
// /lidarr/details/*
|
||||
LidarrDetailsAlbum.ROUTE_NAME: (context) => LidarrDetailsAlbum(),
|
||||
LidarrDetailsArtist.ROUTE_NAME: (context) => LidarrDetailsArtist(),
|
||||
// /lidarr/search/*
|
||||
LidarrSearchResults.ROUTE_NAME: (context) => LidarrSearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _radarr => <String, WidgetBuilder> {
|
||||
// /radarr
|
||||
Radarr.ROUTE_NAME: (context) => Radarr(),
|
||||
// /radarr/*
|
||||
RadarrCatalogue.ROUTE_NAME: (context) => RadarrCatalogue(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
RadarrMissing.ROUTE_NAME: (context) => RadarrMissing(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
RadarrUpcoming.ROUTE_NAME: (context) => RadarrUpcoming(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
RadarrHistory.ROUTE_NAME: (context) => RadarrHistory(refreshIndicatorKey: null, refreshAllPages: null),
|
||||
// /radarr/add/*
|
||||
RadarrAddSearch.ROUTE_NAME: (context) => RadarrAddSearch(),
|
||||
RadarrAddDetails.ROUTE_NAME: (context) => RadarrAddDetails(),
|
||||
// /radarr/details/*
|
||||
RadarrDetailsMovie.ROUTE_NAME: (context) => RadarrDetailsMovie(),
|
||||
RadarrEditMovie.ROUTE_NAME: (context) => RadarrEditMovie(),
|
||||
RadarrSearchResults.ROUTE_NAME: (context) => RadarrSearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _nzbget => <String, WidgetBuilder> {
|
||||
// /nzbget
|
||||
NZBGet.ROUTE_NAME: (context) => NZBGet(),
|
||||
// /nzbget/*
|
||||
NZBGetHistory.ROUTE_NAME: (context) => NZBGetHistory(refreshIndicatorKey: null),
|
||||
NZBGetQueue.ROUTE_NAME: (context) => NZBGetQueue(refreshIndicatorKey: null),
|
||||
NZBGetStatistics.ROUTE_NAME: (context) => NZBGetStatistics(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _sabnzbd => <String, WidgetBuilder> {
|
||||
// /sabnzbd
|
||||
SABnzbd.ROUTE_NAME: (context) => SABnzbd(),
|
||||
// /sabnzbd/*
|
||||
SABnzbdHistory.ROUTE_NAME: (context) => SABnzbdHistory(refreshIndicatorKey: null),
|
||||
SABnzbdQueue.ROUTE_NAME: (context) => SABnzbdQueue(refreshIndicatorKey: null),
|
||||
SABnzbdStatistics.ROUTE_NAME: (context) => SABnzbdStatistics(),
|
||||
// /sabnzbd/history/*
|
||||
SABnzbdHistoryStages.ROUTE_NAME: (context) => SABnzbdHistoryStages(),
|
||||
};
|
||||
}
|
||||
export 'router/module_router.dart';
|
||||
export 'router/page_router.dart';
|
||||
export 'router/router.dart';
|
||||
|
||||
6
lib/core/router/module_router.dart
Normal file
6
lib/core/router/module_router.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:fluro/fluro.dart';
|
||||
|
||||
abstract class LunaModuleRouter {
|
||||
/// Given the [FluroRouter], define all routes and handlers required for the module.
|
||||
void defineAllRoutes(FluroRouter router);
|
||||
}
|
||||
33
lib/core/router/page_router.dart
Normal file
33
lib/core/router/page_router.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:fluro/fluro.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
abstract class LunaPageRouter {
|
||||
/// The full string representation of the route without the named parameters being swapped out
|
||||
final String fullRoute;
|
||||
|
||||
/// Use [FluroRouter] to navigate to the page
|
||||
Future<void> navigateTo(BuildContext context) => LunaRouter.router.navigateTo(context, fullRoute);
|
||||
|
||||
/// Get a string representation of the route with any named parameters inserted
|
||||
String route() => fullRoute;
|
||||
|
||||
/// Used to define the route in the passed in router.
|
||||
///
|
||||
/// If the route requires no parameters, call [noParameterRouteDefinition] with the route as the [Widget] parameter.
|
||||
void defineRoute(FluroRouter router);
|
||||
|
||||
/// Define the route in the passed in router without any parameters required.
|
||||
///
|
||||
/// Already has a definition that:
|
||||
/// - Calls [FluroRouter.define] with the [fullRoute]
|
||||
/// - Creates a basic [Handler]
|
||||
/// - Sets the transition type to [LunaRouter.transitionType]
|
||||
void noParameterRouteDefinition(FluroRouter router, StatefulWidget widget) => router.define(
|
||||
fullRoute,
|
||||
handler: Handler(handlerFunc: (context, params) => widget),
|
||||
transitionType: LunaRouter.transitionType,
|
||||
);
|
||||
|
||||
LunaPageRouter(this.fullRoute);
|
||||
}
|
||||
82
lib/core/router/router.dart
Normal file
82
lib/core/router/router.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluro/fluro.dart';
|
||||
import 'package:lunasea/modules/home/routes.dart';
|
||||
import 'package:lunasea/modules/search/routes.dart';
|
||||
import 'package:lunasea/modules/lidarr/routes.dart';
|
||||
import 'package:lunasea/modules/radarr/routes.dart';
|
||||
import 'package:lunasea/modules/nzbget/routes.dart';
|
||||
import 'package:lunasea/modules/sabnzbd/routes.dart';
|
||||
import 'package:lunasea/modules.dart' show OmbiRouter, SettingsRouter, SonarrRouter, TautulliRouter;
|
||||
|
||||
class LunaRouter {
|
||||
static FluroRouter router = FluroRouter();
|
||||
static TransitionType get transitionType => TransitionType.native;
|
||||
|
||||
/// Calls `defineAllRoutes()` on all module routers that implement [LunaModuleRouter].
|
||||
static void intialize() {
|
||||
SettingsRouter().defineAllRoutes(router);
|
||||
SonarrRouter().defineAllRoutes(router);
|
||||
OmbiRouter().defineAllRoutes(router);
|
||||
TautulliRouter().defineAllRoutes(router);
|
||||
}
|
||||
|
||||
/// **Will be removed when all module routers are integrated.**
|
||||
///
|
||||
/// Returns a map of all module routes.
|
||||
static Map<String, WidgetBuilder> get routes => <String, WidgetBuilder> {
|
||||
Home.ROUTE_NAME: (context) => Home(),
|
||||
..._search,
|
||||
..._lidarr,
|
||||
..._radarr,
|
||||
..._sabnzbd,
|
||||
..._nzbget,
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _search => <String, WidgetBuilder> {
|
||||
// /search
|
||||
Search.ROUTE_NAME: (context) => Search(),
|
||||
// /search/*
|
||||
SearchSearch.ROUTE_NAME: (context) => SearchSearch(),
|
||||
SearchCategories.ROUTE_NAME: (context) => SearchCategories(),
|
||||
SearchSubcategories.ROUTE_NAME: (context) => SearchSubcategories(),
|
||||
SearchResults.ROUTE_NAME: (context) => SearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _lidarr => <String, WidgetBuilder> {
|
||||
// /lidarr
|
||||
Lidarr.ROUTE_NAME: (context) => Lidarr(),
|
||||
// /lidarr/add/*
|
||||
LidarrAddSearch.ROUTE_NAME: (context) => LidarrAddSearch(),
|
||||
LidarrAddDetails.ROUTE_NAME: (context) => LidarrAddDetails(),
|
||||
// /lidarr/edit/*
|
||||
LidarrEditArtist.ROUTE_NAME: (context) => LidarrEditArtist(),
|
||||
// /lidarr/details/*
|
||||
LidarrDetailsAlbum.ROUTE_NAME: (context) => LidarrDetailsAlbum(),
|
||||
LidarrDetailsArtist.ROUTE_NAME: (context) => LidarrDetailsArtist(),
|
||||
// /lidarr/search/*
|
||||
LidarrSearchResults.ROUTE_NAME: (context) => LidarrSearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _radarr => <String, WidgetBuilder> {
|
||||
// /radarr
|
||||
Radarr.ROUTE_NAME: (context) => Radarr(),
|
||||
// /radarr/add/*
|
||||
RadarrAddSearch.ROUTE_NAME: (context) => RadarrAddSearch(),
|
||||
RadarrAddDetails.ROUTE_NAME: (context) => RadarrAddDetails(),
|
||||
// /radarr/details/*
|
||||
RadarrDetailsMovie.ROUTE_NAME: (context) => RadarrDetailsMovie(),
|
||||
RadarrEditMovie.ROUTE_NAME: (context) => RadarrEditMovie(),
|
||||
RadarrSearchResults.ROUTE_NAME: (context) => RadarrSearchResults(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _nzbget => <String, WidgetBuilder> {
|
||||
NZBGet.ROUTE_NAME: (context) => NZBGet(),
|
||||
NZBGetStatistics.ROUTE_NAME: (context) => NZBGetStatistics(),
|
||||
};
|
||||
|
||||
static Map<String, WidgetBuilder> get _sabnzbd => <String, WidgetBuilder> {
|
||||
SABnzbd.ROUTE_NAME: (context) => SABnzbd(),
|
||||
SABnzbdStatistics.ROUTE_NAME: (context) => SABnzbdStatistics(),
|
||||
SABnzbdHistoryStages.ROUTE_NAME: (context) => SABnzbdHistoryStages(),
|
||||
};
|
||||
}
|
||||
2
lib/core/state.dart
Normal file
2
lib/core/state.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'state/module_state.dart';
|
||||
export 'state/state.dart';
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class LunaGlobalState extends ChangeNotifier {
|
||||
abstract class LunaModuleState extends ChangeNotifier {
|
||||
/// Reset the state back to the default
|
||||
void reset();
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
//Exports
|
||||
export 'package:provider/provider.dart';
|
||||
//Imports
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules.dart' show
|
||||
HomeState,
|
||||
SearchState,
|
||||
SettingsState,
|
||||
LidarrState,
|
||||
RadarrState,
|
||||
SonarrState,
|
||||
NZBGetState,
|
||||
SABnzbdState,
|
||||
OmbiState,
|
||||
TautulliState;
|
||||
import 'package:lunasea/modules/home/core.dart' show HomeState;
|
||||
import 'package:lunasea/modules/search/core.dart' show SearchState;
|
||||
import 'package:lunasea/modules/settings/core.dart' show SettingsState;
|
||||
import 'package:lunasea/modules/lidarr/core.dart' show LidarrState;
|
||||
import 'package:lunasea/modules/radarr/core.dart' show RadarrState;
|
||||
import 'package:lunasea/modules/sonarr/core.dart' show SonarrState;
|
||||
import 'package:lunasea/modules/nzbget/core.dart' show NZBGetState;
|
||||
import 'package:lunasea/modules/sabnzbd/core.dart' show SABnzbdState;
|
||||
import 'package:lunasea/modules/ombi/core.dart' show OmbiState;
|
||||
import 'package:lunasea/modules/tautulli/core.dart' show TautulliState;
|
||||
export 'package:provider/provider.dart';
|
||||
|
||||
class LunaProvider {
|
||||
LunaProvider._();
|
||||
class LunaState {
|
||||
LunaState._();
|
||||
|
||||
/// Calls `.reset()` on all states which extend [LunaModuleState].
|
||||
static void reset(BuildContext context) {
|
||||
// General
|
||||
Provider.of<HomeState>(context, listen: false)?.reset();
|
||||
@@ -36,6 +33,9 @@ class LunaProvider {
|
||||
Provider.of<TautulliState>(context, listen: false)?.reset();
|
||||
}
|
||||
|
||||
/// Returns a [MultiProvider] with the provided child.
|
||||
///
|
||||
/// The [MultiProvider] has a [ChangeNotifierProvider] provider added for each module global state object.
|
||||
static MultiProvider providers({ @required Widget child }) => MultiProvider(
|
||||
providers: [
|
||||
// General
|
||||
@@ -3,16 +3,14 @@ import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaTheme {
|
||||
LunaTheme._();
|
||||
|
||||
static ThemeData get darkTheme => LunaSeaDatabaseValue.THEME_AMOLED.data
|
||||
? _pureBlackTheme()
|
||||
: _midnightTheme();
|
||||
/// Returns the active [ThemeData] by checking the theme database value.
|
||||
static ThemeData activeTheme() {
|
||||
return LunaDatabaseValue.THEME_AMOLED.data ? _pureBlackTheme() : _midnightTheme();
|
||||
}
|
||||
|
||||
/// Midnight theme (Default)
|
||||
static ThemeData _midnightTheme() {
|
||||
const _textStyle = TextStyle(
|
||||
color: Colors.white,
|
||||
);
|
||||
const _textStyle = TextStyle(color: Colors.white);
|
||||
return ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
canvasColor: LunaColours.primary,
|
||||
@@ -22,13 +20,7 @@ class LunaTheme {
|
||||
cardColor: LunaColours.secondary,
|
||||
splashColor: LunaColours.splash,
|
||||
dialogBackgroundColor: LunaColours.secondary,
|
||||
dividerColor: LunaColours.accent.withAlpha(0),
|
||||
toggleableActiveColor: LunaColours.accent,
|
||||
dividerTheme: DividerThemeData(
|
||||
color: LunaColours.accent,
|
||||
indent: 100.0,
|
||||
endIndent: 100.0,
|
||||
),
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white,
|
||||
),
|
||||
@@ -52,10 +44,9 @@ class LunaTheme {
|
||||
);
|
||||
}
|
||||
|
||||
/// AMOLED/Pure black theme
|
||||
static ThemeData _pureBlackTheme() {
|
||||
const _textStyle = TextStyle(
|
||||
color: Colors.white,
|
||||
);
|
||||
const _textStyle = TextStyle(color: Colors.white);
|
||||
return ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
canvasColor: Colors.black,
|
||||
@@ -65,13 +56,7 @@ class LunaTheme {
|
||||
cardColor: Colors.black,
|
||||
splashColor: LunaColours.splash,
|
||||
dialogBackgroundColor: Colors.black,
|
||||
dividerColor: LunaColours.accent.withAlpha(0),
|
||||
toggleableActiveColor: LunaColours.accent,
|
||||
dividerTheme: DividerThemeData(
|
||||
color: LunaColours.accent,
|
||||
indent: 72.0,
|
||||
endIndent: 72.0,
|
||||
),
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export 'types/api.dart';
|
||||
export 'types/loading.dart';
|
||||
export 'types/browser.dart';
|
||||
export 'types/loading_state.dart';
|
||||
export 'types/sorter.dart';
|
||||
export 'types/state_global.dart';
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
abstract class API {
|
||||
void logWarning(String methodName, String text);
|
||||
void logError(String methodName, String text, Object error, StackTrace trace, { bool uploadToSentry = true });
|
||||
Future<dynamic> testConnection();
|
||||
}
|
||||
80
lib/core/types/browser.dart
Normal file
80
lib/core/types/browser.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
part 'browser.g.dart';
|
||||
|
||||
@HiveType(typeId: 11, adapterName: 'LunaBrowserAdapter')
|
||||
enum LunaBrowser {
|
||||
@HiveField(0)
|
||||
APPLE_SAFARI,
|
||||
@HiveField(1)
|
||||
BRAVE_BROWSER,
|
||||
@HiveField(2)
|
||||
GOOGLE_CHROME,
|
||||
@HiveField(3)
|
||||
MICROSOFT_EDGE,
|
||||
@HiveField(4)
|
||||
MOZILLA_FIREFOX,
|
||||
}
|
||||
|
||||
extension LunaBrowserExtension on LunaBrowser {
|
||||
String get name {
|
||||
switch(this) {
|
||||
case LunaBrowser.APPLE_SAFARI: return 'Apple Safari';
|
||||
case LunaBrowser.BRAVE_BROWSER: return 'Brave Browser';
|
||||
case LunaBrowser.GOOGLE_CHROME: return 'Google Chrome';
|
||||
case LunaBrowser.MICROSOFT_EDGE: return 'Microsoft Edge';
|
||||
case LunaBrowser.MOZILLA_FIREFOX: return 'Mozilla Firefox';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get key {
|
||||
switch(this) {
|
||||
case LunaBrowser.APPLE_SAFARI: return 'applesafari';
|
||||
case LunaBrowser.BRAVE_BROWSER: return 'bravebrowser';
|
||||
case LunaBrowser.GOOGLE_CHROME: return 'googlechrome';
|
||||
case LunaBrowser.MICROSOFT_EDGE: return 'microsoftedge';
|
||||
case LunaBrowser.MOZILLA_FIREFOX: return 'mozillafirefox';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
LunaBrowser fromKey(String key) {
|
||||
switch(key) {
|
||||
case 'applesafari': return LunaBrowser.APPLE_SAFARI;
|
||||
case 'bravebrowser': return LunaBrowser.BRAVE_BROWSER;
|
||||
case 'googlechrome': return LunaBrowser.GOOGLE_CHROME;
|
||||
case 'microsoftedge': return LunaBrowser.MICROSOFT_EDGE;
|
||||
case 'mozillafirefox': return LunaBrowser.MOZILLA_FIREFOX;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch(this) {
|
||||
case LunaBrowser.APPLE_SAFARI: return CustomIcons.safari;
|
||||
case LunaBrowser.BRAVE_BROWSER: return CustomIcons.bravebrowser;
|
||||
case LunaBrowser.GOOGLE_CHROME: return CustomIcons.chrome;
|
||||
case LunaBrowser.MICROSOFT_EDGE: return CustomIcons.microsoftedge;
|
||||
case LunaBrowser.MOZILLA_FIREFOX: return CustomIcons.firefox;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String formatted(String url) {
|
||||
bool isHttps = url.substring(0, 8) == 'https://';
|
||||
switch(this) {
|
||||
case LunaBrowser.BRAVE_BROWSER: return 'brave://open-url?url=$url';
|
||||
case LunaBrowser.GOOGLE_CHROME: return isHttps
|
||||
? url.replaceFirst('https://', 'googlechromes://')
|
||||
: url.replaceFirst('http://', 'googlechrome://');
|
||||
case LunaBrowser.MICROSOFT_EDGE: return isHttps
|
||||
? url.replaceFirst('https://', 'microsoft-edge-https://')
|
||||
: url.replaceFirst('http://', 'microsoft-edge-http://');
|
||||
case LunaBrowser.MOZILLA_FIREFOX: return 'firefox://open-url?url=$url';
|
||||
case LunaBrowser.APPLE_SAFARI:
|
||||
default: return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,49 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'links.dart';
|
||||
part of 'browser.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class LSBrowsersAdapter extends TypeAdapter<LSBrowsers> {
|
||||
class LunaBrowserAdapter extends TypeAdapter<LunaBrowser> {
|
||||
@override
|
||||
final int typeId = 11;
|
||||
|
||||
@override
|
||||
LSBrowsers read(BinaryReader reader) {
|
||||
LunaBrowser read(BinaryReader reader) {
|
||||
switch (reader.readByte()) {
|
||||
case 0:
|
||||
return LSBrowsers.APPLE_SAFARI;
|
||||
return LunaBrowser.APPLE_SAFARI;
|
||||
case 1:
|
||||
return LSBrowsers.BRAVE_BROWSER;
|
||||
return LunaBrowser.BRAVE_BROWSER;
|
||||
case 2:
|
||||
return LSBrowsers.GOOGLE_CHROME;
|
||||
return LunaBrowser.GOOGLE_CHROME;
|
||||
case 3:
|
||||
return LSBrowsers.MICROSOFT_EDGE;
|
||||
return LunaBrowser.MICROSOFT_EDGE;
|
||||
case 4:
|
||||
return LSBrowsers.MOZILLA_FIREFOX;
|
||||
return LunaBrowser.MOZILLA_FIREFOX;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, LSBrowsers obj) {
|
||||
void write(BinaryWriter writer, LunaBrowser obj) {
|
||||
switch (obj) {
|
||||
case LSBrowsers.APPLE_SAFARI:
|
||||
case LunaBrowser.APPLE_SAFARI:
|
||||
writer.writeByte(0);
|
||||
break;
|
||||
case LSBrowsers.BRAVE_BROWSER:
|
||||
case LunaBrowser.BRAVE_BROWSER:
|
||||
writer.writeByte(1);
|
||||
break;
|
||||
case LSBrowsers.GOOGLE_CHROME:
|
||||
case LunaBrowser.GOOGLE_CHROME:
|
||||
writer.writeByte(2);
|
||||
break;
|
||||
case LSBrowsers.MICROSOFT_EDGE:
|
||||
case LunaBrowser.MICROSOFT_EDGE:
|
||||
writer.writeByte(3);
|
||||
break;
|
||||
case LSBrowsers.MOZILLA_FIREFOX:
|
||||
case LunaBrowser.MOZILLA_FIREFOX:
|
||||
writer.writeByte(4);
|
||||
break;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class LSBrowsersAdapter extends TypeAdapter<LSBrowsers> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is LSBrowsersAdapter &&
|
||||
other is LunaBrowserAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -17,7 +17,6 @@ export 'ui/messages.dart';
|
||||
export 'ui/navigation_bar.dart';
|
||||
export 'ui/network_image.dart';
|
||||
export 'ui/refresh_indicator.dart';
|
||||
export 'ui/rive.dart';
|
||||
export 'ui/shape.dart';
|
||||
export 'ui/snackbar.dart';
|
||||
export 'ui/sticky_header.dart';
|
||||
|
||||
@@ -11,7 +11,7 @@ Widget LSAppBarDropdown({
|
||||
? LunaAppBar(context: context, title: title, actions: actions, hideLeading: true)
|
||||
: AppBar(
|
||||
title: PopupMenuButton<String>(
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
child: Wrap(
|
||||
@@ -28,7 +28,7 @@ Widget LSAppBarDropdown({
|
||||
),
|
||||
],
|
||||
),
|
||||
onSelected: (result) => LunaProfile.changeProfile(context, result),
|
||||
onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result),
|
||||
itemBuilder: (context) {
|
||||
return <PopupMenuEntry<String>>[for(String profile in profiles) PopupMenuItem<String>(
|
||||
value: profile,
|
||||
|
||||
@@ -19,8 +19,8 @@ class LSCard extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [
|
||||
LunaSeaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
LunaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
]),
|
||||
builder: (context, box, widget) => Card(
|
||||
child: Container(
|
||||
@@ -34,7 +34,7 @@ class LSCard extends StatelessWidget {
|
||||
color: color == null
|
||||
? Theme.of(context).primaryColor
|
||||
: color,
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/core/constants.dart';
|
||||
|
||||
class LSCardTile extends StatelessWidget {
|
||||
final Widget title;
|
||||
@@ -34,8 +33,8 @@ class LSCardTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [
|
||||
LunaSeaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
LunaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
]),
|
||||
builder: (context, box, widget) => Card(
|
||||
child: Container(
|
||||
@@ -63,7 +62,7 @@ class LSCardTile extends StatelessWidget {
|
||||
: Constants.UI_CARD_MARGIN
|
||||
: customMargin,
|
||||
elevation: Constants.UI_ELEVATION,
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
color: color == null
|
||||
|
||||
@@ -62,7 +62,7 @@ class _State extends State<LSDescriptionBlock> {
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: () => LunaDialogs.textPreview(context, widget.title, widget.description.trim() ?? 'No summary is available.'),
|
||||
onTap: () => LunaDialogs().textPreview(context, widget.title, widget.description.trim() ?? 'No summary is available.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ abstract class LSDialog {
|
||||
title: LSDialog.title(text: title),
|
||||
content: LSDialog.content(children: content),
|
||||
contentPadding: contentPadding,
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
),
|
||||
|
||||
@@ -6,5 +6,7 @@ class LSDivider extends StatelessWidget {
|
||||
Widget build(BuildContext context) => Divider(
|
||||
thickness: 1.0,
|
||||
color: LunaColours.splash,
|
||||
indent: 60.0,
|
||||
endIndent: 60.0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ import 'package:lunasea/core.dart';
|
||||
// ignore: non_constant_identifier_names
|
||||
Widget LSDrawer({
|
||||
@required String page,
|
||||
}) => LunaSeaDatabaseValue.DRAWER_GROUP_MODULES.data
|
||||
}) => LunaDatabaseValue.DRAWER_GROUP_MODULES.data
|
||||
? LSDrawerCategories(page: page)
|
||||
: LSDrawerNoCategories(page: page);
|
||||
|
||||
@@ -12,12 +12,12 @@ class LSDrawerCategories extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
builder: (context, lunaBox, widget) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Database.indexersBox.listenable(),
|
||||
builder: (context, indexerBox, widget) {
|
||||
ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaSeaDatabaseValue.ENABLED_PROFILE.key));
|
||||
ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaDatabaseValue.ENABLED_PROFILE.key));
|
||||
return Drawer(
|
||||
child: ListView(
|
||||
children: _getDrawerEntries(context, profile, (indexerBox as Box).length > 0),
|
||||
@@ -61,14 +61,14 @@ class LSDrawerCategories extends StatelessWidget {
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
),
|
||||
initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data,
|
||||
initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_AUTOMATION.data,
|
||||
children: List.generate(
|
||||
Database.currentProfileObject.enabledAutomationModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].name,
|
||||
padLeft: true,
|
||||
),
|
||||
),
|
||||
@@ -81,14 +81,14 @@ class LSDrawerCategories extends StatelessWidget {
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
),
|
||||
initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_CLIENTS.data,
|
||||
initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_CLIENTS.data,
|
||||
children: List.generate(
|
||||
Database.currentProfileObject.enabledClientModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].name,
|
||||
padLeft: true,
|
||||
),
|
||||
),
|
||||
@@ -101,14 +101,14 @@ class LSDrawerCategories extends StatelessWidget {
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
),
|
||||
initiallyExpanded: LunaSeaDatabaseValue.DRAWER_EXPAND_MONITORING.data,
|
||||
initiallyExpanded: LunaDatabaseValue.DRAWER_EXPAND_MONITORING.data,
|
||||
children: List.generate(
|
||||
Database.currentProfileObject.enabledMonitoringModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].name,
|
||||
padLeft: true,
|
||||
),
|
||||
),
|
||||
@@ -152,9 +152,9 @@ class LSDrawerCategories extends StatelessWidget {
|
||||
@required BuildContext context,
|
||||
}) {
|
||||
return ListTile(
|
||||
leading: LSIcon(icon: Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].icon),
|
||||
leading: LSIcon(icon: Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].icon),
|
||||
title: Text(
|
||||
Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].name,
|
||||
Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].name,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
|
||||
@@ -5,19 +5,19 @@ import 'package:lunasea/core.dart';
|
||||
Widget LSDrawerHeader() => UserAccountsDrawerHeader(
|
||||
accountName: LSTitle(text: Constants.APPLICATION_NAME),
|
||||
accountEmail: ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
builder: (context, lunaBox, widget) => ValueListenableBuilder(
|
||||
valueListenable: Database.profilesBox.listenable(),
|
||||
builder: (context, profilesBox, widget) => Padding(
|
||||
child: PopupMenuButton<String>(
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
LSSubtitle(
|
||||
text: LunaSeaDatabaseValue.ENABLED_PROFILE.data,
|
||||
text: LunaDatabaseValue.ENABLED_PROFILE.data,
|
||||
),
|
||||
LSIcon(
|
||||
icon: Icons.arrow_drop_down,
|
||||
@@ -26,7 +26,7 @@ Widget LSDrawerHeader() => UserAccountsDrawerHeader(
|
||||
),
|
||||
],
|
||||
),
|
||||
onSelected: (result) => LunaProfile.changeProfile(context, result),
|
||||
onSelected: (result) => LunaProfile().safelyChangeProfiles(context, result),
|
||||
itemBuilder: (context) {
|
||||
return <PopupMenuEntry<String>>[for(String profile in (profilesBox as Box).keys) PopupMenuItem<String>(
|
||||
value: profile,
|
||||
|
||||
@@ -12,12 +12,12 @@ class LSDrawerNoCategories extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]),
|
||||
builder: (context, lunaBox, widget) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Database.indexersBox.listenable(),
|
||||
builder: (context, indexerBox, widget) {
|
||||
ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaSeaDatabaseValue.ENABLED_PROFILE.key));
|
||||
ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaDatabaseValue.ENABLED_PROFILE.key));
|
||||
return Drawer(
|
||||
child: ListView(
|
||||
children: _getDrawerEntries(context, profile, (indexerBox as Box).length > 0),
|
||||
@@ -57,27 +57,27 @@ class LSDrawerNoCategories extends StatelessWidget {
|
||||
Database.currentProfileObject.enabledAutomationModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledAutomationModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledAutomationModules[index]].name,
|
||||
),
|
||||
),
|
||||
...List.generate(
|
||||
Database.currentProfileObject.enabledClientModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledClientModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledClientModules[index]].name,
|
||||
),
|
||||
),
|
||||
...List.generate(
|
||||
Database.currentProfileObject.enabledMonitoringModules.length,
|
||||
(index) => _buildEntry(
|
||||
context: context,
|
||||
route: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].route,
|
||||
icon: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].icon,
|
||||
title: Constants.MODULE_MAP[Database.currentProfileObject.enabledMonitoringModules[index]].name,
|
||||
route: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].route,
|
||||
icon: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].icon,
|
||||
title: Constants.MODULE_METADATA[Database.currentProfileObject.enabledMonitoringModules[index]].name,
|
||||
),
|
||||
),
|
||||
];
|
||||
@@ -116,9 +116,9 @@ class LSDrawerNoCategories extends StatelessWidget {
|
||||
@required BuildContext context,
|
||||
}) {
|
||||
return ListTile(
|
||||
leading: LSIcon(icon: Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].icon),
|
||||
leading: LSIcon(icon: Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].icon),
|
||||
title: Text(
|
||||
Constants.MODULE_MAP[WakeOnLANConstants.MODULE_KEY].name,
|
||||
Constants.MODULE_METADATA[WakeOnLANConstants.MODULE_KEY].name,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/constants.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LSGenericMessage extends StatelessWidget {
|
||||
@@ -19,8 +18,8 @@ class LSGenericMessage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [
|
||||
LunaSeaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
LunaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
]),
|
||||
builder: (context, box, child) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -30,7 +29,7 @@ class LSGenericMessage extends StatelessWidget {
|
||||
child: child,
|
||||
margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
|
||||
elevation: Constants.UI_ELEVATION,
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/constants.dart';
|
||||
import './generic.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LSNotEnabled extends StatelessWidget {
|
||||
final String service;
|
||||
|
||||
@@ -45,8 +45,8 @@ class _State extends State<LSTypewriterMessage> with TickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [
|
||||
LunaSeaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaSeaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
LunaDatabaseValue.THEME_AMOLED.key,
|
||||
LunaDatabaseValue.THEME_AMOLED_BORDER.key,
|
||||
]),
|
||||
builder: (context, box, child) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -56,7 +56,7 @@ class _State extends State<LSTypewriterMessage> with TickerProviderStateMixin {
|
||||
child: child,
|
||||
margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
|
||||
elevation: Constants.UI_ELEVATION,
|
||||
shape: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
shape: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? LSRoundedShapeWithBorder()
|
||||
: LSRoundedShape(),
|
||||
),
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
class RiveAnimations {
|
||||
RiveAnimations._();
|
||||
|
||||
static const CODER = {
|
||||
'path': 'assets/rive/coding.flr',
|
||||
'animation': 'coding',
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,7 @@ const _LEFT_PADDING = 8.0;
|
||||
Future<void> LSSnackBar({
|
||||
@required BuildContext context,
|
||||
@required String title,
|
||||
String message = Constants.CHECK_LOGS_MESSAGE,
|
||||
String message = LunaLogger.CHECK_LOGS_MESSAGE,
|
||||
Duration duration,
|
||||
SNACKBAR_TYPE type = SNACKBAR_TYPE.info,
|
||||
bool showButton = false,
|
||||
@@ -44,7 +44,7 @@ Future<void> LSSnackBar({
|
||||
horizontalDismissDirection: HorizontalDismissDirection.horizontal,
|
||||
margin: EdgeInsets.all(8.0),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
borderColor: LunaSeaDatabaseValue.THEME_AMOLED.data && LunaSeaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data
|
||||
? Colors.white12
|
||||
: Colors.transparent,
|
||||
child: FlashBar(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/core/ui/colors.dart';
|
||||
|
||||
class LSHeader extends StatelessWidget {
|
||||
final String text;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class LunaUUID {
|
||||
static final _generator = Uuid();
|
||||
static String get uuid => _generator.v4();
|
||||
static final Uuid _generator = Uuid();
|
||||
|
||||
/// Generate a new v4 UUID with the base configuration.
|
||||
String get uuid => _generator.v4();
|
||||
|
||||
/// Return the [Uuid] instance.
|
||||
Uuid get generator => _generator;
|
||||
}
|
||||
|
||||
@@ -2,30 +2,47 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
/// LunaSea Entry Point: Initialize & Run Application
|
||||
/// Runs app in guarded zone to attempt to capture fatal (crashing) errors
|
||||
void main() async {
|
||||
await _init();
|
||||
runZonedGuarded<void>(
|
||||
() => runApp(LunaBIOS()),
|
||||
(Object error, StackTrace stack) => LunaLogger.fatal(error, stack),
|
||||
);
|
||||
}
|
||||
///
|
||||
/// Runs app in Sentry guarded zone to attempt to capture fatal (crashing) errors
|
||||
Future<void> main() async => await SentryFlutter.init(
|
||||
(options) => options
|
||||
..dsn = LunaLogger.SENTRY_DSN,
|
||||
appRunner: () async {
|
||||
await _init();
|
||||
runApp(LunaBIOS());
|
||||
}
|
||||
);
|
||||
|
||||
/// Initializes LunaSea before running the BIOS Widget.
|
||||
///
|
||||
/// Sets up (in order):
|
||||
/// - System UI Overlay Styling
|
||||
/// - Logger
|
||||
/// - Network
|
||||
/// - Image Cache
|
||||
/// - Router
|
||||
/// - Firebase
|
||||
/// - IAPs
|
||||
/// - Database
|
||||
///
|
||||
/// Does not call [WidgetsFlutterBinding.ensureInitialized()] as that is called during Sentry's initialization.
|
||||
/// If Sentry is removed, you should call that method to prevent black-screen flashing on launch.
|
||||
Future<void> _init() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
//Set system UI style (navbar, statusbar)
|
||||
//Set system UI overlay style (navbar, statusbar)
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.black,
|
||||
systemNavigationBarDividerColor: Colors.black,
|
||||
statusBarColor: Colors.transparent,
|
||||
));
|
||||
//LunaSea initialization
|
||||
LunaNetworking.initialize();
|
||||
LunaLogger.initialize();
|
||||
LunaNetworking.initialize();
|
||||
LunaImageCache.initialize();
|
||||
LunaRouter.intialize();
|
||||
await LunaFirebase.initialize();
|
||||
await LunaInAppPurchases.initialize();
|
||||
await Database.initialize();
|
||||
}
|
||||
@@ -39,17 +56,20 @@ class LunaBIOS extends StatefulWidget {
|
||||
|
||||
class _State extends State<LunaBIOS> {
|
||||
@override
|
||||
Widget build(BuildContext context) => LunaProvider.providers(
|
||||
Widget build(BuildContext context) => LunaState.providers(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaSeaDatabaseValue.THEME_AMOLED.key]),
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.THEME_AMOLED.key]),
|
||||
builder: (context, box, _) {
|
||||
return MaterialApp(
|
||||
navigatorKey: LunaBIOS.navigatorKey,
|
||||
routes: LunaRouter.routes,
|
||||
onGenerateRoute: LunaRouter.router.generator,
|
||||
darkTheme: LunaTheme.darkTheme,
|
||||
theme: LunaTheme.darkTheme,
|
||||
darkTheme: LunaTheme.activeTheme(),
|
||||
theme: LunaTheme.activeTheme(),
|
||||
title: Constants.APPLICATION_NAME,
|
||||
navigatorKey: LunaBIOS.navigatorKey,
|
||||
navigatorObservers: [
|
||||
SentryNavigatorObserver(),
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:lunasea/core/database/database.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
part 'calendar_starting_day.g.dart';
|
||||
@@ -31,7 +31,7 @@ extension CalendarStartingDayExtension on CalendarStartingDay {
|
||||
case CalendarStartingDay.FRIDAY: return StartingDayOfWeek.friday;
|
||||
case CalendarStartingDay.SATURDAY: return StartingDayOfWeek.saturday;
|
||||
case CalendarStartingDay.SUNDAY: return StartingDayOfWeek.sunday;
|
||||
default: return StartingDayOfWeek.monday;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,33 @@ extension CalendarStartingDayExtension on CalendarStartingDay {
|
||||
case CalendarStartingDay.FRIDAY: return 'Friday';
|
||||
case CalendarStartingDay.SATURDAY: return 'Saturday';
|
||||
case CalendarStartingDay.SUNDAY: return 'Sunday';
|
||||
default: return 'Unknown Starting Day';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get key {
|
||||
switch(this) {
|
||||
case CalendarStartingDay.MONDAY: return 'mon';
|
||||
case CalendarStartingDay.TUESDAY: return 'tue';
|
||||
case CalendarStartingDay.WEDNESDAY: return 'wed';
|
||||
case CalendarStartingDay.THURSDAY: return 'thu';
|
||||
case CalendarStartingDay.FRIDAY: return 'fri';
|
||||
case CalendarStartingDay.SATURDAY: return 'sat';
|
||||
case CalendarStartingDay.SUNDAY: return 'sun';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
CalendarStartingDay fromKey(String key) {
|
||||
switch(key) {
|
||||
case 'mon': return CalendarStartingDay.MONDAY;
|
||||
case 'tue': return CalendarStartingDay.TUESDAY;
|
||||
case 'wed': return CalendarStartingDay.WEDNESDAY;
|
||||
case 'thu': return CalendarStartingDay.THURSDAY;
|
||||
case 'fri': return CalendarStartingDay.FRIDAY;
|
||||
case 'sat': return CalendarStartingDay.SATURDAY;
|
||||
case 'sun': return CalendarStartingDay.SUNDAY;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core/database/database.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
part 'calendar_starting_size.g.dart';
|
||||
@@ -17,28 +17,46 @@ enum CalendarStartingSize {
|
||||
extension CalendarStartingSizeExtension on CalendarStartingSize {
|
||||
CalendarFormat get data {
|
||||
switch(this) {
|
||||
case CalendarStartingSize.ONE_WEEK: return CalendarFormat.week;
|
||||
case CalendarStartingSize.TWO_WEEKS: return CalendarFormat.twoWeeks;
|
||||
case CalendarStartingSize.ONE_MONTH: return CalendarFormat.month;
|
||||
case CalendarStartingSize.ONE_WEEK:
|
||||
default: return CalendarFormat.week;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get name {
|
||||
switch(this) {
|
||||
case CalendarStartingSize.ONE_WEEK: return 'One Week';
|
||||
case CalendarStartingSize.TWO_WEEKS: return 'Two Weeks';
|
||||
case CalendarStartingSize.ONE_MONTH: return 'One Month';
|
||||
case CalendarStartingSize.ONE_WEEK:
|
||||
default: return 'One Week';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get key {
|
||||
switch(this) {
|
||||
case CalendarStartingSize.ONE_WEEK: return 'oneweek';
|
||||
case CalendarStartingSize.TWO_WEEKS: return 'twoweeks';
|
||||
case CalendarStartingSize.ONE_MONTH: return 'onemonth';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch(this) {
|
||||
case CalendarStartingSize.ONE_WEEK: return Icons.photo_size_select_small;
|
||||
case CalendarStartingSize.TWO_WEEKS: return Icons.photo_size_select_large;
|
||||
case CalendarStartingSize.ONE_MONTH: return Icons.photo_size_select_actual;
|
||||
case CalendarStartingSize.ONE_WEEK:
|
||||
default: return Icons.photo_size_select_small;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
CalendarStartingSize fromKey(String key) {
|
||||
switch(key) {
|
||||
case 'oneweek': return CalendarStartingSize.ONE_WEEK;
|
||||
case 'twoweeks': return CalendarStartingSize.TWO_WEEKS;
|
||||
case 'onemonth': return CalendarStartingSize.ONE_MONTH;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/core/database/database.dart';
|
||||
|
||||
part 'calendar_starting_type.g.dart';
|
||||
|
||||
@@ -13,27 +12,35 @@ enum CalendarStartingType {
|
||||
}
|
||||
|
||||
extension CalendarStartingTypeExtension on CalendarStartingType {
|
||||
String get data {
|
||||
switch(this) {
|
||||
case CalendarStartingType.SCHEDULE: return 'schedule';
|
||||
case CalendarStartingType.CALENDAR:
|
||||
default: return 'calendar';
|
||||
}
|
||||
}
|
||||
|
||||
String get name {
|
||||
switch(this) {
|
||||
case CalendarStartingType.SCHEDULE: return 'Schedule';
|
||||
case CalendarStartingType.CALENDAR:
|
||||
default: return 'Calendar';
|
||||
case CalendarStartingType.CALENDAR: return 'Calendar';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get key {
|
||||
switch(this) {
|
||||
case CalendarStartingType.SCHEDULE: return 'schedule';
|
||||
case CalendarStartingType.CALENDAR: return 'calendar';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch(this) {
|
||||
case CalendarStartingType.SCHEDULE: return Icons.calendar_view_day;
|
||||
case CalendarStartingType.CALENDAR:
|
||||
default: return CustomIcons.calendar;
|
||||
case CalendarStartingType.CALENDAR: return CustomIcons.calendar;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
CalendarStartingType fromKey(String key) {
|
||||
switch(key) {
|
||||
case 'schedule': return CalendarStartingType.SCHEDULE;
|
||||
case 'calendar': return CalendarStartingType.CALENDAR;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/home.dart';
|
||||
|
||||
class CalendarAPI extends API {
|
||||
class CalendarAPI {
|
||||
final Map<String, dynamic> lidarr;
|
||||
final Map<String, dynamic> radarr;
|
||||
final Map<String, dynamic> sonarr;
|
||||
@@ -23,22 +23,7 @@ class CalendarAPI extends API {
|
||||
);
|
||||
}
|
||||
|
||||
void logError(String methodName, String text, Object error, StackTrace trace, {
|
||||
bool uploadToSentry = true,
|
||||
}) => LunaLogger.error(
|
||||
'package:lunasea/core/api/calendar/api.dart',
|
||||
methodName,
|
||||
'Home: $text',
|
||||
error,
|
||||
trace,
|
||||
uploadToSentry: uploadToSentry,
|
||||
);
|
||||
|
||||
void logWarning(String methodName, String text) => LunaLogger.warning(
|
||||
'package:lunasea/core/api/calendar/api.dart',
|
||||
methodName,
|
||||
'Home: $text',
|
||||
);
|
||||
void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Home: $text', error, trace);
|
||||
|
||||
Future<bool> testConnection() async => true;
|
||||
|
||||
@@ -80,7 +65,7 @@ class CalendarAPI extends API {
|
||||
Response response = await _client.get('calendar');
|
||||
if(response.data.length > 0) {
|
||||
for(var entry in response.data) {
|
||||
DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.lsDateTime_floor();
|
||||
DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.lunaFloor;
|
||||
if(date != null) {
|
||||
List day = map[date] ?? [];
|
||||
day.add(CalendarLidarrData(
|
||||
@@ -94,10 +79,8 @@ class CalendarAPI extends API {
|
||||
}
|
||||
}
|
||||
}
|
||||
} on DioError catch (error, stack) {
|
||||
logError('_getLidarrUpcoming', 'Failed to fetch Lidarr upcoming content', error, stack, uploadToSentry: false);
|
||||
} catch (error, stack) {
|
||||
logError('_getLidarrUpcoming', 'Failed to fetch Lidarr upcoming content', error, stack);
|
||||
logError('Failed to fetch Lidarr upcoming content', error, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +104,7 @@ class CalendarAPI extends API {
|
||||
Response response = await _client.get('calendar');
|
||||
if(response.data.length > 0) {
|
||||
for(var entry in response.data) {
|
||||
DateTime date = DateTime.tryParse(entry['physicalRelease'] ?? '')?.toLocal()?.lsDateTime_floor();
|
||||
DateTime date = DateTime.tryParse(entry['physicalRelease'] ?? '')?.toLocal()?.lunaFloor;
|
||||
if(date != null) {
|
||||
List day = map[date] ?? [];
|
||||
day.add(CalendarRadarrData(
|
||||
@@ -136,10 +119,8 @@ class CalendarAPI extends API {
|
||||
}
|
||||
}
|
||||
}
|
||||
} on DioError catch (error, stack) {
|
||||
logError('_getRadarrUpcoming', 'Failed to fetch Radarr upcoming content', error, stack, uploadToSentry: false);
|
||||
} catch (error, stack) {
|
||||
logError('_getRadarrUpcoming', 'Failed to fetch Radarr upcoming content', error, stack);
|
||||
logError('Failed to fetch Radarr upcoming content', error, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +144,7 @@ class CalendarAPI extends API {
|
||||
Response response = await _client.get('calendar');
|
||||
if(response.data.length > 0) {
|
||||
for(var entry in response.data) {
|
||||
DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.lsDateTime_floor();
|
||||
DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.lunaFloor;
|
||||
if(date != null) {
|
||||
List day = map[date] ?? [];
|
||||
day.add(CalendarSonarrData(
|
||||
@@ -181,10 +162,8 @@ class CalendarAPI extends API {
|
||||
}
|
||||
}
|
||||
}
|
||||
} on DioError catch (error, stack) {
|
||||
logError('_getSonarrUpcoming', 'Failed to fetch Sonarr upcoming content', error, stack, uploadToSentry: false);
|
||||
} catch (error, stack) {
|
||||
logError('_getSonarrUpcoming', 'Failed to fetch Sonarr upcoming content', error, stack);
|
||||
logError('Failed to fetch Sonarr upcoming content', error, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class CalendarLidarrData extends CalendarData {
|
||||
Future<void> trailingOnPress(BuildContext context) async {
|
||||
await LidarrAPI.from(Database.currentProfileObject).searchAlbums([id])
|
||||
.then((_) => LSSnackBar(context: context, title: 'Searching...', message: albumTitle))
|
||||
.catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure));
|
||||
.catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -19,9 +19,7 @@ class CalendarRadarrData extends CalendarData {
|
||||
@required this.runtime,
|
||||
}): super(id, title);
|
||||
|
||||
String get runtimeString {
|
||||
return runtime.lsTime_runtimeString(dot: true);
|
||||
}
|
||||
String get runtimeString => '\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}';
|
||||
|
||||
TextSpan get subtitle => TextSpan(
|
||||
style: TextStyle(
|
||||
@@ -75,7 +73,7 @@ class CalendarRadarrData extends CalendarData {
|
||||
Future<void> trailingOnPress(BuildContext context) async {
|
||||
await RadarrAPI.from(Database.currentProfileObject).automaticSearchMovie(id)
|
||||
.then((_) => LSSnackBar(context: context, title: 'Searching...', message: title))
|
||||
.catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: Constants.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure));
|
||||
.catchError((_) => LSSnackBar(context: context, title: 'Failed to Search', message: LunaLogger.CHECK_LOGS_MESSAGE, type: SNACKBAR_TYPE.failure));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -93,7 +93,7 @@ class CalendarSonarrData extends CalendarData {
|
||||
|
||||
String get airTimeString {
|
||||
if(airTimeObject != null) {
|
||||
return LunaSeaDatabaseValue.USE_24_HOUR_TIME.data
|
||||
return LunaDatabaseValue.USE_24_HOUR_TIME.data
|
||||
? DateFormat.Hm().format(airTimeObject)
|
||||
: DateFormat('hh:mm\na').format(airTimeObject);
|
||||
}
|
||||
@@ -110,13 +110,10 @@ class CalendarSonarrData extends CalendarData {
|
||||
type: SNACKBAR_TYPE.success,
|
||||
))
|
||||
.catchError((error, stack) {
|
||||
LunaLogger.error(
|
||||
'CalendarSonarrData',
|
||||
'trailingOnPress',
|
||||
LunaLogger().error(
|
||||
'Failed to search for episode: $id',
|
||||
error,
|
||||
stack,
|
||||
uploadToSentry: !(error is DioError),
|
||||
);
|
||||
LSSnackBar(
|
||||
context: context,
|
||||
|
||||
@@ -6,18 +6,21 @@ class HomeConstants {
|
||||
|
||||
static const MODULE_KEY = 'home';
|
||||
|
||||
static const LunaModuleMap MODULE_MAP = LunaModuleMap(
|
||||
static const LunaModuleMetadata MODULE_METADATA = LunaModuleMetadata(
|
||||
name: 'Home',
|
||||
description: 'Home',
|
||||
settingsDescription: 'Configure the Home Screen',
|
||||
helpMessage: '',
|
||||
icon: CustomIcons.home,
|
||||
route: '/',
|
||||
color: Color(LunaColours.ACCENT_COLOR),
|
||||
website: '',
|
||||
github: '',
|
||||
);
|
||||
|
||||
//ignore: non_constant_identifier_names
|
||||
static final ShortcutItem MODULE_QUICK_ACTION = ShortcutItem(
|
||||
type: MODULE_KEY,
|
||||
localizedTitle: MODULE_MAP.name,
|
||||
localizedTitle: MODULE_METADATA.name,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lunasea/core/database/database.dart';
|
||||
import './adapters.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/home/core.dart';
|
||||
|
||||
class HomeDatabase {
|
||||
HomeDatabase._();
|
||||
|
||||
static void registerAdapters() {
|
||||
class HomeDatabase extends LunaModuleDatabase {
|
||||
@override
|
||||
void registerAdapters() {
|
||||
Hive.registerAdapter(CalendarStartingDayAdapter());
|
||||
Hive.registerAdapter(CalendarStartingSizeAdapter());
|
||||
Hive.registerAdapter(CalendarStartingTypeAdapter());
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> export() {
|
||||
Map<String, dynamic> data = {};
|
||||
for(HomeDatabaseValue value in HomeDatabaseValue.values) {
|
||||
switch(value) {
|
||||
// Non-primitive values
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_DAY: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_DAY.data as CalendarStartingDay).key; break;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_SIZE: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_SIZE.data as CalendarStartingSize).key; break;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_TYPE: data[value.key] = (HomeDatabaseValue.CALENDAR_STARTING_TYPE.data as CalendarStartingType).key; break;
|
||||
// Primitive values
|
||||
case HomeDatabaseValue.NAVIGATION_INDEX:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_RADARR:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_SONARR:
|
||||
case HomeDatabaseValue.MODULES_BRAND_COLOURS:
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_PAST:
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: data[value.key] = value.data; break;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void import(Map<String, dynamic> config) {
|
||||
for(String key in config.keys) {
|
||||
HomeDatabaseValue value = valueFromKey(key);
|
||||
if(value != null) switch(value) {
|
||||
// Non-primitive values
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_DAY: value.put(CalendarStartingDay.MONDAY.fromKey(config[key])); break;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_SIZE: value.put(CalendarStartingSize.ONE_WEEK.fromKey(config[key])); break;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_TYPE: value.put(CalendarStartingType.CALENDAR.fromKey(config[key])); break;
|
||||
// Primitive values
|
||||
case HomeDatabaseValue.NAVIGATION_INDEX:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_RADARR:
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_SONARR:
|
||||
case HomeDatabaseValue.MODULES_BRAND_COLOURS:
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_PAST:
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: value.put(config[key]); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HomeDatabaseValue valueFromKey(String key) {
|
||||
switch(key) {
|
||||
case 'HOME_NAVIGATION_INDEX': return HomeDatabaseValue.NAVIGATION_INDEX;
|
||||
case 'HOME_CALENDAR_STARTING_DAY': return HomeDatabaseValue.CALENDAR_STARTING_DAY;
|
||||
case 'HOME_CALENDAR_STARTING_SIZE': return HomeDatabaseValue.CALENDAR_STARTING_SIZE;
|
||||
case 'HOME_CALENDAR_STARTING_TYPE': return HomeDatabaseValue.CALENDAR_STARTING_TYPE;
|
||||
case 'HOME_CALENDAR_ENABLE_LIDARR': return HomeDatabaseValue.CALENDAR_ENABLE_LIDARR;
|
||||
case 'HOME_CALENDAR_ENABLE_RADARR': return HomeDatabaseValue.CALENDAR_ENABLE_RADARR;
|
||||
case 'HOME_CALENDAR_ENABLE_SONARR': return HomeDatabaseValue.CALENDAR_ENABLE_SONARR;
|
||||
case 'HOME_MODULES_BRAND_COLOURS': return HomeDatabaseValue.MODULES_BRAND_COLOURS;
|
||||
case 'HOME_CALENDAR_DAYS_PAST': return HomeDatabaseValue.CALENDAR_DAYS_PAST;
|
||||
case 'HOME_CALENDAR_DAYS_FUTURE': return HomeDatabaseValue.CALENDAR_DAYS_FUTURE;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HomeDatabaseValue {
|
||||
@@ -43,21 +103,41 @@ extension HomeDatabaseValueExtension on HomeDatabaseValue {
|
||||
}
|
||||
|
||||
dynamic get data {
|
||||
final _box = Database.lunaSeaBox;
|
||||
final box = Database.lunaSeaBox;
|
||||
switch(this) {
|
||||
case HomeDatabaseValue.NAVIGATION_INDEX: return _box.get(this.key, defaultValue: 0);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_DAY: return _box.get(this.key, defaultValue: CalendarStartingDay.MONDAY);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_SIZE: return _box.get(this.key, defaultValue: CalendarStartingSize.ONE_WEEK);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_TYPE: return _box.get(this.key, defaultValue: CalendarStartingType.CALENDAR);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: return _box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: return _box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: return _box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.MODULES_BRAND_COLOURS: return _box.get(this.key, defaultValue: false);
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_PAST: return _box.get(this.key, defaultValue: 14);
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: return _box.get(this.key, defaultValue: 14);
|
||||
case HomeDatabaseValue.NAVIGATION_INDEX: return box.get(this.key, defaultValue: 0);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_DAY: return box.get(this.key, defaultValue: CalendarStartingDay.MONDAY);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_SIZE: return box.get(this.key, defaultValue: CalendarStartingSize.ONE_WEEK);
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_TYPE: return box.get(this.key, defaultValue: CalendarStartingType.CALENDAR);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: return box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: return box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: return box.get(this.key, defaultValue: true);
|
||||
case HomeDatabaseValue.MODULES_BRAND_COLOURS: return box.get(this.key, defaultValue: false);
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_PAST: return box.get(this.key, defaultValue: 14);
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: return box.get(this.key, defaultValue: 14);
|
||||
}
|
||||
throw Exception('data not found');
|
||||
}
|
||||
|
||||
void put(dynamic value) => Database.lunaSeaBox.put(this.key, value);
|
||||
void put(dynamic value) {
|
||||
final box = Database.lunaSeaBox;
|
||||
switch(this) {
|
||||
case HomeDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_DAY: if(value.runtimeType == CalendarStartingDay) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_SIZE: if(value.runtimeType == CalendarStartingSize) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_STARTING_TYPE: if(value.runtimeType == CalendarStartingType) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_LIDARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_RADARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_ENABLE_SONARR: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.MODULES_BRAND_COLOURS: if(value.runtimeType == bool) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_PAST: if(value.runtimeType == int) box.put(this.key, value); return;
|
||||
case HomeDatabaseValue.CALENDAR_DAYS_FUTURE: if(value.runtimeType == int) box.put(this.key, value); return;
|
||||
}
|
||||
LunaLogger().warning('HomeDatabaseValueExtension', 'put', 'Attempted to enter data for invalid HomeDatabaseValue: ${this?.toString() ?? 'null'}');
|
||||
}
|
||||
|
||||
void listen(Widget Function(BuildContext, Box<dynamic>, Widget) builder) => ValueListenableBuilder(
|
||||
valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]),
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/home.dart';
|
||||
|
||||
class HomeState extends LunaGlobalState {
|
||||
class HomeState extends LunaModuleState {
|
||||
@override
|
||||
void reset() {}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user