mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-30 20:24:25 +00:00
* [iOS/Build] Add GoogleUtilities pod to prevent archive errors * (chore) version bump * [Git] Initialize blackbox * [Git] Initialize blackbox * [Git/Blackbox] Add me@jagandeepbrar.io as admin * registered in blackbox: ios/ExportOptions.plist * registered in blackbox: ios/GoogleService-Info.plist * registered in blackbox: android/key.properties * registered in blackbox: android/app/google-services.json * Re-encrypted keys * registered in blackbox: android/key.jks * Updated key.properties, re-encrypted * [Android/Keystore] Update same key.properties * [Tautulli/Activity] Incorrectly localized "Transcodes" string * [Radarr, UI] Implemented quick manual import, Implemented new design of ExpandableTile across UI (#378) * Use local path for radarr package * [Radarr/Manual Import] Browse filesystem, trigger quick import * [UI/Buttons] Normalize the height of all buttons * Update packages * [ListView] Exposed itemExtent property * [Lidarr/Releases] Move from LSExpandable to LunaExpandableListTile implementation * [Tautulli/Logs] Use the new expandable table card styling * [SABnzbd/History] Use the newer expandable table card styling * [NZBGet/History] Use the newer expandable table card styling * [UI/Button] Reduce size of the loader and icon to closer match text size * [Sonarr/Releases] Use the newer expandable table card styling * [Sonarr/Episodes] Use the newer expandable table card styling * [UI/Table] Safe-guard title and body text, show emdash when receiving null data * [Radarr/Details] Show monitor status and properly update the overview table on edit or monitor status changes * Updated changelog * [Sonarr/Queue & Episodes] Cleaned up implementation of expandable tile * Translated using Weblate (Norwegian Bokmål) Currently translated at 100.0% (14 of 14 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/nb_NO/ * Translated using Weblate (Swedish) Currently translated at 100.0% (14 of 14 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/sv/ * Translated using Weblate (German) Currently translated at 100.0% (68 of 68 strings) Translation: LunaSea/Tautulli Translate-URL: https://hosted.weblate.org/projects/lunasea/tautulli/de/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 80.8% (55 of 68 strings) Translation: LunaSea/Tautulli Translate-URL: https://hosted.weblate.org/projects/lunasea/tautulli/nb_NO/ * Translated using Weblate (Swedish) Currently translated at 100.0% (68 of 68 strings) Translation: LunaSea/Tautulli Translate-URL: https://hosted.weblate.org/projects/lunasea/tautulli/sv/ * Translated using Weblate (Russian) Currently translated at 57.3% (39 of 68 strings) Translation: LunaSea/Tautulli Translate-URL: https://hosted.weblate.org/projects/lunasea/tautulli/ru/ * (chore) Build localizations * [Tautulli/Webhooks] Implement webhook data handlers for all Tautulli webhooks (#380) * [Tautulli/Webhooks] Handle transcode decision change and watched event types * [Radarr/Sonarr] Safe-guard webhooks by pushing the home route on invalid received data blocks * [Tautulli/Webhooks] Implemented all webhooks * Translated using Weblate (French) Currently translated at 100.0% (78 of 78 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (German) Currently translated at 100.0% (78 of 78 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/de/ * Translated using Weblate (Russian) Currently translated at 92.8% (13 of 14 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/ru/ * [Release] (chore): Dirty v5.0.0+50000013 (#382) * [Radarr/Manual Import] Add search bar for host system path, add stub manual import page * [Radarr/Manual Import] Change path bar text to "File Browser..." * [Radarr/Manual Import] Pass "/" if textController text is null * [Radarr/Import] Stub details state * [Database] Correctly register LunaIndexerIcon hive adapter * [Radarr/Import] Filter directories when typing path, Fetch manual import results with stub cards * [UI/Bottom Bar] Abstract bottom action bar, use on add movie page * [UI/Navigation Bar] Add option to attach stickied top actions to navigation bar * [Radarr/Edit] Use stickied bottom action bar for buttons * [Radarr/Details] use sticky navigation actions for interactive and automatic search * [Dialogs] CheckboxTiles were padded too aggressively in the X-axis * [Sentry] Remove remaining references to Sentry * (chore) Build localizations * (chore) Update changelog * Preparing to shift GitHub repository structure Co-authored-by: Allan Nordhøy <epost@anotheragency.no> Co-authored-by: Patrik Hauguth <patrik.hauguth@gmail.com> Co-authored-by: cpt-kuesel <jln.sltr@pm.me> Co-authored-by: Artem <KovalevArtem.ru@gmail.com> Co-authored-by: Antoine C <contact+weblate@antoinecardon.fr>
This commit is contained in:
2
.blackbox/.gitattributes
vendored
Normal file
2
.blackbox/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
blackbox-admins.txt text eol=lf
|
||||
blackbox-files.txt text eol=lf
|
||||
1
.blackbox/blackbox-admins.txt
Normal file
1
.blackbox/blackbox-admins.txt
Normal file
@@ -0,0 +1 @@
|
||||
me@jagandeepbrar.io
|
||||
5
.blackbox/blackbox-files.txt
Normal file
5
.blackbox/blackbox-files.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
android/app/google-services.json
|
||||
android/key.jks
|
||||
android/key.properties
|
||||
ios/ExportOptions.plist
|
||||
ios/GoogleService-Info.plist
|
||||
BIN
.blackbox/pubring.kbx
Normal file
BIN
.blackbox/pubring.kbx
Normal file
Binary file not shown.
BIN
.blackbox/trustdb.gpg
Normal file
BIN
.blackbox/trustdb.gpg
Normal file
Binary file not shown.
32
.gitignore
vendored
32
.gitignore
vendored
@@ -38,19 +38,21 @@ lib/generated_plugin_registrant.dart
|
||||
ios/Flutter/.last_build_id
|
||||
|
||||
# LunaSea
|
||||
LunaSea.ipa
|
||||
LunaSea.aab
|
||||
LunaSea-armeabi-v7a-release.apk
|
||||
LunaSea-arm64-v8a-release.apk
|
||||
LunaSea-x86_64-release.apk
|
||||
ios/build/
|
||||
_changelog.txt
|
||||
|
||||
# Build Configurations
|
||||
ExportOptions.plist
|
||||
key.properties
|
||||
|
||||
# Firebase
|
||||
android/app/google-services.json
|
||||
ios/GoogleService-Info.plist
|
||||
output/
|
||||
*.ipa
|
||||
*.aab
|
||||
*.apk
|
||||
ios/build/
|
||||
|
||||
# Build Output
|
||||
|
||||
|
||||
# Blackbox
|
||||
/.blackbox/pubring.gpg~
|
||||
/.blackbox/pubring.kbx~
|
||||
/.blackbox/secring.gpg
|
||||
/ios/ExportOptions.plist
|
||||
/ios/GoogleService-Info.plist
|
||||
/android/key.properties
|
||||
/android/app/google-services.json
|
||||
/android/key.jks
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,5 +1,26 @@
|
||||
# LunaSea Changelog
|
||||
|
||||
## GitHub
|
||||
|
||||
#### MEW
|
||||
- `[Radarr/Details]` Show the monitor state of the movie in the overview table
|
||||
- `[Radarr/Manual Import]` Ability to manually import content from the filesystem (quick)
|
||||
|
||||
#### TWEAKS
|
||||
- `[Everything]` Unified the design of all expandable cards in their expanded state
|
||||
- `[Radarr]` Sticky primary button actions to the bottom navigation bar area
|
||||
- `[UI/UX]` Normalize the size of all buttons
|
||||
|
||||
#### FIXES
|
||||
- `[Dialogs]` Checkbox tiles were padded too aggressively in the X-axis
|
||||
- `[Networking]` Headers would not get attached in some networking configurations
|
||||
- `[Radarr/Details]` The overview table would not update when editing a movie
|
||||
- `[Search]` Indexers would not get restored with backups
|
||||
- `[Tautulli/Activity]` Activity header included some incorrectly localized strings
|
||||
- `[Webhooks]` Push the module's home route when receiving malformed data blocks from webhooks
|
||||
|
||||
---
|
||||
|
||||
## v5.0.0 (50000013)
|
||||
|
||||
#### NEW
|
||||
|
||||
BIN
android/app/google-services.json.gpg
Normal file
BIN
android/app/google-services.json.gpg
Normal file
Binary file not shown.
BIN
android/key.jks.gpg
Normal file
BIN
android/key.jks.gpg
Normal file
Binary file not shown.
BIN
android/key.properties.gpg
Normal file
BIN
android/key.properties.gpg
Normal file
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
storePassword=<your password>
|
||||
keyPassword=<your password>
|
||||
keyAlias=<your alias>
|
||||
storeFile=<full path to .jks keystore>
|
||||
storeFile=../key.jks
|
||||
|
||||
@@ -1,130 +1,6 @@
|
||||
{
|
||||
"motd": "This build introduces a revamped logging system. While most changes are not user-facing, the logs are now much more versatile and carry more information within the stack traces, which will allow for easier and better debugging of errors.\n\nThis build also introduces Firebase Analytics, which is being used with Firebase Crashlytics to give breadcrumb tracking for crashes. Breadcrumbs let me know how you got to the point of a crash, and do not contain any identifying information. Just as you can disable Firebase Crashlytics, a new option has been added to completely disable Firebase Analytics.\n\nIf you have previously disabled Firebase Crashlytics, you may need to disable Firebase Crashlytics in the settings again.",
|
||||
"new": [
|
||||
{
|
||||
"module": "-Arr/\nReleases",
|
||||
"changes": [
|
||||
"Match torrent seeder colours to the web GUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Firebase/\nAnalytics",
|
||||
"changes": [
|
||||
"Added analytics for breadcrumb tracking in crashes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Logger",
|
||||
"changes": [
|
||||
"Utilize a new, custom built on-device logging system",
|
||||
"Compact/delete old log entries once the log count passes 100",
|
||||
"Exported logs are now in JSON format"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Routing",
|
||||
"changes": [
|
||||
"Protect all module routes by showing a \"Not Enabled\" screen when the module is not enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/\nSystem",
|
||||
"changes": [
|
||||
"Added toggle to disable Firebase Analytics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Tautulli/\nActivity",
|
||||
"changes": [
|
||||
"Show custom season titles from Plex's new TV agent"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tweaks": [
|
||||
{
|
||||
"module": "Radarr/\nFiles",
|
||||
"changes": [
|
||||
"Add languages to file block",
|
||||
"Small changes to table titles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "System/\nLogs",
|
||||
"changes": [
|
||||
"Reimplemented logger view",
|
||||
"Removed viewing the stack trace within the application (still viewable within the exported logs)",
|
||||
"Added the exception to the expanded table instead of a separate dialog popup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Tautulli/\nActivity",
|
||||
"changes": [
|
||||
"Minor UI tweaks including showing the full season title and italicizing the episode title"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "UI/\nNavbar",
|
||||
"changes": [
|
||||
"Jump instead of animate to page when tapping a navbar item"
|
||||
]
|
||||
}
|
||||
],
|
||||
"fixes": [
|
||||
{
|
||||
"module": "Firebase/\nCrashes",
|
||||
"changes": [
|
||||
"Do not log DioError/networking exceptions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Flutter",
|
||||
"changes": [
|
||||
"Updated packages",
|
||||
"Upgrade all Comet.Tools packages to null-safety/NNBD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Radarr/\nReleases",
|
||||
"changes": [
|
||||
"Negative format scores were not being shown"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Share",
|
||||
"changes": [
|
||||
"The sharesheet could break after the first time it was opened"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Strings",
|
||||
"changes": [
|
||||
"Safe-guard many substring operations"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Tautulli/\nActivity",
|
||||
"changes": [
|
||||
"Play/paused/buffering icon was not properly left aligned to the text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Tautulli/\nUsers",
|
||||
"changes": [
|
||||
"User images would not be fetched on newer versions of Tautulli"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "UI/UX",
|
||||
"changes": [
|
||||
"Modal scroll controller was not being attached to the ListView in a bottom modal sheet",
|
||||
"Do not log errors when network images fail to load"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "URLs",
|
||||
"changes": [
|
||||
"Safe-guard crashing related to launching specific invalid URLs"
|
||||
]
|
||||
}
|
||||
]
|
||||
"motd": "",
|
||||
"new": [],
|
||||
"tweaks": [],
|
||||
"fixes": []
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"radarr.All": "Alle",
|
||||
"radarr.Alphabetical": "Alphabetisch",
|
||||
"radarr.Approved": "Genehmigt",
|
||||
"radarr.Automatic": "Automatisch",
|
||||
"radarr.AvailabilityUnknown": "Verfügbarkeit Unbekannt",
|
||||
"radarr.AvailableIn": "Verfügbar in {}",
|
||||
"radarr.AvailableToday": "Heute Verfügbar",
|
||||
@@ -36,6 +37,9 @@
|
||||
"radarr.HistoryDescription": "Zeige kürzliche Aktivität",
|
||||
"radarr.InCinemasIn": "In den Kinos in {}",
|
||||
"radarr.InCinemasToday": "Heute in den Kinos",
|
||||
"radarr.Interactive": "Interaktiv",
|
||||
"radarr.ManualImport": "Manueller Import",
|
||||
"radarr.ManualImportDescription": "Importiere Filme vom Dateisystem",
|
||||
"radarr.MinimumAvailability": "Minimale Verfügbarkeit",
|
||||
"radarr.Missing": "Fehlend",
|
||||
"radarr.MonitorMovie": "Überwache Film",
|
||||
@@ -51,10 +55,13 @@
|
||||
"radarr.NoHistoryFound": "Kein Verlauf gefunden",
|
||||
"radarr.NoMoviesFound": "Keine Filme gefunden",
|
||||
"radarr.NoResultsFound": "Keine Ergebnisse gefunden",
|
||||
"radarr.NoSubdirectoriesFound": "Keine Unterverzeichnisse gefunden",
|
||||
"radarr.NoSummaryIsAvailable": "Keine Zusammenfassung verfügbar.",
|
||||
"radarr.ParentDirectory": "Übergeordnetes Verzeichnis",
|
||||
"radarr.QualityProfile": "Qualitätsprofil",
|
||||
"radarr.Queue": "Warteschlange",
|
||||
"radarr.QueueDescription": "Zeige aktiven & eingereihten Inhalt",
|
||||
"radarr.Quick": "Schnell",
|
||||
"radarr.RefreshMovie": "Film aktualisieren",
|
||||
"radarr.Rejected": "Abgelehnt",
|
||||
"radarr.Released": "Veröffentlicht",
|
||||
@@ -116,12 +123,12 @@
|
||||
"tautulli.Video": "Video",
|
||||
"tautulli.Users": "Benutzer",
|
||||
"tautulli.User": "Benutzer",
|
||||
"tautulli.Transcodes": "Transkodierungen",
|
||||
"tautulli.Transcode": "Transkodieren",
|
||||
"tautulli.Transcodes": "Umwandlungen",
|
||||
"tautulli.Transcode": "Umwandeln",
|
||||
"tautulli.Title": "Titel",
|
||||
"tautulli.Throttled": "Throttled",
|
||||
"tautulli.Throttled": "Gebremst",
|
||||
"tautulli.TerminationMessage": "Abbruch-Begründung",
|
||||
"tautulli.TerminationAttachMessage": "Du kannst optional unten eine Abbruch-Begründung angeben.",
|
||||
"tautulli.TerminationAttachMessage": "Du kannst optional eine Abbruch-Begründung angeben.",
|
||||
"tautulli.Subtitle": "Untertitel",
|
||||
"tautulli.Stream": "Stream",
|
||||
"tautulli.SessionsMany": "{} Sitzungen",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"radarr.AddMovieAndSearch": "Add + Search",
|
||||
"radarr.Alphabetical": "Alphabetical",
|
||||
"radarr.Approved": "Approved",
|
||||
"radarr.Automatic": "Automatic",
|
||||
"radarr.AvailabilityUnknown": "Availability Unknown",
|
||||
"radarr.AvailableIn": "Available in {}",
|
||||
"radarr.AvailableToday": "Available Today",
|
||||
@@ -26,16 +27,21 @@
|
||||
"radarr.CinemaDateUnknown": "Cinema Date Unknown",
|
||||
"radarr.CutoffUnmet": "Cutoff Unmet",
|
||||
"radarr.DateAdded": "Date Added",
|
||||
"radarr.DirectoryNotFound": "Directory Not Found",
|
||||
"radarr.Discover": "Discover",
|
||||
"radarr.DownloadFailed": "Download Failed",
|
||||
"radarr.DownloadIgnored": "Download Ignored",
|
||||
"radarr.EditMovie": "Edit Movie",
|
||||
"radarr.FileBrowser": "File Browser…",
|
||||
"radarr.FilterCatalogue": "Filter Catalogue",
|
||||
"radarr.GrabbedFrom": "Grabbed from {}",
|
||||
"radarr.History": "History",
|
||||
"radarr.HistoryDescription": "View Recent Activity",
|
||||
"radarr.InCinemasIn": "In Cinemas in {}",
|
||||
"radarr.InCinemasToday": "In Cinemas Today",
|
||||
"radarr.Interactive": "Interactive",
|
||||
"radarr.ManualImport": "Manual Import",
|
||||
"radarr.ManualImportDescription": "Import Movies from the Filesystem",
|
||||
"radarr.MinimumAvailability": "Minimum Availability",
|
||||
"radarr.Missing": "Missing",
|
||||
"radarr.Monitored": "Monitored",
|
||||
@@ -48,13 +54,17 @@
|
||||
"radarr.MovieImported": "Movie Imported ({})",
|
||||
"radarr.MovieNotFound": "Movie Not Found",
|
||||
"radarr.MoviePath": "Movie Path",
|
||||
"radarr.NoFilesFound": "No Files Found",
|
||||
"radarr.NoHistoryFound": "No History Found",
|
||||
"radarr.NoMoviesFound": "No Movies Found",
|
||||
"radarr.NoResultsFound": "No Results Found",
|
||||
"radarr.NoSubdirectoriesFound": "No Subdirectories Found",
|
||||
"radarr.NoSummaryIsAvailable": "No summary is available.",
|
||||
"radarr.ParentDirectory": "Parent Directory",
|
||||
"radarr.QualityProfile": "Quality Profile",
|
||||
"radarr.Queue": "Queue",
|
||||
"radarr.QueueDescription": "View Active & Queued Content",
|
||||
"radarr.Quick": "Quick",
|
||||
"radarr.RefreshMovie": "Refresh Movie",
|
||||
"radarr.Rejected": "Rejected",
|
||||
"radarr.Released": "Released",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"radarr.All": "Tout",
|
||||
"radarr.Alphabetical": "Alphabétique",
|
||||
"radarr.Approved": "Approuvé",
|
||||
"radarr.Automatic": "Automatique",
|
||||
"radarr.AvailabilityUnknown": "Disponibilité inconnue",
|
||||
"radarr.AvailableIn": "Disponible dans {}",
|
||||
"radarr.AvailableToday": "Disponible aujourd'hui",
|
||||
@@ -36,6 +37,9 @@
|
||||
"radarr.HistoryDescription": "Voir les activités récentes",
|
||||
"radarr.InCinemasIn": "Dans les cinémas en {}",
|
||||
"radarr.InCinemasToday": "Au cinéma aujourd'hui",
|
||||
"radarr.Interactive": "Interactive",
|
||||
"radarr.ManualImport": "Importation manuelle",
|
||||
"radarr.ManualImportDescription": "Importer des films depuis le système de fichiers",
|
||||
"radarr.MinimumAvailability": "Disponibilité minimale",
|
||||
"radarr.Missing": "Manquant",
|
||||
"radarr.MonitorMovie": "Surveiller le film",
|
||||
@@ -51,10 +55,13 @@
|
||||
"radarr.NoHistoryFound": "Pas d'historique trouvé",
|
||||
"radarr.NoMoviesFound": "Aucun film trouvé",
|
||||
"radarr.NoResultsFound": "Aucun résultat",
|
||||
"radarr.NoSubdirectoriesFound": "Aucun sous-répertoire trouvé",
|
||||
"radarr.NoSummaryIsAvailable": "Aucun résumé n'est disponible.",
|
||||
"radarr.ParentDirectory": "Répertoire parent",
|
||||
"radarr.QualityProfile": "Profil de qualité",
|
||||
"radarr.Queue": "File d'attente",
|
||||
"radarr.QueueDescription": "Voir le contenu actif et en attente",
|
||||
"radarr.Quick": "Rapide",
|
||||
"radarr.RefreshMovie": "Rafraichir le film",
|
||||
"radarr.Rejected": "Rejeté",
|
||||
"radarr.Released": "Sortie",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"lunasea.AnErrorHasOccurred": "En feil har inntruffet",
|
||||
"lunasea.Cancel": "Avbryt",
|
||||
"lunasea.ChangeProfiles": "Endre profiler",
|
||||
"lunasea.CheckLogsMessage": "Sjekk loggføring for flere detaljer",
|
||||
"lunasea.ComingSoon": "Kommer snart",
|
||||
"lunasea.Dashboard": "Oversikt",
|
||||
"lunasea.GoBack": "Gå tilbake",
|
||||
"lunasea.ModuleIsNotEnabled": "{} er ikke påskrudd",
|
||||
"lunasea.Refresh": "Gjenoppfrisk",
|
||||
@@ -8,6 +12,7 @@
|
||||
"lunasea.SearchTextBar": "Søk …",
|
||||
"lunasea.Settings": "Innstillinger",
|
||||
"lunasea.TryAgain": "Prøv igjen",
|
||||
"lunasea.Unknown": "Ukjent",
|
||||
"radarr.AddMovie": "Legg til film",
|
||||
"radarr.AddMovieAndSearch": "Legg til + søk",
|
||||
"radarr.Age": "Alder",
|
||||
@@ -99,5 +104,73 @@
|
||||
"search.Category": "Kategori",
|
||||
"search.Alphabetical": "Alfabetisk",
|
||||
"search.AllSubcategories": "Alle underkategorier",
|
||||
"search.Age": "Alder"
|
||||
"search.Age": "Alder",
|
||||
"tautulli.ETA": "Estimert tid til ankomst",
|
||||
"tautulli.Throttled": "Strupet",
|
||||
"tautulli.TerminationAttachMessage": "Du kan alternativt legge ved en avsluttingsmelding nedenfor.",
|
||||
"tautulli.Location": "Sted",
|
||||
"tautulli.DeletingTemporarySessionsFailed": "Klarte ikke å slette midlertidige økter",
|
||||
"tautulli.DeletingTemporarySessionsDescription": "Midlertidige økter slettes …",
|
||||
"tautulli.DeletingTemporarySessions": "Sletter midlertidige økter …",
|
||||
"tautulli.DeletingImageCacheFailed": "Klarte ikke å slette bildehurtiglager",
|
||||
"tautulli.DeletingImageCacheDescription": "Tautulli-bildehurtiglageret blir slettet …",
|
||||
"tautulli.Burn": "Brenn",
|
||||
"tautulli.BackupDatabase": "Sikkerhetskopier database",
|
||||
"tautulli.BackupConfiguration": "Sikkhetskopier oppsett",
|
||||
"tautulli.BackingUpDatabaseDescription": "Sikkerhetskopierer databasen din i bakgrunnen …",
|
||||
"tautulli.Year": "År",
|
||||
"tautulli.ViewWebGUI": "Vis nettbasert grensesnitt",
|
||||
"tautulli.Video": "Video",
|
||||
"tautulli.Users": "Brukere",
|
||||
"tautulli.User": "Bruker",
|
||||
"tautulli.Transcodes": "Transkoder",
|
||||
"tautulli.Transcode": "Transkod",
|
||||
"tautulli.Title": "Tittel",
|
||||
"tautulli.TerminateSessionFailed": "Klarte ikke å avslutte økt",
|
||||
"tautulli.TerminateSession": "Avslutt økt",
|
||||
"tautulli.TerminatedSession": "Økt avsluttet",
|
||||
"tautulli.TerminationMessage": "Avsluttingsmelding",
|
||||
"tautulli.TerminationConfirmMessage": "Ønsker du å avslutte denne økten?",
|
||||
"tautulli.Terminate": "Avslutt",
|
||||
"tautulli.Subtitle": "Undertekst",
|
||||
"tautulli.Stream": "Strøm",
|
||||
"tautulli.SessionsMany": "{} økter",
|
||||
"tautulli.SessionsOne": "Én økt",
|
||||
"tautulli.Sessions": "Økter",
|
||||
"tautulli.SessionEnded": "Sesong slutt",
|
||||
"tautulli.Season": "Sesong {}",
|
||||
"tautulli.Quality": "Kvalitet",
|
||||
"tautulli.Product": "Produkt",
|
||||
"tautulli.Player": "Spiller",
|
||||
"tautulli.Platform": "Plattform",
|
||||
"tautulli.NoActiveStreams": "Ingen aktive strømmer",
|
||||
"tautulli.None": "Ingen",
|
||||
"tautulli.More": "Mer",
|
||||
"tautulli.Metadata": "Metadata",
|
||||
"tautulli.Library": "Bibliotek",
|
||||
"tautulli.History": "Historikk",
|
||||
"tautulli.Episode": "Episode {}",
|
||||
"tautulli.Duration": "Varighet",
|
||||
"tautulli.DirectStreams": "Direkte strømmer",
|
||||
"tautulli.DirectStream": "Direkte strøm",
|
||||
"tautulli.DirectPlays": "Direkte avspillinger",
|
||||
"tautulli.DirectPlay": "Direkte avspilling",
|
||||
"tautulli.DeletingImageCache": "Sletter bildehurtiglager …",
|
||||
"tautulli.DeletingCacheFailed": "Klarte ikke å slette hurtiglager",
|
||||
"tautulli.DeletingCacheDescription": "Tautulli-hurtiglageret blir slettet …",
|
||||
"tautulli.DeletingCache": "Sletter hurtiglager …",
|
||||
"tautulli.DeleteTemporarySessions": "Slett midlertidige økter",
|
||||
"tautulli.DeleteImageCache": "Slett bildehurtiglager",
|
||||
"tautulli.DeleteCache": "Slett hurtiglager",
|
||||
"tautulli.Copy": "Kopier",
|
||||
"tautulli.Container": "Beholder",
|
||||
"tautulli.Bandwidth": "Båndbredde",
|
||||
"tautulli.BackingUpConfigurationFailed": "Klarte ikke å sikkerhetskopiere oppsett",
|
||||
"tautulli.BackingUpDatabaseFailed": "Klarte ikke å sikkerhetskopiere database",
|
||||
"tautulli.BackingUpDatabase": "Sikkerhetskopierer database …",
|
||||
"tautulli.BackingUpConfigurationDescription": "Sikkerhetskopierer oppsetet ditt i bakgrunnen",
|
||||
"tautulli.BackingUpConfiguration": "Sikkerhetskopierer oppsett …",
|
||||
"tautulli.Audio": "Lyd",
|
||||
"tautulli.ActivityDetails": "Aktivitetsdetaljer",
|
||||
"tautulli.Activity": "Aktivitet"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"lunasea.AnErrorHasOccurred": "Произошла ошибка",
|
||||
"lunasea.Cancel": "Отмена",
|
||||
"lunasea.ChangeProfiles": "Изменить профили",
|
||||
"lunasea.ComingSoon": "Скоро будет",
|
||||
"lunasea.Dashboard": "Панель управления",
|
||||
@@ -67,5 +68,44 @@
|
||||
"search.Download": "Скачать",
|
||||
"search.Comments": "Комментарии",
|
||||
"search.Categories": "Категории",
|
||||
"search.Category": "Категория"
|
||||
"search.Category": "Категория",
|
||||
"tautulli.Year": "Год",
|
||||
"tautulli.Video": "Видео",
|
||||
"tautulli.Users": "Пользователи",
|
||||
"tautulli.User": "Пользователь",
|
||||
"tautulli.Title": "Название",
|
||||
"tautulli.Subtitle": "Субтитры",
|
||||
"tautulli.Season": "Сезон {}",
|
||||
"tautulli.Quality": "Качество",
|
||||
"tautulli.Product": "Продукт",
|
||||
"tautulli.Player": "Плеер",
|
||||
"tautulli.Platform": "Платформа",
|
||||
"tautulli.None": "Ничего",
|
||||
"tautulli.More": "Ещё",
|
||||
"tautulli.Metadata": "Метаданные",
|
||||
"tautulli.Location": "Местоположение",
|
||||
"tautulli.Library": "Библиотека",
|
||||
"tautulli.History": "История",
|
||||
"tautulli.Episode": "Эпизод {}",
|
||||
"tautulli.Duration": "Продолжительность",
|
||||
"tautulli.DeletingTemporarySessionsFailed": "Не удалось удалить временные сеансы",
|
||||
"tautulli.DeletingTemporarySessionsDescription": "Временные сеансы удаляются",
|
||||
"tautulli.DeletingImageCache": "Удаление кэша изображения…",
|
||||
"tautulli.DeletingCacheFailed": "Не удалось удалить кэш",
|
||||
"tautulli.DeletingCache": "Удаление кэша…",
|
||||
"tautulli.DeleteImageCache": "Удалить кэш изображения",
|
||||
"tautulli.DeleteCache": "Удалить кэш",
|
||||
"tautulli.Copy": "Копировать",
|
||||
"tautulli.Container": "Контейнер",
|
||||
"tautulli.Bandwidth": "Пропускная способность",
|
||||
"tautulli.BackupDatabase": "Резервная база данных",
|
||||
"tautulli.BackupConfiguration": "Конфигурация резервного копирования",
|
||||
"tautulli.BackingUpDatabaseFailed": "Не удалось создать резервную копию базы данных",
|
||||
"tautulli.BackingUpDatabaseDescription": "Резервное копирование базы данных в фоновом режиме",
|
||||
"tautulli.BackingUpDatabase": "Резервное копирование базы данных…",
|
||||
"tautulli.BackingUpConfigurationFailed": "Не удалось создать резервную копию конфигурации",
|
||||
"tautulli.BackingUpConfigurationDescription": "Резервное копирование конфигурации в фоновом режиме",
|
||||
"tautulli.BackingUpConfiguration": "Резервное копирование конфигурации…",
|
||||
"tautulli.Audio": "Аудио",
|
||||
"tautulli.Activity": "Активность"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"lunasea.AnErrorHasOccurred": "Ett fel har inträffat",
|
||||
"lunasea.Cancel": "Avbryt",
|
||||
"lunasea.ChangeProfiles": "Byta Profil",
|
||||
"lunasea.CheckLogsMessage": "Kolla loggen för mera detaljer",
|
||||
"lunasea.ComingSoon": "Kommer Snart",
|
||||
@@ -103,5 +104,73 @@
|
||||
"search.Comments": "Kommentarer",
|
||||
"search.Categories": "Kategorier",
|
||||
"search.Category": "Kategori",
|
||||
"search.AllSubcategories": "Alla Underkategorier"
|
||||
"search.AllSubcategories": "Alla Underkategorier",
|
||||
"tautulli.Year": "År",
|
||||
"tautulli.ViewWebGUI": "Visa Web GUI",
|
||||
"tautulli.Video": "Video",
|
||||
"tautulli.Users": "Användare",
|
||||
"tautulli.User": "Användare",
|
||||
"tautulli.Transcodes": "Omkodningar",
|
||||
"tautulli.Transcode": "Omkoda",
|
||||
"tautulli.Title": "Titel",
|
||||
"tautulli.Throttled": "Strypt",
|
||||
"tautulli.TerminateSessionFailed": "Det gick inte att Tvångsavsluta Session",
|
||||
"tautulli.TerminateSession": "Tvångsavsluta Session",
|
||||
"tautulli.TerminatedSession": "Tvångsavslutad Session",
|
||||
"tautulli.TerminationMessage": "Tvångsavslutnings Meddelande",
|
||||
"tautulli.TerminationConfirmMessage": "Vill du tvångsavsluta denna sessionen?",
|
||||
"tautulli.TerminationAttachMessage": "Du kan skriva in ett tvångsavslutnings meddelande nedan.",
|
||||
"tautulli.Terminate": "Tvångsavsluta",
|
||||
"tautulli.Subtitle": "Undertext",
|
||||
"tautulli.Stream": "Ström",
|
||||
"tautulli.SessionsMany": "{} Sessioner",
|
||||
"tautulli.SessionsOne": "1 Session",
|
||||
"tautulli.Sessions": "Sessioner",
|
||||
"tautulli.SessionEnded": "Session Stoppad",
|
||||
"tautulli.Season": "Säsong {}",
|
||||
"tautulli.Quality": "Kvalitet",
|
||||
"tautulli.Product": "Produkt",
|
||||
"tautulli.Player": "Spelare",
|
||||
"tautulli.Platform": "Platform",
|
||||
"tautulli.NoActiveStreams": "Inga Aktiva Strömmar",
|
||||
"tautulli.None": "Inget",
|
||||
"tautulli.More": "Mer",
|
||||
"tautulli.Metadata": "Metadata",
|
||||
"tautulli.Location": "Plats",
|
||||
"tautulli.Library": "Bibliotek",
|
||||
"tautulli.History": "Historik",
|
||||
"tautulli.ETA": "ETA",
|
||||
"tautulli.Episode": "Episod {}",
|
||||
"tautulli.Duration": "Speltid",
|
||||
"tautulli.DirectStreams": "Direktströmmar",
|
||||
"tautulli.DirectStream": "Direktström",
|
||||
"tautulli.DirectPlays": "Direktuppspelningar",
|
||||
"tautulli.DirectPlay": "Direktuppspelning",
|
||||
"tautulli.DeletingTemporarySessionsFailed": "Ett fel uppstod vid Radering av Temporära Sessioner",
|
||||
"tautulli.DeletingTemporarySessionsDescription": "Temporära sessioner raderas",
|
||||
"tautulli.DeletingTemporarySessions": "Raderar Temporära Sessioner…",
|
||||
"tautulli.DeletingImageCacheFailed": "Ett fel uppstod vid Radering av Cache",
|
||||
"tautulli.DeletingImageCacheDescription": "Tautulli bild cache raderas",
|
||||
"tautulli.DeletingImageCache": "Raderar Bild Cache…",
|
||||
"tautulli.DeletingCacheFailed": "Ett fel uppstod vid Radering av Cache",
|
||||
"tautulli.DeletingCacheDescription": "Tautulli cache raderas",
|
||||
"tautulli.DeletingCache": "Raderar Cache…",
|
||||
"tautulli.DeleteTemporarySessions": "Radera Temporära Sessioner",
|
||||
"tautulli.DeleteImageCache": "Radera Bild Cache",
|
||||
"tautulli.DeleteCache": "Radera Cache",
|
||||
"tautulli.Copy": "Kopiera",
|
||||
"tautulli.Container": "Behållare",
|
||||
"tautulli.Burn": "Bränn",
|
||||
"tautulli.Bandwidth": "Bandbredd",
|
||||
"tautulli.BackupDatabase": "Säkerhetskopiera Databas",
|
||||
"tautulli.BackupConfiguration": "Säkerhetskopiera Konfiguration",
|
||||
"tautulli.BackingUpDatabaseFailed": "Ett fel uppstod vid Säkerhetskopiering av Databasen",
|
||||
"tautulli.BackingUpDatabaseDescription": "Säkerhetskopierar din konfiguration i bakgrunden",
|
||||
"tautulli.BackingUpDatabase": "Säkerhetskopierar Databasen…",
|
||||
"tautulli.BackingUpConfigurationFailed": "Ett fel uppstod vid Säkerhetskopiering",
|
||||
"tautulli.BackingUpConfigurationDescription": "Säkerhetskopierar din konfiguration i bakgrunden",
|
||||
"tautulli.BackingUpConfiguration": "Säkerhetskopiera Konfiguration…",
|
||||
"tautulli.Audio": "Ljud",
|
||||
"tautulli.ActivityDetails": "Aktivitetsdetaljer",
|
||||
"tautulli.Activity": "Aktivitet"
|
||||
}
|
||||
BIN
ios/ExportOptions.plist.gpg
Normal file
BIN
ios/ExportOptions.plist.gpg
Normal file
Binary file not shown.
BIN
ios/GoogleService-Info.plist.gpg
Normal file
BIN
ios/GoogleService-Info.plist.gpg
Normal file
Binary file not shown.
@@ -28,9 +28,10 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe
|
||||
flutter_ios_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
pod 'GoogleUtilities'
|
||||
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '7.3.0'
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '7.3.0'
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
@@ -53,6 +54,7 @@ post_install do |installer|
|
||||
end
|
||||
|
||||
target 'ImageNotification' do
|
||||
pod 'GoogleUtilities'
|
||||
use_frameworks!
|
||||
pod 'Firebase/Messaging'
|
||||
end
|
||||
|
||||
@@ -114,12 +114,12 @@ PODS:
|
||||
- nanopb (~> 2.30906.0)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseFirestore (7.3.0)
|
||||
- FirebaseInstallations (7.7.0):
|
||||
- FirebaseInstallations (7.8.0):
|
||||
- FirebaseCore (~> 7.0)
|
||||
- GoogleUtilities/Environment (~> 7.0)
|
||||
- GoogleUtilities/UserDefaults (~> 7.0)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseInstanceID (7.7.0):
|
||||
- FirebaseInstanceID (7.8.0):
|
||||
- FirebaseCore (~> 7.0)
|
||||
- FirebaseInstallations (~> 7.0)
|
||||
- GoogleUtilities/Environment (~> 7.0)
|
||||
@@ -143,24 +143,38 @@ PODS:
|
||||
- nanopb (~> 2.30906.0)
|
||||
- GoogleDataTransport (8.1.0):
|
||||
- nanopb (~> 2.30906.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (7.2.2):
|
||||
- GoogleUtilities (7.3.1):
|
||||
- GoogleUtilities/AppDelegateSwizzler (= 7.3.1)
|
||||
- GoogleUtilities/Environment (= 7.3.1)
|
||||
- GoogleUtilities/ISASwizzler (= 7.3.1)
|
||||
- GoogleUtilities/Logger (= 7.3.1)
|
||||
- GoogleUtilities/MethodSwizzler (= 7.3.1)
|
||||
- GoogleUtilities/Network (= 7.3.1)
|
||||
- "GoogleUtilities/NSData+zlib (= 7.3.1)"
|
||||
- GoogleUtilities/Reachability (= 7.3.1)
|
||||
- GoogleUtilities/SwizzlerTestHelpers (= 7.3.1)
|
||||
- GoogleUtilities/UserDefaults (= 7.3.1)
|
||||
- GoogleUtilities/AppDelegateSwizzler (7.3.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Environment (7.2.2):
|
||||
- GoogleUtilities/Environment (7.3.1):
|
||||
- PromisesObjC (~> 1.2)
|
||||
- GoogleUtilities/Logger (7.2.2):
|
||||
- GoogleUtilities/ISASwizzler (7.3.1)
|
||||
- GoogleUtilities/Logger (7.3.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/MethodSwizzler (7.2.2):
|
||||
- GoogleUtilities/MethodSwizzler (7.3.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network (7.2.2):
|
||||
- GoogleUtilities/Network (7.3.1):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (7.2.2)"
|
||||
- GoogleUtilities/Reachability (7.2.2):
|
||||
- "GoogleUtilities/NSData+zlib (7.3.1)"
|
||||
- GoogleUtilities/Reachability (7.3.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (7.2.2):
|
||||
- GoogleUtilities/SwizzlerTestHelpers (7.3.1):
|
||||
- GoogleUtilities/MethodSwizzler
|
||||
- GoogleUtilities/UserDefaults (7.3.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMSessionFetcher/Core (1.5.0)
|
||||
- in_app_purchase (0.0.1):
|
||||
@@ -204,6 +218,7 @@ DEPENDENCIES:
|
||||
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
|
||||
- FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `7.3.0`)
|
||||
- Flutter (from `Flutter`)
|
||||
- GoogleUtilities
|
||||
- in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`)
|
||||
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
@@ -302,14 +317,14 @@ SPEC CHECKSUMS:
|
||||
FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350
|
||||
FirebaseCrashlytics: d31325312c92e2cb2f0386d589b9aa44e303d99b
|
||||
FirebaseFirestore: 46e32f479aac127e742b1cf873930f7bb43e7b45
|
||||
FirebaseInstallations: 42c86e7b02ff75b7f27f85833bf5dcb5f38a9774
|
||||
FirebaseInstanceID: cf940324a20ac14a27ad1e931d15ac9d335526db
|
||||
FirebaseInstallations: 7f7ed0e7e27fb51f57291e1876e2ddb1524126c1
|
||||
FirebaseInstanceID: aaecc93b4528bbcafea12c477e26827719ca1183
|
||||
FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232
|
||||
FirebaseStorage: 5002b1895bfe74a5ce92ad54f966e6162d0da2e5
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
GoogleAppMeasurement: 8d3c0aeede16ab7764144b5a4ca8e1d4323841b7
|
||||
GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab
|
||||
GoogleUtilities: 31c5b01f978a70c6cff2afc6272b3f1921614b43
|
||||
GoogleUtilities: e1d9ed4e544fc32a93e00e721400cbc3f377200d
|
||||
GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
|
||||
in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
|
||||
nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53
|
||||
@@ -325,6 +340,6 @@ SPEC CHECKSUMS:
|
||||
SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
|
||||
PODFILE CHECKSUM: d7f0a8efbe8010ddad9d00678781441d461a1402
|
||||
PODFILE CHECKSUM: fca408160c687ba2afb8bca214366f046bdc948c
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
@@ -442,7 +442,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleUtilities-00567490/GoogleUtilities.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||
@@ -824,7 +824,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ImageNotification/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -856,7 +856,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ImageNotification/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -887,7 +887,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ImageNotification/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
||||
@@ -9,6 +9,7 @@ class LunaDatabase extends LunaModuleDatabase {
|
||||
Hive.registerAdapter(LunaLogHiveObjectAdapter());
|
||||
Hive.registerAdapter(LunaLogTypeAdapter());
|
||||
Hive.registerAdapter(LunaBrowserAdapter());
|
||||
Hive.registerAdapter(LunaIndexerIconAdapter());
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
export 'ui/appbar.dart';
|
||||
export 'ui/bottom_bar.dart';
|
||||
export 'ui/bottom_modal_sheet.dart';
|
||||
export 'ui/bottom_navigation_bar.dart';
|
||||
export 'ui/button.dart';
|
||||
export 'ui/card.dart';
|
||||
export 'ui/colors.dart';
|
||||
|
||||
3
lib/core/ui/bottom_bar.dart
Normal file
3
lib/core/ui/bottom_bar.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
export 'bottom_bar/badge.dart';
|
||||
export 'bottom_bar/action_bar.dart';
|
||||
export 'bottom_bar/navigation_bar.dart';
|
||||
45
lib/core/ui/bottom_bar/action_bar.dart
Normal file
45
lib/core/ui/bottom_bar/action_bar.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
/// Create a [LunaBottomActionBar] that contains button actions.
|
||||
///
|
||||
/// The children are expected to be [LunaButton]s or children of [LunaButton].
|
||||
class LunaBottomActionBar extends StatelessWidget {
|
||||
final EdgeInsets padding;
|
||||
final List<Widget> actions;
|
||||
final bool useSafeArea;
|
||||
|
||||
LunaBottomActionBar({
|
||||
@required this.actions,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 8.0),
|
||||
this.useSafeArea = true,
|
||||
Key key,
|
||||
}) : super(key: key) {
|
||||
assert(actions != null && actions.length != 0);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: SafeArea(
|
||||
top: useSafeArea,
|
||||
bottom: useSafeArea,
|
||||
left: useSafeArea,
|
||||
right: useSafeArea,
|
||||
child: Padding(
|
||||
child: LunaButtonContainer(
|
||||
children: actions,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
padding: padding,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
112
lib/core/ui/bottom_bar/navigation_bar.dart
Normal file
112
lib/core/ui/bottom_bar/navigation_bar.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'package:google_nav_bar/google_nav_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaBottomNavigationBar extends StatefulWidget {
|
||||
final PageController pageController;
|
||||
final List<IconData> icons;
|
||||
final List<String> titles;
|
||||
final List<ScrollController> scrollControllers;
|
||||
final List<Widget> topActions;
|
||||
final Function(int) onTabChange;
|
||||
final List<Widget> leadingOnTab;
|
||||
|
||||
LunaBottomNavigationBar({
|
||||
Key key,
|
||||
@required this.pageController,
|
||||
@required this.icons,
|
||||
@required this.titles,
|
||||
this.topActions,
|
||||
this.onTabChange,
|
||||
this.leadingOnTab,
|
||||
this.scrollControllers,
|
||||
}) : super(key: key) {
|
||||
assert(icons.length == titles.length, 'An unequal amount of titles and icons were passed to LunaNavigationBar.');
|
||||
if(leadingOnTab != null) assert(icons.length == leadingOnTab.length, 'An unequal amount of icons and leadingOnTab were passed to LunaNavigationBar.');
|
||||
if(scrollControllers != null) assert(icons.length == scrollControllers.length, 'An unequal amount of icons and scrollControllers were passed to LunaNavigationBar.');
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<LunaBottomNavigationBar> {
|
||||
int _index;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_index = widget.pageController?.initialPage ?? 0;
|
||||
widget.pageController?.addListener(_pageControllerListener);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.pageController?.removeListener(_pageControllerListener);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _pageControllerListener() {
|
||||
if((widget.pageController?.page?.round() ?? _index) == _index) return;
|
||||
setState(() => _index = widget.pageController.page.round());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if(widget.topActions != null && widget.topActions.length != 0) LunaBottomActionBar(
|
||||
actions: widget.topActions,
|
||||
padding: EdgeInsets.fromLTRB(6.0, 8.0, 6.0, 0.0),
|
||||
useSafeArea: false,
|
||||
),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
child: GNav(
|
||||
gap: 8.0,
|
||||
iconSize: 24.0,
|
||||
padding: EdgeInsets.fromLTRB(18.0, 10.0, 12.0, 10.0),
|
||||
duration: Duration(milliseconds: LunaUI.ANIMATION_SPEED),
|
||||
tabBackgroundColor: Theme.of(context).canvasColor,
|
||||
activeColor: LunaColours.accent,
|
||||
tabs: List.generate(widget.icons.length, (index) => GButton(
|
||||
icon: widget.icons[index],
|
||||
text: widget.titles[index],
|
||||
iconSize: 22.0,
|
||||
haptic: true,
|
||||
iconColor: Colors.white,
|
||||
textStyle: TextStyle(
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: LunaUI.FONT_SIZE_NAVIGATION_BAR,
|
||||
color: LunaColours.accent,
|
||||
),
|
||||
leading: widget.leadingOnTab == null ? null : widget.leadingOnTab[index],
|
||||
)).toList(),
|
||||
tabActiveBorder: LunaUI.shouldUseBorder ? Border.all(color: Colors.white12) : null,
|
||||
tabBorder: LunaUI.shouldUseBorder ? Border.all(color: Colors.transparent) : null,
|
||||
selectedIndex: _index,
|
||||
onTabChange: (index) {
|
||||
_onTabChange(index);
|
||||
if(widget.onTabChange != null) widget.onTabChange(index);
|
||||
},
|
||||
),
|
||||
padding: EdgeInsets.all(12.0),
|
||||
),
|
||||
top: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
|
||||
);
|
||||
}
|
||||
|
||||
void _onTabChange(int index) {
|
||||
if(index == _index) {
|
||||
if(widget.scrollControllers != null && widget.scrollControllers[index] != null) widget.scrollControllers[index].lunaAnimateToStart();
|
||||
} else {
|
||||
widget.pageController.lunaJumpToPage(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export 'bottom_navigation_bar/badge.dart';
|
||||
export 'bottom_navigation_bar/bottom_navigation_bar.dart';
|
||||
@@ -1,99 +0,0 @@
|
||||
import 'package:google_nav_bar/google_nav_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaBottomNavigationBar extends StatefulWidget {
|
||||
final PageController pageController;
|
||||
final List<IconData> icons;
|
||||
final List<String> titles;
|
||||
final List<ScrollController> scrollControllers;
|
||||
final Function(int) onTabChange;
|
||||
final List<Widget> leadingOnTab;
|
||||
|
||||
LunaBottomNavigationBar({
|
||||
Key key,
|
||||
@required this.pageController,
|
||||
@required this.icons,
|
||||
@required this.titles,
|
||||
this.onTabChange,
|
||||
this.leadingOnTab,
|
||||
this.scrollControllers,
|
||||
}) : super(key: key) {
|
||||
assert(icons.length == titles.length, 'An unequal amount of titles and icons were passed to LunaNavigationBar.');
|
||||
if(leadingOnTab != null) assert(icons.length == leadingOnTab.length, 'An unequal amount of icons and leadingOnTab were passed to LunaNavigationBar.');
|
||||
if(scrollControllers != null) assert(icons.length == scrollControllers.length, 'An unequal amount of icons and scrollControllers were passed to LunaNavigationBar.');
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<LunaBottomNavigationBar> {
|
||||
int _index;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_index = widget.pageController?.initialPage ?? 0;
|
||||
widget.pageController?.addListener(_pageControllerListener);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.pageController?.removeListener(_pageControllerListener);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _pageControllerListener() {
|
||||
if((widget.pageController?.page?.round() ?? _index) == _index) return;
|
||||
setState(() => _index = widget.pageController.page.round());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
child: GNav(
|
||||
gap: 8.0,
|
||||
iconSize: 24.0,
|
||||
padding: EdgeInsets.fromLTRB(18.0, 10.0, 12.0, 10.0),
|
||||
duration: Duration(milliseconds: LunaUI.ANIMATION_SPEED),
|
||||
tabBackgroundColor: Theme.of(context).canvasColor,
|
||||
activeColor: LunaColours.accent,
|
||||
tabs: List.generate(widget.icons.length, (index) => GButton(
|
||||
icon: widget.icons[index],
|
||||
text: widget.titles[index],
|
||||
iconSize: 22.0,
|
||||
haptic: true,
|
||||
iconColor: Colors.white,
|
||||
textStyle: TextStyle(
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: LunaUI.FONT_SIZE_NAVIGATION_BAR,
|
||||
color: LunaColours.accent,
|
||||
),
|
||||
leading: widget.leadingOnTab == null ? null : widget.leadingOnTab[index],
|
||||
)).toList(),
|
||||
tabActiveBorder: LunaUI.shouldUseBorder ? Border.all(color: Colors.white12) : null,
|
||||
selectedIndex: _index,
|
||||
onTabChange: (index) {
|
||||
_onTabChange(index);
|
||||
if(widget.onTabChange != null) widget.onTabChange(index);
|
||||
},
|
||||
),
|
||||
padding: EdgeInsets.all(12.0),
|
||||
),
|
||||
top: false,
|
||||
),
|
||||
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
|
||||
);
|
||||
}
|
||||
|
||||
void _onTabChange(int index) {
|
||||
if(index == _index) {
|
||||
if(widget.scrollControllers != null && widget.scrollControllers[index] != null) widget.scrollControllers[index].lunaAnimateToStart();
|
||||
} else {
|
||||
widget.pageController.lunaJumpToPage(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,146 +2,223 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
enum _BUTTON_SIZE {
|
||||
REGULAR,
|
||||
SLIM,
|
||||
enum LunaButtonType {
|
||||
TEXT,
|
||||
ICON,
|
||||
LOADER,
|
||||
}
|
||||
|
||||
class LunaButton extends StatelessWidget {
|
||||
final Color textColor;
|
||||
final Color backgroundColor;
|
||||
final EdgeInsets margin;
|
||||
final LunaLoadingState loadingState;
|
||||
final Function onTap;
|
||||
final Function onLongPress;
|
||||
final String text;
|
||||
final Widget child;
|
||||
final _BUTTON_SIZE size;
|
||||
|
||||
/// A Luna-styled button.
|
||||
class LunaButton extends Card {
|
||||
LunaButton._({
|
||||
Key key,
|
||||
@required this.onTap,
|
||||
@required this.onLongPress,
|
||||
@required this.size,
|
||||
@required this.text,
|
||||
@required this.child,
|
||||
@required this.textColor,
|
||||
@required this.backgroundColor,
|
||||
@required this.margin,
|
||||
@required this.loadingState,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Create a standard-sized button.
|
||||
factory LunaButton({
|
||||
Key key,
|
||||
@required Function onTap,
|
||||
Function onLongPress,
|
||||
String text = '',
|
||||
Widget child,
|
||||
Color textColor = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
@required Widget child,
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
LunaLoadingState loadingState = LunaLoadingState.INACTIVE,
|
||||
}) {
|
||||
return LunaButton._(
|
||||
key: key,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
text: text,
|
||||
child: child,
|
||||
textColor: textColor,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
loadingState: loadingState,
|
||||
size: _BUTTON_SIZE.REGULAR,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a slim-sized button, typically used within table blocks.
|
||||
factory LunaButton.slim({
|
||||
Key key,
|
||||
@required Function onTap,
|
||||
Function onLongPress,
|
||||
String text = '',
|
||||
Widget child,
|
||||
Color textColor = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
LunaLoadingState loadingState = LunaLoadingState.INACTIVE,
|
||||
}) {
|
||||
return LunaButton._(
|
||||
key: key,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
text: text,
|
||||
child: child,
|
||||
textColor: textColor,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
loadingState: loadingState,
|
||||
size: _BUTTON_SIZE.SLIM,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Card(
|
||||
child: InkWell(
|
||||
child: size == _BUTTON_SIZE.REGULAR ? _regular() : _slim(),
|
||||
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
|
||||
onTap: () async {
|
||||
HapticFeedback.lightImpact();
|
||||
if(onTap != null && loadingState != LunaLoadingState.ACTIVE) onTap();
|
||||
},
|
||||
onLongPress: () async {
|
||||
HapticFeedback.heavyImpact();
|
||||
if(onLongPress != null && loadingState != LunaLoadingState.ACTIVE) onLongPress();
|
||||
},
|
||||
),
|
||||
margin: margin,
|
||||
color: backgroundColor,
|
||||
elevation: LunaUI.ELEVATION,
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _regular() {
|
||||
return ListTile(title: _title());
|
||||
}
|
||||
|
||||
Widget _slim() {
|
||||
return Padding(
|
||||
child: _title(),
|
||||
padding: EdgeInsets.symmetric(vertical: 14.0),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _title() {
|
||||
if(loadingState == LunaLoadingState.ACTIVE) return LunaLoader(
|
||||
useSafeArea: false,
|
||||
color: textColor,
|
||||
size: size == _BUTTON_SIZE.REGULAR ? 20.0 : 17.0,
|
||||
);
|
||||
if(loadingState == LunaLoadingState.ERROR) return Icon(
|
||||
Icons.error,
|
||||
color: textColor,
|
||||
size: size == _BUTTON_SIZE.REGULAR ? 20.0 : 17.0,
|
||||
);
|
||||
if(child != null) return child;
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: LunaUI.FONT_SIZE_BUTTON,
|
||||
double height = 50.0,
|
||||
Alignment alignment = Alignment.center,
|
||||
Decoration decoration,
|
||||
Function onTap,
|
||||
Function onLongPress,
|
||||
LunaLoadingState loadingState,
|
||||
}) : super(
|
||||
key: key,
|
||||
child: InkWell(
|
||||
child: Container(
|
||||
child: child,
|
||||
decoration: decoration,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
|
||||
onTap: () async {
|
||||
HapticFeedback.lightImpact();
|
||||
if(onTap != null && loadingState != LunaLoadingState.ACTIVE) onTap();
|
||||
},
|
||||
onLongPress: () async {
|
||||
HapticFeedback.heavyImpact();
|
||||
if( onLongPress != null && loadingState != LunaLoadingState.ACTIVE) onLongPress();
|
||||
},
|
||||
),
|
||||
margin: margin,
|
||||
color: backgroundColor,
|
||||
shape: LunaUI.shapeBorder,
|
||||
elevation: LunaUI.ELEVATION,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
) {
|
||||
assert(child != null);
|
||||
}
|
||||
|
||||
/// Create a default button.
|
||||
///
|
||||
/// If [LunaLoadingState] is passed in, will build the correct button based on the type.
|
||||
factory LunaButton({
|
||||
@required LunaButtonType type,
|
||||
Color color = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
String text,
|
||||
IconData icon,
|
||||
LunaLoadingState loadingState,
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
double height = 50.0,
|
||||
Alignment alignment = Alignment.center,
|
||||
Decoration decoration,
|
||||
Function onTap,
|
||||
Function onLongPress,
|
||||
}) {
|
||||
assert(type != null);
|
||||
switch(loadingState) {
|
||||
case LunaLoadingState.ACTIVE: return LunaButton.loader(
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
case LunaLoadingState.ERROR: return LunaButton.icon(
|
||||
icon: Icons.error,
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
default: break;
|
||||
}
|
||||
switch(type) {
|
||||
case LunaButtonType.TEXT:
|
||||
assert(text != null);
|
||||
return LunaButton.text(
|
||||
text: text,
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
case LunaButtonType.ICON:
|
||||
assert(icon != null);
|
||||
return LunaButton.icon(
|
||||
icon: icon,
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
case LunaButtonType.LOADER: return LunaButton.loader(
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
margin: margin,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
}
|
||||
throw Exception("Attempted to create an invalid LunaButton");
|
||||
}
|
||||
|
||||
/// Build a button that contains a centered text string.
|
||||
factory LunaButton.text({
|
||||
@required String text,
|
||||
Color color = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
double height = 50.0,
|
||||
Alignment alignment = Alignment.center,
|
||||
Decoration decoration,
|
||||
Function onTap,
|
||||
Function onLongPress,
|
||||
}) {
|
||||
return LunaButton._(
|
||||
child: Padding(
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: LunaUI.FONT_SIZE_BUTTON,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
),
|
||||
margin: margin,
|
||||
height: height,
|
||||
backgroundColor: backgroundColor,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a button that contains a [LunaLoader].
|
||||
factory LunaButton.loader({
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
Color color = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
double height = 50.0,
|
||||
Alignment alignment = Alignment.center,
|
||||
Decoration decoration,
|
||||
Function onTap,
|
||||
Function onLongPress,
|
||||
}) {
|
||||
return LunaButton._(
|
||||
child: LunaLoader(
|
||||
useSafeArea: false,
|
||||
color: color,
|
||||
size: 16.0,
|
||||
),
|
||||
margin: margin,
|
||||
height: height,
|
||||
backgroundColor: backgroundColor,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
}
|
||||
|
||||
/// Build a button that contains a single, centered [Icon].
|
||||
factory LunaButton.icon({
|
||||
@required IconData icon,
|
||||
Color color = Colors.white,
|
||||
Color backgroundColor = LunaColours.accent,
|
||||
EdgeInsets margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
double height = 50.0,
|
||||
Alignment alignment = Alignment.center,
|
||||
Decoration decoration,
|
||||
Function onTap,
|
||||
Function onLongPress,
|
||||
}) {
|
||||
return LunaButton._(
|
||||
child: Icon(
|
||||
icon,
|
||||
color: color,
|
||||
size: 22.0,
|
||||
),
|
||||
margin: margin,
|
||||
height: height,
|
||||
backgroundColor: backgroundColor,
|
||||
alignment: alignment,
|
||||
decoration: decoration,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export 'deprecated/button.dart';
|
||||
export 'deprecated/card.dart';
|
||||
export 'deprecated/containers.dart';
|
||||
export 'deprecated/description_block.dart';
|
||||
export 'deprecated/dialogs.dart';
|
||||
export 'deprecated/expandable.dart';
|
||||
export 'deprecated/floating_action_button.dart';
|
||||
export 'deprecated/icon.dart';
|
||||
export 'deprecated/input_bar.dart';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export 'button/button.dart';
|
||||
export 'button/button_slim.dart';
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LSButton extends StatelessWidget {
|
||||
final String text;
|
||||
final Function onTap;
|
||||
final Color backgroundColor;
|
||||
final Color textColor;
|
||||
final bool reducedMargin;
|
||||
final LunaLoadingState loadingState;
|
||||
|
||||
LSButton({
|
||||
@required this.text,
|
||||
@required this.onTap,
|
||||
this.backgroundColor = const Color(LunaColours.ACCENT_COLOR),
|
||||
this.textColor = Colors.white,
|
||||
this.reducedMargin = false,
|
||||
this.loadingState = LunaLoadingState.INACTIVE,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Card(
|
||||
child: InkWell(
|
||||
child: ListTile(
|
||||
title: loadingState == LunaLoadingState.ACTIVE
|
||||
? LSLoader(
|
||||
color: Colors.white,
|
||||
size: 20.0,
|
||||
)
|
||||
: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: Constants.UI_FONT_SIZE_STICKYHEADER,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: loadingState == LunaLoadingState.ACTIVE ? null : onTap == null ? null : () async {
|
||||
HapticFeedback.lightImpact();
|
||||
onTap();
|
||||
},
|
||||
),
|
||||
color: backgroundColor,
|
||||
margin: reducedMargin
|
||||
? EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0)
|
||||
: LunaUI.MARGIN_CARD,
|
||||
elevation: LunaUI.ELEVATION,
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LSButtonSlim extends StatelessWidget {
|
||||
final String text;
|
||||
final Function onTap;
|
||||
final Color backgroundColor;
|
||||
final Color textColor;
|
||||
final EdgeInsets margin;
|
||||
final Widget widget;
|
||||
|
||||
LSButtonSlim({
|
||||
@required this.text,
|
||||
@required this.onTap,
|
||||
this.widget,
|
||||
this.backgroundColor = const Color(LunaColours.ACCENT_COLOR),
|
||||
this.textColor = Colors.white,
|
||||
this.margin = const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Card(
|
||||
child: InkWell(
|
||||
child: Padding(
|
||||
child: widget != null ? widget :Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
fontSize: Constants.UI_FONT_SIZE_STICKYHEADER,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 14.0),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: onTap == null ? null : () async {
|
||||
HapticFeedback.lightImpact();
|
||||
onTap();
|
||||
}
|
||||
),
|
||||
color: backgroundColor,
|
||||
margin: margin,
|
||||
elevation: LunaUI.ELEVATION,
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LSExpandable extends StatefulWidget {
|
||||
final ExpandableController controller;
|
||||
final Widget collapsed;
|
||||
final Widget expanded;
|
||||
|
||||
LSExpandable({
|
||||
@required this.controller,
|
||||
@required this.collapsed,
|
||||
@required this.expanded,
|
||||
Key key,
|
||||
}): super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<LSExpandable> {
|
||||
@override
|
||||
Widget build(BuildContext context) => ExpandableNotifier(
|
||||
controller: widget.controller,
|
||||
child: Expandable(
|
||||
collapsed: widget.collapsed,
|
||||
expanded: widget.expanded,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -31,9 +31,13 @@ class LSGenericMessage extends StatelessWidget {
|
||||
elevation: LunaUI.ELEVATION,
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
if(showButton) LSButton(
|
||||
text: buttonText,
|
||||
onTap: onTapHandler,
|
||||
if(showButton) LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(
|
||||
text: buttonText,
|
||||
onTap: onTapHandler,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -59,9 +59,13 @@ class _State extends State<LSTypewriterMessage> with TickerProviderStateMixin {
|
||||
elevation: LunaUI.ELEVATION,
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
if(widget.showButton) LSButton(
|
||||
text: widget.buttonText,
|
||||
onTap: widget.onTapHandler,
|
||||
if(widget.showButton) LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(
|
||||
text: widget.buttonText,
|
||||
onTap: widget.onTapHandler,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class LunaListView extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final double itemExtent;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final ScrollPhysics physics;
|
||||
final ScrollController controller;
|
||||
@@ -10,6 +11,7 @@ class LunaListView extends StatelessWidget {
|
||||
Key key,
|
||||
@required this.children,
|
||||
@required this.controller,
|
||||
this.itemExtent,
|
||||
this.padding,
|
||||
this.physics = const AlwaysScrollableScrollPhysics(),
|
||||
}) : super(key: key);
|
||||
@@ -26,6 +28,7 @@ class LunaListView extends StatelessWidget {
|
||||
child: ListView(
|
||||
controller: controller,
|
||||
children: children,
|
||||
itemExtent: itemExtent,
|
||||
padding: padding != null ? padding : EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 8.0+(MediaQuery.of(context).padding.bottom),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
class LunaListViewBuilder extends StatelessWidget {
|
||||
final int itemCount;
|
||||
final Widget Function(BuildContext, int) itemBuilder;
|
||||
final double itemExtent;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final ScrollPhysics physics;
|
||||
final ScrollController controller;
|
||||
@@ -11,6 +12,7 @@ class LunaListViewBuilder extends StatelessWidget {
|
||||
Key key,
|
||||
@required this.itemCount,
|
||||
@required this.itemBuilder,
|
||||
this.itemExtent,
|
||||
@required this.controller,
|
||||
this.padding,
|
||||
this.physics = const AlwaysScrollableScrollPhysics(),
|
||||
@@ -37,6 +39,7 @@ class LunaListViewBuilder extends StatelessWidget {
|
||||
physics: physics,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: itemBuilder,
|
||||
itemExtent: itemExtent,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,10 +3,12 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class LunaListViewModal extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final double itemExtent;
|
||||
|
||||
LunaListViewModal({
|
||||
Key key,
|
||||
@required this.children,
|
||||
this.itemExtent,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -16,6 +18,7 @@ class LunaListViewModal extends StatelessWidget {
|
||||
child: ListView(
|
||||
controller: ModalScrollController.of(context),
|
||||
children: children,
|
||||
itemExtent: itemExtent,
|
||||
padding: EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 8.0+(MediaQuery.of(context).padding.bottom),
|
||||
|
||||
@@ -7,6 +7,7 @@ class LunaPagedListView<T> extends StatefulWidget {
|
||||
final ScrollController scrollController;
|
||||
final void Function(int) listener;
|
||||
final Widget Function(BuildContext, T, int) itemBuilder;
|
||||
final double itemExtent;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final String noItemsFoundMessage;
|
||||
|
||||
@@ -18,6 +19,7 @@ class LunaPagedListView<T> extends StatefulWidget {
|
||||
@required this.itemBuilder,
|
||||
@required this.noItemsFoundMessage,
|
||||
@required this.scrollController,
|
||||
this.itemExtent,
|
||||
this.padding,
|
||||
}) : super(key: key) {
|
||||
assert(refreshKey != null);
|
||||
@@ -54,6 +56,7 @@ class _State<T> extends State<LunaPagedListView<T>> {
|
||||
child: PagedListView<int, T>(
|
||||
pagingController: widget.pagingController,
|
||||
scrollController: widget.scrollController,
|
||||
itemExtent: widget.itemExtent,
|
||||
builderDelegate: PagedChildBuilderDelegate<T>(
|
||||
itemBuilder: widget.itemBuilder,
|
||||
firstPageErrorIndicatorBuilder: (context) => LunaMessage.error(onTap: () => Future.sync(() => widget.pagingController.refresh())),
|
||||
|
||||
@@ -120,7 +120,11 @@ class LunaMessage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if(buttonText != null) LSButton(text: buttonText, onTap: onTap),
|
||||
if(buttonText != null) LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(text: buttonText, onTap: onTap),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -26,10 +26,7 @@ class LunaTableContent extends StatelessWidget {
|
||||
this.titleColor = Colors.white70,
|
||||
this.bodyColor = Colors.white,
|
||||
this.padding = const EdgeInsets.symmetric(vertical: 4.0),
|
||||
}) : super(key: key) {
|
||||
assert(title != null);
|
||||
assert(body != null);
|
||||
}
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -49,7 +46,7 @@ class LunaTableContent extends StatelessWidget {
|
||||
Widget _title() {
|
||||
return Expanded(
|
||||
child: Text(
|
||||
title.toUpperCase(),
|
||||
title?.toUpperCase() ?? LunaUI.TEXT_EMDASH,
|
||||
textAlign: titleAlign,
|
||||
style: TextStyle(
|
||||
color: titleColor,
|
||||
@@ -64,7 +61,7 @@ class LunaTableContent extends StatelessWidget {
|
||||
return Expanded(
|
||||
child: InkWell(
|
||||
child: Text(
|
||||
body,
|
||||
body ?? LunaUI.TEXT_EMDASH,
|
||||
textAlign: bodyAlign,
|
||||
style: TextStyle(
|
||||
color: bodyColor,
|
||||
|
||||
@@ -62,7 +62,7 @@ class LunaChangelog {
|
||||
),
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(text: 'Full Changelog', onTap: () async => Constants.URL_CHANGELOG.lunaOpenGenericLink()),
|
||||
LunaButton.text(text: 'Full Changelog', onTap: () async => Constants.URL_CHANGELOG.lunaOpenGenericLink()),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -35,7 +35,6 @@ class LunaEncryption {
|
||||
final _encrypter = Encrypter(AES(key));
|
||||
return _encrypter.decrypt64(data, iv: iv);
|
||||
} 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 ENCRYPTION_FAILURE;
|
||||
|
||||
@@ -6,12 +6,15 @@ class LunaExpandableListTile extends StatefulWidget {
|
||||
final String title;
|
||||
final TextSpan collapsedSubtitle1;
|
||||
final TextSpan collapsedSubtitle2;
|
||||
final LunaIconButton collapsedTrailing;
|
||||
final Widget collapsedTrailing;
|
||||
final Widget collapsedLeading;
|
||||
final Color backgroundColor;
|
||||
final Function onLongPress;
|
||||
final List<LunaHighlightedNode> expandedHighlightedNodes;
|
||||
final List<LunaTableContent> expandedTableContent;
|
||||
final List<LunaButton> expandedTableButtons;
|
||||
final Widget expandedCustomWidget;
|
||||
final bool initialExpanded;
|
||||
|
||||
/// Create a [LunaExpandableListTile] which is a list tile that expands into a table-style card.
|
||||
///
|
||||
@@ -23,11 +26,14 @@ class LunaExpandableListTile extends StatefulWidget {
|
||||
@required this.collapsedSubtitle1,
|
||||
this.collapsedSubtitle2,
|
||||
this.collapsedTrailing,
|
||||
this.collapsedLeading,
|
||||
this.onLongPress,
|
||||
this.expandedHighlightedNodes,
|
||||
this.expandedTableContent,
|
||||
this.expandedTableButtons,
|
||||
this.expandedCustomWidget,
|
||||
this.backgroundColor,
|
||||
this.initialExpanded = false,
|
||||
}) : super(key: key) {
|
||||
assert(title != null);
|
||||
assert(collapsedSubtitle1 != null);
|
||||
@@ -41,7 +47,13 @@ class LunaExpandableListTile extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _State extends State<LunaExpandableListTile> {
|
||||
final ExpandableController controller = ExpandableController();
|
||||
ExpandableController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = ExpandableController(initialExpanded: widget.initialExpanded);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -84,6 +96,8 @@ class _State extends State<LunaExpandableListTile> {
|
||||
onLongPress: widget.onLongPress,
|
||||
contentPadding: widget.collapsedSubtitle2 != null,
|
||||
trailing: widget.collapsedTrailing,
|
||||
leading: widget.collapsedLeading,
|
||||
color: widget.backgroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -148,6 +162,7 @@ class _State extends State<LunaExpandableListTile> {
|
||||
onTap: controller.toggle,
|
||||
onLongPress: widget.onLongPress,
|
||||
),
|
||||
color: widget.backgroundColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,8 @@ class LunaFiveLineCardWithPoster extends StatelessWidget {
|
||||
final Color titleColor;
|
||||
final Map posterHeaders;
|
||||
final LunaIconButton trailing;
|
||||
final double _height = 105.0;
|
||||
final double _width = 70.0;
|
||||
final double _padding = 8.0;
|
||||
static final double itemExtent = 105.0;
|
||||
static final double _padding = 8.0;
|
||||
|
||||
LunaFiveLineCardWithPoster({
|
||||
Key key,
|
||||
@@ -81,8 +80,8 @@ class LunaFiveLineCardWithPoster extends StatelessWidget {
|
||||
return LunaNetworkImage(
|
||||
url: posterUrl,
|
||||
placeholderAsset: posterPlaceholder,
|
||||
height: _height,
|
||||
width: _width,
|
||||
height: itemExtent,
|
||||
width: itemExtent/1.5,
|
||||
headers: posterHeaders,
|
||||
);
|
||||
}
|
||||
@@ -107,7 +106,7 @@ class LunaFiveLineCardWithPoster extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
),
|
||||
height: (_height-(_padding*2)),
|
||||
height: (itemExtent-(_padding*2)),
|
||||
),
|
||||
padding: EdgeInsets.all(_padding),
|
||||
),
|
||||
@@ -144,7 +143,7 @@ class LunaFiveLineCardWithPoster extends StatelessWidget {
|
||||
child: trailing,
|
||||
padding: EdgeInsets.only(right: 12.0),
|
||||
),
|
||||
height: _height,
|
||||
height: itemExtent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,8 @@ class LunaFourLineCardWithPoster extends StatelessWidget {
|
||||
final Color titleColor;
|
||||
final Map posterHeaders;
|
||||
final LunaIconButton trailing;
|
||||
final double _height = 90.0;
|
||||
final double _width = 60.0;
|
||||
final double _padding = 8.0;
|
||||
static final double itemExtent = 90.0;
|
||||
static final double _padding = 8.0;
|
||||
|
||||
LunaFourLineCardWithPoster({
|
||||
Key key,
|
||||
@@ -68,6 +67,7 @@ class LunaFourLineCardWithPoster extends StatelessWidget {
|
||||
uri: backgroundUrl,
|
||||
headers: posterHeaders,
|
||||
),
|
||||
height: itemExtent,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ class LunaFourLineCardWithPoster extends StatelessWidget {
|
||||
return LunaNetworkImage(
|
||||
url: posterUrl,
|
||||
placeholderAsset: posterPlaceholder,
|
||||
height: _height,
|
||||
width: _width,
|
||||
height: itemExtent,
|
||||
width: itemExtent/1.5,
|
||||
headers: posterHeaders,
|
||||
);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class LunaFourLineCardWithPoster extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
),
|
||||
height: (_height-(_padding*2)),
|
||||
height: (itemExtent-(_padding*2)),
|
||||
),
|
||||
padding: EdgeInsets.all(_padding),
|
||||
),
|
||||
@@ -136,7 +136,7 @@ class LunaFourLineCardWithPoster extends StatelessWidget {
|
||||
child: trailing,
|
||||
padding: EdgeInsets.only(right: 12.0),
|
||||
),
|
||||
height: _height,
|
||||
height: itemExtent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,8 @@ class LunaTwoLineCardWithPoster extends StatelessWidget {
|
||||
final Color titleColor;
|
||||
final Map posterHeaders;
|
||||
final LunaIconButton trailing;
|
||||
final double _height = 67.0;
|
||||
final double _width = 67.0;
|
||||
final double _padding = 8.0;
|
||||
static final double itemExtent = 67.0;
|
||||
static final double _padding = 8.0;
|
||||
|
||||
LunaTwoLineCardWithPoster({
|
||||
Key key,
|
||||
@@ -65,8 +64,8 @@ class LunaTwoLineCardWithPoster extends StatelessWidget {
|
||||
return LunaNetworkImage(
|
||||
url: posterUrl,
|
||||
placeholderAsset: posterPlaceholder,
|
||||
height: _height,
|
||||
width: _width,
|
||||
height: itemExtent,
|
||||
width: itemExtent,
|
||||
headers: posterHeaders,
|
||||
);
|
||||
}
|
||||
@@ -87,7 +86,7 @@ class LunaTwoLineCardWithPoster extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
),
|
||||
height: (_height-(_padding*2)),
|
||||
height: (itemExtent-(_padding*2)),
|
||||
),
|
||||
padding: EdgeInsets.all(_padding),
|
||||
),
|
||||
@@ -124,7 +123,7 @@ class LunaTwoLineCardWithPoster extends StatelessWidget {
|
||||
child: trailing,
|
||||
padding: EdgeInsets.only(right: 12.0),
|
||||
),
|
||||
height: _height,
|
||||
height: itemExtent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:lunasea/core.dart';
|
||||
///
|
||||
/// Runs app in guarded zone to attempt to capture fatal (crashing) errors
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await _init();
|
||||
runZonedGuarded(
|
||||
() => runApp(
|
||||
@@ -35,10 +34,8 @@ Future<void> main() async {
|
||||
/// - 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 overlay style (navbar, statusbar)
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.black,
|
||||
|
||||
@@ -218,22 +218,16 @@ class _State extends State<LidarrAddDetails> {
|
||||
);
|
||||
},
|
||||
),
|
||||
LSContainerRow(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(
|
||||
text: 'Add',
|
||||
onTap: () async => _addArtist(false),
|
||||
reducedMargin: true,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
LunaButton.text(
|
||||
text: 'Add + Search',
|
||||
backgroundColor: LunaColours.orange,
|
||||
onTap: () async => _addArtist(true),
|
||||
reducedMargin: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -144,9 +144,13 @@ class _State extends State<LidarrEditArtist> {
|
||||
trailing: LSIconButton(icon: Icons.arrow_forward_ios_rounded),
|
||||
onTap: () => _changeMetadata(),
|
||||
),
|
||||
LSButton(
|
||||
text: 'Update Artist',
|
||||
onTap: () async => _save().catchError((_) {}),
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(
|
||||
text: 'Update Artist',
|
||||
onTap: () async => _save().catchError((_) {}),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -110,7 +110,7 @@ class _State extends State<LidarrSearchResults> {
|
||||
? [LSGenericMessage(text: 'No Results Found')]
|
||||
: List.generate(
|
||||
filtered.length,
|
||||
(index) => LidarrSearchResultTile(data: filtered[index]),
|
||||
(index) => LidarrReleasesTile(release: filtered[index]),
|
||||
);
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
|
||||
@@ -1,185 +1,156 @@
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/lidarr.dart';
|
||||
import 'package:lunasea/modules/dashboard.dart';
|
||||
|
||||
class LidarrSearchResultTile extends StatelessWidget {
|
||||
final LidarrReleaseData data;
|
||||
final ExpandableController _controller = ExpandableController();
|
||||
class LidarrReleasesTile extends StatefulWidget {
|
||||
final LidarrReleaseData release;
|
||||
|
||||
LidarrSearchResultTile({
|
||||
LidarrReleasesTile({
|
||||
Key key,
|
||||
@required this.data,
|
||||
@required this.release,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LSExpandable(
|
||||
controller: _controller,
|
||||
collapsed: _collapsed(context),
|
||||
expanded: _expanded(context),
|
||||
);
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<LidarrReleasesTile> {
|
||||
LunaLoadingState _downloadState = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaExpandableListTile(
|
||||
title: widget.release.title,
|
||||
collapsedSubtitle1: _subtitle1(),
|
||||
collapsedSubtitle2: _subtitle2(),
|
||||
collapsedTrailing: _trailing(),
|
||||
expandedHighlightedNodes: _highlightedNodes(),
|
||||
expandedTableContent: _tableContent(),
|
||||
expandedTableButtons: _tableButtons(),
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle1() {
|
||||
return TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
color: lunaProtocolColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
text: widget.release.protocol.lunaCapitalizeFirstLetters(),
|
||||
),
|
||||
if(widget.release.isTorrent) TextSpan(
|
||||
text: ' (${widget.release.seeders}/${widget.release.leechers})',
|
||||
style: TextStyle(
|
||||
color: lunaProtocolColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.release.indexer ?? LunaUI.TEXT_EMDASH),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.release.ageHours?.lunaHoursToAge() ?? LunaUI.TEXT_EMDASH),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle2() {
|
||||
return TextSpan(
|
||||
children: [
|
||||
TextSpan(text: widget.release.quality ?? LunaUI.TEXT_EMDASH),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.release.size?.lunaBytesToString() ?? LunaUI.TEXT_EMDASH),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _trailing() {
|
||||
return LunaIconButton(
|
||||
icon: widget.release.approved
|
||||
? Icons.file_download
|
||||
: Icons.report_rounded,
|
||||
color: widget.release.approved
|
||||
? Colors.white
|
||||
: LunaColours.red,
|
||||
onPressed: () async => widget.release.approved ? _startDownload() : _showWarnings(),
|
||||
onLongPress: _startDownload,
|
||||
loadingState: _downloadState,
|
||||
);
|
||||
}
|
||||
|
||||
List<LunaHighlightedNode> _highlightedNodes() {
|
||||
return [
|
||||
LunaHighlightedNode(
|
||||
text: widget.release.protocol.lunaCapitalizeFirstLetters(),
|
||||
backgroundColor: lunaProtocolColor,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<LunaTableContent> _tableContent() {
|
||||
return [
|
||||
LunaTableContent(title: 'source', body: widget.release.protocol.lunaCapitalizeFirstLetters()),
|
||||
LunaTableContent(title: 'age', body: widget.release.ageHours?.lunaHoursToAge() ?? LunaUI.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'indexer', body: widget.release.indexer ?? LunaUI.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'size', body: widget.release.size?.lunaBytesToString() ?? LunaUI.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'quality', body: widget.release.quality ?? LunaUI.TEXT_EMDASH),
|
||||
if(widget.release.protocol == 'torrent' && widget.release.seeders != null) LunaTableContent(title: 'seeders', body: '${widget.release.seeders}'),
|
||||
if(widget.release.protocol == 'torrent' && widget.release.leechers != null) LunaTableContent(title: 'leechers', body: '${widget.release.leechers}'),
|
||||
];
|
||||
}
|
||||
|
||||
Color get lunaProtocolColor {
|
||||
if(!data.isTorrent) return LunaColours.accent;
|
||||
int seeders = data.seeders ?? 0;
|
||||
if(!widget.release.isTorrent) return LunaColours.accent;
|
||||
int seeders = widget.release.seeders ?? 0;
|
||||
if(seeders > 10) return LunaColours.blue;
|
||||
if(seeders > 0) return LunaColours.orange;
|
||||
return LunaColours.red;
|
||||
}
|
||||
|
||||
Widget _expanded(BuildContext context) => LSCard(
|
||||
child: InkWell(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
LSTitle(text: data.title, softWrap: true, maxLines: 12),
|
||||
Padding(
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
LSTextHighlighted(
|
||||
text: data.protocol.lunaCapitalizeFirstLetters(),
|
||||
bgColor: lunaProtocolColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(top: 8.0, bottom: 2.0),
|
||||
),
|
||||
Padding(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
if(data.isTorrent) TextSpan(
|
||||
text: '${data.seeders} Seeders\t•\t${data.leechers} Leechers\n',
|
||||
style: TextStyle(
|
||||
color: lunaProtocolColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
),
|
||||
TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'),
|
||||
TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}\t•\t'),
|
||||
TextSpan(text: '${data.indexer}\n'),
|
||||
TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 6.0, bottom: 10.0),
|
||||
),
|
||||
Padding(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: LSButtonSlim(
|
||||
text: 'Download',
|
||||
onTap: () => _startDownload(context),
|
||||
margin: data.approved
|
||||
? EdgeInsets.zero
|
||||
: EdgeInsets.only(right: 6.0),
|
||||
),
|
||||
),
|
||||
if(!data.approved) Expanded(
|
||||
child: LSButtonSlim(
|
||||
text: 'Rejected',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () => _showWarnings(context),
|
||||
margin: EdgeInsets.only(left: 6.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(bottom: 2.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
List<LunaButton> _tableButtons() {
|
||||
return [
|
||||
LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: 'Download',
|
||||
onTap: _startDownload,
|
||||
loadingState: _downloadState,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: () => _controller.toggle(),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _collapsed(BuildContext context) => LSCardTile(
|
||||
title: LSTitle(text: data.title),
|
||||
subtitle: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
color: lunaProtocolColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
text: data.protocol.lunaCapitalizeFirstLetters(),
|
||||
),
|
||||
if(data.isTorrent) TextSpan(
|
||||
text: ' (${data.seeders}/${data.leechers})',
|
||||
style: TextStyle(
|
||||
color: lunaProtocolColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
),
|
||||
TextSpan(text: '\t•\t${data.indexer}\t•\t'),
|
||||
TextSpan(text: '${data.ageHours.lunaHoursToAge() ?? 'Unknown'}\n'),
|
||||
TextSpan(text: '${data.quality ?? 'Unknown'}\t•\t'),
|
||||
TextSpan(text: '${data.size.lunaBytesToString() ?? 'Unknown'}'),
|
||||
],
|
||||
if(!widget.release.approved) LunaButton.text(
|
||||
text: 'Rejected',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: _showWarnings,
|
||||
),
|
||||
),
|
||||
trailing: LSIconButton(
|
||||
icon: data.approved
|
||||
? Icons.file_download
|
||||
: Icons.report_rounded,
|
||||
color: data.approved
|
||||
? Colors.white
|
||||
: LunaColours.red,
|
||||
onPressed: () async => data.approved
|
||||
? _startDownload(context)
|
||||
: _showWarnings(context),
|
||||
),
|
||||
padContent: true,
|
||||
onTap: () => _controller.toggle(),
|
||||
);
|
||||
|
||||
Future<void> _startDownload(BuildContext context) async {
|
||||
LidarrAPI _api = LidarrAPI.from(Database.currentProfileObject);
|
||||
await _api.downloadRelease(data.guid, data.indexerId)
|
||||
.then((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Downloading...',
|
||||
message: data.title,
|
||||
type: SNACKBAR_TYPE.success,
|
||||
showButton: true,
|
||||
buttonText: 'Back',
|
||||
buttonOnPressed: () => Navigator.of(context).popUntil((Route<dynamic> route) {
|
||||
return !route.willHandlePopInternally
|
||||
&& route is ModalRoute
|
||||
&& (route.settings.name == Lidarr.ROUTE_NAME || route.settings.name == Dashboard.ROUTE_NAME);
|
||||
}),
|
||||
))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Start Downloading',
|
||||
message: LunaLogger.checkLogsMessage,
|
||||
type: SNACKBAR_TYPE.failure,
|
||||
));
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _showWarnings(BuildContext context) async => await LunaDialogs().showRejections(context, data.rejections?.cast<String>() ?? []);
|
||||
Future<void> _startDownload() async {
|
||||
setState(() => _downloadState = LunaLoadingState.ACTIVE);
|
||||
LidarrAPI _api = LidarrAPI.from(Database.currentProfileObject);
|
||||
await _api.downloadRelease(widget.release.guid, widget.release.indexerId)
|
||||
.then((_) {
|
||||
showLunaSuccessSnackBar(
|
||||
title: 'Downloading...',
|
||||
message: widget.release.title,
|
||||
showButton: true,
|
||||
buttonText: 'Back',
|
||||
buttonOnPressed: () => Navigator.of(context).popUntil((Route<dynamic> route) {
|
||||
return !route.willHandlePopInternally
|
||||
&& route is ModalRoute
|
||||
&& (route.settings.name == Lidarr.ROUTE_NAME || route.settings.name == Dashboard.ROUTE_NAME);
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catchError((error, stack) {
|
||||
showLunaErrorSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Start Downloading',
|
||||
error: error,
|
||||
);
|
||||
});
|
||||
setState(() => _downloadState = LunaLoadingState.INACTIVE);
|
||||
}
|
||||
|
||||
Future<void> _showWarnings() async => await LunaDialogs().showRejections(context, (widget.release.rejections ?? []).cast<String>());
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/nzbget.dart';
|
||||
|
||||
class NZBGetHistoryTile extends StatelessWidget {
|
||||
class NZBGetHistoryTile extends StatefulWidget {
|
||||
final NZBGetHistoryData data;
|
||||
final ExpandableController _controller = ExpandableController();
|
||||
final Function() refresh;
|
||||
|
||||
NZBGetHistoryTile({
|
||||
@@ -14,151 +12,89 @@ class NZBGetHistoryTile extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LSExpandable(
|
||||
controller: _controller,
|
||||
collapsed: _collapsed(context),
|
||||
expanded: _expanded(context),
|
||||
);
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
Widget _expanded(BuildContext context) => LSCard(
|
||||
child: InkWell(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
LSTitle(
|
||||
text: data.name,
|
||||
softWrap: true,
|
||||
maxLines: 12,
|
||||
),
|
||||
Padding(
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
LSTextHighlighted(
|
||||
text: data.statusString,
|
||||
bgColor: data.statusColor,
|
||||
),
|
||||
LSTextHighlighted(
|
||||
text: data.healthString,
|
||||
bgColor: LunaColours.blueGrey,
|
||||
)
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(top: 8.0, bottom: 2.0),
|
||||
),
|
||||
Padding(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: data.completeTime),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.sizeReadable),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(
|
||||
text: data.category.isEmpty
|
||||
? "No Category"
|
||||
: data.category
|
||||
),
|
||||
TextSpan(text: '\n'),
|
||||
TextSpan(text: 'Average Speed of ${data.downloadSpeed}'),
|
||||
TextSpan(text: '\n\n'),
|
||||
TextSpan(
|
||||
text: data.storageLocation,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 6.0, bottom: 10.0),
|
||||
),
|
||||
Padding(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: LSButtonSlim(
|
||||
text: 'Delete',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () async => _deleteButton(context),
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(bottom: 2.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
class _State extends State<NZBGetHistoryTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaExpandableListTile(
|
||||
title: widget.data.name,
|
||||
collapsedSubtitle1: _subtitle1(),
|
||||
collapsedSubtitle2: _subtitle2(),
|
||||
expandedHighlightedNodes: _expandedHighlightedNodes(),
|
||||
expandedTableContent: _expandedTableContent(),
|
||||
expandedTableButtons: _expandedTableButtons(),
|
||||
onLongPress: () async => _handlePopup(),
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle1() {
|
||||
return TextSpan(
|
||||
children: [
|
||||
TextSpan(text: widget.data.completeTime),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.data.sizeReadable),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: (widget.data.category ?? '').isEmpty ? 'No Category' : widget.data.category),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle2() {
|
||||
return TextSpan(
|
||||
text: widget.data.statusString,
|
||||
style: TextStyle(
|
||||
color: widget.data.statusColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: () => _controller.toggle(),
|
||||
onLongPress: () async => _handlePopup(context),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
Widget _collapsed(BuildContext context) => LSCardTile(
|
||||
title: LSTitle(text: data.name),
|
||||
subtitle: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: data.completeTime),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.sizeReadable),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(
|
||||
text: data.category.isEmpty
|
||||
? "No Category"
|
||||
: data.category
|
||||
),
|
||||
TextSpan(text: '\n'),
|
||||
TextSpan(
|
||||
text: data.statusString,
|
||||
style: TextStyle(
|
||||
color: data.statusColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
),
|
||||
],
|
||||
List<LunaHighlightedNode> _expandedHighlightedNodes() {
|
||||
return [
|
||||
LunaHighlightedNode(
|
||||
text: widget.data.statusString,
|
||||
backgroundColor: widget.data.statusColor,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
),
|
||||
padContent: true,
|
||||
onTap: () => _controller.toggle(),
|
||||
onLongPress: () async => _handlePopup(context),
|
||||
);
|
||||
LunaHighlightedNode(
|
||||
text: widget.data.healthString,
|
||||
backgroundColor: LunaColours.blueGrey,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _handlePopup(BuildContext context) async {
|
||||
List values = await NZBGetDialogs.historySettings(context, data.name);
|
||||
List<LunaTableContent> _expandedTableContent() {
|
||||
return [
|
||||
LunaTableContent(title: 'age', body: widget.data.completeTime),
|
||||
LunaTableContent(title: 'size', body: widget.data.sizeReadable),
|
||||
LunaTableContent(title: 'category', body: (widget.data.category ?? '').isEmpty ? 'No Category' : widget.data.category),
|
||||
LunaTableContent(title: 'speed', body: widget.data.downloadSpeed),
|
||||
LunaTableContent(title: 'path', body: widget.data.storageLocation),
|
||||
];
|
||||
}
|
||||
|
||||
List<LunaButton> _expandedTableButtons() {
|
||||
return [
|
||||
LunaButton.text(
|
||||
text: 'Delete',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () async => _deleteButton(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _handlePopup() async {
|
||||
List values = await NZBGetDialogs.historySettings(context, widget.data.name);
|
||||
if(values[0]) switch(values[1]) {
|
||||
case 'retry': {
|
||||
await NZBGetAPI.from(Database.currentProfileObject).retryHistoryEntry(data.id)
|
||||
await NZBGetAPI.from(Database.currentProfileObject).retryHistoryEntry(widget.data.id)
|
||||
.then((_) {
|
||||
refresh();
|
||||
widget.refresh();
|
||||
LSSnackBar(
|
||||
context: context,
|
||||
title: 'Retrying Job...',
|
||||
message: data.name,
|
||||
message: widget.data.name,
|
||||
);
|
||||
})
|
||||
.catchError((_) => LSSnackBar(
|
||||
@@ -169,8 +105,8 @@ class NZBGetHistoryTile extends StatelessWidget {
|
||||
));
|
||||
break;
|
||||
}
|
||||
case 'hide': await NZBGetAPI.from(Database.currentProfileObject).deleteHistoryEntry(data.id, hide: true)
|
||||
.then((_) => _handleDelete(context, 'History Hidden'))
|
||||
case 'hide': await NZBGetAPI.from(Database.currentProfileObject).deleteHistoryEntry(widget.data.id, hide: true)
|
||||
.then((_) => _handleDelete('History Hidden'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Hide History',
|
||||
@@ -178,8 +114,8 @@ class NZBGetHistoryTile extends StatelessWidget {
|
||||
type: SNACKBAR_TYPE.failure,
|
||||
));
|
||||
break;
|
||||
case 'delete': await NZBGetAPI.from(Database.currentProfileObject).deleteHistoryEntry(data.id, hide: true)
|
||||
.then((_) => _handleDelete(context, 'History Deleted'))
|
||||
case 'delete': await NZBGetAPI.from(Database.currentProfileObject).deleteHistoryEntry(widget.data.id, hide: true)
|
||||
.then((_) => _handleDelete('History Deleted'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Delete History',
|
||||
@@ -189,13 +125,13 @@ class NZBGetHistoryTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteButton(BuildContext context) async {
|
||||
Future<void> _deleteButton() async {
|
||||
List<dynamic> values = await NZBGetDialogs.deleteHistory(context);
|
||||
if(values[0]) await NZBGetAPI.from(Database.currentProfileObject).deleteHistoryEntry(
|
||||
data.id,
|
||||
widget.data.id,
|
||||
hide: values[1],
|
||||
)
|
||||
.then((_) => _handleDelete(context, values[1] ? 'History Hidden' : 'History Deleted'))
|
||||
.then((_) => _handleDelete(values[1] ? 'History Hidden' : 'History Deleted'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Delete History',
|
||||
@@ -204,13 +140,13 @@ class NZBGetHistoryTile extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
void _handleDelete(BuildContext context, String title) {
|
||||
void _handleDelete(String title) {
|
||||
LSSnackBar(
|
||||
context: context,
|
||||
title: title,
|
||||
message: data.name,
|
||||
message: widget.data.name,
|
||||
type: SNACKBAR_TYPE.success,
|
||||
);
|
||||
refresh();
|
||||
widget.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +265,32 @@ class RadarrAPIHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Execute a quick import.
|
||||
Future<bool> quickImport({
|
||||
@required BuildContext context,
|
||||
@required String path,
|
||||
bool showSnackbar = true,
|
||||
}) async {
|
||||
assert(path != null && path.isNotEmpty);
|
||||
if(context.read<RadarrState>().enabled) {
|
||||
return await context.read<RadarrState>().api.command.downloadedMoviesScan(path: path)
|
||||
.then((_) async {
|
||||
if(showSnackbar) showLunaSuccessSnackBar(
|
||||
context: context,
|
||||
title: 'Running Quick Import${LunaUI.TEXT_ELLIPSIS}',
|
||||
message: path,
|
||||
);
|
||||
return true;
|
||||
})
|
||||
.catchError((error, stack) {
|
||||
LunaLogger().error('Failed to execute downloaded movies scan: $path', error, stack);
|
||||
showLunaErrorSnackBar(title: 'Failed to Quick Import', error: error);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Trigger an automatic search of a movie.
|
||||
Future<bool> automaticSearch({
|
||||
@required BuildContext context,
|
||||
|
||||
@@ -301,39 +301,36 @@ class RadarrDialogs {
|
||||
text: 'Close',
|
||||
onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
|
||||
),
|
||||
|
||||
],
|
||||
title: LSDialog.title(text: 'Tags'),
|
||||
content: Selector<RadarrState, Future<List<RadarrTag>>>(
|
||||
selector: (_, state) => state.tags,
|
||||
builder: (context, future, _) => FutureBuilder(
|
||||
future: future,
|
||||
builder: (context, AsyncSnapshot<List<RadarrTag>> snapshot) => LSDialog.content(
|
||||
children: (snapshot.data?.length ?? 0) == 0
|
||||
? [ LSDialog.textContent(text: 'No Tags Found') ]
|
||||
: List.generate(
|
||||
snapshot.data.length,
|
||||
(index) => CheckboxListTile(
|
||||
title: Text(
|
||||
snapshot.data[index].label,
|
||||
style: TextStyle(
|
||||
fontSize: LSDialog.BODY_SIZE,
|
||||
color: Colors.white,
|
||||
),
|
||||
builder: (context, AsyncSnapshot<List<RadarrTag>> snapshot) {
|
||||
if((snapshot.data?.length ?? 0) == 0) return LSDialog.content(
|
||||
children: [
|
||||
LSDialog.textContent(text: 'No Tags Found'),
|
||||
],
|
||||
);
|
||||
return LSDialog.content(
|
||||
children: List.generate(
|
||||
snapshot.data.length,
|
||||
(index) => LSDialog.checkbox(
|
||||
title: snapshot.data[index].label,
|
||||
value: context.watch<RadarrMoviesEditState>().tags.where((tag) => tag.id == snapshot.data[index].id).length != 0,
|
||||
onChanged: (selected) {
|
||||
List<RadarrTag> _tags = context.read<RadarrMoviesEditState>().tags;
|
||||
selected ? _tags.add(snapshot.data[index]) : _tags.removeWhere((tag) => tag.id == snapshot.data[index].id);
|
||||
context.read<RadarrMoviesEditState>().tags = _tags;
|
||||
},
|
||||
),
|
||||
value: context.watch<RadarrMoviesEditState>().tags.where((tag) => tag.id == snapshot.data[index].id).length != 0,
|
||||
onChanged: (selected) {
|
||||
List<RadarrTag> _tags = context.read<RadarrMoviesEditState>().tags;
|
||||
selected ? _tags.add(snapshot.data[index]) : _tags.removeWhere((tag) => tag.id == snapshot.data[index].id);
|
||||
context.read<RadarrMoviesEditState>().tags = _tags;
|
||||
},
|
||||
contentPadding: LSDialog.tileContentPadding(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
contentPadding: LSDialog.textDialogContentPadding(),
|
||||
contentPadding: LSDialog.listDialogContentPadding(),
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
),
|
||||
@@ -359,32 +356,30 @@ class RadarrDialogs {
|
||||
selector: (_, state) => state.tags,
|
||||
builder: (context, future, _) => FutureBuilder(
|
||||
future: future,
|
||||
builder: (context, AsyncSnapshot<List<RadarrTag>> snapshot) => LSDialog.content(
|
||||
children: (snapshot.data?.length ?? 0) == 0
|
||||
? [ LSDialog.textContent(text: 'No Tags Found') ]
|
||||
: List.generate(
|
||||
snapshot.data.length,
|
||||
(index) => CheckboxListTile(
|
||||
title: Text(
|
||||
snapshot.data[index].label,
|
||||
style: TextStyle(
|
||||
fontSize: LSDialog.BODY_SIZE,
|
||||
color: Colors.white,
|
||||
),
|
||||
builder: (context, AsyncSnapshot<List<RadarrTag>> snapshot) {
|
||||
if((snapshot.data?.length ?? 0) == 0) return LSDialog.content(
|
||||
children: [
|
||||
LSDialog.textContent(text: 'No Tags Found'),
|
||||
],
|
||||
);
|
||||
return LSDialog.content(
|
||||
children: List.generate(
|
||||
snapshot.data.length,
|
||||
(index) => LSDialog.checkbox(
|
||||
title: snapshot.data[index].label,
|
||||
value: context.watch<RadarrAddMovieDetailsState>().tags.where((tag) => tag.id == snapshot.data[index].id).length != 0,
|
||||
onChanged: (selected) {
|
||||
List<RadarrTag> _tags = context.read<RadarrAddMovieDetailsState>().tags;
|
||||
selected ? _tags.add(snapshot.data[index]) : _tags.removeWhere((tag) => tag.id == snapshot.data[index].id);
|
||||
context.read<RadarrAddMovieDetailsState>().tags = _tags;
|
||||
},
|
||||
),
|
||||
value: context.watch<RadarrAddMovieDetailsState>().tags.where((tag) => tag.id == snapshot.data[index].id).length != 0,
|
||||
onChanged: (selected) {
|
||||
List<RadarrTag> _tags = context.read<RadarrAddMovieDetailsState>().tags;
|
||||
selected ? _tags.add(snapshot.data[index]) : _tags.removeWhere((tag) => tag.id == snapshot.data[index].id);
|
||||
context.read<RadarrAddMovieDetailsState>().tags = _tags;
|
||||
},
|
||||
contentPadding: LSDialog.tileContentPadding(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
contentPadding: LSDialog.textDialogContentPadding(),
|
||||
contentPadding: LSDialog.listDialogContentPadding(),
|
||||
shape: LunaUI.shapeBorder,
|
||||
),
|
||||
),
|
||||
@@ -442,7 +437,6 @@ class RadarrDialogs {
|
||||
),
|
||||
],
|
||||
content: [
|
||||
LSDialog.textContent(text: 'Are you sure you want to remove the movie from Radarr?\n'),
|
||||
RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST.listen(
|
||||
builder: (context, value, _) => LSDialog.checkbox(
|
||||
title: 'Add to Exclusion List',
|
||||
@@ -458,7 +452,7 @@ class RadarrDialogs {
|
||||
),
|
||||
),
|
||||
],
|
||||
contentPadding: LSDialog.textDialogContentPadding(),
|
||||
contentPadding: LSDialog.listDialogContentPadding(),
|
||||
);
|
||||
return _flag;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ class RadarrRouter extends LunaModuleRouter {
|
||||
RadarrAddMovieDetailsRouter().defineRoute(router);
|
||||
RadarrHomeRouter().defineRoute(router);
|
||||
RadarrHistoryRouter().defineRoute(router);
|
||||
RadarrManualImportRouter().defineRoute(router);
|
||||
RadarrManualImportDetailsRouter().defineRoute(router);
|
||||
RadarrMoviesDetailsRouter().defineRoute(router);
|
||||
RadarrMoviesEditRouter().defineRoute(router);
|
||||
RadarrReleasesRouter().defineRoute(router);
|
||||
|
||||
@@ -44,35 +44,17 @@ extension _EventTypeExtension on _EventType {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _downloadEvent(Map<dynamic, dynamic> data) async {
|
||||
int movieId = int.tryParse(data['id']);
|
||||
Future<void> _downloadEvent(Map<dynamic, dynamic> data) async => _goToMovieDetails(int.tryParse(data['id']));
|
||||
Future<void> _grabEvent(Map<dynamic, dynamic> data) async => _goToMovieDetails(int.tryParse(data['id']));
|
||||
Future<void> _healthEvent(Map<dynamic, dynamic> data) async => RadarrSystemStatusRouter().navigateTo(LunaState.navigatorKey.currentContext);
|
||||
Future<void> _renameEvent(Map<dynamic, dynamic> data) async => _goToMovieDetails(int.tryParse(data['id']));
|
||||
Future<void> _testEvent(Map<dynamic, dynamic> data) async => LunaModule.RADARR.launch();
|
||||
|
||||
Future<void> _goToMovieDetails(int movieId) async {
|
||||
if(movieId != null) return RadarrMoviesDetailsRouter().navigateTo(
|
||||
LunaState.navigatorKey.currentContext,
|
||||
movieId: movieId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _grabEvent(Map<dynamic, dynamic> data) async {
|
||||
int movieId = int.tryParse(data['id']);
|
||||
if(movieId != null) return RadarrMoviesDetailsRouter().navigateTo(
|
||||
LunaState.navigatorKey.currentContext,
|
||||
movieId: movieId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _healthEvent(Map<dynamic, dynamic> data) async {
|
||||
return RadarrSystemStatusRouter().navigateTo(LunaState.navigatorKey.currentContext);
|
||||
}
|
||||
|
||||
Future<void> _renameEvent(Map<dynamic, dynamic> data) async {
|
||||
int movieId = int.tryParse(data['id']);
|
||||
if(movieId != null) return RadarrMoviesDetailsRouter().navigateTo(
|
||||
LunaState.navigatorKey.currentContext,
|
||||
movieId: movieId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _testEvent(Map<dynamic, dynamic> data) async {
|
||||
return LunaModule.RADARR.launch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ export 'routes/add_movie_details.dart';
|
||||
export 'routes/catalogue.dart';
|
||||
export 'routes/edit_movie.dart';
|
||||
export 'routes/history.dart';
|
||||
export 'routes/manual_import.dart';
|
||||
export 'routes/manual_import_details.dart';
|
||||
export 'routes/missing.dart';
|
||||
export 'routes/more.dart';
|
||||
export 'routes/movie_details.dart';
|
||||
|
||||
@@ -56,10 +56,17 @@ class _State extends State<_RadarrMoviesAddDetailsRoute> with LunaLoadCallbackMi
|
||||
title: 'radarr.AddMovie'.tr(),
|
||||
message: 'radarr.MovieNotFound'.tr(),
|
||||
);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: _appBar(),
|
||||
body: _body(),
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => RadarrAddMovieDetailsState(
|
||||
movie: arguments.movie,
|
||||
isDiscovery: arguments.isDiscovery,
|
||||
),
|
||||
builder: (context, _) => Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: _appBar(),
|
||||
body: _body(),
|
||||
bottomNavigationBar: RadarrAddMovieDetailsActionBar(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,45 +87,42 @@ class _State extends State<_RadarrMoviesAddDetailsRoute> with LunaLoadCallbackMi
|
||||
builder: (context, AsyncSnapshot<List<Object>> snapshot) {
|
||||
if(snapshot.hasError) return LunaMessage.error(onTap: _refreshKey.currentState.show);
|
||||
if(snapshot.hasData) {
|
||||
return _content(snapshot.data[0], snapshot.data[1], snapshot.data[2]);
|
||||
return _content(
|
||||
context,
|
||||
rootFolders: snapshot.data[0],
|
||||
qualityProfiles: snapshot.data[1],
|
||||
tags: snapshot.data[2],
|
||||
);
|
||||
}
|
||||
return LunaLoader();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _content(List<RadarrRootFolder> rootFolders, List<RadarrQualityProfile> qualityProfiles, List<RadarrTag> tags) {
|
||||
_RadarrAddMovieDetailsArguments arguments = ModalRoute.of(context).settings.arguments;
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => RadarrAddMovieDetailsState(
|
||||
movie: arguments.movie,
|
||||
isDiscovery: arguments.isDiscovery,
|
||||
rootFolders: rootFolders,
|
||||
qualityProfiles: qualityProfiles,
|
||||
tags: tags,
|
||||
),
|
||||
builder: (context, _) => LunaListView(
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RadarrAddMovieSearchResultTile(
|
||||
movie: context.read<RadarrAddMovieDetailsState>().movie,
|
||||
onTapShowOverview: true,
|
||||
exists: false,
|
||||
isExcluded: false,
|
||||
),
|
||||
RadarrAddMovieDetailsMonitoredTile(),
|
||||
RadarrAddMovieDetailsRootFolderTile(),
|
||||
RadarrAddMovieDetailsMinimumAvailabilityTile(),
|
||||
RadarrAddMovieDetailsQualityProfileTile(),
|
||||
RadarrAddMovieDetailsTagsTile(),
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
RadarrAddMovieDetailsAddButton(searchOnAdd: false),
|
||||
RadarrAddMovieDetailsAddButton(searchOnAdd: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _content(BuildContext context, {
|
||||
List<RadarrRootFolder> rootFolders,
|
||||
List<RadarrQualityProfile> qualityProfiles,
|
||||
List<RadarrTag> tags,
|
||||
}) {
|
||||
context.read<RadarrAddMovieDetailsState>().initializeAvailability();
|
||||
context.read<RadarrAddMovieDetailsState>().initializeQualityProfile(qualityProfiles);
|
||||
context.read<RadarrAddMovieDetailsState>().initializeRootFolder(rootFolders);
|
||||
context.read<RadarrAddMovieDetailsState>().initializeTags(tags);
|
||||
return LunaListView(
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RadarrAddMovieSearchResultTile(
|
||||
movie: context.read<RadarrAddMovieDetailsState>().movie,
|
||||
onTapShowOverview: true,
|
||||
exists: false,
|
||||
isExcluded: false,
|
||||
),
|
||||
RadarrAddMovieDetailsMonitoredTile(),
|
||||
RadarrAddMovieDetailsRootFolderTile(),
|
||||
RadarrAddMovieDetailsMinimumAvailabilityTile(),
|
||||
RadarrAddMovieDetailsQualityProfileTile(),
|
||||
RadarrAddMovieDetailsTagsTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,34 +5,11 @@ import 'package:lunasea/modules/radarr.dart';
|
||||
class RadarrAddMovieDetailsState extends ChangeNotifier {
|
||||
final bool isDiscovery;
|
||||
final RadarrMovie movie;
|
||||
|
||||
|
||||
RadarrAddMovieDetailsState({
|
||||
@required this.movie,
|
||||
@required this.isDiscovery,
|
||||
@required List<RadarrRootFolder> rootFolders,
|
||||
@required List<RadarrQualityProfile> qualityProfiles,
|
||||
@required List<RadarrTag> tags,
|
||||
}) {
|
||||
assert(movie != null);
|
||||
assert(isDiscovery != null);
|
||||
assert(rootFolders != null);
|
||||
assert(qualityProfiles != null);
|
||||
assert(tags != null);
|
||||
_tags = (tags ?? []).where((tag) => ((RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS.data as List) ?? []).contains(tag.id)).toList();
|
||||
_monitored = RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MONITORED_STATE.data ?? true;
|
||||
_rootFolder = (rootFolders ?? []).firstWhere(
|
||||
(element) => element.id == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID.data,
|
||||
orElse: () => (rootFolders?.length ?? 0) != 0 ? rootFolders[0] : RadarrRootFolder(id: -1, freeSpace: 0, path: Constants.TEXT_EMDASH),
|
||||
);
|
||||
_qualityProfile = (qualityProfiles ?? []).firstWhere(
|
||||
(element) => element.id == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID.data,
|
||||
orElse: () => (qualityProfiles?.length ?? 0) != 0 ? qualityProfiles[0] : RadarrQualityProfile(id: -1, name: Constants.TEXT_EMDASH),
|
||||
);
|
||||
_availability = RadarrAvailability.values.firstWhere(
|
||||
(element) => element.value == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID.data,
|
||||
orElse: () => RadarrAvailability.ANNOUNCED,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
bool _monitored = true;
|
||||
bool get monitored => _monitored;
|
||||
@@ -51,6 +28,12 @@ class RadarrAddMovieDetailsState extends ChangeNotifier {
|
||||
RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID.put(_availability.value);
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeAvailability() {
|
||||
_availability = RadarrAvailability.values.firstWhere(
|
||||
(element) => element.value == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID.data,
|
||||
orElse: () => RadarrAvailability.ANNOUNCED,
|
||||
);
|
||||
}
|
||||
|
||||
RadarrRootFolder _rootFolder;
|
||||
RadarrRootFolder get rootFolder => _rootFolder;
|
||||
@@ -60,6 +43,12 @@ class RadarrAddMovieDetailsState extends ChangeNotifier {
|
||||
RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID.put(_rootFolder.id);
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeRootFolder(List<RadarrRootFolder> rootFolders) {
|
||||
_rootFolder = (rootFolders ?? []).firstWhere(
|
||||
(element) => element.id == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID.data,
|
||||
orElse: () => (rootFolders?.length ?? 0) != 0 ? rootFolders[0] : RadarrRootFolder(id: -1, freeSpace: 0, path: Constants.TEXT_EMDASH),
|
||||
);
|
||||
}
|
||||
|
||||
RadarrQualityProfile _qualityProfile;
|
||||
RadarrQualityProfile get qualityProfile => _qualityProfile;
|
||||
@@ -69,6 +58,12 @@ class RadarrAddMovieDetailsState extends ChangeNotifier {
|
||||
RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID.put(_qualityProfile.id);
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeQualityProfile(List<RadarrQualityProfile> qualityProfiles) {
|
||||
_qualityProfile = (qualityProfiles ?? []).firstWhere(
|
||||
(element) => element.id == RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID.data,
|
||||
orElse: () => (qualityProfiles?.length ?? 0) != 0 ? qualityProfiles[0] : RadarrQualityProfile(id: -1, name: Constants.TEXT_EMDASH),
|
||||
);
|
||||
}
|
||||
|
||||
List<RadarrTag> _tags = [];
|
||||
List<RadarrTag> get tags => _tags;
|
||||
@@ -78,6 +73,9 @@ class RadarrAddMovieDetailsState extends ChangeNotifier {
|
||||
RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS.put((tags ?? []).map<int>((tag) => tag.id).toList());
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeTags(List<RadarrTag> tags) {
|
||||
_tags = (tags ?? []).where((tag) => ((RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS.data as List) ?? []).contains(tag.id)).toList();
|
||||
}
|
||||
|
||||
LunaLoadingState _state = LunaLoadingState.INACTIVE;
|
||||
LunaLoadingState get state => _state;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'widgets/bottom_action_bar.dart';
|
||||
export 'widgets/button_add_movie.dart';
|
||||
export 'widgets/tile_minimum_availability.dart';
|
||||
export 'widgets/tile_monitored.dart';
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrAddMovieDetailsActionBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaBottomActionBar(
|
||||
actions: [
|
||||
RadarrAddMovieDetailsAddButton(searchOnAdd: false),
|
||||
RadarrAddMovieDetailsAddButton(searchOnAdd: true),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ class RadarrAddMovieDetailsAddButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: searchOnAdd ? 'radarr.AddMovieAndSearch'.tr() : 'radarr.AddMovie'.tr(),
|
||||
backgroundColor: searchOnAdd ? LunaColours.orange : LunaColours.accent,
|
||||
onTap: () async => _onTap(context),
|
||||
|
||||
@@ -86,7 +86,7 @@ class _State extends State<RadarrCatalogueRoute> with AutomaticKeepAliveClientMi
|
||||
LunaMessage.inList(text: 'radarr.NoMoviesFound'.tr()),
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(
|
||||
LunaButton.text(
|
||||
text: query.length > 20
|
||||
? 'radarr.SearchFor'.tr(args: ['"${query.substring(0, min(20, query.length))}${LunaUI.TEXT_ELLIPSIS}"'])
|
||||
: 'radarr.SearchFor'.tr(args: ['"$query"']),
|
||||
@@ -99,13 +99,10 @@ class _State extends State<RadarrCatalogueRoute> with AutomaticKeepAliveClientMi
|
||||
return LunaListViewBuilder(
|
||||
controller: RadarrNavigationBar.scrollControllers[0],
|
||||
itemCount: _filtered.length,
|
||||
itemBuilder: (context, index) {
|
||||
if(_filtered[index].id == null) return Container(height: 0.0);
|
||||
return RadarrCatalogueTile(
|
||||
movie: _filtered[index],
|
||||
profile: qualityProfiles.firstWhere((element) => element.id == _filtered[index].qualityProfileId, orElse: null),
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) => RadarrCatalogueTile(
|
||||
movie: _filtered[index],
|
||||
profile: qualityProfiles.firstWhere((element) => element.id == _filtered[index].qualityProfileId, orElse: null),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -119,8 +116,8 @@ class _State extends State<RadarrCatalogueRoute> with AutomaticKeepAliveClientMi
|
||||
bool ascending = context.read<RadarrState>().moviesSortAscending;
|
||||
// Filter
|
||||
List<RadarrMovie> filtered = movies.where((movie) {
|
||||
if(query != null && query.isNotEmpty) return movie.title.toLowerCase().contains(query.toLowerCase());
|
||||
return movie != null;
|
||||
if(query != null && query.isNotEmpty && movie.id != null) return movie.title.toLowerCase().contains(query.toLowerCase());
|
||||
return (movie != null && movie.id != null);
|
||||
}).toList();
|
||||
filtered = filter.filter(filtered);
|
||||
// Sort
|
||||
|
||||
@@ -46,10 +46,17 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L
|
||||
title: 'radarr.EditMovie'.tr(),
|
||||
message: 'radarr.MovieNotFound'.tr(),
|
||||
);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: _appBar(),
|
||||
body: _body(),
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => RadarrMoviesEditState(),
|
||||
builder: (context, _) {
|
||||
LunaLoadingState state = context.select<RadarrMoviesEditState, LunaLoadingState>((state) => state.state);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: _appBar(),
|
||||
body: state == LunaLoadingState.ERROR ? _bodyError() : _body(context),
|
||||
bottomNavigationBar: state == LunaLoadingState.ERROR ? null : RadarrEditMovieActionBar(),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,12 +67,19 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body() {
|
||||
Widget _bodyError() {
|
||||
return LunaMessage.goBack(
|
||||
context: context,
|
||||
text: 'lunasea.AnErrorHasOccurred'.tr(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: Future.wait([
|
||||
context.watch<RadarrState>().movies,
|
||||
context.watch<RadarrState>().qualityProfiles,
|
||||
context.watch<RadarrState>().tags,
|
||||
context.select<RadarrState, Future<List<RadarrMovie>>>((state) => state.movies),
|
||||
context.select<RadarrState, Future<List<RadarrQualityProfile>>>((state) => state.qualityProfiles),
|
||||
context.select<RadarrState, Future<List<RadarrTag>>>((state) => state.tags),
|
||||
]),
|
||||
builder: (context, AsyncSnapshot<List<Object>> snapshot) {
|
||||
if(snapshot.hasError) return LunaMessage.error(onTap: loadCallback);
|
||||
@@ -75,33 +89,37 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L
|
||||
orElse: () => null,
|
||||
);
|
||||
if(movie == null) return LunaLoader();
|
||||
return _list(movie, snapshot.data[1], snapshot.data[2]);
|
||||
return _list(
|
||||
context,
|
||||
movie: movie,
|
||||
qualityProfiles: snapshot.data[1],
|
||||
tags: snapshot.data[2],
|
||||
);
|
||||
}
|
||||
return LunaLoader();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _list(RadarrMovie movie, List<RadarrQualityProfile> qualityProfiles, List<RadarrTag> tags) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => RadarrMoviesEditState(movie: movie, qualityProfiles: qualityProfiles, tags: tags),
|
||||
builder: (context, _) {
|
||||
if(context.watch<RadarrMoviesEditState>().state == LunaLoadingState.ERROR) return LunaMessage.goBack(
|
||||
context: context,
|
||||
text: 'lunasea.AnErrorHasOccurred'.tr(),
|
||||
);
|
||||
return LunaListView(
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RadarrMoviesEditMonitoredTile(),
|
||||
RadarrMoviesEditMinimumAvailabilityTile(),
|
||||
RadarrMoviesEditQualityProfileTile(profiles: qualityProfiles),
|
||||
RadarrMoviesEditPathTile(),
|
||||
RadarrMoviesEditTagsTile(),
|
||||
RadarrMoviesEditUpdateMovieButton(movie: movie),
|
||||
],
|
||||
);
|
||||
}
|
||||
Widget _list(BuildContext context, {
|
||||
RadarrMovie movie,
|
||||
List<RadarrQualityProfile> qualityProfiles,
|
||||
List<RadarrTag> tags,
|
||||
}) {
|
||||
if(context.read<RadarrMoviesEditState>().movie == null) {
|
||||
context.read<RadarrMoviesEditState>().movie = movie;
|
||||
context.read<RadarrMoviesEditState>().initializeQualityProfile(qualityProfiles);
|
||||
context.read<RadarrMoviesEditState>().initializeTags(tags);
|
||||
}
|
||||
return LunaListView(
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RadarrMoviesEditMonitoredTile(),
|
||||
RadarrMoviesEditMinimumAvailabilityTile(),
|
||||
RadarrMoviesEditQualityProfileTile(profiles: qualityProfiles),
|
||||
RadarrMoviesEditPathTile(),
|
||||
RadarrMoviesEditTagsTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,13 @@ import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrMoviesEditState extends ChangeNotifier {
|
||||
RadarrMoviesEditState({
|
||||
@required RadarrMovie movie,
|
||||
@required List<RadarrQualityProfile> qualityProfiles,
|
||||
@required List<RadarrTag> tags,
|
||||
}) {
|
||||
_monitored = movie.monitored ?? true;
|
||||
_path = movie.path ?? '';
|
||||
_availability = movie.minimumAvailability ?? RadarrAvailability.ANNOUNCED;
|
||||
_qualityProfile = qualityProfiles.firstWhere(
|
||||
(profile) => profile.id == movie.qualityProfileId,
|
||||
orElse: () => qualityProfiles.length == 0 ? null : qualityProfiles[0],
|
||||
);
|
||||
_tags = (tags ?? []).where((tag) => (movie.tags ?? []).contains(tag.id)).toList();
|
||||
RadarrMovie _movie;
|
||||
RadarrMovie get movie => _movie;
|
||||
set movie(RadarrMovie movie) {
|
||||
_movie = movie;
|
||||
initializeMonitored();
|
||||
initializeAvailability();
|
||||
initializePath();
|
||||
}
|
||||
|
||||
LunaLoadingState _state = LunaLoadingState.INACTIVE;
|
||||
@@ -33,6 +27,9 @@ class RadarrMoviesEditState extends ChangeNotifier {
|
||||
_monitored = monitored;
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeMonitored() {
|
||||
_monitored = movie.monitored ?? false;
|
||||
}
|
||||
|
||||
String _path = '';
|
||||
String get path => _path;
|
||||
@@ -41,6 +38,9 @@ class RadarrMoviesEditState extends ChangeNotifier {
|
||||
_path = path;
|
||||
notifyListeners();
|
||||
}
|
||||
void initializePath() {
|
||||
_path = movie.path ?? '';
|
||||
}
|
||||
|
||||
RadarrQualityProfile _qualityProfile;
|
||||
RadarrQualityProfile get qualityProfile => _qualityProfile;
|
||||
@@ -49,6 +49,12 @@ class RadarrMoviesEditState extends ChangeNotifier {
|
||||
_qualityProfile = qualityProfile;
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeQualityProfile(List<RadarrQualityProfile> qualityProfiles) {
|
||||
_qualityProfile = qualityProfiles.firstWhere(
|
||||
(profile) => profile.id == movie.qualityProfileId,
|
||||
orElse: () => qualityProfiles.length == 0 ? null : qualityProfiles[0],
|
||||
);
|
||||
}
|
||||
|
||||
RadarrAvailability _availability;
|
||||
RadarrAvailability get availability => _availability;
|
||||
@@ -57,6 +63,9 @@ class RadarrMoviesEditState extends ChangeNotifier {
|
||||
_availability = availability;
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeAvailability() {
|
||||
_availability = movie.minimumAvailability ?? RadarrAvailability.ANNOUNCED;
|
||||
}
|
||||
|
||||
List<RadarrTag> _tags;
|
||||
List<RadarrTag> get tags => _tags;
|
||||
@@ -65,4 +74,7 @@ class RadarrMoviesEditState extends ChangeNotifier {
|
||||
_tags = tags;
|
||||
notifyListeners();
|
||||
}
|
||||
void initializeTags(List<RadarrTag> tags) {
|
||||
_tags = (tags ?? []).where((tag) => (movie.tags ?? []).contains(tag.id)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'widgets/bottom_action_bar.dart';
|
||||
export 'widgets/button_update_movie.dart';
|
||||
export 'widgets/tile_monitored.dart';
|
||||
export 'widgets/tile_path.dart';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrEditMovieActionBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaBottomActionBar(
|
||||
actions: [
|
||||
RadarrEditMovieUpdateMovieButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrMoviesEditUpdateMovieButton extends StatelessWidget {
|
||||
final RadarrMovie movie;
|
||||
|
||||
RadarrMoviesEditUpdateMovieButton({
|
||||
Key key,
|
||||
@required this.movie,
|
||||
}) : super(key: key);
|
||||
|
||||
class RadarrEditMovieUpdateMovieButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(
|
||||
text: 'radarr.UpdateMovie'.tr(),
|
||||
loadingState: context.watch<RadarrMoviesEditState>().state,
|
||||
onTap: () async {
|
||||
context.read<RadarrMoviesEditState>().state = LunaLoadingState.ACTIVE;
|
||||
RadarrMovie updatedMovie = movie.updateEdits(context.read<RadarrMoviesEditState>());
|
||||
bool result = await RadarrAPIHelper().updateMovie(context: context, movie: updatedMovie);
|
||||
if(result) Navigator.of(context).lunaSafetyPop();
|
||||
},
|
||||
),
|
||||
],
|
||||
return LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: 'radarr.UpdateMovie'.tr(),
|
||||
loadingState: context.watch<RadarrMoviesEditState>().state,
|
||||
onTap: () async {
|
||||
context.read<RadarrMoviesEditState>().state = LunaLoadingState.ACTIVE;
|
||||
if(context.read<RadarrMoviesEditState>().movie != null) {
|
||||
RadarrMovie movie = context.read<RadarrMoviesEditState>().movie.updateEdits(context.read<RadarrMoviesEditState>());
|
||||
bool result = await RadarrAPIHelper().updateMovie(context: context, movie: movie);
|
||||
if(result) Navigator.of(context).lunaSafetyPop();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
3
lib/modules/radarr/routes/manual_import.dart
Normal file
3
lib/modules/radarr/routes/manual_import.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
export 'manual_import/route.dart';
|
||||
export 'manual_import/state.dart';
|
||||
export 'manual_import/widgets.dart';
|
||||
91
lib/modules/radarr/routes/manual_import/route.dart
Normal file
91
lib/modules/radarr/routes/manual_import/route.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportRouter extends RadarrPageRouter {
|
||||
RadarrManualImportRouter() : super('/radarr/manualimport');
|
||||
|
||||
@override
|
||||
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _RadarrManualImportRoute());
|
||||
}
|
||||
|
||||
|
||||
class _RadarrManualImportRoute extends StatefulWidget {
|
||||
@override
|
||||
State<_RadarrManualImportRoute> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<_RadarrManualImportRoute> with LunaScrollControllerMixin {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => RadarrManualImportState(context),
|
||||
builder: (context, _) => Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: _appBar(),
|
||||
body: _body(context),
|
||||
bottomNavigationBar: RadarrManualImportBottomActionBar(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appBar() {
|
||||
return LunaAppBar(
|
||||
title: 'radarr.ManualImport'.tr(),
|
||||
scrollControllers: [scrollController],
|
||||
bottom: RadarrManualImportPathBar(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: context.select<RadarrManualImportState, Future<RadarrFileSystem>>((state) => state.directories),
|
||||
builder: (context, AsyncSnapshot<RadarrFileSystem> snapshot) {
|
||||
if(snapshot.hasError) {
|
||||
if(snapshot.connectionState != ConnectionState.waiting) LunaLogger().error(
|
||||
'Unable to fetch Radarr filesystem',
|
||||
snapshot.error,
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
return LunaMessage.error(onTap: () => context.read<RadarrManualImportState>().fetchDirectories(
|
||||
context,
|
||||
context.read<RadarrManualImportState>().currentPath,
|
||||
));
|
||||
}
|
||||
if(snapshot.hasData) return _list(context, snapshot.data);
|
||||
return LunaLoader();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _list(BuildContext context, RadarrFileSystem fileSystem) {
|
||||
if((fileSystem?.directories?.length ?? 0) == 0 && (fileSystem.parent == null || fileSystem.parent.isEmpty)) return LunaMessage(
|
||||
text: 'radarr.NoSubdirectoriesFound'.tr(),
|
||||
);
|
||||
return Selector<RadarrManualImportState, String>(
|
||||
selector: (_, state) => state.currentPath,
|
||||
builder: (context, path, _) {
|
||||
List<RadarrFileSystemDirectory> directories = _filterDirectories(path, fileSystem);
|
||||
return LunaListView(
|
||||
key: ObjectKey(fileSystem.directories),
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RadarrManualImportParentDirectoryTile(fileSystem: fileSystem),
|
||||
...List.generate(
|
||||
directories.length,
|
||||
(index) => RadarrManualImportDirectoryTile(directory: directories[index]),
|
||||
),
|
||||
]
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<RadarrFileSystemDirectory> _filterDirectories(String path, RadarrFileSystem fileSystem) {
|
||||
if(path == null || path.isEmpty) return fileSystem.directories ?? [];
|
||||
if(fileSystem?.directories == null || fileSystem.directories.length == 0) return [];
|
||||
return fileSystem.directories.where((element) => element.path.contains(path)).toList();
|
||||
}
|
||||
}
|
||||
36
lib/modules/radarr/routes/manual_import/state.dart
Normal file
36
lib/modules/radarr/routes/manual_import/state.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportState extends ChangeNotifier {
|
||||
RadarrManualImportState(BuildContext context) {
|
||||
fetchDirectories(context, null);
|
||||
}
|
||||
|
||||
String _currentPath;
|
||||
String get currentPath => _currentPath;
|
||||
set currentPath(String path) {
|
||||
_currentPath = path;
|
||||
updateTextControllerText();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
TextEditingController currentPathTextController = TextEditingController();
|
||||
void updateTextControllerText() {
|
||||
currentPathTextController.text = _currentPath;
|
||||
currentPathTextController.selection = TextSelection.fromPosition(TextPosition(offset: _currentPath.length));
|
||||
}
|
||||
|
||||
Future<RadarrFileSystem> _directories;
|
||||
Future<RadarrFileSystem> get directories => _directories;
|
||||
void fetchDirectories(BuildContext context, String path) {
|
||||
if(context.read<RadarrState>().enabled) {
|
||||
_directories = context.read<RadarrState>().api.fileSystem.get(
|
||||
path: path,
|
||||
includeFiles: false,
|
||||
allowFoldersWithoutTrailingSlashes: false,
|
||||
).whenComplete(() => currentPath = path ?? '');
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
4
lib/modules/radarr/routes/manual_import/widgets.dart
Normal file
4
lib/modules/radarr/routes/manual_import/widgets.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
export 'widgets/appbar_path_bar.dart';
|
||||
export 'widgets/bottom_action_bar.dart';
|
||||
export 'widgets/directory_tile.dart';
|
||||
export 'widgets/parent_directory_tile.dart';
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportPathBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(62.0);
|
||||
|
||||
@override
|
||||
State<RadarrManualImportPathBar> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<RadarrManualImportPathBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: LunaTextInputBar(
|
||||
labelIcon: Icons.sd_storage_rounded,
|
||||
labelText: 'radarr.FileBrowser'.tr(),
|
||||
controller: context.watch<RadarrManualImportState>().currentPathTextController,
|
||||
autofocus: false,
|
||||
onChanged: (value) {
|
||||
context.read<RadarrManualImportState>().currentPath = value;
|
||||
if(value.endsWith('/') || value.isEmpty) {
|
||||
context.read<RadarrManualImportState>().fetchDirectories(context, value);
|
||||
}
|
||||
},
|
||||
margin: EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 14.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(top: 1.0, bottom: 1.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportBottomActionBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaBottomActionBar(
|
||||
actions: [
|
||||
LunaButton.text(
|
||||
text: 'radarr.Quick'.tr(),
|
||||
onTap: () async {
|
||||
String path = context.read<RadarrManualImportState>().currentPath;
|
||||
RadarrAPIHelper().quickImport(context: context, path: path);
|
||||
},
|
||||
),
|
||||
LunaButton.text(
|
||||
text: 'radarr.Interactive'.tr(),
|
||||
backgroundColor: LunaColours.orange,
|
||||
onTap: () async {
|
||||
String path = context.read<RadarrManualImportState>().currentPath;
|
||||
RadarrManualImportDetailsRouter().navigateTo(
|
||||
context,
|
||||
path: (path ?? '').isNotEmpty ? path : '/',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportDirectoryTile extends StatefulWidget {
|
||||
final RadarrFileSystemDirectory directory;
|
||||
|
||||
RadarrManualImportDirectoryTile({
|
||||
Key key,
|
||||
@required this.directory,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<RadarrManualImportDirectoryTile> {
|
||||
LunaLoadingState _loadingState = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if(
|
||||
widget.directory == null ||
|
||||
widget.directory.path == null ||
|
||||
widget.directory.path.isEmpty
|
||||
) return SizedBox(height: 0.0);
|
||||
return LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: widget.directory?.name ?? LunaUI.TEXT_EMDASH),
|
||||
subtitle: LunaText.subtitle(
|
||||
text: widget.directory.path,
|
||||
),
|
||||
trailing: LunaIconButton(
|
||||
icon: Icons.arrow_forward_ios_rounded,
|
||||
loadingState: _loadingState,
|
||||
),
|
||||
onTap: () async {
|
||||
if(_loadingState == LunaLoadingState.INACTIVE) {
|
||||
if(mounted) setState(() => _loadingState = LunaLoadingState.ACTIVE);
|
||||
context.read<RadarrManualImportState>().fetchDirectories(
|
||||
context,
|
||||
widget.directory.path,
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportParentDirectoryTile extends StatefulWidget {
|
||||
final RadarrFileSystem fileSystem;
|
||||
|
||||
RadarrManualImportParentDirectoryTile({
|
||||
Key key,
|
||||
@required this.fileSystem,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<RadarrManualImportParentDirectoryTile> {
|
||||
LunaLoadingState _loadingState = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if(
|
||||
widget.fileSystem == null ||
|
||||
widget.fileSystem.parent == null ||
|
||||
widget.fileSystem.parent.isEmpty
|
||||
) return SizedBox(height: 0.0);
|
||||
return LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: LunaUI.TEXT_ELLIPSIS),
|
||||
subtitle: LunaText.subtitle(text: 'radarr.ParentDirectory'.tr()),
|
||||
trailing: LunaIconButton(
|
||||
icon: Icons.arrow_upward_rounded,
|
||||
loadingState: _loadingState,
|
||||
),
|
||||
onTap: () async {
|
||||
if(_loadingState == LunaLoadingState.INACTIVE) {
|
||||
if(mounted) setState(() => _loadingState = LunaLoadingState.ACTIVE);
|
||||
context.read<RadarrManualImportState>().fetchDirectories(
|
||||
context,
|
||||
widget.fileSystem.parent,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
3
lib/modules/radarr/routes/manual_import_details.dart
Normal file
3
lib/modules/radarr/routes/manual_import_details.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
export 'manual_import_details/route.dart';
|
||||
export 'manual_import_details/state.dart';
|
||||
export 'manual_import_details/widgets.dart';
|
||||
98
lib/modules/radarr/routes/manual_import_details/route.dart
Normal file
98
lib/modules/radarr/routes/manual_import_details/route.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class _RadarrManualImportDetailsArguments {
|
||||
final String path;
|
||||
|
||||
_RadarrManualImportDetailsArguments({
|
||||
@required this.path,
|
||||
}) {
|
||||
assert(path != null);
|
||||
}
|
||||
}
|
||||
|
||||
class RadarrManualImportDetailsRouter extends RadarrPageRouter {
|
||||
RadarrManualImportDetailsRouter() : super('/radarr/manualimport/details');
|
||||
|
||||
@override
|
||||
Future<void> navigateTo(BuildContext context, {
|
||||
@required String path,
|
||||
}) => LunaRouter.router.navigateTo(
|
||||
context,
|
||||
route(),
|
||||
routeSettings: RouteSettings(arguments: _RadarrManualImportDetailsArguments(path: path)),
|
||||
);
|
||||
|
||||
@override
|
||||
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _RadarrMoviesAddDetailsRoute());
|
||||
}
|
||||
|
||||
class _RadarrMoviesAddDetailsRoute extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<_RadarrMoviesAddDetailsRoute> with LunaScrollControllerMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_RadarrManualImportDetailsArguments arguments = ModalRoute.of(context).settings.arguments;
|
||||
if(arguments == null || arguments.path == null || arguments.path.isEmpty) return LunaInvalidRoute(
|
||||
title: 'radarr.ManualImport'.tr(),
|
||||
message: 'radarr.DirectoryNotFound'.tr(),
|
||||
);
|
||||
return ChangeNotifierProvider(
|
||||
create: (BuildContext context) => RadarrManualImportDetailsState(context, path: arguments.path),
|
||||
builder: (context, _) {
|
||||
return Scaffold(
|
||||
appBar: _appBar(),
|
||||
body: _body(context),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appBar() {
|
||||
return LunaAppBar(
|
||||
title: 'radarr.ManualImport'.tr(),
|
||||
scrollControllers: [scrollController],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: context.select<RadarrManualImportDetailsState, Future<List<RadarrManualImport>>>((state) => state.manualImport),
|
||||
builder: (context, AsyncSnapshot<List<RadarrManualImport>> snapshot) {
|
||||
if(snapshot.hasError) {
|
||||
if(snapshot.connectionState != ConnectionState.waiting) LunaLogger().error(
|
||||
'Unable to fetch Radarr manual import: ${context.read<RadarrManualImportDetailsState>().path}',
|
||||
snapshot.error,
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
return LunaMessage.error(
|
||||
onTap: () => context.read<RadarrManualImportDetailsState>().fetchManualImport(context),
|
||||
);
|
||||
}
|
||||
if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
|
||||
return _list(context, manualImport: snapshot.data);
|
||||
}
|
||||
return LunaLoader();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _list(BuildContext context, {
|
||||
@required List<RadarrManualImport> manualImport,
|
||||
}) {
|
||||
if((manualImport?.length ?? 0) == 0) return LunaMessage(
|
||||
text: 'radarr.NoFilesFound'.tr(),
|
||||
buttonText: 'lunasea.Refresh'.tr(),
|
||||
onTap: () => context.read<RadarrManualImportDetailsState>().fetchManualImport(context),
|
||||
);
|
||||
return LunaListViewBuilder(
|
||||
controller: scrollController,
|
||||
itemCount: manualImport.length,
|
||||
itemBuilder: (context, index) => RadarrManualImportDetailsImportTile(manualImport: manualImport[index]),
|
||||
);
|
||||
}
|
||||
}
|
||||
25
lib/modules/radarr/routes/manual_import_details/state.dart
Normal file
25
lib/modules/radarr/routes/manual_import_details/state.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportDetailsState extends ChangeNotifier {
|
||||
final String path;
|
||||
|
||||
RadarrManualImportDetailsState(BuildContext context, {
|
||||
@required this.path,
|
||||
}) {
|
||||
fetchManualImport(context);
|
||||
}
|
||||
|
||||
Future<List<RadarrManualImport>> _manualImport;
|
||||
Future<List<RadarrManualImport>> get manualImport => _manualImport;
|
||||
Future<void> fetchManualImport(BuildContext context) async {
|
||||
if(context.read<RadarrState>().enabled) {
|
||||
_manualImport = context.read<RadarrState>().api.manualImport.get(
|
||||
folder: path,
|
||||
filterExistingFiles: true,
|
||||
);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export 'widgets/import_tile.dart';
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrManualImportDetailsImportTile extends StatelessWidget {
|
||||
final RadarrManualImport manualImport;
|
||||
|
||||
RadarrManualImportDetailsImportTile({
|
||||
Key key,
|
||||
@required this.manualImport,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: manualImport.relativePath),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -33,25 +33,32 @@ class _State extends State<RadarrMoreRoute> with AutomaticKeepAliveClientMixin {
|
||||
trailing: LunaIconButton(icon: LunaIcons.history, color: LunaColours.list(0)),
|
||||
onTap: () async => RadarrHistoryRouter().navigateTo(context),
|
||||
),
|
||||
LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: 'radarr.ManualImport'.tr()),
|
||||
subtitle: LunaText.subtitle(text: 'radarr.ManualImportDescription'.tr()),
|
||||
trailing: LunaIconButton(icon: Icons.person_rounded, color: LunaColours.list(1)),
|
||||
onTap: () async => RadarrManualImportRouter().navigateTo(context),
|
||||
),
|
||||
LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: 'radarr.Queue'.tr()),
|
||||
subtitle: LunaText.subtitle(text: 'radarr.QueueDescription'.tr()),
|
||||
trailing: LunaIconButton(icon: Icons.queue, color: LunaColours.list(1)),
|
||||
trailing: LunaIconButton(icon: Icons.queue, color: LunaColours.list(2)),
|
||||
onTap: () async => RadarrQueueRouter().navigateTo(context),
|
||||
),
|
||||
LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: 'radarr.SystemStatus'.tr()),
|
||||
subtitle: LunaText.subtitle(text: 'radarr.SystemStatusDescription'.tr()),
|
||||
trailing: LunaIconButton(icon: LunaIcons.monitoring, color: LunaColours.list(2)),
|
||||
trailing: LunaIconButton(icon: LunaIcons.monitoring, color: LunaColours.list(3)),
|
||||
onTap: () async => RadarrSystemStatusRouter().navigateTo(context),
|
||||
),
|
||||
LunaListTile(
|
||||
context: context,
|
||||
title: LunaText.title(text: 'radarr.Tags'.tr()),
|
||||
subtitle: LunaText.subtitle(text: 'radarr.TagsDescription'.tr()),
|
||||
trailing: LunaIconButton(icon: Icons.style, color: LunaColours.list(3)),
|
||||
trailing: LunaIconButton(icon: Icons.style, color: LunaColours.list(4)),
|
||||
onTap: () async => RadarrTagsRouter().navigateTo(context),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -35,16 +35,15 @@ class _State extends State<_RadarrMoviesDetailsRoute> with LunaLoadCallbackMixin
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
RadarrMovie movie;
|
||||
PageController _pageController;
|
||||
bool _loaded = false;
|
||||
|
||||
@override
|
||||
Future<void> loadCallback() async {
|
||||
if(widget.movieId > 0) {
|
||||
_findMovie(await context.read<RadarrState>().movies);
|
||||
RadarrMovie result = _findMovie(await context.read<RadarrState>().movies);
|
||||
setState(() => movie = result);
|
||||
context.read<RadarrState>().fetchQualityProfiles();
|
||||
context.read<RadarrState>().fetchTags();
|
||||
await context.read<RadarrState>().resetSingleMovie(widget.movieId);
|
||||
_findMovie(await context.read<RadarrState>().movies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,15 +53,11 @@ class _State extends State<_RadarrMoviesDetailsRoute> with LunaLoadCallbackMixin
|
||||
_pageController = PageController(initialPage: RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS.data);
|
||||
}
|
||||
|
||||
void _findMovie(List<RadarrMovie> movies) {
|
||||
RadarrMovie _movie = movies.firstWhere(
|
||||
RadarrMovie _findMovie(List<RadarrMovie> movies) {
|
||||
return movies.firstWhere(
|
||||
(movie) => movie.id == widget.movieId,
|
||||
orElse: () => null,
|
||||
);
|
||||
if(mounted) setState(() {
|
||||
movie = _movie;
|
||||
_loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
List<RadarrTag> _findTags(List<int> tagIds, List<RadarrTag> tags) {
|
||||
@@ -105,18 +100,17 @@ class _State extends State<_RadarrMoviesDetailsRoute> with LunaLoadCallbackMixin
|
||||
|
||||
Widget _bottomNavigationBar() {
|
||||
if(movie == null) return null;
|
||||
return RadarrMovieDetailsNavigationBar(pageController: _pageController);
|
||||
return RadarrMovieDetailsNavigationBar(
|
||||
pageController: _pageController,
|
||||
movie: movie,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body() {
|
||||
return Consumer<RadarrState>(
|
||||
builder: (context, state, _) => FutureBuilder(
|
||||
future: Future.wait([state.qualityProfiles, state.tags]),
|
||||
future: Future.wait([state.qualityProfiles, state.tags, state.movies]),
|
||||
builder: (context, AsyncSnapshot<List<Object>> snapshot) {
|
||||
if(_loaded && movie == null) return LunaMessage.goBack(
|
||||
text: 'Movie Not Found',
|
||||
context: context,
|
||||
);
|
||||
if(snapshot.hasError) {
|
||||
if(snapshot.connectionState != ConnectionState.waiting) LunaLogger().error(
|
||||
'Unable to pull Radarr movie details',
|
||||
@@ -126,7 +120,11 @@ class _State extends State<_RadarrMoviesDetailsRoute> with LunaLoadCallbackMixin
|
||||
return LunaMessage.error(onTap: loadCallback);
|
||||
}
|
||||
if(snapshot.hasData) {
|
||||
if(movie == null) return LunaLoader();
|
||||
movie = _findMovie(snapshot.data[2]);
|
||||
if(movie == null) return LunaMessage.goBack(
|
||||
text: 'Movie Not Found',
|
||||
context: context,
|
||||
);
|
||||
RadarrQualityProfile qualityProfile = _findQualityProfile(movie.qualityProfileId, snapshot.data[0]);
|
||||
List<RadarrTag> tags = _findTags(movie.tags, snapshot.data[1]);
|
||||
return _pages(qualityProfile, tags);
|
||||
|
||||
@@ -3,7 +3,6 @@ export 'widgets/cast_crew_tile.dart';
|
||||
export 'widgets/files_extra_file_block.dart';
|
||||
export 'widgets/files_file_block.dart';
|
||||
export 'widgets/navigation_bar.dart';
|
||||
export 'widgets/overview_download_buttons.dart';
|
||||
export 'widgets/overview_movie_description_tile.dart';
|
||||
export 'widgets/overview_movie_information_block.dart';
|
||||
export 'widgets/overview_movie_links_section.dart';
|
||||
|
||||
@@ -37,11 +37,12 @@ class _State extends State<RadarrMovieDetailsFilesFileBlock> {
|
||||
LunaTableContent(title: 'added on', body: widget.movieFile?.lunaDateAdded),
|
||||
],
|
||||
buttons: [
|
||||
if(widget.movieFile?.mediaInfo != null) LunaButton.slim(
|
||||
if(widget.movieFile?.mediaInfo != null) LunaButton.text(
|
||||
text: 'Media Info',
|
||||
onTap: () async => _viewMediaInfo(),
|
||||
),
|
||||
LunaButton.slim(
|
||||
LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: 'Delete',
|
||||
onTap: () async => _deleteFile(),
|
||||
backgroundColor: LunaColours.red,
|
||||
|
||||
@@ -1,24 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrMovieDetailsNavigationBar extends StatelessWidget {
|
||||
class RadarrMovieDetailsNavigationBar extends StatefulWidget {
|
||||
static const List<IconData> icons = [Icons.subject_rounded, Icons.insert_drive_file_outlined, Icons.history, Icons.person];
|
||||
static const List<String> titles = ['Overview', 'Files', 'History', 'Cast & Crew'];
|
||||
static List<ScrollController> scrollControllers = List.generate(icons.length, (_) => ScrollController());
|
||||
final PageController pageController;
|
||||
final RadarrMovie movie;
|
||||
|
||||
RadarrMovieDetailsNavigationBar({
|
||||
Key key,
|
||||
@required this.pageController,
|
||||
@required this.movie,
|
||||
}): super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<RadarrMovieDetailsNavigationBar> {
|
||||
LunaLoadingState _automaticLoadingState = LunaLoadingState.INACTIVE;
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaBottomNavigationBar(
|
||||
pageController: pageController,
|
||||
scrollControllers: scrollControllers,
|
||||
icons: icons,
|
||||
titles: titles,
|
||||
pageController: widget.pageController,
|
||||
scrollControllers: RadarrMovieDetailsNavigationBar.scrollControllers,
|
||||
icons: RadarrMovieDetailsNavigationBar.icons,
|
||||
titles: RadarrMovieDetailsNavigationBar.titles,
|
||||
topActions: [
|
||||
LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: 'Automatic',
|
||||
onTap: _automatic,
|
||||
loadingState: _automaticLoadingState,
|
||||
),
|
||||
LunaButton.text(
|
||||
text: 'Interactive',
|
||||
backgroundColor: LunaColours.orange,
|
||||
onTap: _manual,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _automatic() async {
|
||||
setState(() => _automaticLoadingState = LunaLoadingState.ACTIVE);
|
||||
RadarrAPIHelper().automaticSearch(context: context, movieId: widget.movie.id, title: widget.movie.title)
|
||||
.then((value) {
|
||||
if(mounted) setState(() {
|
||||
_automaticLoadingState = value ? LunaLoadingState.INACTIVE : LunaLoadingState.ERROR;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _manual() async => RadarrReleasesRouter().navigateTo(context, movieId: widget.movie.id ?? -1);
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/radarr.dart';
|
||||
|
||||
class RadarrMovieDetailsOverviewDownloadButtons extends StatefulWidget {
|
||||
final RadarrMovie movie;
|
||||
|
||||
RadarrMovieDetailsOverviewDownloadButtons({
|
||||
Key key,
|
||||
@required this.movie,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<RadarrMovieDetailsOverviewDownloadButtons> {
|
||||
LunaLoadingState _automaticLoadingState = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(
|
||||
text: 'Automatic',
|
||||
onTap: () async => _automatic(context),
|
||||
loadingState: _automaticLoadingState,
|
||||
),
|
||||
LunaButton(
|
||||
text: 'Interactive',
|
||||
backgroundColor: LunaColours.orange,
|
||||
onTap: () async => _manual(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _automatic(BuildContext context) async {
|
||||
setState(() => _automaticLoadingState = LunaLoadingState.ACTIVE);
|
||||
RadarrAPIHelper().automaticSearch(context: context, movieId: widget.movie.id, title: widget.movie.title)
|
||||
.then((value) {
|
||||
if(mounted) setState(() {
|
||||
_automaticLoadingState = value ? LunaLoadingState.INACTIVE : LunaLoadingState.ERROR;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _manual(BuildContext context) async => RadarrReleasesRouter().navigateTo(context, movieId: widget.movie.id ?? -1);
|
||||
}
|
||||
@@ -18,11 +18,12 @@ class RadarrMovieDetailsOverviewInformationBlock extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return LunaTableCard(
|
||||
content: [
|
||||
LunaTableContent(title: 'monitor', body: (movie?.monitored ?? false) ? 'Yes' : 'No'),
|
||||
LunaTableContent(title: 'path', body: movie?.path),
|
||||
LunaTableContent(title: 'quality', body: qualityProfile?.name),
|
||||
LunaTableContent(title: 'availability', body: movie?.lunaMinimumAvailability),
|
||||
LunaTableContent(title: 'status', body: movie?.status?.readable ?? Constants.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'tags', body: movie?.lunaTags(tags) ?? Constants.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'status', body: movie?.status?.readable),
|
||||
LunaTableContent(title: 'tags', body: movie?.lunaTags(tags)),
|
||||
LunaTableContent(title: '', body: ''),
|
||||
LunaTableContent(title: 'in cinemas', body: movie?.lunaInCinemasOn),
|
||||
LunaTableContent(title: 'digital', body: movie?.lunaDigitalReleaseDate),
|
||||
@@ -32,7 +33,7 @@ class RadarrMovieDetailsOverviewInformationBlock extends StatelessWidget {
|
||||
LunaTableContent(title: 'year', body: movie?.lunaYear),
|
||||
LunaTableContent(title: 'studio', body: movie?.lunaStudio),
|
||||
LunaTableContent(title: 'runtime', body: movie?.lunaRuntime),
|
||||
LunaTableContent(title: 'rating', body: movie?.certification ?? Constants.TEXT_EMDASH),
|
||||
LunaTableContent(title: 'rating', body: movie?.certification),
|
||||
LunaTableContent(title: 'genres', body: movie?.lunaGenres),
|
||||
LunaTableContent(title: 'alternate titles', body: movie?.lunaAlternateTitles),
|
||||
],
|
||||
|
||||
@@ -36,7 +36,6 @@ class _State extends State<RadarrMovieDetailsOverviewPage> with AutomaticKeepAli
|
||||
children: [
|
||||
RadarrMovieDetailsOverviewDescriptionTile(movie: widget.movie),
|
||||
RadarrMovieDetailsOverviewLinksSection(movie: widget.movie),
|
||||
RadarrMovieDetailsOverviewDownloadButtons(movie: widget.movie),
|
||||
RadarrMovieDetailsOverviewInformationBlock(
|
||||
movie: widget.movie,
|
||||
qualityProfile: widget.qualityProfile,
|
||||
|
||||
@@ -18,15 +18,17 @@ class _State extends State<RadarrReleasesTile> {
|
||||
LunaLoadingState _downloadState = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LunaExpandableListTile(
|
||||
title: widget.release.title,
|
||||
collapsedSubtitle1: _subtitle1(),
|
||||
collapsedSubtitle2: _subtitle2(),
|
||||
collapsedTrailing: _trailing(),
|
||||
expandedHighlightedNodes: _highlightedNodes(),
|
||||
expandedTableContent: _tableContent(),
|
||||
expandedTableButtons: _tableButtons(),
|
||||
);
|
||||
Widget build(BuildContext context) {
|
||||
return LunaExpandableListTile(
|
||||
title: widget.release.title,
|
||||
collapsedSubtitle1: _subtitle1(),
|
||||
collapsedSubtitle2: _subtitle2(),
|
||||
collapsedTrailing: _trailing(),
|
||||
expandedHighlightedNodes: _highlightedNodes(),
|
||||
expandedTableContent: _tableContent(),
|
||||
expandedTableButtons: _tableButtons(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _trailing() {
|
||||
return LunaIconButton(
|
||||
@@ -92,12 +94,13 @@ class _State extends State<RadarrReleasesTile> {
|
||||
|
||||
List<LunaButton> _tableButtons() {
|
||||
return [
|
||||
LunaButton.slim(
|
||||
LunaButton(
|
||||
type: LunaButtonType.TEXT,
|
||||
text: 'Download',
|
||||
onTap: _startDownload,
|
||||
loadingState: _downloadState,
|
||||
),
|
||||
if(widget.release.rejected) LunaButton.slim(
|
||||
if(widget.release.rejected) LunaButton.text(
|
||||
text: 'Rejected',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: _showWarnings,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/sabnzbd.dart';
|
||||
|
||||
class SABnzbdHistoryTile extends StatelessWidget {
|
||||
class SABnzbdHistoryTile extends StatefulWidget {
|
||||
final SABnzbdHistoryData data;
|
||||
final ExpandableController _controller = ExpandableController();
|
||||
final Function() refresh;
|
||||
|
||||
SABnzbdHistoryTile({
|
||||
@@ -14,159 +12,104 @@ class SABnzbdHistoryTile extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LSExpandable(
|
||||
controller: _controller,
|
||||
collapsed: _collapsed(context),
|
||||
expanded: _expanded(context),
|
||||
);
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
Widget _expanded(BuildContext context) => LSCard(
|
||||
child: InkWell(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
LSTitle(
|
||||
text: data.name,
|
||||
softWrap: true,
|
||||
maxLines: 12,
|
||||
),
|
||||
Padding(
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
LSTextHighlighted(
|
||||
text: data.status,
|
||||
bgColor: data.statusColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(top: 8.0, bottom: 2.0),
|
||||
),
|
||||
Padding(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: data.completeTimeString),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.sizeReadable),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.category),
|
||||
TextSpan(text: '\n\n'),
|
||||
TextSpan(
|
||||
text: data.storageLocation,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 6.0, bottom: 10.0),
|
||||
),
|
||||
Padding(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: LSButtonSlim(
|
||||
text: 'Stages',
|
||||
onTap: () async => _enterStages(context),
|
||||
margin: EdgeInsets.only(right: 6.0),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: LSButtonSlim(
|
||||
text: 'Delete',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () async => _delete(context),
|
||||
margin: EdgeInsets.only(left: 6.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.only(bottom: 2.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
class _State extends State<SABnzbdHistoryTile> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaExpandableListTile(
|
||||
title: widget.data.name,
|
||||
collapsedSubtitle1: _subtitle1(),
|
||||
collapsedSubtitle2: _subtitle2(),
|
||||
expandedTableContent: _expandedTableContent(),
|
||||
expandedHighlightedNodes: _expandedHighlightedNodes(),
|
||||
expandedTableButtons: _expandedButtons(),
|
||||
onLongPress: () async => _handlePopup(),
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle1() {
|
||||
return TextSpan(
|
||||
children: [
|
||||
TextSpan(text: widget.data.completeTimeString),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.data.sizeReadable),
|
||||
TextSpan(text: LunaUI.TEXT_BULLET.lunaPad()),
|
||||
TextSpan(text: widget.data.category),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _subtitle2() {
|
||||
return TextSpan(
|
||||
text: widget.data.statusString,
|
||||
style: TextStyle(
|
||||
color: widget.data.statusColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS),
|
||||
onTap: () => _controller.toggle(),
|
||||
onLongPress: () async => _handlePopup(context),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
Widget _collapsed(BuildContext context) => LSCardTile(
|
||||
title: LSTitle(text: data.name),
|
||||
subtitle: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: Constants.UI_FONT_SIZE_SUBTITLE,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: data.completeTimeString),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.sizeReadable),
|
||||
TextSpan(text: '\t•\t'),
|
||||
TextSpan(text: data.category),
|
||||
TextSpan(text: '\n'),
|
||||
TextSpan(
|
||||
text: data.statusString,
|
||||
style: TextStyle(
|
||||
color: data.statusColor,
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
),
|
||||
)
|
||||
],
|
||||
List<LunaTableContent> _expandedTableContent() {
|
||||
return [
|
||||
LunaTableContent(title: 'age', body: widget.data.completeTimeString),
|
||||
LunaTableContent(title: 'size', body: widget.data.sizeReadable),
|
||||
LunaTableContent(title: 'category', body: widget.data.category),
|
||||
LunaTableContent(title: 'path', body: widget.data.storageLocation),
|
||||
];
|
||||
}
|
||||
|
||||
List<LunaHighlightedNode> _expandedHighlightedNodes() {
|
||||
return [
|
||||
LunaHighlightedNode(
|
||||
text: widget.data.status,
|
||||
backgroundColor: widget.data.statusColor,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
),
|
||||
padContent: true,
|
||||
onTap: () => _controller.toggle(),
|
||||
onLongPress: () async => _handlePopup(context),
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _enterStages(BuildContext context) async {
|
||||
List<LunaButton> _expandedButtons() {
|
||||
return [
|
||||
LunaButton.text(
|
||||
text: 'Stages',
|
||||
onTap: () async => _enterStages(),
|
||||
),
|
||||
LunaButton.text(
|
||||
text: 'Delete',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () async => _delete(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> _enterStages() async {
|
||||
final dynamic result = await Navigator.of(context).pushNamed(
|
||||
SABnzbdHistoryStages.ROUTE_NAME,
|
||||
arguments: SABnzbdHistoryStagesArguments(data: data),
|
||||
arguments: SABnzbdHistoryStagesArguments(data: widget.data),
|
||||
);
|
||||
if(result != null) switch(result[0]) {
|
||||
case 'delete': _handleRefresh(context, 'History Deleted'); break;
|
||||
case 'delete': _handleRefresh('History Deleted'); break;
|
||||
default: LunaLogger().warning('SABnzbdHistoryTile', '_enterDetails', 'Unknown Case: ${result[0]}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handlePopup(BuildContext context) async {
|
||||
List values = await SABnzbdDialogs.historySettings(context, data.name, data.failed);
|
||||
Future<void> _handlePopup() async {
|
||||
List values = await SABnzbdDialogs.historySettings(context, widget.data.name, widget.data.failed);
|
||||
if(values[0]) switch(values[1]) {
|
||||
case 'retry': _retry(context); break;
|
||||
case 'password': _password(context); break;
|
||||
case 'delete': _delete(context); break;
|
||||
case 'retry': _retry(); break;
|
||||
case 'password': _password(); break;
|
||||
case 'delete': _delete(); break;
|
||||
default: LunaLogger().warning('SABnzbdHistoryTile', '_handlePopup', 'Unknown Case: ${values[1]}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _delete(BuildContext context) async {
|
||||
Future<void> _delete() async {
|
||||
List values = await SABnzbdDialogs.deleteHistory(context);
|
||||
if(values[0]) {
|
||||
SABnzbdAPI.from(Database.currentProfileObject).deleteHistory(data.nzoId)
|
||||
.then((_) => _handleRefresh(context, 'History Deleted'))
|
||||
SABnzbdAPI.from(Database.currentProfileObject).deleteHistory(widget.data.nzoId)
|
||||
.then((_) => _handleRefresh('History Deleted'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Delete History',
|
||||
@@ -176,10 +119,10 @@ class SABnzbdHistoryTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _password(BuildContext context) async {
|
||||
Future<void> _password() async {
|
||||
List values = await SABnzbdDialogs.setPassword(context);
|
||||
if(values[0]) SABnzbdAPI.from(Database.currentProfileObject).retryFailedJobPassword(data.nzoId, values[1])
|
||||
.then((_) => _handleRefresh(context, 'Password Set / Retrying...'))
|
||||
if(values[0]) SABnzbdAPI.from(Database.currentProfileObject).retryFailedJobPassword(widget.data.nzoId, values[1])
|
||||
.then((_) => _handleRefresh('Password Set / Retrying...'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Set Password / Retry Job',
|
||||
@@ -188,9 +131,9 @@ class SABnzbdHistoryTile extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _retry(BuildContext context) async {
|
||||
SABnzbdAPI.from(Database.currentProfileObject).retryFailedJob(data.nzoId)
|
||||
.then((_) => _handleRefresh(context, 'Retrying Job'))
|
||||
Future<void> _retry() async {
|
||||
SABnzbdAPI.from(Database.currentProfileObject).retryFailedJob(widget.data.nzoId)
|
||||
.then((_) => _handleRefresh('Retrying Job'))
|
||||
.catchError((_) => LSSnackBar(
|
||||
context: context,
|
||||
title: 'Failed to Retry Job',
|
||||
@@ -199,13 +142,13 @@ class SABnzbdHistoryTile extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
void _handleRefresh(BuildContext context, String title) {
|
||||
void _handleRefresh(String title) {
|
||||
LSSnackBar(
|
||||
context: context,
|
||||
title: title,
|
||||
message: data.name,
|
||||
message: widget.data.name,
|
||||
type: SNACKBAR_TYPE.success,
|
||||
);
|
||||
refresh();
|
||||
widget.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class SearchResultTile extends StatelessWidget {
|
||||
|
||||
List<LunaButton> _tableButtons(BuildContext context) {
|
||||
return [
|
||||
LunaButton.slim(
|
||||
LunaButton.text(
|
||||
text: 'search.Download'.tr(),
|
||||
onTap: () async => _sendToClient(context),
|
||||
),
|
||||
|
||||
@@ -79,7 +79,7 @@ class _State extends State<SettingsHeaderRoute> with LunaScrollControllerMixin {
|
||||
Widget _addHeader() {
|
||||
return LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(
|
||||
LunaButton.text(
|
||||
text: 'Add Header',
|
||||
onTap: () async {
|
||||
await HeaderUtility().addHeader(context, headers: _headers());
|
||||
|
||||
@@ -58,10 +58,15 @@ class _State extends State<_SettingsAccountPasswordResetRoute> with LunaScrollCo
|
||||
),
|
||||
),
|
||||
),
|
||||
LSButton(
|
||||
text: 'Reset Password',
|
||||
onTap: _resetPassword,
|
||||
LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton.text(
|
||||
text: 'Reset Password',
|
||||
onTap: _resetPassword,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class _State extends State<SettingsAccountSignedInPage> {
|
||||
Widget _signOutButton() {
|
||||
return LunaButtonContainer(
|
||||
children: [
|
||||
LunaButton(
|
||||
LunaButton.text(
|
||||
text: 'Sign Out',
|
||||
backgroundColor: LunaColours.red,
|
||||
onTap: () async {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user