From f35c4bf571df407505f9b4820c631adb9ba3e11e Mon Sep 17 00:00:00 2001 From: Jagandeep Brar Date: Sun, 7 Mar 2021 20:30:58 -0600 Subject: [PATCH] [Release] v5.0.0 (50000011) (#343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Radarr] Added very basic localization file for testing * Added minimal LunaSea localization file * Translations update from Weblate (#325) * Added translation using Weblate (German) * Translated using Weblate (German) Currently translated at 25.0% (1 of 4 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/de/ * Added translation using Weblate (French) * Translated using Weblate (French) Currently translated at 25.0% (1 of 4 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (French) Currently translated at 100.0% (4 of 4 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Deleted translation using Weblate (German) Co-authored-by: Jagandeep Brar Co-authored-by: Antoine C * [Localization] Updated keys * [Strings] Removed internal constant LunaSea to ease localization efforts * Added translation using Weblate (French) * Translated using Weblate (French) Currently translated at 100.0% (1 of 1 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/fr/ * [Radarr] Added additional localization strings * [Locale/Radarr] Additional strings * Added translation using Weblate (Norwegian Bokmål) * Added translation using Weblate (Norwegian Bokmål) * Translated using Weblate (French) Currently translated at 100.0% (11 of 11 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 100.0% (1 of 1 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/nb_NO/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 90.9% (10 of 11 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/nb_NO/ * Translated using Weblate (French) Currently translated at 100.0% (11 of 11 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * [localizations] sort keys * [Packages] Migration to Fluttter v2 and NNBD Compatibility (#329) * Updated packages * Added system level Firebase Crashlytics to replace Sentry * Started migration to crashlytics * [Crashlytics] Adjusted dialog to reflect analytics, add crash button * [Settings] Removed test crash tile * [Changelog] Pull changelog version and build from application package * Added easy_localization package * Added stub localization handler * [Localization] Added package and assembly script (#330) * Updated packages * Added system level Firebase Crashlytics to replace Sentry * Started migration to crashlytics * [Crashlytics] Adjusted dialog to reflect analytics, add crash button * [Settings] Removed test crash tile * [Changelog] Pull changelog version and build from application package * Added easy_localization package * Added stub localization handler * [Localization] Added easy_localization package, added script to concat all localization files * Removed license shield * [Localization] Integrated system localization handling (#331) * [Localization] use dash delimiter * Translated using Weblate (English) Currently translated at 100.0% (11 of 11 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/en/ * [Localization] Added ability to switch languages, updated Radarr localization (#333) * [Scripts, Localization] Updated prepare script to print nicer JSON, add additional strings for Radarr * Updated localizations, switch remaining Radarr strings that are available * Translated using Weblate (French) Currently translated at 100.0% (12 of 12 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 100.0% (12 of 12 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/nb_NO/ * Generated new local assets * [radarr/add_movie] Strings adapted for localizations and added to localization files * Translated using Weblate (English) Currently translated at 100.0% (2 of 2 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/en/ * [Localizations] Rebuild * Translated using Weblate (French) Currently translated at 100.0% (18 of 18 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (French) Currently translated at 100.0% (2 of 2 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/fr/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 100.0% (2 of 2 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/nb_NO/ * [Radarr/Add Movie] Adapted for localized strings * [Radarr/Add Details] Adapted for localized strings * [Radarr/Edit Movie] Adapted for localized strings * Translated using Weblate (French) Currently translated at 100.0% (3 of 3 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/fr/ * Translated using Weblate (French) Currently translated at 100.0% (25 of 25 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * [Chore] Rebuilt localizations * [Radarr/Dialogs] Updated a few dialogs to reflect localized titles * [Localization] Ability to disable localization options internally * [DateTime] Show "Just Now" when time difference is under 10 seconds * [Search] Switch to new XML parsing package/engine * Translated using Weblate (Norwegian Bokmål) Currently translated at 96.7% (30 of 31 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/nb_NO/ * Translated using Weblate (French) Currently translated at 100.0% (31 of 31 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (French) Currently translated at 100.0% (4 of 4 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/fr/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 100.0% (4 of 4 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/nb_NO/ * [Localization] (chore) Rebuild localization * [Chore] Moved localization to system folder * Added translation using Weblate (Swedish) * Added translation using Weblate (Swedish) * Translated using Weblate (Swedish) Currently translated at 100.0% (4 of 4 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/sv/ * Translated using Weblate (Swedish) Currently translated at 100.0% (31 of 31 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/sv/ * [Localization] Added support for Swedish (sv) * [Radarr/Types] Adapted all type readable strings for localization * [Radarr] Continued work on adapting for localization * Translated using Weblate (French) Currently translated at 100.0% (71 of 71 strings) Translation: LunaSea/Radarr Translate-URL: https://hosted.weblate.org/projects/lunasea/radarr/fr/ * Translated using Weblate (French) Currently translated at 100.0% (9 of 9 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/fr/ * [Localization] (chore) Build localization * [Chore] Cleanup, move and adapt drawer to LunaUI * [Drawer] Cleanup drawer builder * Translated using Weblate (English) Currently translated at 100.0% (9 of 9 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/en/ * Translated using Weblate (English) Currently translated at 100.0% (10 of 10 strings) Translation: LunaSea/LunaSea Translate-URL: https://hosted.weblate.org/projects/lunasea/lunasea/en/ * [UI] Move "luna_ui" to "ui", old "ui" to "deprecated_ui" * [-arr/Add] Tags set when adding content are now stored in the database * [-arr/Links] Added Trakt link, normalize sizes, update TMDB logo * Add weblate link, disable additional languages on release mode, updated changelog Co-authored-by: Weblate (bot) Co-authored-by: Antoine C Co-authored-by: Allan Nordhøy Co-authored-by: Weblate Co-authored-by: Patrik Hauguth --- CHANGELOG.md | 30 ++ README.md | 1 + android/app/build.gradle | 1 + android/app/src/main/AndroidManifest.xml | 3 + android/build.gradle | 1 + assets/changelog.json | 94 +++- assets/images/services/imdb.png | Bin 10032 -> 10494 bytes assets/images/services/noalbumart.png | Bin 12167 -> 0 bytes assets/images/services/themoviedb.png | Bin 9168 -> 91785 bytes assets/images/services/thetvdb.png | Bin 19844 -> 12523 bytes assets/images/services/trakt.png | Bin 0 -> 18836 bytes assets/images/services/tvmaze.png | Bin 9354 -> 12672 bytes assets/images/services/youtube.png | Bin 7438 -> 12242 bytes assets/localization/en.json | 82 ++++ assets/localization/fr.json | 82 ++++ assets/localization/nb-NO.json | 37 ++ assets/localization/sv.json | 37 ++ ios/Podfile.lock | 94 ++-- ios/Runner.xcodeproj/project.pbxproj | 199 +++++---- ios/Runner/Info.plist | 2 + lib/core.dart | 3 +- lib/core/constants.dart | 5 +- lib/core/deprecated_ui.dart | 20 + lib/core/deprecated_ui/button.dart | 2 + .../{ui => deprecated_ui}/button/button.dart | 0 .../button/button_slim.dart | 0 lib/core/deprecated_ui/card.dart | 2 + lib/core/{ui => deprecated_ui}/card/card.dart | 3 + .../{ui => deprecated_ui}/card/card_tile.dart | 0 lib/core/deprecated_ui/containers.dart | 1 + .../{ui => deprecated_ui}/containers/row.dart | 0 .../description_block.dart | 0 lib/core/{ui => deprecated_ui}/dialogs.dart | 0 .../{luna_ui => deprecated_ui}/divider.dart | 5 +- .../{ui => deprecated_ui}/expandable.dart | 0 .../floating_action_button.dart | 0 .../floating_action_button.dart | 0 .../floating_action_button_animated.dart | 0 .../floating_action_button_extended.dart | 0 lib/core/{ui => deprecated_ui}/icon.dart | 0 lib/core/{ui => deprecated_ui}/icon/icon.dart | 0 .../icon/icon_button.dart | 0 .../icon/icon_custom.dart | 0 .../{luna_ui => deprecated_ui}/input_bar.dart | 23 +- lib/core/{ui => deprecated_ui}/listview.dart | 0 .../listview/listview.dart | 0 .../listview/listview_builder.dart | 0 .../listview/listview_reorderable.dart | 0 .../listview/listview_stickyheader.dart | 0 .../{luna_ui => deprecated_ui}/loader.dart | 4 +- lib/core/{ui => deprecated_ui}/messages.dart | 0 .../messages/error_message.dart | 0 .../messages/generic.dart | 0 .../messages/not_enabled.dart | 0 .../messages/typewriter.dart | 0 .../messages/unknown_route.dart | 0 .../{ui => deprecated_ui}/navigation_bar.dart | 0 .../navigation_bar/navigation_bar.dart | 0 lib/core/deprecated_ui/network_image.dart | 38 ++ lib/core/deprecated_ui/refresh_indicator.dart | 28 ++ lib/core/deprecated_ui/shape.dart | 19 + lib/core/deprecated_ui/snackbar.dart | 90 ++++ lib/core/deprecated_ui/table.dart | 2 + .../{ui => deprecated_ui}/table/block.dart | 0 .../{ui => deprecated_ui}/table/content.dart | 0 lib/core/deprecated_ui/text.dart | 4 + .../{ui => deprecated_ui}/text/header.dart | 0 .../text/text_highlighted.dart | 0 .../text/text_subtitle.dart | 0 .../text/text_title.dart | 0 lib/core/extensions/datetime.dart | 1 + lib/core/extensions/string.dart | 6 + lib/core/luna_ui.dart | 61 --- lib/core/luna_ui/button.dart | 146 ------- lib/core/luna_ui/card.dart | 24 - lib/core/luna_ui/containers.dart | 1 - lib/core/luna_ui/network_image.dart | 42 -- lib/core/luna_ui/refresh_indicator.dart | 15 - lib/core/luna_ui/shape.dart | 14 - lib/core/luna_ui/snackbar.dart | 4 - lib/core/luna_ui/table.dart | 2 - lib/core/luna_ui/text.dart | 67 --- lib/core/models.dart | 2 +- lib/core/models/{state => }/module_state.dart | 0 lib/core/models/state.dart | 2 - lib/core/system.dart | 2 + lib/core/system/firebase.dart | 1 + lib/core/system/firebase/crashlytics.dart | 17 + lib/core/system/firebase/messaging.dart | 2 +- lib/core/system/localization.dart | 81 ++++ lib/core/system/logger.dart | 17 +- lib/core/system/modules.dart | 2 +- lib/core/{models/state => system}/state.dart | 0 lib/core/ui.dart | 57 ++- lib/core/{luna_ui => ui}/appbar.dart | 0 .../{luna_ui => ui}/bottom_modal_sheet.dart | 0 .../bottom_navigation_bar.dart | 0 .../bottom_navigation_bar/badge.dart | 0 .../bottom_navigation_bar.dart | 0 lib/core/ui/button.dart | 148 ++++++- lib/core/ui/card.dart | 27 +- lib/core/{luna_ui => ui}/colors.dart | 0 lib/core/ui/containers.dart | 2 +- .../containers/button_container.dart | 0 lib/core/{luna_ui => ui}/controllers.dart | 0 .../controllers/page_controller.dart | 2 +- lib/core/{luna_ui => ui}/decoration.dart | 0 .../decoration/card_decoration.dart | 0 lib/core/ui/divider.dart | 5 +- lib/core/ui/drawer/drawer.dart | 143 +++--- lib/core/ui/drawer/drawer_header.dart | 100 ++--- lib/core/{luna_ui => ui}/header.dart | 0 .../{luna_ui => ui}/highlighted_node.dart | 0 lib/core/{luna_ui => ui}/icons.dart | 0 .../{luna_ui => ui}/icons/icon_button.dart | 0 .../{luna_ui => ui}/icons/icon_custom.dart | 0 lib/core/ui/input_bar.dart | 27 +- lib/core/{luna_ui => ui}/invalid_route.dart | 0 lib/core/{luna_ui => ui}/list_tile.dart | 0 .../{luna_ui => ui}/list_tile/list_tile.dart | 0 lib/core/{luna_ui => ui}/list_view.dart | 0 .../{luna_ui => ui}/list_view/list_view.dart | 0 .../list_view/list_view_builder.dart | 0 .../list_view/list_view_modal.dart | 0 .../list_view/paged_list_view.dart | 0 lib/core/ui/loader.dart | 4 +- lib/core/{luna_ui => ui}/message.dart | 0 lib/core/{luna_ui => ui}/message/message.dart | 10 +- lib/core/ui/network_image.dart | 70 +-- .../{luna_ui => ui}/popup_menu_button.dart | 0 lib/core/ui/refresh_indicator.dart | 37 +- lib/core/ui/shape.dart | 25 +- .../{luna_ui => ui}/sliver_sticky_header.dart | 0 lib/core/ui/snackbar.dart | 94 +--- .../{luna_ui => ui}/snackbar/snackbar.dart | 0 .../snackbar/snackbar_error.dart | 0 .../snackbar/snackbar_info.dart | 0 .../snackbar/snackbar_success.dart | 0 lib/core/{luna_ui => ui}/switch.dart | 0 lib/core/ui/table.dart | 4 +- .../{luna_ui => ui}/table/table_card.dart | 0 .../{luna_ui => ui}/table/table_content.dart | 0 lib/core/ui/text.dart | 71 ++- lib/core/{luna_ui => ui}/theme.dart | 0 lib/core/utilities/changelog.dart | 8 +- lib/main.dart | 38 +- lib/modules/dashboard/routes/home.dart | 4 +- lib/modules/lidarr/routes/lidarr.dart | 2 +- lib/modules/nzbget/routes/nzbget.dart | 2 +- lib/modules/radarr/core/api_helper.dart | 1 - lib/modules/radarr/core/database.dart | 43 +- lib/modules/radarr/core/dialogs.dart | 8 +- .../core/extensions/radarr_event_type.dart | 14 +- lib/modules/radarr/core/state.dart | 2 - .../radarr/core/types/filter_movies.dart | 12 +- .../radarr/core/types/filter_releases.dart | 6 +- .../radarr/core/types/settings_global.dart | 10 +- .../radarr/core/types/settings_movie.dart | 8 +- .../radarr/core/types/sorting_movies.dart | 16 +- .../radarr/core/types/sorting_releases.dart | 12 +- .../radarr/routes/add_movie/route.dart | 6 +- .../widgets/discovery_results_tile.dart | 4 +- .../add_movie/widgets/navigation_bar.dart | 12 +- .../add_movie/widgets/page_discover.dart | 4 +- .../routes/add_movie/widgets/page_search.dart | 10 +- .../widgets/search_results_tile.dart | 4 +- .../add_movie/widgets/search_search_bar.dart | 4 +- .../routes/add_movie_details/route.dart | 6 +- .../routes/add_movie_details/state.dart | 3 +- .../widgets/button_add_movie.dart | 2 +- .../widgets/tile_minimum_availability.dart | 2 +- .../widgets/tile_monitored.dart | 4 +- .../widgets/tile_quality_profile.dart | 2 +- .../widgets/tile_root_folder.dart | 2 +- .../add_movie_details/widgets/tile_tags.dart | 2 +- .../radarr/routes/catalogue/route.dart | 10 +- .../widgets/search_bar_filter_button.dart | 2 +- .../widgets/search_bar_sort_button.dart | 2 +- .../radarr/routes/edit_movie/route.dart | 12 +- .../widgets/button_update_movie.dart | 2 +- .../edit_movie/widgets/tile_monitored.dart | 4 +- .../routes/edit_movie/widgets/tile_path.dart | 4 +- .../widgets/tile_quality_profile.dart | 2 +- .../widgets/tile_radarr_availability.dart | 2 +- .../routes/edit_movie/widgets/tile_tags.dart | 4 +- lib/modules/radarr/routes/history/route.dart | 4 +- lib/modules/radarr/routes/missing/route.dart | 4 +- .../routes/missing/widgets/missing_tile.dart | 2 +- lib/modules/radarr/routes/more/route.dart | 16 +- .../widgets/overview_movie_links_section.dart | 37 +- .../movie_details/widgets/page_overview.dart | 2 +- lib/modules/radarr/routes/queue/route.dart | 4 +- lib/modules/radarr/routes/radarr/route.dart | 2 +- .../routes/radarr/widgets/navigation_bar.dart | 18 +- lib/modules/radarr/routes/upcoming/route.dart | 4 +- .../upcoming/widgets/upcoming_tile.dart | 16 +- lib/modules/sabnzbd/routes/sabnzbd.dart | 2 +- lib/modules/search/core/api/api.dart | 110 +++-- lib/modules/search/core/api/data/result.dart | 13 +- lib/modules/search/routes/categories.dart | 5 +- lib/modules/search/routes/indexers.dart | 2 +- lib/modules/search/routes/results.dart | 5 +- lib/modules/search/routes/search.dart | 1 + lib/modules/settings/core/dialogs.dart | 45 +- .../routes/account/pages/signed_in.dart | 2 +- .../pages/connection_details.dart | 2 +- .../pages/connection_details.dart | 2 +- .../routes/configuration_quickactions.dart | 2 +- .../pages/connection_details.dart | 2 +- .../pages/default_sorting.dart | 6 +- .../pages/connection_details.dart | 2 +- .../pages/connection_details.dart | 2 +- .../pages/connection_details.dart | 2 +- .../routes/donations/pages/thank_you.dart | 2 +- lib/modules/settings/routes/profiles.dart | 4 +- lib/modules/settings/routes/resources.dart | 9 +- lib/modules/settings/routes/settings.dart | 6 +- lib/modules/settings/routes/system/route.dart | 28 +- lib/modules/sonarr.dart | 2 +- lib/modules/sonarr/core/database.dart | 41 +- lib/modules/sonarr/modules.dart | 13 - lib/modules/sonarr/routes.dart | 13 + .../sonarr/{modules => routes}/history.dart | 0 .../{modules => routes}/history/route.dart | 0 .../{modules => routes}/history/widgets.dart | 0 .../history/widgets/history_tile.dart | 0 .../sonarr/{modules => routes}/missing.dart | 0 .../{modules => routes}/missing/route.dart | 0 .../{modules => routes}/missing/widgets.dart | 0 .../missing/widgets/missing_tile.dart | 0 .../sonarr/{modules => routes}/queue.dart | 0 .../{modules => routes}/queue/route.dart | 0 .../{modules => routes}/queue/widgets.dart | 0 .../queue/widgets/queue_tile.dart | 0 .../sonarr/{modules => routes}/releases.dart | 0 .../{modules => routes}/releases/route.dart | 0 .../{modules => routes}/releases/widgets.dart | 0 .../releases/widgets/appbar.dart | 0 .../widgets/appbar_filter_button.dart | 0 .../releases/widgets/appbar_sort_button.dart | 0 .../releases/widgets/tile_release.dart | 0 .../{modules => routes}/season_details.dart | 0 .../season_details/route.dart | 0 .../season_details/widgets.dart | 0 .../season_details/widgets/all_seasons.dart | 0 .../season_details/widgets/season.dart | 0 .../season_details/widgets/season_header.dart | 0 .../season_details/widgets/tile_episode.dart | 0 .../sonarr/{modules => routes}/series.dart | 0 .../{modules => routes}/series/route.dart | 0 .../{modules => routes}/series/widgets.dart | 0 .../series/widgets/search_bar.dart | 0 .../widgets/search_bar_filter_button.dart | 0 .../widgets/search_bar_sort_button.dart | 0 .../series/widgets/series_tile.dart | 0 .../{modules => routes}/series_add.dart | 0 .../{modules => routes}/series_add/route.dart | 0 .../series_add/widgets.dart | 0 .../series_add/widgets/appbar.dart | 0 .../series_add/widgets/search_results.dart | 0 .../widgets/search_results_tile.dart | 0 .../series_add_details.dart | 0 .../series_add_details/route.dart | 0 .../series_add_details/state.dart | 3 +- .../series_add_details/widgets.dart | 0 .../widgets/appbar_link_action.dart | 0 .../widgets/button_add_series.dart | 0 .../widgets/tile_language_profile.dart | 0 .../widgets/tile_monitor_status.dart | 0 .../widgets/tile_monitored.dart | 0 .../widgets/tile_quality_profile.dart | 0 .../widgets/tile_root_folder.dart | 0 .../widgets/tile_series_type.dart | 0 .../series_add_details/widgets/tile_tags.dart | 0 .../widgets/tile_use_season_folders.dart | 0 .../{modules => routes}/series_details.dart | 0 .../series_details/route.dart | 0 .../series_details/widgets.dart | 0 .../widgets/appbar_edit_action.dart | 0 .../widgets/appbar_settings_action.dart | 0 .../widgets/navigation_bar.dart | 0 .../series_details/widgets/overview.dart | 92 ++-- .../widgets/season_all_tile.dart | 0 .../series_details/widgets/season_list.dart | 0 .../series_details/widgets/season_tile.dart | 0 .../{modules => routes}/series_edit.dart | 0 .../series_edit/route.dart | 0 .../series_edit/state.dart | 0 .../series_edit/widgets.dart | 0 .../widgets/button_update_series.dart | 0 .../widgets/tile_language_profile.dart | 0 .../series_edit/widgets/tile_monitored.dart | 0 .../widgets/tile_quality_profile.dart | 0 .../series_edit/widgets/tile_series_path.dart | 0 .../series_edit/widgets/tile_series_type.dart | 0 .../series_edit/widgets/tile_tags.dart | 0 .../widgets/tile_use_season_folders.dart | 0 .../sonarr/{modules => routes}/sonarr.dart | 0 .../{modules => routes}/sonarr/route.dart | 2 +- .../{modules => routes}/sonarr/widgets.dart | 0 .../widgets/appbar_add_series_action.dart | 0 .../appbar_global_settings_action.dart | 0 .../sonarr/widgets/navigation_bar.dart | 0 .../sonarr/{modules => routes}/tags.dart | 0 .../{modules => routes}/tags/route.dart | 0 .../{modules => routes}/tags/widgets.dart | 0 .../tags/widgets/appbar_action_add_tag.dart | 0 .../tags/widgets/tag_tile.dart | 0 .../sonarr/{modules => routes}/upcoming.dart | 0 .../{modules => routes}/upcoming/route.dart | 0 .../{modules => routes}/upcoming/widgets.dart | 0 .../upcoming/widgets/upcoming_tile.dart | 0 lib/modules/tautulli.dart | 2 +- lib/modules/tautulli/modules.dart | 27 -- lib/modules/tautulli/routes.dart | 27 ++ .../{modules => routes}/activity.dart | 0 .../{modules => routes}/activity/route.dart | 0 .../{modules => routes}/activity/widgets.dart | 0 .../activity/widgets/activity_status.dart | 0 .../activity/widgets/activity_tile.dart | 0 .../{modules => routes}/activity_details.dart | 0 .../activity_details/route.dart | 0 .../activity_details/widgets.dart | 0 .../activity_details/widgets/information.dart | 0 .../activity_details/widgets/metadata.dart | 0 .../widgets/terminate_session.dart | 0 .../activity_details/widgets/user.dart | 0 .../check_for_updates.dart | 0 .../check_for_updates/route.dart | 0 .../check_for_updates/widgets.dart | 0 .../check_for_updates/widgets/pms_tile.dart | 0 .../widgets/tautulli_tile.dart | 0 .../tautulli/{modules => routes}/graphs.dart | 0 .../{modules => routes}/graphs/route.dart | 0 .../{modules => routes}/graphs/widgets.dart | 0 .../graphs/widgets/navigation_bar.dart | 0 .../graphs/widgets/type_button.dart | 0 .../graphs_play_by_period.dart | 0 .../graphs_play_by_period/route.dart | 0 .../graphs_play_by_period/widgets.dart | 0 .../widgets/daily_play_count_graph.dart | 0 .../play_count_by_day_of_week_graph.dart | 0 .../play_count_by_top_platforms_graph.dart | 0 .../play_count_by_top_users_graph.dart | 0 .../widgets/plays_by_month_graph.dart | 0 .../graphs_stream_information.dart | 0 .../graphs_stream_information/route.dart | 0 .../graphs_stream_information/widgets.dart | 0 .../daily_stream_type_breakdown_graph.dart | 0 ...y_count_by_platform_stream_type_graph.dart | 0 ...play_count_by_source_resolution_graph.dart | 0 ...play_count_by_stream_resolution_graph.dart | 0 .../play_count_by_user_stream_type_graph.dart | 0 .../tautulli/{modules => routes}/history.dart | 0 .../{modules => routes}/history/route.dart | 0 .../{modules => routes}/history/widgets.dart | 0 .../history/widgets/history_tile.dart | 0 .../{modules => routes}/history_details.dart | 0 .../history_details/route.dart | 0 .../history_details/widgets.dart | 0 .../history_details/widgets/information.dart | 0 .../history_details/widgets/metadata.dart | 0 .../history_details/widgets/user.dart | 0 .../ipaddress_details.dart | 0 .../ipaddress_details/route.dart | 0 .../ipaddress_details/widgets.dart | 0 .../widgets/geolocation_tile.dart | 0 .../ipaddress_details/widgets/whois_tile.dart | 0 .../{modules => routes}/libraries.dart | 0 .../{modules => routes}/libraries/route.dart | 0 .../libraries/widgets.dart | 0 .../libraries/widgets/library_tile.dart | 0 .../libraries_details.dart | 0 .../libraries_details/route.dart | 0 .../libraries_details/widgets.dart | 0 .../widgets/information.dart | 0 .../widgets/information_details.dart | 0 .../widgets/information_global_stats.dart | 0 .../widgets/navigation_bar.dart | 0 .../libraries_details/widgets/user_stats.dart | 0 .../widgets/user_stats_tile.dart | 0 .../tautulli/{modules => routes}/logs.dart | 0 .../{modules => routes}/logs/route.dart | 0 .../{modules => routes}/logs/widgets.dart | 0 .../logs/widgets/logins_tile.dart | 0 .../logs/widgets/newsletters_tile.dart | 0 .../logs/widgets/notifications_tile.dart | 0 .../logs/widgets/plex_media_scanner.dart | 0 .../logs/widgets/plex_media_server_tile.dart | 0 .../logs/widgets/tautulli_tile.dart | 0 .../{modules => routes}/logs_logins.dart | 0 .../logs_logins/route.dart | 0 .../logs_logins/widgets.dart | 0 .../logs_logins/widgets/log_tile.dart | 0 .../{modules => routes}/logs_newsletters.dart | 0 .../logs_newsletters/route.dart | 0 .../logs_newsletters/widgets.dart | 0 .../logs_newsletters/widgets/log_tile.dart | 0 .../logs_notifications.dart | 0 .../logs_notifications/route.dart | 0 .../logs_notifications/widgets.dart | 0 .../logs_notifications/widgets/log_tile.dart | 0 .../logs_plex_media_scanner.dart | 0 .../logs_plex_media_scanner/route.dart | 0 .../logs_plex_media_scanner/widgets.dart | 0 .../widgets/log_tile.dart | 0 .../logs_plex_media_server.dart | 0 .../logs_plex_media_server/route.dart | 0 .../logs_plex_media_server/widgets.dart | 0 .../widgets/log_tile.dart | 0 .../{modules => routes}/logs_tautulli.dart | 0 .../logs_tautulli/route.dart | 0 .../logs_tautulli/widgets.dart | 0 .../logs_tautulli/widgets/log_tile.dart | 0 .../{modules => routes}/media_details.dart | 0 .../media_details/route.dart | 0 .../media_details/widgets.dart | 0 .../media_details/widgets/history.dart | 0 .../media_details/widgets/metadata.dart | 0 .../widgets/metadata_header.dart | 0 .../widgets/metadata_metadata.dart | 0 .../widgets/metadata_summary.dart | 0 .../media_details/widgets/navigation_bar.dart | 0 .../widgets/switcher_buttons.dart | 0 .../tautulli/{modules => routes}/more.dart | 0 .../{modules => routes}/more/route.dart | 0 .../{modules => routes}/more/widgets.dart | 0 .../more/widgets/check_for_updates_tile.dart | 0 .../more/widgets/graphs_tile.dart | 0 .../more/widgets/libraries_tile.dart | 0 .../more/widgets/logs_tile.dart | 0 .../more/widgets/recently_added_tile.dart | 0 .../more/widgets/search_tile.dart | 0 .../more/widgets/statistics_tile.dart | 0 .../more/widgets/synced_items_tile.dart | 0 .../{modules => routes}/recently_added.dart | 0 .../recently_added/route.dart | 0 .../recently_added/widgets.dart | 0 .../recently_added/widgets/content_tile.dart | 0 .../tautulli/{modules => routes}/search.dart | 0 .../{modules => routes}/search/route.dart | 0 .../{modules => routes}/search/widgets.dart | 0 .../search/widgets/search_appbar.dart | 0 .../search/widgets/search_result_tile.dart | 0 .../search/widgets/search_results.dart | 0 .../{modules => routes}/statistics.dart | 0 .../{modules => routes}/statistics/route.dart | 0 .../statistics/widgets.dart | 0 .../statistics/widgets/media_tile.dart | 0 .../statistics/widgets/platform_tile.dart | 0 .../widgets/recently_watched_tile.dart | 0 .../statistics/widgets/stream_tile.dart | 0 .../statistics/widgets/time_range_button.dart | 0 .../statistics/widgets/type_button.dart | 0 .../statistics/widgets/user_tile.dart | 0 .../{modules => routes}/synced_items.dart | 0 .../synced_items/route.dart | 0 .../synced_items/widgets.dart | 0 .../widgets/synced_item_tile.dart | 0 .../{modules => routes}/tautulli.dart | 0 .../{modules => routes}/tautulli/route.dart | 2 +- .../{modules => routes}/tautulli/widgets.dart | 0 .../tautulli/widgets/global_settings.dart | 0 .../tautulli/widgets/navigation_bar.dart | 0 .../tautulli/{modules => routes}/users.dart | 0 .../{modules => routes}/users/route.dart | 0 .../{modules => routes}/users/widgets.dart | 0 .../users/widgets/user_tile.dart | 0 .../{modules => routes}/users_details.dart | 0 .../users_details/route.dart | 0 .../users_details/widgets.dart | 0 .../users_details/widgets/history.dart | 0 .../users_details/widgets/ip_addresses.dart | 0 .../users_details/widgets/navigation_bar.dart | 0 .../users_details/widgets/profile.dart | 0 .../users_details/widgets/synced_items.dart | 0 lib/modules/wake_on_lan/core/api.dart | 44 +- localization/lunasea/en.json | 11 + localization/lunasea/fr.json | 11 + localization/lunasea/nb-NO.json | 6 + localization/lunasea/sv.json | 6 + localization/radarr/en.json | 73 ++++ localization/radarr/fr.json | 73 ++++ localization/radarr/nb-NO.json | 33 ++ localization/radarr/sv.json | 33 ++ pubspec.lock | 411 ++++++++---------- pubspec.yaml | 95 ++-- scripts/{build_aab => build/aab.sh} | 2 +- scripts/{build_apk => build/apk.sh} | 2 +- scripts/{build_ipa => build/ipa.sh} | 2 +- scripts/build_localizations.dart | 62 +++ 491 files changed, 2429 insertions(+), 1566 deletions(-) delete mode 100644 assets/images/services/noalbumart.png create mode 100644 assets/images/services/trakt.png create mode 100644 assets/localization/en.json create mode 100644 assets/localization/fr.json create mode 100644 assets/localization/nb-NO.json create mode 100644 assets/localization/sv.json create mode 100644 lib/core/deprecated_ui.dart create mode 100644 lib/core/deprecated_ui/button.dart rename lib/core/{ui => deprecated_ui}/button/button.dart (100%) rename lib/core/{ui => deprecated_ui}/button/button_slim.dart (100%) create mode 100644 lib/core/deprecated_ui/card.dart rename lib/core/{ui => deprecated_ui}/card/card.dart (94%) rename lib/core/{ui => deprecated_ui}/card/card_tile.dart (100%) create mode 100644 lib/core/deprecated_ui/containers.dart rename lib/core/{ui => deprecated_ui}/containers/row.dart (100%) rename lib/core/{ui => deprecated_ui}/description_block.dart (100%) rename lib/core/{ui => deprecated_ui}/dialogs.dart (100%) rename lib/core/{luna_ui => deprecated_ui}/divider.dart (64%) rename lib/core/{ui => deprecated_ui}/expandable.dart (100%) rename lib/core/{ui => deprecated_ui}/floating_action_button.dart (100%) rename lib/core/{ui => deprecated_ui}/floating_action_button/floating_action_button.dart (100%) rename lib/core/{ui => deprecated_ui}/floating_action_button/floating_action_button_animated.dart (100%) rename lib/core/{ui => deprecated_ui}/floating_action_button/floating_action_button_extended.dart (100%) rename lib/core/{ui => deprecated_ui}/icon.dart (100%) rename lib/core/{ui => deprecated_ui}/icon/icon.dart (100%) rename lib/core/{ui => deprecated_ui}/icon/icon_button.dart (100%) rename lib/core/{ui => deprecated_ui}/icon/icon_custom.dart (100%) rename lib/core/{luna_ui => deprecated_ui}/input_bar.dart (87%) rename lib/core/{ui => deprecated_ui}/listview.dart (100%) rename lib/core/{ui => deprecated_ui}/listview/listview.dart (100%) rename lib/core/{ui => deprecated_ui}/listview/listview_builder.dart (100%) rename lib/core/{ui => deprecated_ui}/listview/listview_reorderable.dart (100%) rename lib/core/{ui => deprecated_ui}/listview/listview_stickyheader.dart (100%) rename lib/core/{luna_ui => deprecated_ui}/loader.dart (91%) rename lib/core/{ui => deprecated_ui}/messages.dart (100%) rename lib/core/{ui => deprecated_ui}/messages/error_message.dart (100%) rename lib/core/{ui => deprecated_ui}/messages/generic.dart (100%) rename lib/core/{ui => deprecated_ui}/messages/not_enabled.dart (100%) rename lib/core/{ui => deprecated_ui}/messages/typewriter.dart (100%) rename lib/core/{ui => deprecated_ui}/messages/unknown_route.dart (100%) rename lib/core/{ui => deprecated_ui}/navigation_bar.dart (100%) rename lib/core/{ui => deprecated_ui}/navigation_bar/navigation_bar.dart (100%) create mode 100644 lib/core/deprecated_ui/network_image.dart create mode 100644 lib/core/deprecated_ui/refresh_indicator.dart create mode 100644 lib/core/deprecated_ui/shape.dart create mode 100644 lib/core/deprecated_ui/snackbar.dart create mode 100644 lib/core/deprecated_ui/table.dart rename lib/core/{ui => deprecated_ui}/table/block.dart (100%) rename lib/core/{ui => deprecated_ui}/table/content.dart (100%) create mode 100644 lib/core/deprecated_ui/text.dart rename lib/core/{ui => deprecated_ui}/text/header.dart (100%) rename lib/core/{ui => deprecated_ui}/text/text_highlighted.dart (100%) rename lib/core/{ui => deprecated_ui}/text/text_subtitle.dart (100%) rename lib/core/{ui => deprecated_ui}/text/text_title.dart (100%) delete mode 100644 lib/core/luna_ui.dart delete mode 100644 lib/core/luna_ui/button.dart delete mode 100644 lib/core/luna_ui/card.dart delete mode 100644 lib/core/luna_ui/containers.dart delete mode 100644 lib/core/luna_ui/network_image.dart delete mode 100644 lib/core/luna_ui/refresh_indicator.dart delete mode 100644 lib/core/luna_ui/shape.dart delete mode 100644 lib/core/luna_ui/snackbar.dart delete mode 100644 lib/core/luna_ui/table.dart delete mode 100644 lib/core/luna_ui/text.dart rename lib/core/models/{state => }/module_state.dart (100%) delete mode 100644 lib/core/models/state.dart create mode 100644 lib/core/system/firebase/crashlytics.dart create mode 100644 lib/core/system/localization.dart rename lib/core/{models/state => system}/state.dart (100%) rename lib/core/{luna_ui => ui}/appbar.dart (100%) rename lib/core/{luna_ui => ui}/bottom_modal_sheet.dart (100%) rename lib/core/{luna_ui => ui}/bottom_navigation_bar.dart (100%) rename lib/core/{luna_ui => ui}/bottom_navigation_bar/badge.dart (100%) rename lib/core/{luna_ui => ui}/bottom_navigation_bar/bottom_navigation_bar.dart (100%) rename lib/core/{luna_ui => ui}/colors.dart (100%) rename lib/core/{luna_ui => ui}/containers/button_container.dart (100%) rename lib/core/{luna_ui => ui}/controllers.dart (100%) rename lib/core/{luna_ui => ui}/controllers/page_controller.dart (94%) rename lib/core/{luna_ui => ui}/decoration.dart (100%) rename lib/core/{luna_ui => ui}/decoration/card_decoration.dart (100%) rename lib/core/{luna_ui => ui}/header.dart (100%) rename lib/core/{luna_ui => ui}/highlighted_node.dart (100%) rename lib/core/{luna_ui => ui}/icons.dart (100%) rename lib/core/{luna_ui => ui}/icons/icon_button.dart (100%) rename lib/core/{luna_ui => ui}/icons/icon_custom.dart (100%) rename lib/core/{luna_ui => ui}/invalid_route.dart (100%) rename lib/core/{luna_ui => ui}/list_tile.dart (100%) rename lib/core/{luna_ui => ui}/list_tile/list_tile.dart (100%) rename lib/core/{luna_ui => ui}/list_view.dart (100%) rename lib/core/{luna_ui => ui}/list_view/list_view.dart (100%) rename lib/core/{luna_ui => ui}/list_view/list_view_builder.dart (100%) rename lib/core/{luna_ui => ui}/list_view/list_view_modal.dart (100%) rename lib/core/{luna_ui => ui}/list_view/paged_list_view.dart (100%) rename lib/core/{luna_ui => ui}/message.dart (100%) rename lib/core/{luna_ui => ui}/message/message.dart (93%) rename lib/core/{luna_ui => ui}/popup_menu_button.dart (100%) rename lib/core/{luna_ui => ui}/sliver_sticky_header.dart (100%) rename lib/core/{luna_ui => ui}/snackbar/snackbar.dart (100%) rename lib/core/{luna_ui => ui}/snackbar/snackbar_error.dart (100%) rename lib/core/{luna_ui => ui}/snackbar/snackbar_info.dart (100%) rename lib/core/{luna_ui => ui}/snackbar/snackbar_success.dart (100%) rename lib/core/{luna_ui => ui}/switch.dart (100%) rename lib/core/{luna_ui => ui}/table/table_card.dart (100%) rename lib/core/{luna_ui => ui}/table/table_content.dart (100%) rename lib/core/{luna_ui => ui}/theme.dart (100%) delete mode 100644 lib/modules/sonarr/modules.dart create mode 100644 lib/modules/sonarr/routes.dart rename lib/modules/sonarr/{modules => routes}/history.dart (100%) rename lib/modules/sonarr/{modules => routes}/history/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/history/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/history/widgets/history_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/missing.dart (100%) rename lib/modules/sonarr/{modules => routes}/missing/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/missing/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/missing/widgets/missing_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/queue.dart (100%) rename lib/modules/sonarr/{modules => routes}/queue/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/queue/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/queue/widgets/queue_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/widgets/appbar.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/widgets/appbar_filter_button.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/widgets/appbar_sort_button.dart (100%) rename lib/modules/sonarr/{modules => routes}/releases/widgets/tile_release.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/widgets/all_seasons.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/widgets/season.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/widgets/season_header.dart (100%) rename lib/modules/sonarr/{modules => routes}/season_details/widgets/tile_episode.dart (100%) rename lib/modules/sonarr/{modules => routes}/series.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/widgets/search_bar.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/widgets/search_bar_filter_button.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/widgets/search_bar_sort_button.dart (100%) rename lib/modules/sonarr/{modules => routes}/series/widgets/series_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add/widgets/appbar.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add/widgets/search_results.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add/widgets/search_results_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/state.dart (94%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/appbar_link_action.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/button_add_series.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_language_profile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_monitor_status.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_monitored.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_quality_profile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_root_folder.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_series_type.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_tags.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_add_details/widgets/tile_use_season_folders.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/appbar_edit_action.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/appbar_settings_action.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/navigation_bar.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/overview.dart (66%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/season_all_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/season_list.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_details/widgets/season_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/state.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/button_update_series.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_language_profile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_monitored.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_quality_profile.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_series_path.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_series_type.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_tags.dart (100%) rename lib/modules/sonarr/{modules => routes}/series_edit/widgets/tile_use_season_folders.dart (100%) rename lib/modules/sonarr/{modules => routes}/sonarr.dart (100%) rename lib/modules/sonarr/{modules => routes}/sonarr/route.dart (97%) rename lib/modules/sonarr/{modules => routes}/sonarr/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/sonarr/widgets/appbar_add_series_action.dart (100%) rename lib/modules/sonarr/{modules => routes}/sonarr/widgets/appbar_global_settings_action.dart (100%) rename lib/modules/sonarr/{modules => routes}/sonarr/widgets/navigation_bar.dart (100%) rename lib/modules/sonarr/{modules => routes}/tags.dart (100%) rename lib/modules/sonarr/{modules => routes}/tags/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/tags/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/tags/widgets/appbar_action_add_tag.dart (100%) rename lib/modules/sonarr/{modules => routes}/tags/widgets/tag_tile.dart (100%) rename lib/modules/sonarr/{modules => routes}/upcoming.dart (100%) rename lib/modules/sonarr/{modules => routes}/upcoming/route.dart (100%) rename lib/modules/sonarr/{modules => routes}/upcoming/widgets.dart (100%) rename lib/modules/sonarr/{modules => routes}/upcoming/widgets/upcoming_tile.dart (100%) delete mode 100644 lib/modules/tautulli/modules.dart create mode 100644 lib/modules/tautulli/routes.dart rename lib/modules/tautulli/{modules => routes}/activity.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity/widgets/activity_status.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity/widgets/activity_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/widgets/information.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/widgets/metadata.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/widgets/terminate_session.dart (100%) rename lib/modules/tautulli/{modules => routes}/activity_details/widgets/user.dart (100%) rename lib/modules/tautulli/{modules => routes}/check_for_updates.dart (100%) rename lib/modules/tautulli/{modules => routes}/check_for_updates/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/check_for_updates/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/check_for_updates/widgets/pms_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/check_for_updates/widgets/tautulli_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs/widgets/navigation_bar.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs/widgets/type_button.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets/daily_play_count_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_play_by_period/widgets/plays_by_month_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart (100%) rename lib/modules/tautulli/{modules => routes}/history.dart (100%) rename lib/modules/tautulli/{modules => routes}/history/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/history/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/history/widgets/history_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details/widgets/information.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details/widgets/metadata.dart (100%) rename lib/modules/tautulli/{modules => routes}/history_details/widgets/user.dart (100%) rename lib/modules/tautulli/{modules => routes}/ipaddress_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/ipaddress_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/ipaddress_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/ipaddress_details/widgets/geolocation_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/ipaddress_details/widgets/whois_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries/widgets/library_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/information.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/information_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/information_global_stats.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/navigation_bar.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/user_stats.dart (100%) rename lib/modules/tautulli/{modules => routes}/libraries_details/widgets/user_stats_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/logins_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/newsletters_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/notifications_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/plex_media_scanner.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/plex_media_server_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs/widgets/tautulli_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_logins.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_logins/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_logins/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_logins/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_newsletters.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_newsletters/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_newsletters/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_newsletters/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_notifications.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_notifications/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_notifications/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_notifications/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_scanner.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_scanner/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_scanner/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_scanner/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_server.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_server/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_server/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_plex_media_server/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_tautulli.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_tautulli/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_tautulli/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/logs_tautulli/widgets/log_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/history.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/metadata.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/metadata_header.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/metadata_metadata.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/metadata_summary.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/navigation_bar.dart (100%) rename lib/modules/tautulli/{modules => routes}/media_details/widgets/switcher_buttons.dart (100%) rename lib/modules/tautulli/{modules => routes}/more.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/check_for_updates_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/graphs_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/libraries_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/logs_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/recently_added_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/search_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/statistics_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/more/widgets/synced_items_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/recently_added.dart (100%) rename lib/modules/tautulli/{modules => routes}/recently_added/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/recently_added/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/recently_added/widgets/content_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/search.dart (100%) rename lib/modules/tautulli/{modules => routes}/search/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/search/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/search/widgets/search_appbar.dart (100%) rename lib/modules/tautulli/{modules => routes}/search/widgets/search_result_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/search/widgets/search_results.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/media_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/platform_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/recently_watched_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/stream_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/time_range_button.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/type_button.dart (100%) rename lib/modules/tautulli/{modules => routes}/statistics/widgets/user_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/synced_items.dart (100%) rename lib/modules/tautulli/{modules => routes}/synced_items/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/synced_items/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/synced_items/widgets/synced_item_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/tautulli.dart (100%) rename lib/modules/tautulli/{modules => routes}/tautulli/route.dart (97%) rename lib/modules/tautulli/{modules => routes}/tautulli/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/tautulli/widgets/global_settings.dart (100%) rename lib/modules/tautulli/{modules => routes}/tautulli/widgets/navigation_bar.dart (100%) rename lib/modules/tautulli/{modules => routes}/users.dart (100%) rename lib/modules/tautulli/{modules => routes}/users/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/users/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/users/widgets/user_tile.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/route.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets/history.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets/ip_addresses.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets/navigation_bar.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets/profile.dart (100%) rename lib/modules/tautulli/{modules => routes}/users_details/widgets/synced_items.dart (100%) create mode 100644 localization/lunasea/en.json create mode 100644 localization/lunasea/fr.json create mode 100644 localization/lunasea/nb-NO.json create mode 100644 localization/lunasea/sv.json create mode 100644 localization/radarr/en.json create mode 100644 localization/radarr/fr.json create mode 100644 localization/radarr/nb-NO.json create mode 100644 localization/radarr/sv.json rename scripts/{build_aab => build/aab.sh} (96%) rename scripts/{build_apk => build/apk.sh} (98%) rename scripts/{build_ipa => build/ipa.sh} (97%) create mode 100644 scripts/build_localizations.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b8f505c..4f3ae243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # LunaSea Changelog +## v5.0.0 (50000011) + +#### NEW +- `[Radarr/Add]` Tags set when adding a movie are now stored in the database +- `[Radarr/Overview]` Add a link to the movie's Trakt page +- `[Settings]` Added a link to the Weblate localization page +- `[Settings]` Added language switcher (currently only English, but preparing for WIP localization support) +- `[Sonarr/Add]` Tags set when adding a series are now stored in the database +- `[Sonarr/Overview]` Add a link to the series' Trakt page + +#### TWEAKS +- `[Branding/TMDB]` Updated the logo to The Movie Database's new logo +- `[Logging/Tracking]` Switched from Sentry to Firebase Crashlytics +- `[Radarr/Overview]` Move the links underneath the description to more closely match the layout of the web GUI +- `[Search]` Switched to a new package to parse XML responses +- `[Settings/Tracking]` Adjusted the dialog and warn users that a reboot of the application is required to set tracking state +- `[Sonarr/Overview]` Move the links underneath the description to more closely match the layout of the web GUI +- `[DateTime]` Show "Just Now" when time differences are less than 10 seconds + +#### FIXES +- `[Branding]` Normalize size of all branded logos to have the same widths for consistency +- `[Flutter]` Fully move to Flutter v2 +- `[Flutter]` Upgrade many packages to NNBD +- `[Radarr/Add]` Grey screen could potentially be shown if no initial search query is passed to the route +- `[Radarr/Add]` The navigation bar could jitter on first load in some cases +- `[Radarr/Catalogue]` The search query would not stay active between profile switches +- `[Radarr/Sorting]` Changing the releases sorting/filtering method could throw an error + +--- + ## v5.0.0 (50000010) #### NEW diff --git a/README.md b/README.md index 5e7089cf..925527ab 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,4 @@ LunaSea even comes with support for multiple instances of applications using pro - [Gitbook](https://www.gitbook.com): Gitbook is used to host LunaSea's documentation - [Hellonext](https://www.hellonext.co): Hellonext is used to host LunaSea's feedback, changelog, and roadmap +- [Weblate](https://hosted.weblate.org/engage/lunasea): Weblate is used to gather community localization for LunaSea diff --git a/android/app/build.gradle b/android/app/build.gradle index 1db81ab9..bd7f5d03 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,6 +24,7 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def keystoreProperties = new Properties() diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 16c60076..db30c7ac 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,6 +13,9 @@ + #sIo7Xt_2;^5*G6y)R- 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 Y1Ert2(><)Y{De)CIA2c literal 10032 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 P1YB}bN|L4GM*jZ?F6?2~ diff --git a/assets/images/services/noalbumart.png b/assets/images/services/noalbumart.png deleted file mode 100644 index 5a0678589d29ebd69566dd0275deca2810a8660b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12167 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 literal 9168 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 BEA{{Y diff --git a/assets/images/services/thetvdb.png b/assets/images/services/thetvdb.png index e781cd2fd274c54f200908766ade9398d3cd290f..a7d761593b66ec2003d51a45d362b5816ee4861c 100644 GIT binary patch delta 11531 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 oHDtkPg1&&H|02AGd|xpq zP(v;$CYqm2<>a7fA!!7t*U;!tuOKOC$R8B+2PnAbG!&FNG~R!w^`WW%DFXv3!vY2Q zhQfq=OoPm!VE-xa3AsMKq#z$iO)pnPb5|29YePqXJ+Y!afS8q$nUMpM2a^T!Uv$tH zS+D*p4fPk22L-!eK?1oT+Dd6TKtUm6Jbj>{QqpjspkA4qt7B$I!^6bP%EZdb z04c%X;AZ1!=*nQ@K=x9}f7K&q;$UoVZtG|cupxe`*U$*yWzh3w>aW(%} zOEwPwbPLi!rl%Yx7Di^K|BVdN+5hJ6e^9v^+WtR@9L-JtA8kG5ypTN)#tV=9PaN<* zKSV7tb88bD2S|SeS=pKSpLzSga{kr3f1}j=7bOSt|DgQ0oPVQ)bd2|-y~&f%r~&|1 zg8!Qg@iYBrssE@z4)Vai7&;n?89JH>vNE%>Ffg+*FmtOiv+%O8@Un7fztr$Qiv3#^ zPou%Bschl^uyT5OhJswI%#aro%hNYM)BlzIpQQhyRI@R66lDE}o5M+KKf_J>DAmvq zuo;}BpZStWQ)7z+0PfB%1N1U7_)!#-wwip@XUh`Xo`0#8z{6$-x76PjW!;xm^Z zJR6sq1QzvV+I-a}{3u&rqzZ@I!x&yGU{&8c_JdTAX~*%Ga)g6Q7Q5nH)z@Q-Fm}{8 zFvL(#Z$m`kvBR;r?cI7V`~QGDaHXx#}6ai zImZgVMEqgAi9de^+o?;vdlnU9IK*0QkyW>oMz;tK%RtN5m^e0)&~7jclq9Z9~KQwrvm=L}}A$$|QN6gF1()d9Tx2vURm%n})snAz`vD0v>Res8z z=@7`-0HWhz+YSBgV*4^EWx>MIi0<=!zHq+7+gujV#ScVF^HU=? zvLmx}iG*)Da9^6B#gK9r;R;Ukka+jO%xb|SObKHzbAU^x&PMx}rLVK*RVEQ`fghqiX#acY;qnHaji70!-@d>)pL&jAj>_J{`u%MaUC`;J=#yx(AM~oaPGm zJG1f=BE=;{pAG7>mUylor&4_>PDm7B*exZ}pJgxWW&>oy< zlTzG5d{2I!NZu5Duh(6?$cGo6gy$6o5^eeS^;d+p&fdv777+UPFNYieV>8~lpF7n$y#E8WYn!x+p*lKK(f2v+0U`} z#m0*Wt2ECzMgB8zbW5+=za`UCcBR56*c@4ZP(C#`eacvQxMAC7VTgT<~o1+Ht$AH0$8Ivg3YG^Jv_D`No&7SEEq zT9GZnJ3RYzcPw-BWVCBPA1{RK%9$rBx*i50kEacCV zka%nZ7F~X9uqV<+p_EIIa88Ry@WTi9MCUl?eH`iL>uUdF`N#tA-*yjp8j8b5iA6id zpg!tFBK$n}69>m(_nc)78LI5M^3ZozPXdf;0E0-eXM&xs^!ex9J*i8@u4HJp-4_iD zhvVt34?L76+MCW=ICst}!%G^)?zhPA=c;CHYAKy_>f~FSbffQP{Pt2G<&=ZqUqssh zRSH3b>rJN?@7CPiSB=Y7-Tq*8R|mpW1#6qeWL+}vW&E@cdAQW`q7wV>SO-8c?F|i_ zL)7aGqo8T_%1L`RDbM=ZG=7a%W2%XT-I$D2No||i*UnXK%oeo$e5Y~?;2|Ktg}3CF zeqg9W1R3Az%wu{*fn)-dkuPNOLLtaR`iO7%Y4W| zjo6at0-kA2G+!Ry2JnafjDis_?Q6F_g!a9F{nJ~6rp(v-P+6Ww^ar>Ux#o5vxf=`o zn=5td78gE;M5!Bekf!xqPcT6CVCH>1gtGm49ui&FSP#|rhLNCq5#yT=ND#$ju1H(ZN|p{VZH^y)sZ zflph!pf~pEj_y-*Q*gfkStZ3NF}vR@K5kKhCRlYnE7O^`lXVPVOU zr>@ajS~hg=VO6&zjXxe{Q6_$3a;Zt-bo}x5Dw94r`#oCEiz*$K&k~2z``OuMws*#K z9#?B8&9T|IweQp5zp%v5vJrjws3ue7K4WE*?v1WgU!&}1Y?bCctT~W$J)2ZO+q7O< zAXkvWLqthpS&25io!M8(Q@t&&s&?g!v# z;9NHC_`A9DEap)^P_cW@n{%&bbpdafuCMP{P|6cPlP*OYEB)jW`=#6ry#fNe&GB5u zAWlq+%BPX>-60zz^=qq{WYHRtUqJ~!_@->o=rM#a;Q5#363{E!x9UUDWUe#ZyW~9J z+}+~dgR$iXAj}7!y|xESdoAdH?lmoG0Xd(0|C(~1_s`ClJ-NIx@bSR~2YX7R8NRH& zs17j0jC-O(1oIgvA#d)CM)Z!7V^6o@S1749=77ITYr!GlFwe(2`-ou-66<@oI5+Sr zJw}E1nR$u(TE9Er?pj+M%f7SyJNhC}jnf|{k-JQO} zx}B~R2VB?m?Jth-j;!5I^!`GtEjbq+1p5RxQE1^juc|=wu~a&@vteDwn$p^#^j;zn zYqyKW(4??pvv`+f;X4U~@uu!H4-UU{P_B5bOBY-%_!K23$rv!(h%}P_tD;rb=*Z8n z`RnQacLOZCN185QnwLrN29et+N!?4?h}~npgaLrNy<#x!p`F$+$`??bkuKuScPF}$ zd zO>EkJqh6nY1^JD)uBfkpG!uOY>kuB&OjM_{1+BvFur1mv^!lR*8JEQ zU~#4+Yr{4Zxc5J%G56J5EINUD&@q}%J1U!TJRy-Zx zS5+Tsq3;&-W5D{}+KbuT=CIDQ_Q$U+!Bbr%E6tHjv|kI&@q)&G^EM;L3NWqpJ~P4m ze6cx=#EExS$JDUHtN3CTe^$Dz^OrOj$DFg%v3Zjc=3)Wwvrf188_G5!4*P*U*d(O~ zdyC=K8Qy;QlJ5|3SGrX4T(Ap4&U+Urb2ULLH9$2UGxlL~}p zRx}=eY?RneT2i>w-8wQFaRMH;I#u09*>g$~^o*DFp~-4Oi<81g_aZ78oY1U}XbV%g zR!9Y?($jj=iCebh^IUlA@9S*`edH8f+UOJNO{bO%FTwT;C2z%9+5BHrcUOe)koKO3 zw}5MwkKBBs^Q%#w7Tfb?mHjJgO+rcYvQ4URq%| zUl?lvzs0FakiE<@^~@;meO}r=F!aNhP~?9mKS@?Esah3WiBD%+{ZVM+o@qVZ&f_1G z+S8mWRA3Vx#p{k~Z#I+@QxrRl2OX{`aIYGP=sWl6p~s|wUVYh(fc^=dTomE!-G^2;#EBbzwFwe>{3N^SXfU$r!p3rG3$Qi^|-~2h0x&|;#rRx1qR^pID zF)jBa%hWpA0rNq;wD^9*j$hQPq73P9*@5P6TWhP*^;40YZ0&tT^U_s`y}N$TkF<(J zJ?m0p=9lV&lZ2-rk3vPEWwabJXUvmhkE~fY9P0FcvNN+l3loz-T zxcDCc*_SAvYNx<}*9zbH{w%2H>FIK0Uu;1qF0cloy~u4BY>q{CVgx?xkY@88+sLxL z2pS30e2qbM9izu3z5d8WbYYCCrAqS!Mw!VJ zS-oCAd)Ev5CV!)u)zt|{?pvNE?Lze0QkXlAhd}M=#9`mvI9|f^1RtU3JX;1;S=r)5!l_9@RqJcIeyvzPxF?RqWQmPL z3a9aixM)3Z5_o_yc}CXGBv9(*zf2gr_`7vkgWUw|y`yQ{*9mo&Pb0|gRCa2E4sr+N ze;}QX34Dxf6*v9P{2~y2h+^b>`MW51m=N=6MgEu3EIXfNO&DOOZu!U=U5+$058WZQ z6!mgd&r)V;*({ud#qxIq@=>vy>#T_xT$!IS6_(Y@6g)yz#JWgV=`kHI(7X3tTnKp> zI=1rF=1X}<@E9A`4`NS&w!K{^_zC#&7J2+Z-}`d7j%=rFh|MPRkIcZrn0ZBXdBxIs zj+#3H{Ieh_Qf1NajktJcSQVD)9nHkwtQjXzpaX}xr`SE&guc;!U{Po{q^|>CBin;} zPIT>Vj#-@56>qlN>oW=&CEA1@$<2AJw-D`%_S~5SfL(|5Zw+6}#w;=6Ph&DnJdH$5 z%>xIP>s>Cu!pF>QVr~}YCeJ@kNh#_rqw>ayqy@Ag#%EGj%@)vrMg%e zu0@$Z{2GyDhcXPOe#+9cO){hY(Yk=}D(R-fz{;uh&P7$e==cmV6&>c~!Kot5p*vQ! z(8W(_`k?Nnu@#=b5@1l#eq9B3@K|UjV;#d#+1Rb?nNf7TJpDGo!Fqxf@TN?DiolDH z>s`oR)A^%0c@KYITEb)i>uK{xQ=>p0^GiaVb{aOAh!^#_jyQ*YvFvsGW7F3*p)+Zc zQ7AF(At+!p{Ns;mg1Wn8neZ6;N{X%)&m-=00DU=?G0l!sf?A{;3HTuW@+^l3jck?9 zrZM(MinKZF$#^^2?pSUg$(w5@6}!me&l`o3fWJ<705v)9mmR z1Zr&KK`ZhSp8^8Z&SC45lCW*cf{b*A3RgZ(kG823F=0_Xn&7z_+)C2N4tPdkr>*~v zRv)?cKKw$5=l}iLp4G&t^xQZ(S@ysWYuXlW;N;RDUX^c_a<~|J;Bphxrmy={&@n@~ zv(sAa{it@%pz}pPke~M!UvfY3f%l7MWns3G$S3bizt2KxFZC5+L?JaT&ot(`U`w}e zXq%tpl(wEn_vUlH8ORF*F)!y&F*p$?Qcv0vv z-&Ds2NAMhrTW5H}cu+cG8cW&*;#l>L%e03MZK{1PolCseapxH-^xB0UN(Lcg#jL2U zr^xEsAuRLp*$RO|@TVZgbX-uj^O#F=f*vsj_Ivqyjx+*pNK2A|(Q3+{(B82Kxu%v7 zg5lwutpxYbHs~Kh5wEoOGd>(PMvzPdVL!`z2Q-W!T&rUrOQ&n{$fJ7J!jgSWJffPw zJ+@5h7v^tCTun0Zf+g;^2azu@0{4PEM`AdY&r82w$_ARiAe-+FZh1`vQ@;9$1N*6DN9p za51G4x{Xb(IQWP^Uh<^`Qba3s8Lf=S&4ax&(y01&cc@B}QtQV!jG$(LYCzVls)MS2 zRe^{6D8tJPn^ta@%LT&w+>7bE?i+t>HFT~Dm$WcLsyigP$(2guuf2hX4|u-@tBvvU zhxH@c!@x+5_uWbb@yz4NnV+d}BsK(1u;6!#C1OO`xsb&A6GQrwIXWt!A%zg!fcg|MvLZ~f}nEjJRRVz_Ay;7Kdwwt`}{hADnNE?<7_}Noq zJ>@rI&fn`p%An?AP-~>0n2icdE=lg~6#SCdZ0OpngGqUugS;ivVt?ofW*gO^h#g!1 zn!MO9Fg;L%;wR+C}`YkOMa zRGC9?ll5~i?Ntj1Z2$v~AmVW74K<=&i1dKZCX!Txhp0h`rV z+kWrDb1FALOb06^1P=H;P^4NdUr2hJOE~^mlJ1SX3EHkl0-1l88s(E6uml797kPt{ zWB}H49S@_esGPE}+zR+25oFEM)?I~!r$51Z#2liZEyFE9SR>q|8X*by9YRlC? zs%Nbu#@~#V527*yC~U>C!J0PdcjTNjcvE6y;lgXmr3+2H1HJCo8Vn?>^_FEZpZaWr z-L~qx*-m}PUtjm@F5uh%3OUP7H6LG_xDnG+AZ|I7wG~^|QCPt>dWwGAT28!@7K)iX zuo-2q`+0!V)?J65Q@%kvckPJVrh{<0`MCho%L26I!5WMRF`uXaqIjHou6!thpM zN;udp&Lwa+-($s7FD2L`?(+5|Wl>3y)7x>vu}YfmM~5bQ-$vKUHhkOOguE(OC~^U< z*!lsfJEeB6M<|_uYSl)LxHn1IjYYRBjme7e7=hNi!BftYXfql3PFyvQe&AW*CwN37 z!c-mghZ4`*lxx*ZeD^qZw((;d`nwzcVl)^XI_R?;(1$ShoOd6>#9f-*06Kd0e5sK=p9yD*D<`?R2+v$9Rtyc=IWIr6EF`#7uvknY4_Ks6$~<5LFi@J)ygRDybAIByLOR|2wNZ@~5#t>TAVX*UW=&-vZK##q(0Q%&*jeaA zaYe;TWe&!+-b0e5AlG5MDc>+$oAUEpvl`+*Kf5|V-Nr0YFHd{wE%l9_nB<)u6)@Fc zxP?`ha-RvbFo8pi$r;nhmPdR-E|>ukfR)^UIXh%W4f3cMl6SKTdt-z5RPNlWOGun; zKjyDhNyR_YCGhH@itw|Ay1ouxEhWJ22j|~99>VD@ZqhJ|R=|3UU0xPCT{!h9MqsW-aIKrF%sZv!_9z-BK7P8_N| zY+ia>FF17Z#>Me%j7z=8uDI^5trlmPP*;mqC8yLBus`u_Ii5j7VCK|ZX@8wa9ZEmt z<6o(_FrvXX01|KrN8ad`&~%W4WQ6g}k_Cyj2JM#0@UK$LBP!}S%6OWv<$T>WzW2xs z*CA-f;-P2CD=q3{gXh4-Sjo1p-*W*P`$nu1aujZfU3DxbhofS)4 z5|GSQxcUJd`gRD&;YJp|x9X$RV9_ZtQhI85;?Nk{da4!d^*Vp%Aicy=iF0O;*W?>4 z3YK$ygjhWgFWyL{<&g6OPoyu>)hELNr-NR&2Q%1!)=(d&Fw)?}b0rHnfiW6~A~m`6 z-gIh?GbV$yzqyNQ-TV(FsnnaJIb46g0UsI?2Eeuyxba5ciwfQuc}Io#Y%{%|YdXo2 z%w=O>h)sJHVYRjRSJ_i?RmWD@!O@s=2tfl;G{(N}Y&?`RP{J5D{Cg&;ZvcA1a6Uo3$T++9I zqv>B$QZr+(dhgayTW@c|1h{T$!m8|V+6L4pRX4~Q z!8nQ5YiA>sFz^TDmB}48g{yhzIy6L>4tf?iy}QK9(eZJ7P(#mTuA$Mrj}BEsg>k4e zN>ew}r`yv#Y^zQQyV;EpeDx>wuX=IY_Ik5Mr=-qlJE;fVy03P)R1{9ZFsSkRi5yjKC|hro4gcbJ^`oaR+H5La_Qzz1ih* zL$>+i@;6jXi*g{&%wTAJ=X48w=Sgc8J}jV8+WeR9H(_)7Rt-6Sq*=nIm}kRZA(KeK zEssk+`t-DWYl(UbLA-sAnnIQ7Ou#0SHouGZhG@ubhGBbPQjp<}!7qj**H>ubLz1)2 zUjjq)P8lAoXLQ!{>d=E1Kem-4d#1r$Pt!}UCSQ5#|K6=go#l)$v(32SJVKxFA7pHr z1LtaT|K7tl%|DMROcFmxI&20;qDG}x(`wb_-QyvdjBlcwY7CW{3CP9Af=BQMS|l-D z^eX5Ne#d_tV9k#EHZb+<8oEZ3?`$a7sR)Ki*osKJ!Ang$vwz|Ek#tsNTU_q`XLleSunRFw0H&#v`CT2W z6TWxw6uDxp^oNxg-{{artsW`yW#D}M*2CUgw&bYZ2L(B~eZWpO?Xf{4O`J<>=Ja3= z^Hg7^0TDD+vHTiIH&RdjWe*eKa$_19=0MZ8ZgWdxk1Jr+YFf`@%;8B|y>welo;GFh zpa=Y}W3-Wt0$b{SsGW8z4@&6dc^UPdo4p7KV0q40Cta=e2Vy$2>?xd1l()&Eg)~tK zZ*whSs85v|c&1zl-B#T4YY&F_0JY842!gghby~jSkb8VpD-VtJNjU4|cLlBbYIm^( z#(Q8oLlw%jePU9q#ppNsGSj!49`3Krh!dRE>~hpbzNf6IBJSmBooeQ}98$>5w(I}Y zBQn4aPcj1^x(#@nbMl-w&uEY)7z@Mop`?I-QoSDMgvLnXsq)P7{rlxz_ABkRkg!`T&TT zF+2%yqDmdVM%1JJs{Xps`NE=ch`6*UX52or1m{E^LH6iA;$FP)@lYEDF+Q_7C& zwpt{`E2oa5DVBBm82f6L*F=Wy%h3wWTfj+&W^kjHMC&FAy!HVL`sIL51)z?>A-1?` zmMpVrre9)nQSk$=rD?eFAR20O1`Q8m3hJ4{wE{xpz{kwtL%o)!R@LT~;~!r%j-54* z&ntpv{HSOd9F%^RUUwH3!3`$bYuO>UI`fLmh4P$l>E%dtw^iDu@)0K(*(7izez1Rn~GQV7W++qq(K|6cZuI8X{HV~F@bCgg{)?OE!7&(q~6RLE5sx@B|Wo?oQ zr|vAWUff_f$S|aFNkNQ&DBl=R-Y-ePv*r0fL1C9Yhi_z~PHviGLpe7P>6^Z|4Lh+s zVS6Gz5c7e!k^{XVLwpQ7D74h33%zBkRLxG!KvC5L>F0T*OT%>>3(rb`)Xx14FwZlQ zxK*Xzztq41GIz9sn?8}==B|TeFvcAm^46Ea-kT@>39NL_Ia7rg-=UYBD zjWJqF!x1){&0h#)$XNl|(LInhLw%l&`qr4>@zEflv?GBP?r={|M|5adS#(MceViaX z6v-SWMQ24cQ9=S@um9!MxPEk1O$jh1-d$86ITiIj)BG};`$`!U5kFvry?<=F7f&GEU22>sSbIH zR%P1%LG~;%N5ymsXv%K!7L6vb80U)w4fseUq!OLn}cXrwD3B>e5;eClw z>0maRf!vRWzhPYPz{Rzi#%h5*`?6n;4DASpk%R5)J|Dm2o%eb?29gMV8nLTAW__Rnnk@#tE+n3K3Jb5&Yt(WJ>e)pbz=cCxWS zJVMztrv=B1ho&q~Xq1%+K1C*L{ugKi_4HVD3Lx>}&`XX~I>ZFEDp=S8=wp&LDCc zyJMuV981OM6ELEK3A?D0s_v-(ESz$mS;3SN9G8KS#2pNdH=PsI(Lz>r@6wG$Su>2} zM_$pz=Nq_ThzU@-06&vz^R0Mu)hvrdN|^b^Fob)G>RL#z8hj*&st_aYKsig+jmQr* zaSJ6{%UNvhCfKEGx|ghX?TI#CWWNcbn|A4lJ)#S;Z z|Fsx`V!#vUb1mMzBn`FC^B{#NX|g93#3|Y8QO-_cyvu3cfba(Ai}jaeq&MjP9oC`S zd!tup7H*-8M-45q)T~IH3RA!Bew+LhGE?&LW7?VVnBzmsH13kN-$1|?`&O?Yz_y&^oIi}bqBvLJ&4J~&pOjz|ysah; z*rDn``3sjO({Ng{j>H^Kv_jAc$C>NXqEDVvJdr;n?Wv$Z>4J&E|duA!0*6Pe#v z{1-(#ib1Jnn;5G?QwH3l9KFf6l1yV*VE=t{2XSMQL1sDJ&sn@*?t8p)M9M`s-u9t;-jVK`uIc&&Qq@-FDEdNYYMT6{r&FK+s z1e4>WH6`mqn5JxXj;&uT7ZM5q5O}_&dj?B`wjxdh^5&JhH6&{-9?_d_TrVJYDLJTT{ zazS=^bpp(nbaU-zZmbY(1rL$8Kab;fOC;wsmfd(XmAMnNBKv?zTGl@pDIH|Gyd+1o zCoEV_XXy<*%l-)HnV2H@%}OhuKaRi3+UQ}U8ZV7jjW7rn1NbLpmTRxJ;TKM$#IViU zCknE6PjUFHN3Q$e@JBAX{cN%1h=v@%&rGVGQF&)y_G{3G)fH*Tw^H#>Hbq^Oy4u`l zqA-$OO8_~)}Hbc{jnl2%pf@(;X(I6?czhRLy z{x7`&C$!aXsom2dONXH$E@pRZ?>EW)-3SI#$*5&ZiO4nR_OuH&`RH8RFwb{O9R5~T zeu5l*X*}F~I&XP^Q(JQK3JY}~9;z?4&^`!l0_JUYK2nN(JN38$NYCxXdk?M_RDPGy zhGA>mni293oNmsQcg4Hy*`bXoKS-|X*G?Jm$F`!6pjcG&g07ftEN$d>1feNYtu*1RD~3Ru`GKLfHMAHbK=WytEmQ- zE#Uj7k(%_C8~YAUZW6qhIPCEG$d6nuLWKl`S0cO3vc?$Ex|Zm1pTvK8+)h?w0{9L& zHCRcd-($1_5swkZRd}h~naNX?8nXL1f$>=>vo~HdOEWr}UOg9cQ!_d2LKV)IFwsEm zYuUg!dCN(+nS3l%qa-hU*z`%vgk3sa!g75b}csH?E``j4mD{HdbkdI(! zQX`5hTA&4gd3U}^e;`_Gv`so6oX-Q_$x*IB?k4v&O^Q{orYH={oqToj&b~#ROR8Lb zto!YZ6+eILsyBmdC+}=uv|%T;e-RZ#ZDD>ZN+l=dYZ~QO`!IfY!v#?)MbW=HAWDS> zvL&%(q&P3zJ+M{ta2#qUngR?Had{WT-ytfo*xhy-@W%Z0D6fR*nyRj{^&EPMk?v{o z5!0ipUMU_qRU7U!Z*Hpx^zoU#h70cb+QYSqG@pj z1MiSKE?|_&oAaNTPE8qIS0&XfP(~NcwmUdb4u@)`EKu&fAF1#1=il}~1p^NLn8@B+ z902P&cBrbtEL4Qs%O52I<7!+p*mQi4bezCgRZdp+yUIMNzfY z`|1HWqH{*;UBNDAoy9(k^HQo-Rk!>?7r6|b^J$6F>Eh%1yPn-r4+)NJ+%-CwZ1~+* zt28}`CWND_Y>}D;_f74JCdj)k6kx$FvuN@4H`*Nr+b)>Xr=U%O|zYi=cSCKr`TAVNnIyaA~{5n(6;LXL<2$-BF^JebPp+25C?8R`+*dN zQkGds0nZ28MDU_sk=pX1t1wldFb$Ja7dH5Gx3P5AIpzZi>NB`CD?`{T(PJ?Y`)EVF zCLh3(v%P<}K6u-gEkD7Ck1^1kZGaxqNX^}}ink(IT3XA}o{-`y_^A0oA8!va*jM=< zL@6iY6ta7nR{@Z$)HTCp3&nUAcQ=6Gc!m4;t9E7duN1t*-zOYm%Y-x!eIxwSf7y$x zc5^}b>vg`z1gMUd(v{DcQFoZPu*(MPnL0%1J6&w2Waugpe|Q*j2^vTF*mP}MeON;p zwZ1+r^!F?e8)2RB_Q88LAck8li_Ya^!Q+NptD-h`T-YaQn{X-V8bbN8v4?aRmGeG# z#(hj(jOG0ne>pY;$AXDp_q&mdDbYgTyT;exulSQb_XelPZLL=OEfr>V4kmGQn59e+ zra!>P&gfH}?uMaUvvG~Kp!9QWxlS}+S$)Ry%r>c|0P&c`fNE~O#2uHuJ@;x4{c&Jq z|29?u&fy)7Y+!}Ir;IB6pVx`Rp+H$G6TvHl5MF!+madW*&Hgac1RZHP*03t!y!yVzZ@7)cjTE z8G4H_!bwK%?_GUf$-oxldBiTfEX#y;5iLT5CuFGaj8s-}mRUi&5!@eOcm0I8sn|W| zto|Z-vMQ=WjWQy`LG0rPY$XNogBoQ?*n{J#C8#Fyl`Erw_){?0}({Osf@QOLOrTw>X+lghR$=2S)slr{nH?fms3z3SW%=~ z0}bnnq`9ie@hy~#*nIT4%7h6fJZ$e;u1J_!;6lu9s{#r+hQw5?){vZFnbuT|8Grxx z^`;c+6#Q_n=Hm16W;ZRqD*G?4yL&Nbj;X_Zf`|ZL)E7Chh1B1~oZ+f`N}yT|2Cb7K z`J8zz&9`&$yyIj2nVbt6-+H*kK(eaP$@vvsROkok03bZ)q-{%98eG=W-0I$=!v7W! z5<;`3^d=Cw_So|#S6m!^=OCcgkLvj(SeP4f6703GV4Zq;GH$|N(U^hFSp65pN>|1_ z@loDXG@`Aw$aF*p-uggndAtx=?a>_lW|l~|xh-Gg8=q#;Tv{Ea((FPb&E3s@o=F`t zvO?_$oz3RkDthf*_kzRWCGpiZR8A7uCkP!Bf)t`ZrX!l=Mn@;DxkjC%-Brhkz&(RN zkoetoCGNgnlgek1`#F<=%a}wkuS<7oSYhzDFW3$>_GRKz5ad>ZZl`3*^gFlONnUKU zhk0BL!Cz&r9(`hKh%fkxPr|f&kv|vPN_eS2^=Z=O^b|hp7B}^d5l06;Fw-rUw5Onb z#AMYHh=b6mitQx1Y7=(}D*O|f`5AS?L3*12qfBSFaUYr2v)QVo|DEK^19pN@7QxO9 zKOGvTnU%t0kkR&(nO)#>Q;8rk9IoZm+S*GJ$%t(KY7}b$*+^kF5l^pgAAX!=YwT8!xI@U+4m%oDI&~K&nkYNlu!O-o#br{2<-xRGj*?bT44{0DIi!o&&jhTx{ckmI7ptIx{YrTwIej*!{TZN^3NBr7Jk=+->)U1xy`_S)oYC&H+^f!P_N`7&iu?&qFC&0Az6%TIEiXz~?Ht}u6Qo>FX@ z9x?7_J9_jxog*YCn`qL8HWoQn|C*ZYiOk{T`Wx(otl@@Ifpf2t9e@d_TBHCH@c`R8 zDd`jPK0l+At`O$19HW=I{5pN6oR8Zje)Tk00vU9=^-jM_4GBed<(EL*xEQ&q^zRV` zM`n*C={b}KJ8mF~1@M+^O^RWpcA#bCW?uxhyq=OO<_rS#3o=F;HV)QnVSs^;>gs4A z0lp)hemFpq)1zHm${gE3EQLfol88Vr+m_Arg_gDhc$=Qwk1z5^A_ z{q`e`!9OpD+R7dlSJ~6saac`U|54;5S>$`9zZWYA9>a|v%2&^!8n3F~{P0qP0OTW< z%+i_IEKz5)A?};wRE~G(9vn}XYvJgkc$>4tgGU!moeR(||+ytso=y$p` z&#Olfx*?`jBI4Ce`4!T1P<7W2{GGUCa-Azv${DfmHj+y>GkZLRvGSheWh5S7~~q5-Pzoq#}njd!Pi0M{P3sOB=tNfyVv(JR?+-M}C z!LRB{^-ahbE?^loAGeu-pPagA0aGVLaIYnv~W zOUqrVl#{-GYGUG!6th&?N$bt%km)1+8Jfd^rO^b&xOw6?FOgEbFiMC`eeMed z$H<`3>)rmXORYBnrq=rP0Wg@0eFFFNgUWpFL~`XM53Vl1BCVYEwJDRtUNbB!&o*x> z_XvO%KR{7Q)M_koXSgWon1>Z*B(ji!4l^q`N*J`CaizX}h*Hf1$K<8vDNM&{x#d}J z3d?r1pB>Q!0p}RX-7K`Jt1_;>=+)^2FIrr;l}A6?lZ|;j`YhH&?`^rbvkrz!N?!tx z;`{o|K7-n(6{e|>l{3@~1Hn|G@9&v6xsvTx6;0nh_$N6ew`4L{?-A`tkA5;(csyX? z3PKFzdRao>KsGo;u}y9cXG>{Ma2F0`a(~{syeBSHyXn&XYOfw+`L|fTpsuW^)*y4R z?2gDa!=Wl4!cDD|jMr8L13cXfYZ->aS&rc-Zuf$DQ2PANBW_ZTDGJUrMw*xwh6T-0 z$xm{|YSG&T+?M)PVH<~!;@k_EYpsOs@v|_UFC}(avW;T!h&+>(bC}`amoQCeW5W7{ zd5{wilgkKLdv>@Q4nyKeqQ>}PApoKr*GWzrEzbR1RZI4CHV>D~wlRQTKj6 zZ}a>P=guOcrc>Cl9sbIi5<~-+kh?1G9+BZiZ9N1BIipT!UguiuC#A63AUUYZod<8s4zSgt2CVWK)XYIzGY=O6tM^vezEIJNye=F7^Rz^VlE=AMuWrxt(9h* zYjc0@W>V}{eUGO-(rF7WA$&x>X^T~uRhDM=4L=TTvcPOX7R(A=JRvvERIccK3VUGM zB*N&@#pFJE4%3o=O;H;Y$0b8^vOkRDxsjJ{cJ#C75Xfwiza#SSyFV*cRzmD40kSsvI!PFi-t8U$mWN|rE z@pg5YFzr^M4Sg#LtJ$sCx>@u)+7TY^%{O5rJ7}?zVo&23%;1Y54O(u};;3 zGgl2r+c!Jx)BF2R%#D({t%mKwQ`F5)yhia(I*M953OrRT>aJ0m@7*6D^+UZzly`7h z2&~+I_0i(6%wKs({7s8`Th641lPkB`0+k?U?Y!?y^Xw8+Qv?d}7F&d;kY`|4|>T-41^wC{8UccK#()o6s&j7hdu z+yD|>)}#4qd=W>UI(IH@Ddj0b&(jPFRy#vj6roA$xH@cvnpxe-d;Bl#xt!2+?$@e9 z11{gC-SBW%u@;Mr5F@6h8Oxov4{Bk$Q{scp4EeFQ`q6w3Wtbt#Tyd|S1yfj_I2%^) zeVx_)-c_yp=B96Y{N}cJVo+$Yo_S(Kc5N6FnZPM?qg_nUEmwI~$~=?Oa4JCZZh3Wg zZ~7#}8Q7lLCG_Sp^7zvTTK5x#-}eh;l&zo5A9?|wIqC(~^E9P(4MJwK0ws?vo}-h= z*U=tFfIcVM$oCp^d3hqdh|Wqv4<}B9D%WZkZ_USPUEb{3do?E`zt=eX#LP_nJj}Jq zFo<+}8{S80tek_F{Y^nXmesiIxID#HXTG17;)tIJ0^it23I^>~XQnxB=+kYidP--w zxTP#>fO&7Apx`i`{x3kGfrJ2eMMAhvg{|X=cf&IFDwv?`e9ZuP?X7E(XL}*^DQFy}O~`}{9t)N;akU8U$9NSECQ+L6L>dqY;GSALA7{uP z3%nf2|DOL zToj~dG2{Pp)peVe++aurImbiHg%hAuH)_DTlw^rZ6oZm^F3pe@xHvD9NGC6S}9+xgSTHkz*8SEOf z#PJ^Ei9c<~<|6vV=L}4j|FgdHt1CaBemKjKwe5D`0jBvCv7h(ktX+07Nbb${0~xP> z*=+Ege}896s%wF50Ux-8XJBe_SS|YH*!KN8_WSo<+O9VKi~Qz^5fuU4yJoLJ0@y?O0Xp|j2ye3OzYbDE|vfP za{mQ;clRHbwz#6fU-G=Mta8sT-*RQ&^NXeGcP-4U9mNzaTc!ImTOK^8fkl+vkjRbdVEChFVx8UxLyVJP4ySsekop)zuXLtYq zs!vtjs(XLe>h|gLbXNy`R+K`1hxZNw0s>h^T3q>Uet7FH2yk!XV(jV3+XQK^EF}t2 zHcGJnw#Wx)$QUaqK+wI>2oP|P7!c5ZMBZKyka!TVf6)*SUm@}TN$Ww<{4E3ZCc_Hx zHbQ)S>&5|Q5HNqs1K;L9P5Q0BY5K?f*~|rCVQJuCZAbFi&YFaciG_*tjR&0x`7fK9 zQ2*5NkO}=y+T~3SVu$Oy$lHWyBduu<0fB`1r$a&{r{cWnFf&utaL`bY=QXspWHKYZaWEGk(@^+KB4%v| zAmMuRj)hF{9SI2uznzgWud=x0-|)AU0GX+SgAFe;v$L}^lQTP$wVeqwD-RD3GYcCt z8yn-B1f#vHm4kr`qm@1RKa~8J9&v!Zp`DqHgPFAz$sfH2->n@T1jxw#82V@Xho1l! zvwvB#vj01*w*Z;{aF|({SeXAe*jupw^zXl+TnudfPmqI|@&94#59c4Szmo9}ANl`y z!28#MYKog#0<7%c;t^zJW8weHxBtQUS9Je|)c6;Yjq|@D|Bdr+$hW|F#q0oo3P{!3 z+CuPut0#Wu|CIU<1qyEmZe-wKAa39Q5M*OvV`XGvXJq-L%EH3S$-~RRqxBCB|4r=Q zRQ$;XuZA+f-rB@|E>(YyZCYw>&@dpJM#C;`~SD|F!#8`hxG?((+$#Bf)nL3}rbG z5DYIe;v%XpkjEZ~v1&byR?{oHsYGU`rf9L6pFi|K8j4eVCy1m`MC_%oi^0K+iQALA zn31)b85-#+X+CM@rssBx0pUnbb^l%(r+Wuw@L3yxMkeAPpPyotoWJj%9x!RLAl6cT zvp?C=H)-+%uCVjws7Uwv)ysqP^;+Fh^cN-q0t^fk6r_*0urLV;8ruIK%TI;~v5Yx5AUbSnZ9!bV_Jci0+;iae9KUZRDk)xQxy_IV0zI^mYf=q_)bVD&k z!1#5CfT^IX`l}iTGRG~LH`x*~2wgxMM#4=A=EE3wVH=tPzhg;s&j(}yT;7dkO&@R5 zqj_xKh@e9E8=80Ty8I!^GoCVR=1EBa2|Br0Z}}L7g;|nvxXP|vX|)&eB;CXnZ`W59 z{>|VnD{Pr2y&~DNhk^Q_RAL*D12Gs893&pW_dNu{M;Hon zC79BTU}hXv&n-PMSyx3L1?v*m9QGL#v2J4)A2NUcvvCEIPlBP$32*i!Nca~1BYuHf z%m-^2s9!qITbN#xjvn9&gGyjy*^=8UcM{e$Dt?3#+WNQ@ydlS0xr8|}U@U$%&?xH~q<*HRAY}&_-sWx2p)=U+C@z%YgW;+~2w8a?$NRN_K7Up+ zSIODzxj>GlV|PiCq|EoSNK2zLV{x93I`8(!7!J4v=^%Bu5bdFcv^}$0VaLt@DkZel zWmmktSimssB$PoE2C9%G6HW|=3dDJc2UaE}-`GNfEWcsk8nj2axR)%lT+}POKOg2& zxEN)Ra$#jaXea}>gji`9p^1-ufmsmZNBfEXwLr=|A#k`m(ldGJYggn6SD)Roo}m@1 zQg!>!<@pGRJPRa)8viDSnJ)oQu>*uAUQB|T7uuLD2Dx2UvXoGf%- zKUpwrEjR2g%bR(&{&<;?li*8@x)tOEW&k=YCL9M69})N9~=EX*o-C2F6U!ASDqc zwkHw=4gT=BKSp&b5F?LkSkbQL)}EOYvE5G^&=njkH^$^Wc~9UQQK{*pUg^VTi&KnJQJe>Cvuv@7>-* zxziH*OWK8NM8T%~nli1c_C+h(V}rdwC|ARG<>XB_YQt{W26_|)d!iNZBv53xT_#AN)->eFa36)>&*+3gThuEDc_M~scdEX8 zEWy-It2I0w*k&Ff&)#TFQ zrZKm@8q*@ta+CMz=Y{H}#kh^lp}#lq%42hX_FE+C?>%!;Q=_=%AU+zY|vH7{W{#uRR%n&tWiJ*orx@W zCT?7qz%3p&R$C4tfKqnjJsUb%T9SINqA-_@Ci3>;Xnx~IrGcqlGQgZ}ee1#8D#=+5 z;MxF2-DWpD;YX9S3gMrqct6mOq&&?c%x`mSWkSt#TJbT=9=(RfZTTO@^Dpp^&DhQj z^Es0W9dNgrribBQ3fp(1A5yHH(gE@@QFzAfH*0I@F+ujxlk~-Pks?uLvuiQos&9=7xc*My|LuS_ z|I*i&sH&q9Qv%cFm${Z}P>_TqboV=$%#V*xhyF7IMp7Aj%WyjJ`%Qf#a6h2&w+jIl zEs2niWV*?24my`R%^V$bTWV2D2bsIpi4ay?yR?5||J{v!? zCZ=nyw5ysmI6@wXuRM^n`gusy~;i{&{?0Pv|o){AlaaZHmbx#T3qvt!i#iy(&*50rMC15`61F=3a+~UPZ2CN1iEfzs#n!N5imOMoVPR5g zDxAr@A?O{vcMh7l6)E^3KXCy#sMfW7c|s(ErzsEZx=E-`ko7sf0%Kp`^G_y0`v82N zlN??~F)aBcZz2~8#&g%)d2Cy;q@Jp-ygOririKq0pUmI!`~es(ICc8&{>|JIUKODMb2Gno;u(I zjv$r`(Uj}Ug0{m%Tpyps>H05A$o2M**ExhDV0shgeUXK_kQvvTFmrExJ>-7~tkc?; zG$ejUG#mCm{xvm9l$|Azdl=gB@fAsZI%@Aco}Y7RohL_HGKJeNr>{cd?ITW3&31*o zlHya9z=g=(=E6dMlz#|qo7-T{8Y?rU4=rZ@Z@wKvryoUzrX*20VY$wIdK`)oAlrFP z`A|by`uwywxAYa`p@8*i(e)-9T|<0EI*L}-@ao$T)&LJ8+3*ATSc23JyU=*}&Qg7e z1V6K8HK=V>{4ZL3&RSy29w0c%eBcRd{|ASmp|*;bxL$RH`E~R@^rv=zb=z{xN35(P zk@n+*jh?Q)>|w$T&I+4F-NZHpwtPFH-0}P6WPObW;&MKL?Dx3*9&QDm*<#w$mFLfW z+duhfJ*Cp5cKkCX!I~d*xYF)i#Okq0CW?-ADRk^h)b{s^x|^ujV0db%pV)&s;VWz$ z1@;%={aWsOKL5IN?>2d7%oyzUtMK7OV=VT{9m)5-^}(oQpid8lS(~tA`S@$-m`_Iz8vdw6SouoOWmd4QS=anC|~eMx{1ehh?Ls9 zfOsQ1^}3nM>c}?4X?-sHc_glO+9#xUB&ILafhJ|VU?R_C*mlm7jU&STo8fgZ31%K_HzJ zq1SXTgL}8a73}qKK(2PC;M+5_<#6niT!CFPEW8+ULY66sdH4emt z|N8i`+}VKl0r+j}*EMfkpY+^|-2fc;dvH3p{UBrF1;0dQwfv{iRc_B=rwQx_?ytOq zYLKL2d>EIL=?Fn}N2{Ru$wY`aGN(yPIl8HnR@l~Wrt#g@-8D^Pa-R3d^3*A8Ns2%4 z9#=^MYA-XNQbGPII*HS&Cna+>1 zx6pc4B)QUAjdQOaXbPaI(o3SiVdB*5r3*Kj2rMub=_rMLz;To@l#6bY%VzIE*e{pC z(581ZP>YQ+KKRmeuOzTfcI%wgOhawJg^0Wf3=Ri#I{^&x zbulR!Q+>x;@#};-OHF-~0)yMj>bvtF7TJDMuUm+RW<(w$5nsj%+tRRZRb*5+G!Nfl zVb`BQPBALd8Jw3S867D7Ds0xan-#7Pl$=W!aCbTFVm{mSN`yxuF<+sAOzYd^ciP;S<=9CNuF+Pk ziCDt+7hdp;L^mFLY57)g7(X=>fFN8v81+wieywt@ZGUCqP|h5U4Ln7UY@mfr9%#mP z=7AGEox2i6|I*L6mrJ!rdz7BF=^9zJ#)bBYN0=f8KJ=&=p>Tj=UqE}c!TT3EDrDPy zqPmt$W>0VOqIB-To~`be^u_y`2gezN6~Wdxfa7ntyqx5xS^dW^J$4ZSF6KW|A46}p z+47sM%I7Ql<2XPen>CwZY3f&=j2`jip_;A1!54ygrS9c3*n)nRxJb;SN|QD4*{-lL z!)D6vHv++1;f}%(t!Ox+(Zq+;4QS=%g@LK-_$s{e^J38(>&4 zbZ{}F%Y{j0(A+MZ$l8^&`plQQZ=9uV_(h7x^MJfIhfi;Q#I-As$$VjY-$LIu#W*z2 z+0ZhbHzmr?!L`I*Xe`*= z&F>XdYeF$2f)_kjOy13WMfHX3I8q7&>O7*-J?dReJU5(kn4Vp*fqicoY`Bshwuc7U zn3i=6I5_Fp%Yi{cIh`izmjvHI^{%=eHAQ7#j}H(p)44n7&ygH^pp!SObazSoA=FOS zWy_Gjf+a2neS%f>MJWS5t7KX~dWLIlHFBwqDxb0|X$p7=P9fA0^HZI_o1c=Fo$s*O zA@M07=^^6ZOR>}qSB#55F$OZqnMU2`z1%n_)m@I2>I)F15!Q{&;qWP}Da7_-Wzz=7 zWQ?{FFnI?ZnK^k>u|f6@M3A;sA75M~X%0tykst{;a;>2ZIWx|3b? z?Iy?A$7~xM;L7HzwLtFXo#b^MNnio3DvYnUfy9*Ee==0vqIID7Z>OP0Lv6vYKYu_Tec;&qW!^_q~O6Yo)cTYBR3pLHgTZfoN4sal9v< zI%t}!&d-TBuyGw3SEv-j(l|Rt)=$08*2?2}%rH^uCxFD2gw$k1z0W(Rt|n%$&v8Vf zS*ZTGl>0SVP)N_d6wtqqDyFL_S0hez+*Wad+bU_95UFlcR#JmMSoIoeAq>4nM?^|jF1`hS)4NI^zxP2+jNc!S$y{z8IBPQ&FP`F zIZ!&ca5vQ6%s~{Ig;+y?;+_JVBQ=C0uA|5M9UtbE{T*+LP;|)_}Z&=RuS=q^3Qj2FTfof3loeG;E&WDejdo5We-`$JTv&n9f!9AE-{Y-*bTg>{-`|zsKIZqy*dTTtj+qM0XK z{91M6A7O4@K_SPJh7SXzaJDB}G`IRvjoKADu-Cxgkr420-kw|><+pik@9H)gSj&BT#Sdu=k3ad1jUrk3jb*iG1o*1}n zWDD#bzJw*c&~$#0k*rI&^Rh zOIC!|hnFqsb>n@H>YmP#3)RLNtw=FsITA4^)P1bYuL~#qcu0>O8)oIZ#7?P^HGh^a z@37zG=k=gsDDm#ANttLanoD2mJ^&A(MB3c!|OhCIr@u!av0Q--q@?{i(BS&CC(Wp=~cgZ?3m&TxY~#r{m2fa zdrw*>LWpO+j(PxEN7MhN=6#3H!-s6#&a!#b>w_JzMB`H)S;en0i5wwTAb??VOx)E0 z-S-u5)+bYp?BM0VsinApe`3Cd3ZIt0l5-jp>FYqJYunIVLe{;8ciM-5ICWk-xAR|E zxChXW`a6WO%1JIEYahpkBanJU@R_LPE{e6f2D*T{z1Qf*D|nN;YoE4ta>$wyy!Ypo zRZqU#<0*=5YX4~QV3ri5GDt8IgNH;A%+}t!2=?|`a*W5~s`_^3e2~?oaTml-{KcoEVi`qw`&>e?9f!u$)y@uo~sN&&+fkG(9eQ5g1S08z_ zdtah8JNNtYm}_+C1959gULseL_QiQaqeFF;2B3BC+WQ=_jDh2la|^*QNK13EAj8yE zJ7zEAAq>QZ4M`XPt*xTR_@shd@Zi_d(h^Rq!z;SLs?-lD?2=dg+C#6y`(8QhaLX?i z!m?qh&Ut02fY|Ccn9n_DTv+=eSa!W{>INz5i&1LYE916T>)Ur#rbkQTo+O0oo`rFa zX;t#-YG32+L)vQyGY(VCE=*R|&x_p}H>o0r6WRiBf7UUOr`bV!X z>|=Mq-CPGsit=0bZeYwD*iXJUdVKGuC6~ZN{0Txfso7k?4>L@b0lZDr%T(`%;GoyJu z_1Rj_Z(Ijfb%^sjeK+MX{p%yY-~0)XA<-gunSLlreNhHQX6Dj0-Pz+tm2-40&E^_dYX%Yhs~bV&!yL=)Mo=b z2@wh?dIW|v@C}ME3G(?b@FF$IaM{OYv2M=l0CKFaj3;)DL@Fb}D$(7&-?{WId@CVP ziiR`2aMGVjtgDrEUzLnR=P7ep#+EWW!s0gWI-qMD2T5H*qs(6Dy_yIY|Ef`c$+df`z&NvvFE=fB6;)mt1kUYuzRyA*s}Kh^9SFt3g3Q3vy|m%d|4 ze^TWGqY_%q@HM^Q9VP`>Sd-#h+2obm}6#{Q~xCFOdf-<3H{9j z6YTz&TqA1S!G=ybt)HXU$l@%JpG*gq-{>h$D_r}6SaMB*Su`-Y)^A|+(%G|^`K!?< zoX8cOwaya`(7t!&`mGv6^|S0*-0MA}wAv0$DN|~=z;tWc!&J-mb~*0zSG&;_>*G&0 zX5(ZQi1C|y&mPm2t!J`M4KJ&?(b>vu%ej2rmJ~WE6pI%0?Ys_T8SsJSc5ZP9W>~h_uDV}+=}dDXvshiP@Tc)X8<2C@mcuXz zqx&e>fJM4kxevd+SUjKarbIhIP0V@l!&uZ|V7g*N1}}>aPq*zl=g9l;5pOEOpJv_D z(kq7(QN53Iu!c+&uAYxH>_pe+GmXXS9I^4`zx+qM*u`t~_R`Y^Jct>C3<9 zgg1p+**XlYR-Cw_A~U7=K>Sc}EM5?7#1iGHiVn~0wd?eQd2gI*r1LX1itYXSPmQfL zJ(GeAQpZyxVpWiy*Ji2nDXDIYdL_CO$a^G@QjiaJs{FZ96 z8dnM`oSly}2d>RDd7uq9y=>X^1X#V0_>Ok&>ls|bYn(>9>lQIQOQW;eLumuR(ViiW zyIkA`?==+oF;XLO$e+g*zW_s64ceBz-?BswXQBzaYB3sOoK{M5ruw#_yFq@bX|^4G z$urtL!pvRr#Evg#3a(i3>PSn5{$lD=|& zBz~EVMqg?-N7D`su4Wx-c}J?>@c^2dKwHb2M*ejrq5s(pTEiQlP6JX!HblboVhZx3 zexmAoDyjb(CF>b(U)KzlrXsgv=FL6+A-o#w;)}~lxnZNyT2&|iV^fDgB%dRwud$&z zV_H}#i(SV*$vvRNE2q9X{S;8(K?=JndpPNS+S9pa-rH_~HI`T<6$EO_54nO-H-mhp z1dY04-L-*Q)kxkf1*hy`|93cUnu^3R3J@}}GKA#=`S(1f{s5h2=V9Ngx zm!;}7%V59FL-4_CJMluUZK0m)9xQ84gy||PaZ5{_pV6~3N094NYjyGpxmnz)+6vKk z=6_&caz^Uuvtp0PJ4l=bk|l_iAp3RFgZryD-Zxp?GlPJbe&4|ZWd$Psl`jSbu%o3_ zrgFQE5zDN#L7o)WW!CAhUAf$_Hi}%6w<+nNdfo$5KLPczI>p2yJX!2azA{hpfvgw- zc{=C&j<}X!51UdDW?XOY!U>?aR`yq}gDnX*NBHfUj?bkGAOV`Hrk>;=4+XM;hmn|b z(zd^ZG=LIW&_!h1dN_?`UEg+?8X(qET%N2f+PUBb(aM8mx8xgF26J4iI%xwX`U0Gd zcbq>?;KO+jXJc`X17H%{v{d}jN0Q-?0qPx@baM+sBJAs!Y9i50o~Bv`X>Y(Az0OE0 zbdeivG!(jt2r|^jS805|asOQC!6Ff-c8uz}ZZr6CA}p3?7+ev6)|JY&ZQ>o~OJhT| z(JN_f_s(8l?~A{p>$^eN$L0K*hFg^)OA0oQA(;F|TAs5>V|j|B&Pt&6M6x(FYEN*j zm((}ehS9mRL;gbc8xPZC?3CQ#NpT22$OwlsQ>505^V4zdGs=+)9xL14%Jo;c)O}l$ zBMzan^QDo-bUpisgLwWH7OUtWYh!3)c6gNO{X;lrEwW^R`G(o(5Pt*d>N;z=@K0?k!ZZKw&P|Wg=U^eM^JO zt;&rc4jgs2`mA(rj>4V@9Kbop0MYlpP}yIp`r{dHEDvGY+U1GC+EWX2X~byrMgSZZ^h%JEv0hZmt1~85_o>`7EiZp+t^7_QEFM;lbBgqdWUL5WGfZ3dU2FvoPk<&pQ0_ib7l(E_P zy8K?26M%gq<5q}=bgc6LO@m8-^fPuJr->+G&IA}{RZ}_1or#YI{Cr;ja36BAtkCSn zJdJcIXX9Ip#DO4MC=*uDL`hj^zu6n6so9x?dxrOIYf&}L1rneKGCuGflT=_o4{Ymr zLUd`qet3FTV=hk(lQU!TXNWIE;oiUEt}o>Hpu0M)rHaD$_H%N>IeUl_INxbFGnm;S zNyMW!BjbtBuzD!rk#poWgC?9F-k$reTZl4K5l61pHb=^QbI3EXm53Nw-I(F4bX>8X zM&+`)Z2EllR%PxGVt5vqaPCY3zESmZz}{@iZM7$}=0wQ{=VTBd2pA2a|B6+r_k1!6 zPdwaj(`!D=du;9yjzkQ=2#?s)i(^7J(ePBeR@=WT)UKUCr);`>arVt?!{Q}Wb5Fk6 z(}+#}HR>aLUOPpC!b4>(-CuCBBp<^(1W&*Z(q@N826aK#VV(GDc3c||+_?w)vU7Y` zq1_b2kBc)`vD)HiR0(!|K$#_0F;?t09Tu7}$kM|JsRFPB>8=}N43(}5hC4hF2YQq@ zuGlm|qr4OKV0fkYgzwYV4A(j7$|5dW_}R{*WC-nyR&hK;9B8poZux~K$*)~Ay~+)p zz%O1y|TtiJ)<+AU4&6oB=c?+2j1A|uj&jYdqpg^1+ zKmwAd5DH#o>)gVkbsN{V*^i<~4VJy#V>Ri*35V{k$Wi)KKkU8~R4J>?k9Z$vc4N@I zLOGF#i5tvJ^o^>sholIB{s?o8)%*<3u&dEp&EH&B+GqZo^gFNHF<_GgXb>30pNz?c+_u$`L}V)ALM&*?ua-`!mh zSQaKpR9(ugeGiX0X3uD%-b=nG{a~HS=s_!b9>ynMWdZn=Pvz)P-%q&wSP*)HyE{4= zhn#j_-Kv$U8{pZghfBISCkbgZ#x?H;{6NpovP`MGy zj(avh=!y81YE%vm$i<^~w6kjPV;A@+)^|15AWz>BA$d-jTxVr1Ay3t)z>2IGYH>Bo zQFqaWis*{b=K5&spc(Q^IyDjfrRN62z|EvUQG0=phBq39`9p-BsLy9!!iy)zU!sTf`bA=E=jI1q5W&C$6-ElM z+GNZa*{eMC-loy$ZV*?z7w!8khNG+IWW!&wSuuF`@cnhZP*+e~MTqHAxK z&Svob2L-twv09AqwXGJ$)o+JAh%-wa&d|q=9BifTSVI@%6n=dS-VTT>Hb@2)S;h1- zqp1lt8Rj|lQu<9XG_rl241)@D~?~=r00wVcmiQHW6kKbabY2 zScSIPM^BZRL=BQ%7l8fIF=lFV7Qlu)Dnae?p1ta0boS1ksP`2hnZv33JiqKrsRI6S zRoa6U&SB4U&kO7GYJB}S{dB<|Qk9AcsD&j$($y}6NA@S(h!4l#9*Yz6g@lX_ILNjt zp9F&O+>)J&+3uDCa~=Das*B=pllwjpgeY24A|DQ8hkGDP>-PvmR3KbO@b*Gp=kBHE%wfT9XQlt!l5kRLkY*@3yAEeHyegJlE&H(pAcRe@X z7vx^{&k_@-Aq^aJU@_@v5s+sUMl0d@^|P#AD;*z- zUAh{+NjXmtcFv0IDAtSW;a<7nlyn)myWwu!g&4ZnkM6@i;!p` zDmWM!xU*Ff;Cr=SVBO~Kp8-aK^R;{Z6(|0I{QKl+{i+3=W9yW{fmPFCnKDLpjI9BT z{w3h}{Y$F`V|=U*GY5qiw?BLOs{(xFmU|eMRDsZ=Idyxi(~JNcKk{;?uFKnvPoGpW zmS>)o@uZwAchj3xOQC`ZgzsgUifAe5y>?dG-)t5LffGS()p@)~tE?lY8i%NnHhuvyX5-!kJ;C-pIUp z%~0&@eB!8_$U19wbrh!O>(FMy^Q*;hNaQ}IKZJ2ybpDOr!o%`*heh=cXLirNnWuU(^6BFr9Lqut9bg3*_eaM3-r(EGJ8 zXPHZJz{?>DCE#US^sEA0pHxl<^|VW~(a>+y|G(Vs{ri%!4+q-at4D%S6BZ{D0R8==T5s literal 9354 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 Ef)RolB>(^b diff --git a/assets/images/services/youtube.png b/assets/images/services/youtube.png index 64d91a3e83fc4e9cb61c6fa227df7929044deea1..5b16511c6f8450217bbf601ea4aaf72fab4c5c85 100644 GIT binary patch literal 12242 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 X&F_MWljyQ{3=EK*nrzt%i$DGge9{^i literal 7438 zcmX9@bzD@>*S@ecETKp&NGM+gMRMs}P(qNDk`|Dal1^z@P+3Y+YAH!6>DHAJfu)-T z>F$Q*U4QQ%bMJg+=FWZQoacVdbI$#srJ)R_fKUJc09I9bssjKZ2)_r1(TIJPtoIRt#IY2A^n-MQGbq#_ zkri>|g~U>XJSHpS$O(}re*DNAMJU7m#?Y!ulmnWZI(RJA^>7kNAQ-RYW^Xpa)2mZx zR51J*D!m}4dEJsswY{14aw6-|!RLSyUN>rP00<&W`}K_qlF_e*JlYD!vQA_rM~T0O z)P1h@UB>Fi*AC#YC*fFZF|Psre|Xd|D|QBIKTzf=m30`QiN(2+cu!VXYSoO634XGY z2-(FJah(N{IpL*+(7KBAYz@ewFjj;BKnBS~|B_Xqa*Uj~Y3X%>g@}pphF(ECxboAS*qy4Jn@ppf+4Q1_hsqA)-#ptp$e>e|~$uNS(Z%A5sn> ziDvqwOTgpyC~IItqq6Ww_j_r`H;Yl=PD^!_&lxWjr_z)aNqczi?Zw}8xm>ANeW;-qUctb zoB>S_kdy)Cw1vhKIF)tg#Ewq?0u`Vl2X~jeEyxdw>_AFp@S?U|W=mY2oDNjM@68Cv zfq+-+8ADMBEg8uI4W8Mp)V^hrV*@#EV=Fjj50iXhfrzF~4(1YbMg@a?Fd8QC098MzQF~G5nS_pjM3+%Xq zUagFIC`x|1x=tmd1~}kAVc^bY+ykC^&IgGH-T0RjAMGCky9q> zsKq}4JS1}JeD#ORRO7WxR<{cVM~7aR3px%Qa#*u(Sc zWD!Dg`$sS|-2L{C<-1p98|;L?uc!dPjtm0&NTi~}fT>-I#s)HJPs%vM34y6w&u8% zcUt{#WD*o7G1HICyF>Z_K!IVQY+w#Y&Wy#!s!e3ACA~tu5?lm;BRS*<4(#>Vlq*re zLPx&>z&CO{7?8>MHz-; z3;>33mn*!sRrbN{x8l%12IA%5#;^%{=8fOFimJ0@ZEm9+Ng1?6ZW^3GF!OKTnaUft z@(MZQn$^yJ`GOb|!=7R3A6_CRu?s)@kmu(oFTV8UJvmg0D*cuyqyEvjd;#E}p){u)Dth?i!_*~fS%-{bAKErl3GATS< z!+46OyqYp=W(1qJ`yIFqmqw$+V=pCi9UDJyE;i4WENVEbciC}w#y==-#dNjKUDzph z&ES5~S9I2D?2ezc`dO%jqh{OH`W362 zz`}*I=ygLdr-izG+3-oW6}y>!aNvy^q5kOJ{v^)9;oN}Q#()SJ8KpF|i~VYA)y}6V zq+cuKHhNY%WZ|V()C3R0zYJA@91jS+bhT#D@}XpD05*gxj=MD@@WD5rQydedJ+T`& zJMVe4ElMxc!jl+E^4itw;Wz!SUZHo2V-Yihm-=tF8d-<5PAiJ0LxTpv@W35Vm%8Rd zIAJ>vax3wQ*J7v!ysq`L6BFQ-Oi*Gse`ZrYYCF14gf#SMdV}k<`)_tmhs}9%@8rj5 zbGWv=O?8Q&*0FVCRUk2gK<1?b{}*0y!g+e(>FyKL@^D(w2l2SVNwz56vZ4+Nj|;td zBn@j{ivRjJzwkS&?i$v?-*;P28y+e#oOx|co;H5UPr9X81h9%FH%JU7WP!(Y-BNv= z1lOh{uk-95%Rq{lka==}f3Cm?T~1l!)DJ_PL|}st{@TM{JoG1b%g6cD+wQCw!U4;$ zs3!T^-0`8^9WDo2sE(VLE!%-usuqK{YB$fw=w8uQ6!vbnX|lf=y49F_D_f`QiXL4E zgiT|1dU?xW-Gw|3CeJWu?UPNdkNt0)2$hqx*~IBx@4usK_ww}kxyAm`UA4RW&e&4B z8sT`5E?tm1zbzcRJ&?IkX!MJ7QNZDkCOKrBb(4JKw9mEPr{A?c^2hTxKiy}`JTCeY zv?5;g-6}3PEM2+5x-WR_gtp;$`Wpi;KMRzvRT-LWGn zJ!zA@so34KQOiyq^6DXIyui!4;2~zAW1Y);ms0qsIiqctyJ;^RXjN%}BT>-lR!=W8 z)*s$IqtXQBi&<~v;n6J}-=FMkobDd}b-H=Bi~8|VciYp=YUQ1J!5^$Vw+U6&#>ScV zH0D8wrg2qU0qX(q`mMOR1?uP>+qFntc($%UYk&y4D5WiUWjT7z%X^{CK-H6}(sFxKwtuM54M`{|%O_rEWU??v=Yk%g%lG zC~}DRG~9aS-JjJzSQTy7!zz=P2&0Zb`+LxHxc4sCwUwMb=Ue-mn=#z!1g-l1D)iQOCnE5m&(UmU47Y$_s3eTDPGJEKYLp^IhP@(9tZ?KDSJzxny; zHf(K=S#+abw!RG8De0fGW0?F6;jJ&;bZawZ@yTpiYMVCw(35Sz`*V}{mt$YX`cJ;j zMO|hgZv0GTFEq)RBP`KfqS`-z?PJw~0n0tThoc_;YpRVdtQdXU*3RR-nQ1hNfOULV ztt}w+oJFyUC*8QGPpD2Y1}{;|)mQ2r#3XOd?@%_dPy5~Xod~W)MEL}H49(>aWV!m^ zwlP}}37?_PhTMW`?Ypz62I5q{Gxsk1teLUGklEa;kFnI%V~-a*tr!rd_Cx6fm}u3h z%i!arSgy&Ky_%l!q%-@N$@#lmGBD8S|LFYUK?3n>3P@_fr*@51FPlp{IKOmTG>TyU zzcfm;0-u-+mcL=CzqpqT*_=I|$=Xi_DJ+{#Yz-6(4qbMp(+E9-cX<80vA3Hx%~G+~ zYrCvTyv(L}$tCZnuII>>wz>H$*+i?oYL6u1qYnN`MTAJT5JH{s4La0T(rqO zk54mr7?-v44|aR{Xv-S;o_jRoQtUM}QFla(ZNxV?dKr2?W{1dcm_{d=nr2mW!8GFoqH5CRx1}e*E@##W0;(q zbM-Hmox7}l6TW~>3^;a2+XdWsbo|CDgD!_Y{r-&>tt!1MKO5kbvH2=e2I4_w<6%@_ zej7Tk@h?WbWjpJ-0>4Zy!I27sSZtHX zTU_Zpa>6g}BSprcDSzyQYF8ylIwOv3IPD;A&*e0>i4IUG{#-nw#@vKp14Fa1VuxY}CJ-T37Ues|H`(eSW$;x; z`1GQV+3=-+CJ1o86+-ew81j!yhZ5+b`C@{=QqF0bJV8zL1G~jmsg*2xj^QX2bf@?+F@WW! zSU*WR2#P@olEGQ58iT%xvl7gE-<-pj zdoz+YVJV?N{a8L38H7wvTZ^RdJLgS&(-$m1P4m@3j)z_

YJ^~_01e24{O#Xkp>|4Ept|ql zJu)#kJw3>-Zc>F^?zvGX`c$hf9WzYv$CLoSp|eGupGlb~jD@OSnyBioMAJ{^l`=q{ z5C$sjx^5w78s@Q~tEER=gkjpTkvqQs@)T!yLjvwx84{BkK^H*}>Ed)xUS@p`0L{^> za8zSo=aJdo(|2bE-ACL<NgCYRt^H2oR z$9o(tfdkS}FB5H^BbOfdGPqLT1Au6&Xc9I@(G?>msyGE{eIXgD*45xlXJ z`QN0_bAtAJZr?})Kn@rZg?#&+T~56{rH`BP*Kb=I^s5!i;D4yR9`zhJLk97F{r^rdChzI- zPk)x94EpD5YP>;$1T%CPIN&dp894ypPDtmM92JZ!kN=rPQt2|DH7um~3PZ3@*E302 z8gjj5{=?iEi`Oy%5Q&vUoJi4Zsr5M7CKYA$t^H`X7Z(8V<4@`SuyDci{x1CBH87@6 zAjVf5Aaz-}ak}(zugd$Rqo$1&NHkzEB(HyXkOTLWW0XogrG`U)NK>UpNZ_40a>N}X zP}%GlbuxhFu*N@3#S>ByO5!D9Rbldc7n^n`KV=JVIoh1`djf`Yq9kcZ9|omOT8e0z{F68Xx@-G=y^SlIe}sUfbx<02mp^*yw9TC5GnLq@ojhzu|d_^`{BJ^ z8C3)wR2Nl2)sI(75COayhl^=Izd`cy z?`7^>%_yBJIr+U38a-`}UjM*saa|g~cXae%<;2L)s3rF`>8tw%WFa;2?ur;7=zI9Z z%RPk!@OtCO@%I~91`Sygna8wvZ*~CA%ue&0>(&C<7i=2MpXF&S>!#(Cr zVDnh=-GxaL4WtvXct_=y)iX3!u8d{E!!&{935}@?wE+h=h$c&W8M4vH_bG?Zmkml0 zZNQ-#b;pL?!`?S5pf_feGvX2L%e~XeDqSXsZif5`Q#xSSf4K9O{LPn6fWkZ zOU$)0=_{AudoK0Lepe%vH(guBuLzAF$Q=rc7SCCIk(#UJjXm`q9Y$F~?(;q&!vsD1 z0?uzOrpbbIFWib0F-`CguI=V(Ggs4c&Bv`y^Wvr7%qSty=Kxu7EaRCyGqWvk7+aV`-$-7sbjem6%Q#}o5WfE< zProH!9aC;4)WW^ObW7|6TW`HgBJQBQ!`=c}r7})YZTzt0$C!CKT>0s;9XH3XfT%;U zP2sZCF8XkCuh{PhiOG|^BZ_hX1wSd@_jYN?C_o_nV`6(8v-(1cn!YD~7tQu(ipMWR`6agP<;>g~OnFNB9gqEY z!b)#D`$In66t!~t$j0>5%n*-dWI?f*j6kK!J;9 zv{Vkhbx#~e!;HLI zZt!wEO8>qt@}-m9G$H9g*qvi!ft){_Hw2M(&Hh)y?M6Yd))A(7!b(1}m8{ba>8?np4yFL3@gc&j;^JH#&ccUCMDfgnynQ5Z^U4k5y(LL zffsohc{GwP9(5(3v9-Lmf60^N3tI)Pi#;qCm5HVsKR0ZOP@|eXkr`lvLP z_lXh7YQiXKD%;=h8^wnq2%=}2fn?9@4-W26Gc@5c!!_NG*$~%KroiG>{leD|}pW1NK%}+=R4X z17@YCfzMZ*Cg+jkrPO?XF56Z=Bn`AD^&SfO7D?-$yjJIb+H?}=viJSfv$r!W3Y0B%C(mhMll@S}*qM)<7bz?&#>t&FzlxkfmA6|#OcGl}{O38m0@t6E> z(Bn6*oSb+SSp5i|QlVYQD;zGU#-I&s_8$M$}l72C}d1MkEUe-hDT@wyx+%&9498u54ixKmY)-&X_{6yk>g+lR4iD zYt12%h 7.3.0) - Firebase/CoreOnly (7.3.0): - FirebaseCore (= 7.3.0) + - Firebase/Crashlytics (7.3.0): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 7.3.0) - Firebase/Firestore (7.3.0): - Firebase/CoreOnly - FirebaseFirestore (~> 7.3.0) @@ -54,18 +57,21 @@ PODS: - Firebase/Storage (7.3.0): - Firebase/CoreOnly - FirebaseStorage (~> 7.3.0) - - firebase_auth (0.20.1): + - firebase_auth (1.0.0): - Firebase/Auth (= 7.3.0) - firebase_core - Flutter - - firebase_core (0.7.0): + - firebase_core (1.0.0): - Firebase/CoreOnly (= 7.3.0) - Flutter - - firebase_messaging (8.0.0-dev.15): + - firebase_crashlytics (1.0.0): + - Firebase/Crashlytics (= 7.3.0) + - firebase_core + - firebase_messaging (9.0.0): - Firebase/Messaging (= 7.3.0) - firebase_core - Flutter - - firebase_storage (7.0.0): + - firebase_storage (8.0.0): - Firebase/Storage (= 7.3.0) - firebase_core - Flutter @@ -78,18 +84,24 @@ PODS: - FirebaseCoreDiagnostics (~> 7.0) - GoogleUtilities/Environment (~> 7.0) - GoogleUtilities/Logger (~> 7.0) - - FirebaseCoreDiagnostics (7.6.0): + - FirebaseCoreDiagnostics (7.3.0): - GoogleDataTransport (~> 8.0) - GoogleUtilities/Environment (~> 7.0) - GoogleUtilities/Logger (~> 7.0) - - nanopb (~> 2.30907.0) + - nanopb (~> 2.30906.0) + - FirebaseCrashlytics (7.3.0): + - FirebaseCore (~> 7.0) + - FirebaseInstallations (~> 7.0) + - GoogleDataTransport (~> 8.0) + - nanopb (~> 2.30906.0) + - PromisesObjC (~> 1.2) - FirebaseFirestore (7.3.0) - - FirebaseInstallations (7.6.0): + - FirebaseInstallations (7.7.0): - FirebaseCore (~> 7.0) - GoogleUtilities/Environment (~> 7.0) - GoogleUtilities/UserDefaults (~> 7.0) - PromisesObjC (~> 1.2) - - FirebaseInstanceID (7.6.0): + - FirebaseInstanceID (7.7.0): - FirebaseCore (~> 7.0) - FirebaseInstallations (~> 7.0) - GoogleUtilities/Environment (~> 7.0) @@ -105,8 +117,8 @@ PODS: - FirebaseCore (~> 7.0) - GTMSessionFetcher/Core (~> 1.4) - Flutter (1.0.0) - - GoogleDataTransport (8.2.0): - - nanopb (~> 2.30907.0) + - GoogleDataTransport (8.1.0): + - nanopb (~> 2.30906.0) - GoogleUtilities/AppDelegateSwizzler (7.2.2): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -127,19 +139,17 @@ PODS: - GTMSessionFetcher/Core (1.5.0) - in_app_purchase (0.0.1): - Flutter - - nanopb (2.30907.0): - - nanopb/decode (= 2.30907.0) - - nanopb/encode (= 2.30907.0) - - nanopb/decode (2.30907.0) - - nanopb/encode (2.30907.0) + - nanopb (2.30906.0): + - nanopb/decode (= 2.30906.0) + - nanopb/encode (= 2.30906.0) + - nanopb/decode (2.30906.0) + - nanopb/encode (2.30906.0) - network_info_plus (0.0.1): - Flutter - package_info (0.0.1): - Flutter - path_provider (0.0.1): - Flutter - - "permission_handler (5.1.0+2)": - - Flutter - PromisesObjC (1.2.12) - quick_actions (0.0.1): - Flutter @@ -147,14 +157,10 @@ PODS: - SDWebImage (5.10.4): - SDWebImage/Core (= 5.10.4) - SDWebImage/Core (5.10.4) - - Sentry (6.1.4): - - Sentry/Core (= 6.1.4) - - Sentry/Core (6.1.4) - - sentry_flutter (0.0.1): - - Flutter - - Sentry (~> 6.1.4) - share_plus (0.0.1): - Flutter + - shared_preferences (0.0.1): + - Flutter - SwiftyGif (5.4.0) - url_launcher (0.0.1): - Flutter @@ -166,6 +172,7 @@ DEPENDENCIES: - Firebase/Messaging - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `7.3.0`) @@ -174,10 +181,9 @@ DEPENDENCIES: - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - quick_actions (from `.symlinks/plugins/quick_actions/ios`) - - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) SPEC REPOS: @@ -188,6 +194,7 @@ SPEC REPOS: - FirebaseAuth - FirebaseCore - FirebaseCoreDiagnostics + - FirebaseCrashlytics - FirebaseInstallations - FirebaseInstanceID - FirebaseMessaging @@ -199,7 +206,6 @@ SPEC REPOS: - PromisesObjC - Reachability - SDWebImage - - Sentry - SwiftyGif EXTERNAL SOURCES: @@ -213,6 +219,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" + firebase_crashlytics: + :path: ".symlinks/plugins/firebase_crashlytics/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" firebase_storage: @@ -230,14 +238,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" - permission_handler: - :path: ".symlinks/plugins/permission_handler/ios" quick_actions: :path: ".symlinks/plugins/quick_actions/ios" - sentry_flutter: - :path: ".symlinks/plugins/sentry_flutter/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" + shared_preferences: + :path: ".symlinks/plugins/shared_preferences/ios" url_launcher: :path: ".symlinks/plugins/url_launcher/ios" @@ -247,41 +253,41 @@ CHECKOUT OPTIONS: :tag: 7.3.0 SPEC CHECKSUMS: - cloud_firestore: 357b92d4778d7715d76a393db5f89e7dcc9b7401 + cloud_firestore: efd6d2bafe5147690cff2ccfbde13752a56fa281 connectivity_plus: f5e5e0b894ec1187bfab9816d6c2f034da8d46da DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Firebase: 26223c695fe322633274198cb19dca8cb7e54416 - firebase_auth: 7220e26fc14a501a63093edc10443de6bca0d7e8 - firebase_core: 91b27774a52f41f8b58484a75edf71197ac01c59 - firebase_messaging: caf0273c76e54f0a10dfe5048ac47b89e75bb78a - firebase_storage: 554b8ce9192bfe0bd35f63da42d0d1f6ae819ccc + firebase_auth: f07f3ee03813d95157ee08ce763c649c656174b5 + firebase_core: f47224dd6a9b928b4cf0128b0d004ec9e47d4bf2 + firebase_crashlytics: a62319db93baf2bfeca8a2384af3f29e3d62b09c + firebase_messaging: fc1811236795c2313b8339c35d31295b1cd8486f + firebase_storage: a4b292db6551a64c3697c7e67cb1df524b494bb1 FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 - FirebaseCoreDiagnostics: ee1184d51da3293335b83355aad20f537acc24cf + FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350 + FirebaseCrashlytics: d31325312c92e2cb2f0386d589b9aa44e303d99b FirebaseFirestore: 46e32f479aac127e742b1cf873930f7bb43e7b45 - FirebaseInstallations: 6e4e77396559bc2ae0504823837ed737b1c7e47f - FirebaseInstanceID: cf243611700eddc916b093353adbd4da822cfa63 + FirebaseInstallations: 42c86e7b02ff75b7f27f85833bf5dcb5f38a9774 + FirebaseInstanceID: cf940324a20ac14a27ad1e931d15ac9d335526db FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232 FirebaseStorage: 5002b1895bfe74a5ce92ad54f966e6162d0da2e5 Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c - GoogleDataTransport: 1024b1a4dfbd7a0e92cb20d7e0a6f1fb66b449a4 + GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab GoogleUtilities: 31c5b01f978a70c6cff2afc6272b3f1921614b43 GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6 - nanopb: 59221d7f958fb711001e6a449489542d92ae113e + nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 network_info_plus: b78876159360f5580608c2cea620d6ceffabd0ad package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 quick_actions: 6cb2390c4dab0e737c94573c27e18d9666710720 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SDWebImage: c666b97e1fa9c64b4909816a903322018f0a9c84 - Sentry: 9d055e2de30a77685e86b219acf02e59b82091fc - sentry_flutter: 8b2b2e1c0101ef562106b5d272d3c0c741512c01 share_plus: 2e6276f752c279a84cd68a9fc27dfbbe2a74b072 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4a416e4b..f825c68d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,13 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2A9501F70E4ADE3B795C4BD7 /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8B58106185CA1FE7B7C6832 /* Pods_ImageNotification.framework */; }; - 3355779EEB7C12DC262232AF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5A8F6AE14D184A58D8A7331 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -19,6 +17,8 @@ 9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */; }; 9C7092B325E77E470056AE0B /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7092B225E77E470056AE0B /* NotificationService.m */; }; 9C7092B725E77E470056AE0B /* ImageNotification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9C7092AF25E77E470056AE0B /* ImageNotification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + DDC17BDC4C5044EAC4528466 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FD90D1B5F59BF35333F5918 /* Pods_Runner.framework */; }; + F9367914379E317C1CA4A461 /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1A8C9DEAECA3D6663706E77 /* Pods_ImageNotification.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -56,13 +56,19 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 06B283DCF068808AD480D95A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 30F261257F9F405626922227 /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = ""; }; + 3392DF551C1BF6D77A2B3208 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 688E21D2AFE08631A83E52E2 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = ""; }; + 713F55F4545D00C46010F8E5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7EF2984A57FD1E0C98A7277A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7FD90D1B5F59BF35333F5918 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 805E3FD8D2123AFBFB3413E4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -79,13 +85,7 @@ 9C7092B125E77E470056AE0B /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = ""; }; 9C7092B225E77E470056AE0B /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = ""; }; 9C7092B425E77E470056AE0B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9C9E2734C6D4DC2FF1815F04 /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = ""; }; - A046209115F73D5EC348E242 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - D5A8F6AE14D184A58D8A7331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DBA43D9DEB26304D56B42917 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = ""; }; - E2F8912DE8B8A6F72277E96C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - E8B58106185CA1FE7B7C6832 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - FAED45593175690B5B62C40E /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = ""; }; + F1A8C9DEAECA3D6663706E77 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -94,7 +94,7 @@ buildActionMask = 2147483647; files = ( 9C2991242453FE3100B93AB2 /* StoreKit.framework in Frameworks */, - 3355779EEB7C12DC262232AF /* Pods_Runner.framework in Frameworks */, + DDC17BDC4C5044EAC4528466 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,7 +102,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2A9501F70E4ADE3B795C4BD7 /* Pods_ImageNotification.framework in Frameworks */, + F9367914379E317C1CA4A461 /* Pods_ImageNotification.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -182,8 +182,8 @@ isa = PBXGroup; children = ( 9C2991232453FE3100B93AB2 /* StoreKit.framework */, - D5A8F6AE14D184A58D8A7331 /* Pods_Runner.framework */, - E8B58106185CA1FE7B7C6832 /* Pods_ImageNotification.framework */, + F1A8C9DEAECA3D6663706E77 /* Pods_ImageNotification.framework */, + 7FD90D1B5F59BF35333F5918 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -191,12 +191,12 @@ F5D05D221249C73674C5DB0D /* Pods */ = { isa = PBXGroup; children = ( - E2F8912DE8B8A6F72277E96C /* Pods-Runner.debug.xcconfig */, - 7EF2984A57FD1E0C98A7277A /* Pods-Runner.release.xcconfig */, - A046209115F73D5EC348E242 /* Pods-Runner.profile.xcconfig */, - 9C9E2734C6D4DC2FF1815F04 /* Pods-ImageNotification.debug.xcconfig */, - FAED45593175690B5B62C40E /* Pods-ImageNotification.release.xcconfig */, - DBA43D9DEB26304D56B42917 /* Pods-ImageNotification.profile.xcconfig */, + 30F261257F9F405626922227 /* Pods-ImageNotification.debug.xcconfig */, + 3392DF551C1BF6D77A2B3208 /* Pods-ImageNotification.release.xcconfig */, + 688E21D2AFE08631A83E52E2 /* Pods-ImageNotification.profile.xcconfig */, + 06B283DCF068808AD480D95A /* Pods-Runner.debug.xcconfig */, + 713F55F4545D00C46010F8E5 /* Pods-Runner.release.xcconfig */, + 805E3FD8D2123AFBFB3413E4 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -208,16 +208,17 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - A453887CF430C3DC2D462993 /* [CP] Check Pods Manifest.lock */, + C5D0533ADA1E3EA4004D7DAE /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - FB187DB152E6AC8A61142056 /* [CP] Embed Pods Frameworks */, - 3BA7760032A55E21F5FE79FC /* [CP] Copy Pods Resources */, 9C7092B825E77E470056AE0B /* Embed App Extensions */, + 9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */, + E436C3831B5DC5C13E209B54 /* [CP] Embed Pods Frameworks */, + 36A81FE406DC4A67407ADCD2 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -233,7 +234,7 @@ isa = PBXNativeTarget; buildConfigurationList = 9C7092BC25E77E470056AE0B /* Build configuration list for PBXNativeTarget "ImageNotification" */; buildPhases = ( - BBE8B4F04EBFF199FEB18CC2 /* [CP] Check Pods Manifest.lock */, + 6236732558BE076D98ED487B /* [CP] Check Pods Manifest.lock */, 9C7092AB25E77E470056AE0B /* Sources */, 9C7092AC25E77E470056AE0B /* Frameworks */, 9C7092AD25E77E470056AE0B /* Resources */, @@ -312,21 +313,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 3BA7760032A55E21F5FE79FC /* [CP] Copy Pods Resources */ = { + 36A81FE406DC4A67407ADCD2 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -344,43 +331,21 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Run Script"; + name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - A453887CF430C3DC2D462993 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - BBE8B4F04EBFF199FEB18CC2 /* [CP] Check Pods Manifest.lock */ = { + 6236732558BE076D98ED487B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -402,7 +367,63 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - FB187DB152E6AC8A61142056 /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + 9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "Firebase Crashlytics"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${PODS_ROOT}/FirebaseCrashlytics/run\n"; + }; + C5D0533ADA1E3EA4004D7DAE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + E436C3831B5DC5C13E209B54 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -414,6 +435,7 @@ "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", "${BUILT_PRODUCTS_DIR}/FirebaseInstanceID/FirebaseInstanceID.framework", "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", @@ -424,7 +446,6 @@ "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", @@ -434,8 +455,8 @@ "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", - "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -445,6 +466,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstanceID.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", @@ -455,7 +477,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", @@ -465,8 +486,8 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -592,7 +613,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -706,7 +730,8 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -730,7 +755,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -763,7 +791,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -794,7 +825,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ImageNotification/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.4; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.lunasea.lunasea.ImageNotification; @@ -822,7 +857,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ImageNotification/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.4; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.lunasea.lunasea.ImageNotification; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -849,7 +888,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ImageNotification/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.4; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.lunasea.lunasea.ImageNotification; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c6ca70e4..a0031e8a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -74,5 +74,7 @@ UIViewControllerBasedStatusBarAppearance + FirebaseCrashlyticsCollectionEnabled + diff --git a/lib/core.dart b/lib/core.dart index 31aa35c8..204a5f89 100644 --- a/lib/core.dart +++ b/lib/core.dart @@ -1,8 +1,8 @@ export 'core/constants.dart'; export 'core/database.dart'; +export 'core/deprecated_ui.dart'; export 'core/dialogs.dart'; export 'core/extensions.dart'; -export 'core/luna_ui.dart'; export 'core/mixins.dart'; export 'core/models.dart'; export 'core/system.dart'; @@ -10,6 +10,7 @@ export 'core/ui.dart'; export 'core/utilities.dart'; export 'core/widgets.dart'; export 'dart:async'; +export 'package:easy_localization/easy_localization.dart'; export 'package:flash/flash.dart'; export 'package:fluro/fluro.dart'; export 'package:hive/hive.dart'; diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 60b15f57..66272867 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -1,8 +1,6 @@ class Constants { Constants._(); - static const APPLICATION_NAME = 'LunaSea'; - static const TEXT_EMDASH = '—'; static const TEXT_BULLET = '•'; static const TEXT_ELLIPSIS = '…'; @@ -18,13 +16,14 @@ class Constants { static const NO_SERVICES_ENABLED = '<>'; static const URL_CHANGELOG = 'https://feedback.lunasea.app/changelog'; + static const URL_CRASHLYTICS = 'https://firebase.google.com/products/crashlytics'; static const URL_DISCORD = 'https://www.lunasea.app/discord'; static const URL_DOCUMENTATION = 'https://www.lunasea.app/docs'; static const URL_FEEDBACK = 'https://www.lunasea.app/feedback'; static const URL_GITHUB = 'https://www.lunasea.app/github'; static const URL_REDDIT = 'https://www.lunasea.app/reddit'; - static const URL_SENTRY = 'https://sentry.io'; static const URL_TESTFLIGHT = 'https://www.lunasea.app/testflight'; + static const URL_WEBLATE = 'https:/www.lunasea.app/translate'; static const URL_WEBSITE = 'https://www.lunasea.app'; static const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15'; diff --git a/lib/core/deprecated_ui.dart b/lib/core/deprecated_ui.dart new file mode 100644 index 00000000..e65aaad7 --- /dev/null +++ b/lib/core/deprecated_ui.dart @@ -0,0 +1,20 @@ +export 'deprecated_ui/button.dart'; +export 'deprecated_ui/card.dart'; +export 'deprecated_ui/containers.dart'; +export 'deprecated_ui/description_block.dart'; +export 'deprecated_ui/dialogs.dart'; +export 'deprecated_ui/divider.dart'; +export 'deprecated_ui/expandable.dart'; +export 'deprecated_ui/floating_action_button.dart'; +export 'deprecated_ui/icon.dart'; +export 'deprecated_ui/input_bar.dart'; +export 'deprecated_ui/listview.dart'; +export 'deprecated_ui/loader.dart'; +export 'deprecated_ui/messages.dart'; +export 'deprecated_ui/navigation_bar.dart'; +export 'deprecated_ui/network_image.dart'; +export 'deprecated_ui/refresh_indicator.dart'; +export 'deprecated_ui/shape.dart'; +export 'deprecated_ui/snackbar.dart'; +export 'deprecated_ui/table.dart'; +export 'deprecated_ui/text.dart'; diff --git a/lib/core/deprecated_ui/button.dart b/lib/core/deprecated_ui/button.dart new file mode 100644 index 00000000..f4f488d7 --- /dev/null +++ b/lib/core/deprecated_ui/button.dart @@ -0,0 +1,2 @@ +export 'button/button.dart'; +export 'button/button_slim.dart'; diff --git a/lib/core/ui/button/button.dart b/lib/core/deprecated_ui/button/button.dart similarity index 100% rename from lib/core/ui/button/button.dart rename to lib/core/deprecated_ui/button/button.dart diff --git a/lib/core/ui/button/button_slim.dart b/lib/core/deprecated_ui/button/button_slim.dart similarity index 100% rename from lib/core/ui/button/button_slim.dart rename to lib/core/deprecated_ui/button/button_slim.dart diff --git a/lib/core/deprecated_ui/card.dart b/lib/core/deprecated_ui/card.dart new file mode 100644 index 00000000..9502976c --- /dev/null +++ b/lib/core/deprecated_ui/card.dart @@ -0,0 +1,2 @@ +export 'card/card.dart'; +export 'card/card_tile.dart'; diff --git a/lib/core/ui/card/card.dart b/lib/core/deprecated_ui/card/card.dart similarity index 94% rename from lib/core/ui/card/card.dart rename to lib/core/deprecated_ui/card/card.dart index ebbd89bf..fe92f045 100644 --- a/lib/core/ui/card/card.dart +++ b/lib/core/deprecated_ui/card/card.dart @@ -7,6 +7,7 @@ class LSCard extends StatelessWidget { final bool reducedMargin; final Color color; final Decoration decoration; + final double height; LSCard({ @required this.child, @@ -14,6 +15,7 @@ class LSCard extends StatelessWidget { this.reducedMargin = false, this.color, this.decoration, + this.height, }); @override @@ -26,6 +28,7 @@ class LSCard extends StatelessWidget { child: Container( child: child, decoration: decoration, + height: height, ), elevation: LunaUI.ELEVATION, margin: reducedMargin diff --git a/lib/core/ui/card/card_tile.dart b/lib/core/deprecated_ui/card/card_tile.dart similarity index 100% rename from lib/core/ui/card/card_tile.dart rename to lib/core/deprecated_ui/card/card_tile.dart diff --git a/lib/core/deprecated_ui/containers.dart b/lib/core/deprecated_ui/containers.dart new file mode 100644 index 00000000..f4b62f99 --- /dev/null +++ b/lib/core/deprecated_ui/containers.dart @@ -0,0 +1 @@ +export 'containers/row.dart'; diff --git a/lib/core/ui/containers/row.dart b/lib/core/deprecated_ui/containers/row.dart similarity index 100% rename from lib/core/ui/containers/row.dart rename to lib/core/deprecated_ui/containers/row.dart diff --git a/lib/core/ui/description_block.dart b/lib/core/deprecated_ui/description_block.dart similarity index 100% rename from lib/core/ui/description_block.dart rename to lib/core/deprecated_ui/description_block.dart diff --git a/lib/core/ui/dialogs.dart b/lib/core/deprecated_ui/dialogs.dart similarity index 100% rename from lib/core/ui/dialogs.dart rename to lib/core/deprecated_ui/dialogs.dart diff --git a/lib/core/luna_ui/divider.dart b/lib/core/deprecated_ui/divider.dart similarity index 64% rename from lib/core/luna_ui/divider.dart rename to lib/core/deprecated_ui/divider.dart index 17331404..8789f3e3 100644 --- a/lib/core/luna_ui/divider.dart +++ b/lib/core/deprecated_ui/divider.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class LunaDivider extends Divider { - LunaDivider() : super( +class LSDivider extends StatelessWidget { + @override + Widget build(BuildContext context) => Divider( thickness: 1.0, color: LunaColours.splash, indent: 60.0, diff --git a/lib/core/ui/expandable.dart b/lib/core/deprecated_ui/expandable.dart similarity index 100% rename from lib/core/ui/expandable.dart rename to lib/core/deprecated_ui/expandable.dart diff --git a/lib/core/ui/floating_action_button.dart b/lib/core/deprecated_ui/floating_action_button.dart similarity index 100% rename from lib/core/ui/floating_action_button.dart rename to lib/core/deprecated_ui/floating_action_button.dart diff --git a/lib/core/ui/floating_action_button/floating_action_button.dart b/lib/core/deprecated_ui/floating_action_button/floating_action_button.dart similarity index 100% rename from lib/core/ui/floating_action_button/floating_action_button.dart rename to lib/core/deprecated_ui/floating_action_button/floating_action_button.dart diff --git a/lib/core/ui/floating_action_button/floating_action_button_animated.dart b/lib/core/deprecated_ui/floating_action_button/floating_action_button_animated.dart similarity index 100% rename from lib/core/ui/floating_action_button/floating_action_button_animated.dart rename to lib/core/deprecated_ui/floating_action_button/floating_action_button_animated.dart diff --git a/lib/core/ui/floating_action_button/floating_action_button_extended.dart b/lib/core/deprecated_ui/floating_action_button/floating_action_button_extended.dart similarity index 100% rename from lib/core/ui/floating_action_button/floating_action_button_extended.dart rename to lib/core/deprecated_ui/floating_action_button/floating_action_button_extended.dart diff --git a/lib/core/ui/icon.dart b/lib/core/deprecated_ui/icon.dart similarity index 100% rename from lib/core/ui/icon.dart rename to lib/core/deprecated_ui/icon.dart diff --git a/lib/core/ui/icon/icon.dart b/lib/core/deprecated_ui/icon/icon.dart similarity index 100% rename from lib/core/ui/icon/icon.dart rename to lib/core/deprecated_ui/icon/icon.dart diff --git a/lib/core/ui/icon/icon_button.dart b/lib/core/deprecated_ui/icon/icon_button.dart similarity index 100% rename from lib/core/ui/icon/icon_button.dart rename to lib/core/deprecated_ui/icon/icon_button.dart diff --git a/lib/core/ui/icon/icon_custom.dart b/lib/core/deprecated_ui/icon/icon_custom.dart similarity index 100% rename from lib/core/ui/icon/icon_custom.dart rename to lib/core/deprecated_ui/icon/icon_custom.dart diff --git a/lib/core/luna_ui/input_bar.dart b/lib/core/deprecated_ui/input_bar.dart similarity index 87% rename from lib/core/luna_ui/input_bar.dart rename to lib/core/deprecated_ui/input_bar.dart index 5a42c3e9..741b6448 100644 --- a/lib/core/luna_ui/input_bar.dart +++ b/lib/core/deprecated_ui/input_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class LunaTextInputBar extends StatefulWidget { +class LSTextInputBar extends StatefulWidget { final TextEditingController controller; final TextInputAction action; final TextInputType keyboardType; @@ -11,12 +11,12 @@ class LunaTextInputBar extends StatefulWidget { final bool autofocus; final bool obscureText; final bool isFormField; - final void Function(String) onChanged; + final void Function(String, bool) onChanged; final void Function(String) onSubmitted; final String Function(String) validator; final EdgeInsets margin; - LunaTextInputBar({ + LSTextInputBar({ @required this.controller, this.onChanged, this.onSubmitted, @@ -30,15 +30,13 @@ class LunaTextInputBar extends StatefulWidget { this.autofocus = false, this.obscureText = false, this.isFormField = false, - }) { - assert(controller != null); - } + }); @override State createState() => _State(); } -class _State extends State { +class _State extends State { bool _isFocused = false; @override @@ -72,10 +70,7 @@ class _State extends State { color: LunaColours.accent, size: 24.0, ), - onTap: widget.onChanged == null ? null : () { - widget.controller.text = ''; - widget.onChanged(''); - } + onTap: () => widget?.onChanged('', true), ), opacity: !_isFocused || widget.controller.text == '' ? 0.0 : 1.0, duration: Duration(milliseconds: 200), @@ -103,7 +98,7 @@ class _State extends State { autocorrect: false, keyboardType: widget.keyboardType, validator: widget?.validator, - onChanged: widget.onChanged == null ? null : widget.onChanged, + onChanged: widget.onChanged == null ? null : (value) => widget.onChanged(value, false), onFieldSubmitted: widget?.onSubmitted, ); @@ -118,7 +113,7 @@ class _State extends State { obscureText: widget.obscureText, autocorrect: false, keyboardType: widget.keyboardType, - onChanged: widget.onChanged == null ? null : widget.onChanged, + onChanged: widget.onChanged == null ? null : (value) => widget.onChanged(value, false), onSubmitted: widget?.onSubmitted, ); -} +} \ No newline at end of file diff --git a/lib/core/ui/listview.dart b/lib/core/deprecated_ui/listview.dart similarity index 100% rename from lib/core/ui/listview.dart rename to lib/core/deprecated_ui/listview.dart diff --git a/lib/core/ui/listview/listview.dart b/lib/core/deprecated_ui/listview/listview.dart similarity index 100% rename from lib/core/ui/listview/listview.dart rename to lib/core/deprecated_ui/listview/listview.dart diff --git a/lib/core/ui/listview/listview_builder.dart b/lib/core/deprecated_ui/listview/listview_builder.dart similarity index 100% rename from lib/core/ui/listview/listview_builder.dart rename to lib/core/deprecated_ui/listview/listview_builder.dart diff --git a/lib/core/ui/listview/listview_reorderable.dart b/lib/core/deprecated_ui/listview/listview_reorderable.dart similarity index 100% rename from lib/core/ui/listview/listview_reorderable.dart rename to lib/core/deprecated_ui/listview/listview_reorderable.dart diff --git a/lib/core/ui/listview/listview_stickyheader.dart b/lib/core/deprecated_ui/listview/listview_stickyheader.dart similarity index 100% rename from lib/core/ui/listview/listview_stickyheader.dart rename to lib/core/deprecated_ui/listview/listview_stickyheader.dart diff --git a/lib/core/luna_ui/loader.dart b/lib/core/deprecated_ui/loader.dart similarity index 91% rename from lib/core/luna_ui/loader.dart rename to lib/core/deprecated_ui/loader.dart index fc9cc497..63d0e569 100644 --- a/lib/core/luna_ui/loader.dart +++ b/lib/core/deprecated_ui/loader.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:lunasea/core.dart'; -class LunaLoader extends StatelessWidget { +class LSLoader extends StatelessWidget { final double size; final Color color; - LunaLoader({ + LSLoader({ Key key, this.size = 25.0, this.color, diff --git a/lib/core/ui/messages.dart b/lib/core/deprecated_ui/messages.dart similarity index 100% rename from lib/core/ui/messages.dart rename to lib/core/deprecated_ui/messages.dart diff --git a/lib/core/ui/messages/error_message.dart b/lib/core/deprecated_ui/messages/error_message.dart similarity index 100% rename from lib/core/ui/messages/error_message.dart rename to lib/core/deprecated_ui/messages/error_message.dart diff --git a/lib/core/ui/messages/generic.dart b/lib/core/deprecated_ui/messages/generic.dart similarity index 100% rename from lib/core/ui/messages/generic.dart rename to lib/core/deprecated_ui/messages/generic.dart diff --git a/lib/core/ui/messages/not_enabled.dart b/lib/core/deprecated_ui/messages/not_enabled.dart similarity index 100% rename from lib/core/ui/messages/not_enabled.dart rename to lib/core/deprecated_ui/messages/not_enabled.dart diff --git a/lib/core/ui/messages/typewriter.dart b/lib/core/deprecated_ui/messages/typewriter.dart similarity index 100% rename from lib/core/ui/messages/typewriter.dart rename to lib/core/deprecated_ui/messages/typewriter.dart diff --git a/lib/core/ui/messages/unknown_route.dart b/lib/core/deprecated_ui/messages/unknown_route.dart similarity index 100% rename from lib/core/ui/messages/unknown_route.dart rename to lib/core/deprecated_ui/messages/unknown_route.dart diff --git a/lib/core/ui/navigation_bar.dart b/lib/core/deprecated_ui/navigation_bar.dart similarity index 100% rename from lib/core/ui/navigation_bar.dart rename to lib/core/deprecated_ui/navigation_bar.dart diff --git a/lib/core/ui/navigation_bar/navigation_bar.dart b/lib/core/deprecated_ui/navigation_bar/navigation_bar.dart similarity index 100% rename from lib/core/ui/navigation_bar/navigation_bar.dart rename to lib/core/deprecated_ui/navigation_bar/navigation_bar.dart diff --git a/lib/core/deprecated_ui/network_image.dart b/lib/core/deprecated_ui/network_image.dart new file mode 100644 index 00000000..43534516 --- /dev/null +++ b/lib/core/deprecated_ui/network_image.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +// ignore: non_constant_identifier_names +Widget LSNetworkImage({ + @required double height, + @required double width, + @required String url, + @required String placeholder, + Map headers, + bool roundCorners = true, +}) => ClipRRect( + child: Container( + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset(placeholder), + if(url != null) FadeInImage( + height: height, + width: width, + fadeInDuration: Duration(milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED), + fadeOutDuration: Duration(milliseconds: 1), + placeholder: AssetImage(placeholder), + fit: BoxFit.cover, + image: NetworkImage( + url, + headers: headers, + ), + ), + ], + ), + height: height, + width: width, + ), + borderRadius: roundCorners + ? BorderRadius.circular(Constants.UI_BORDER_RADIUS) + : null, +); \ No newline at end of file diff --git a/lib/core/deprecated_ui/refresh_indicator.dart b/lib/core/deprecated_ui/refresh_indicator.dart new file mode 100644 index 00000000..a9f344ce --- /dev/null +++ b/lib/core/deprecated_ui/refresh_indicator.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class LSRefreshIndicator extends StatefulWidget { + final Widget child; + final Function onRefresh; + final GlobalKey refreshKey; + + LSRefreshIndicator({ + @required this.child, + @required this.onRefresh, + @required this.refreshKey, + }); + + @override + State createState() => _State(); +} + +class _State extends State { + @override + Widget build(BuildContext context) { + return RefreshIndicator( + key: widget.refreshKey, + backgroundColor: Theme.of(context).primaryColor, + onRefresh: widget.onRefresh, + child: widget.child, + ); + } +} \ No newline at end of file diff --git a/lib/core/deprecated_ui/shape.dart b/lib/core/deprecated_ui/shape.dart new file mode 100644 index 00000000..462e6be3 --- /dev/null +++ b/lib/core/deprecated_ui/shape.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +// ignore: non_constant_identifier_names +ShapeBorder LSRoundedShape({ + double borderRadius = Constants.UI_BORDER_RADIUS, +}) => RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), +); + +// ignore: non_constant_identifier_names +ShapeBorder LSRoundedShapeWithBorder({ + double borderRadius = Constants.UI_BORDER_RADIUS, +}) => RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), + side: BorderSide( + color: Colors.white12, + ), +); diff --git a/lib/core/deprecated_ui/snackbar.dart b/lib/core/deprecated_ui/snackbar.dart new file mode 100644 index 00000000..84285668 --- /dev/null +++ b/lib/core/deprecated_ui/snackbar.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flash/flash.dart'; +import 'package:lunasea/core.dart'; + +enum SNACKBAR_TYPE { + success, + failure, + info, +} + +const _LEFT_PADDING = 8.0; + +// ignore: non_constant_identifier_names +Future LSSnackBar({ + @required BuildContext context, + @required String title, + String message = LunaLogger.CHECK_LOGS_MESSAGE, + Duration duration, + SNACKBAR_TYPE type = SNACKBAR_TYPE.info, + bool showButton = false, + String buttonText = 'view', + Function buttonOnPressed, +}) async { + if(showButton) assert(buttonOnPressed != null); + Color color; + IconData icon; + switch(type) { + case SNACKBAR_TYPE.failure: color = LunaColours.red; icon = Icons.error_outline; break; + case SNACKBAR_TYPE.success: color = LunaColours.accent; icon = Icons.check_circle_outline; break; + case SNACKBAR_TYPE.info: color = LunaColours.blue; icon = Icons.info_outline; break; + } + showFlash( + context: context, + duration: duration != null + ? duration + : showButton + ? Duration(seconds: 4) + : Duration(seconds: 2), + builder: (context, controller) => Flash( + backgroundColor: Theme.of(context).primaryColor, + controller: controller, + style: FlashStyle.floating, + boxShadows: [BoxShadow(blurRadius: 6.0, spreadRadius: 4.0, color: Colors.black.withOpacity(0.10))], + horizontalDismissDirection: HorizontalDismissDirection.horizontal, + margin: EdgeInsets.all(8.0), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data + ? Colors.white12 + : Colors.transparent, + child: FlashBar( + title: Text( + title, + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + fontSize: Constants.UI_FONT_SIZE_TITLE, + ), + ), + message: Text( + message, + style: TextStyle( + color: Colors.white70, + fontSize: Constants.UI_FONT_SIZE_SUBTITLE, + ), + ), + icon: Padding( + child: LSIconButton( + icon: icon, + color: color, + ), + padding: EdgeInsets.only(left: _LEFT_PADDING), + ), + primaryAction: showButton + ? TextButton( + child: Text( + buttonText.toUpperCase(), + style: TextStyle( + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + color: LunaColours.accent, + ), + ), + onPressed: () { + controller.dismiss(); + buttonOnPressed(); + }, + ) + : null, + ), + ), + ); +} diff --git a/lib/core/deprecated_ui/table.dart b/lib/core/deprecated_ui/table.dart new file mode 100644 index 00000000..59e9bf2b --- /dev/null +++ b/lib/core/deprecated_ui/table.dart @@ -0,0 +1,2 @@ +export 'table/block.dart'; +export 'table/content.dart'; diff --git a/lib/core/ui/table/block.dart b/lib/core/deprecated_ui/table/block.dart similarity index 100% rename from lib/core/ui/table/block.dart rename to lib/core/deprecated_ui/table/block.dart diff --git a/lib/core/ui/table/content.dart b/lib/core/deprecated_ui/table/content.dart similarity index 100% rename from lib/core/ui/table/content.dart rename to lib/core/deprecated_ui/table/content.dart diff --git a/lib/core/deprecated_ui/text.dart b/lib/core/deprecated_ui/text.dart new file mode 100644 index 00000000..271a8863 --- /dev/null +++ b/lib/core/deprecated_ui/text.dart @@ -0,0 +1,4 @@ +export 'text/header.dart'; +export 'text/text_highlighted.dart'; +export 'text/text_title.dart'; +export 'text/text_subtitle.dart'; diff --git a/lib/core/ui/text/header.dart b/lib/core/deprecated_ui/text/header.dart similarity index 100% rename from lib/core/ui/text/header.dart rename to lib/core/deprecated_ui/text/header.dart diff --git a/lib/core/ui/text/text_highlighted.dart b/lib/core/deprecated_ui/text/text_highlighted.dart similarity index 100% rename from lib/core/ui/text/text_highlighted.dart rename to lib/core/deprecated_ui/text/text_highlighted.dart diff --git a/lib/core/ui/text/text_subtitle.dart b/lib/core/deprecated_ui/text/text_subtitle.dart similarity index 100% rename from lib/core/ui/text/text_subtitle.dart rename to lib/core/deprecated_ui/text/text_subtitle.dart diff --git a/lib/core/ui/text/text_title.dart b/lib/core/deprecated_ui/text/text_title.dart similarity index 100% rename from lib/core/ui/text/text_title.dart rename to lib/core/deprecated_ui/text/text_title.dart diff --git a/lib/core/extensions/datetime.dart b/lib/core/extensions/datetime.dart index 962661f8..aa74de07 100644 --- a/lib/core/extensions/datetime.dart +++ b/lib/core/extensions/datetime.dart @@ -13,6 +13,7 @@ extension DateTimeExtension on DateTime { String get lunaAge { if(this == null) return 'Unknown'; Duration diff = DateTime.now().toLocal().difference(this); + if(diff.inSeconds < 10) return 'Just Now'; if(diff.inDays >= 1) return '${diff.inDays} ${diff.inDays == 1 ? 'Day' : 'Days'} Ago'; if(diff.inHours >= 1) return '${diff.inHours} ${diff.inHours == 1 ? 'Hour' : 'Hours'} Ago'; if(diff.inMinutes >= 1) return '${diff.inMinutes} ${diff.inMinutes == 1 ? 'Minute' : 'Minutes'} Ago'; diff --git a/lib/core/extensions/string.dart b/lib/core/extensions/string.dart index eb28d4fa..8fd9cb1f 100644 --- a/lib/core/extensions/string.dart +++ b/lib/core/extensions/string.dart @@ -65,6 +65,12 @@ extension StringLinksExtension on String { /// Attach this string as a series ID to TheTVDB and attempt to launch it as a URL. Future lunaOpenTheTVDB() async => await _openLink('https://www.thetvdb.com/?id=$this&tab=series'); + /// Attach this string as a TVDB ID to Trakt and attempt to launch it as a URL. + Future lunaOpenTraktSeries() async => await _openLink('http://trakt.tv/search/tvdb/$this?id_type=show'); + + /// Attach this string as a TMDB ID to Trakt and attempt to launch it as a URL. + Future lunaOpenTraktMovie() async => await _openLink('https://trakt.tv/search/tmdb/$this?id_type=movie'); + /// Attach this string as a series ID to TVMaze and attempt to launch it as a URL. Future lunaOpenTVMaze() async => await _openLink('https://www.tvmaze.com/shows/$this'); } diff --git a/lib/core/luna_ui.dart b/lib/core/luna_ui.dart deleted file mode 100644 index 18e3263e..00000000 --- a/lib/core/luna_ui.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; -export 'luna_ui/appbar.dart'; -export 'luna_ui/bottom_modal_sheet.dart'; -export 'luna_ui/bottom_navigation_bar.dart'; -export 'luna_ui/button.dart'; -export 'luna_ui/card.dart'; -export 'luna_ui/colors.dart'; -export 'luna_ui/containers.dart'; -export 'luna_ui/controllers.dart'; -export 'luna_ui/decoration.dart'; -export 'luna_ui/divider.dart'; -export 'luna_ui/header.dart'; -export 'luna_ui/highlighted_node.dart'; -export 'luna_ui/icons.dart'; -export 'luna_ui/input_bar.dart'; -export 'luna_ui/invalid_route.dart'; -export 'luna_ui/list_tile.dart'; -export 'luna_ui/list_view.dart'; -export 'luna_ui/loader.dart'; -export 'luna_ui/message.dart'; -export 'luna_ui/network_image.dart'; -export 'luna_ui/popup_menu_button.dart'; -export 'luna_ui/refresh_indicator.dart'; -export 'luna_ui/shape.dart'; -export 'luna_ui/sliver_sticky_header.dart'; -export 'luna_ui/snackbar.dart'; -export 'luna_ui/switch.dart'; -export 'luna_ui/table.dart'; -export 'luna_ui/text.dart'; -export 'luna_ui/theme.dart'; - -class LunaUI { - // Text Constants - static const String TEXT_ARROW_LEFT = '←'; - static const String TEXT_ARROW_RIGHT = '→'; - static const String TEXT_BULLET = '•'; - static const String TEXT_ELLIPSIS = '…'; - static const String TEXT_EMDASH = '—'; - // Font-Related - static const double FONT_SIZE_APP_BAR = 18.0; - static const double FONT_SIZE_BUTTON = 14.0; - static const double FONT_SIZE_GRAPH_LEGEND = 10.0; - static const double FONT_SIZE_HEADER = 18.0; - static const double FONT_SIZE_HIGHLIGHTED_NODE = 12.0; - static const double FONT_SIZE_MESSAGES = 16.0; - static const double FONT_SIZE_NAVIGATION_BAR = 13.0; - static const double FONT_SIZE_SUBHEADER = 12.0; - static const double FONT_SIZE_SUBTITLE = 13.0; - static const double FONT_SIZE_TITLE = 16.0; - static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600; - // UI-Related - static const int ANIMATION_SPEED = 250; - static const int ANIMATION_IMAGE_FADE_IN_SPEED = 125; - static const double BORDER_RADIUS = 10.0; - static const double ELEVATION = 0.0; - static const EdgeInsets MARGIN_CARD = const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0); - // Border-Related - static bool get shouldUseBorder => LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data; - static ShapeBorder get shapeBorder => shouldUseBorder ? LunaShapeBorder.roundedWithBorder() : LunaShapeBorder.rounded(); -} diff --git a/lib/core/luna_ui/button.dart b/lib/core/luna_ui/button.dart deleted file mode 100644 index 47d72a74..00000000 --- a/lib/core/luna_ui/button.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:lunasea/core.dart'; - -enum _BUTTON_SIZE { - REGULAR, - SLIM, -} - -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; - - 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, - 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( - 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, - ), - textAlign: TextAlign.center, - ); - } -} \ No newline at end of file diff --git a/lib/core/luna_ui/card.dart b/lib/core/luna_ui/card.dart deleted file mode 100644 index 3844552e..00000000 --- a/lib/core/luna_ui/card.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaCard extends Card { - LunaCard({ - Key key, - @required BuildContext context, - @required Widget child, - EdgeInsets margin = LunaUI.MARGIN_CARD, - Color color, - Decoration decoration, - }) : super( - key: key, - child: Container(child: child, decoration: decoration), - margin: margin, - color: color == null ? Theme.of(context).primaryColor : color, - shape: LunaUI.shapeBorder, - elevation: LunaUI.ELEVATION, - clipBehavior: Clip.antiAlias, - ) { - assert(context != null); - assert(child != null); - } -} diff --git a/lib/core/luna_ui/containers.dart b/lib/core/luna_ui/containers.dart deleted file mode 100644 index d6d516d7..00000000 --- a/lib/core/luna_ui/containers.dart +++ /dev/null @@ -1 +0,0 @@ -export 'containers/button_container.dart'; diff --git a/lib/core/luna_ui/network_image.dart b/lib/core/luna_ui/network_image.dart deleted file mode 100644 index 634b8612..00000000 --- a/lib/core/luna_ui/network_image.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaNetworkImage extends ClipRRect { - LunaNetworkImage({ - double height, - double width, - String url, - @required String placeholderAsset, - Map headers, - bool roundCorners = true, - }) : super( - child: Container( - child: Stack( - fit: StackFit.expand, - children: [ - Image.asset( - placeholderAsset, - height: height, - width: width, - ), - if(url != null && url.isNotEmpty) FadeInImage( - height: height, - width: width, - fadeInDuration: Duration(milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED), - fadeOutDuration: Duration(milliseconds: 1), - placeholder: AssetImage(placeholderAsset), - fit: BoxFit.cover, - image: NetworkImage(url, headers: headers?.cast()), - ), - ], - ), - height: height, - width: width, - ), - borderRadius: roundCorners ? BorderRadius.circular(LunaUI.BORDER_RADIUS) : null, - ) { - // assert(height != null); - // assert(width != null); - assert(placeholderAsset != null); - } -} diff --git a/lib/core/luna_ui/refresh_indicator.dart b/lib/core/luna_ui/refresh_indicator.dart deleted file mode 100644 index af0d67c5..00000000 --- a/lib/core/luna_ui/refresh_indicator.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class LunaRefreshIndicator extends RefreshIndicator { - LunaRefreshIndicator({ - GlobalKey key, - @required BuildContext context, - @required Future Function() onRefresh, - @required Widget child, - }) : super( - key: key, - backgroundColor: Theme.of(context).primaryColor, - onRefresh: onRefresh, - child: child, - ); -} diff --git a/lib/core/luna_ui/shape.dart b/lib/core/luna_ui/shape.dart deleted file mode 100644 index 7d369d31..00000000 --- a/lib/core/luna_ui/shape.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaShapeBorder extends RoundedRectangleBorder { - LunaShapeBorder._internal({ - bool useBorder = false, - }) : super( - borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), - side: useBorder ? BorderSide(color: Colors.white12) : BorderSide.none, - ); - - factory LunaShapeBorder.rounded() => LunaShapeBorder._internal(); - factory LunaShapeBorder.roundedWithBorder() => LunaShapeBorder._internal(useBorder: true); -} diff --git a/lib/core/luna_ui/snackbar.dart b/lib/core/luna_ui/snackbar.dart deleted file mode 100644 index b4d4b8f5..00000000 --- a/lib/core/luna_ui/snackbar.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'snackbar/snackbar.dart'; -export 'snackbar/snackbar_error.dart'; -export 'snackbar/snackbar_info.dart'; -export 'snackbar/snackbar_success.dart'; diff --git a/lib/core/luna_ui/table.dart b/lib/core/luna_ui/table.dart deleted file mode 100644 index 5d4c60a9..00000000 --- a/lib/core/luna_ui/table.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'table/table_card.dart'; -export 'table/table_content.dart'; diff --git a/lib/core/luna_ui/text.dart b/lib/core/luna_ui/text.dart deleted file mode 100644 index b3bd0cb4..00000000 --- a/lib/core/luna_ui/text.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lunasea/core.dart'; - -class LunaText extends Text { - /// Create a new [Text] widget. - LunaText({ - @required String text, - Key key, - int maxLines, - TextOverflow overflow, - bool softWrap, - TextStyle style, - TextAlign textAlign, - }) : super( - text, - key: key, - maxLines: maxLines, - overflow: overflow, - softWrap: softWrap, - style: style, - textAlign: textAlign, - ); - - /// Create a [LunaText] widget with the styling pre-assigned to be a LunaSea title. - factory LunaText.title({ - Key key, - @required String text, - int maxLines = 1, - bool softWrap = false, - TextAlign textAlign = TextAlign.start, - Color color = Colors.white, - bool darken = false, - }) => LunaText( - text: text, - key: key, - maxLines: maxLines, - overflow: TextOverflow.fade, - softWrap: softWrap, - textAlign: textAlign, - style: TextStyle( - color: darken ? color.withOpacity(0.30) : color, - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: LunaUI.FONT_SIZE_TITLE, - ), - ); - - /// Create a [LunaText] widget with the styling pre-assigned to be a LunaSea subtitle. - factory LunaText.subtitle({ - Key key, - @required String text, - int maxLines = 1, - bool softWrap = false, - TextAlign textAlign = TextAlign.start, - Color color = Colors.white70, - bool darken = false, - }) => LunaText( - key: key, - text: text, - softWrap: softWrap, - maxLines: maxLines, - textAlign: textAlign, - style: TextStyle( - color: darken ? color.withOpacity(0.30) : color, - fontSize: LunaUI.FONT_SIZE_SUBTITLE, - ), - ); -} diff --git a/lib/core/models.dart b/lib/core/models.dart index a495c030..53f40065 100644 --- a/lib/core/models.dart +++ b/lib/core/models.dart @@ -1,5 +1,5 @@ export 'models/hive_objects.dart'; +export 'models/module_state.dart'; export 'models/router.dart'; -export 'models/state.dart'; export 'models/types.dart'; export 'models/webhooks.dart'; diff --git a/lib/core/models/state/module_state.dart b/lib/core/models/module_state.dart similarity index 100% rename from lib/core/models/state/module_state.dart rename to lib/core/models/module_state.dart diff --git a/lib/core/models/state.dart b/lib/core/models/state.dart deleted file mode 100644 index 22c651f1..00000000 --- a/lib/core/models/state.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'state/module_state.dart'; -export 'state/state.dart'; diff --git a/lib/core/system.dart b/lib/core/system.dart index 8cc81e2c..de5d743c 100644 --- a/lib/core/system.dart +++ b/lib/core/system.dart @@ -1,8 +1,10 @@ export 'system/firebase.dart'; export 'system/image_cache.dart'; export 'system/in_app_purchases.dart'; +export 'system/localization.dart'; export 'system/logger.dart'; export 'system/modules.dart'; export 'system/networking.dart'; export 'system/quick_actions.dart'; export 'system/router.dart'; +export 'system/state.dart'; diff --git a/lib/core/system/firebase.dart b/lib/core/system/firebase.dart index 2b0b0877..f736b7a9 100644 --- a/lib/core/system/firebase.dart +++ b/lib/core/system/firebase.dart @@ -1,5 +1,6 @@ export 'firebase/auth.dart'; export 'firebase/core.dart'; +export 'firebase/crashlytics.dart'; export 'firebase/firestore.dart'; export 'firebase/messaging.dart'; export 'firebase/storage.dart'; diff --git a/lib/core/system/firebase/crashlytics.dart b/lib/core/system/firebase/crashlytics.dart new file mode 100644 index 00000000..5eb69de1 --- /dev/null +++ b/lib/core/system/firebase/crashlytics.dart @@ -0,0 +1,17 @@ +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:lunasea/core.dart'; + +class LunaFirebaseCrashlytics { + /// Returns an instance of [FirebaseCrashlytics]. + /// + /// Throws an error if [LunaFirebase.initialize] has not been called. + static FirebaseCrashlytics get instance => FirebaseCrashlytics.instance; + + /// Set the enabled state of Firebase Crashlytics. + /// + /// If `enabled` is supplied, use that value, else use [LunaDatabaseValue.ENABLED_SENTRY]. + void setEnabledState() { + bool state = LunaDatabaseValue.ENABLED_SENTRY.data; + instance.setCrashlyticsCollectionEnabled(state); + } +} diff --git a/lib/core/system/firebase/messaging.dart b/lib/core/system/firebase/messaging.dart index 4ba34a60..f279375f 100644 --- a/lib/core/system/firebase/messaging.dart +++ b/lib/core/system/firebase/messaging.dart @@ -90,7 +90,7 @@ class LunaFirebaseMessaging { if(result) { module?.handleWebhook(message.data); } else { - showLunaErrorSnackBar(title: 'Unknown Profile', message: '"$profile" does not exist in ${Constants.APPLICATION_NAME}'); + showLunaErrorSnackBar(title: 'Unknown Profile', message: '"$profile" does not exist in LunaSea'); } } } diff --git a/lib/core/system/localization.dart b/lib/core/system/localization.dart new file mode 100644 index 00000000..7c95d13a --- /dev/null +++ b/lib/core/system/localization.dart @@ -0,0 +1,81 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaLocalization { + /// List of [LunaLanguage] values with all currently supported languages + List get supportedLanguages => LunaLanguage.values.where((lang) => lang.enabled).toList(); + + /// Returns an iterable list of [Locale] objects containing all supported & enabled locales. + Iterable get supportedLocales => supportedLanguages.map((lang) => lang.locale).toList(); + + Locale get fallbackLocale => LunaLanguage.ENGLISH.locale; + + /// Directory containing all localization files + String get fileDirectory => 'assets/localization'; + + /// Initialize the localization system. + Future initialize() async { + await EasyLocalization.ensureInitialized(); + } +} + +enum LunaLanguage { + ENGLISH, + FRENCH, + NORWEGIAN_BOKMAL, + SWEDISH, +} + +extension LunaLanguageExtension on LunaLanguage { + LunaLanguage fromLocale(Locale locale) { + if(locale.toLanguageTag() == LunaLanguage.ENGLISH.languageTag) return LunaLanguage.ENGLISH; + if(locale.toLanguageTag() == LunaLanguage.FRENCH.languageTag) return LunaLanguage.FRENCH; + if(locale.toLanguageTag() == LunaLanguage.NORWEGIAN_BOKMAL.languageTag) return LunaLanguage.NORWEGIAN_BOKMAL; + if(locale.toLanguageTag() == LunaLanguage.SWEDISH.languageTag) return LunaLanguage.SWEDISH; + return null; + } + + bool get enabled { + switch(this) { + case LunaLanguage.ENGLISH: return true; + case LunaLanguage.FRENCH: return kDebugMode; + case LunaLanguage.NORWEGIAN_BOKMAL: return kDebugMode; + case LunaLanguage.SWEDISH: return kDebugMode; + } + throw Exception('Invalid LunaLanguage'); + } + + String get name { + switch(this) { + case LunaLanguage.ENGLISH: return 'English'; + case LunaLanguage.FRENCH: return 'French'; + case LunaLanguage.NORWEGIAN_BOKMAL: return 'Norwegian (Bokmål)'; + case LunaLanguage.SWEDISH: return 'Swedish'; + } + throw Exception('Invalid LunaLanguage'); + } + + String get languageTag { + switch(this) { + case LunaLanguage.ENGLISH: return 'en'; + case LunaLanguage.FRENCH: return 'fr'; + case LunaLanguage.NORWEGIAN_BOKMAL: return 'nb-NO'; + case LunaLanguage.SWEDISH: return 'sv'; + } + throw Exception('Invalid LunaLanguage'); + } + + Locale get locale { + switch(this) { + case LunaLanguage.ENGLISH: return Locale('en'); + case LunaLanguage.FRENCH: return Locale('fr'); + case LunaLanguage.NORWEGIAN_BOKMAL: return Locale('nb', 'NO'); + case LunaLanguage.SWEDISH: return Locale('sv'); + } + throw Exception('Invalid LunaLanguage'); + } + + Future use(BuildContext context) => context.setLocale(this.locale); +} diff --git a/lib/core/system/logger.dart b/lib/core/system/logger.dart index c10b6931..04bf7b35 100644 --- a/lib/core/system/logger.dart +++ b/lib/core/system/logger.dart @@ -1,25 +1,24 @@ import 'dart:async'; import 'dart:io'; -import 'package:dio/dio.dart'; import 'package:f_logs/f_logs.dart' show FLog, DataLogType, FormatType, LogsConfig; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:stack_trace/stack_trace.dart'; class LunaLogger { - static const SENTRY_DSN = 'https://511f76efcf714ecfb5ed6b26b5819bd6@o426090.ingest.sentry.io/5367513'; static const CHECK_LOGS_MESSAGE = 'Check the logs for more details'; /// Initialize the logger by setting the timestamp format and capturing errors on [FlutterError.onError]. void initialize() { + LunaFirebaseCrashlytics().setEnabledState(); LogsConfig config = FLog.getDefaultConfigurations() ..formatType = FormatType.FORMAT_SQUARE ..timestampFormat = 'MMMM dd, y - hh:mm:ss a'; FLog.applyConfigurations(config); FlutterError.onError = (FlutterErrorDetails details, { bool forceReport = false }) async { if (kDebugMode) FlutterError.dumpErrorToConsole(details, forceReport: forceReport); + LunaFirebaseCrashlytics.instance.recordFlutterError(details); Zone.current.handleUncaughtError(details.exception, details.stack); }; } @@ -40,6 +39,7 @@ class LunaLogger { /// Log a new error-level log. void error(String text, dynamic error, StackTrace stack) { + if(LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled) LunaFirebaseCrashlytics.instance.recordError(error, stack); Trace _trace = stack == null ? null : Trace.from(stack); FLog.error( className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown', @@ -48,15 +48,12 @@ class LunaLogger { exception: error, stacktrace: stack, dataLogType: DataLogType.DEFAULT.toString(), - ); - if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException( - error, - stackTrace: stack, - ); + ); } /// Log a new fatal-level log. void fatal(dynamic error, StackTrace stack) { + if(LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled) LunaFirebaseCrashlytics.instance.recordError(error, stack); Trace _trace = stack == null ? null : Trace.from(stack); FLog.fatal( className: (_trace?.frames?.length ?? 0) >= 1 ? _trace.frames[1].uri.toString() ?? 'Unknown' : 'Unknown', @@ -66,10 +63,6 @@ class LunaLogger { stacktrace: stack, dataLogType: DataLogType.DEFAULT.toString(), ); - if(!(error is DioError) && LunaDatabaseValue.ENABLED_SENTRY.data) Sentry.captureException( - error, - stackTrace: stack, - ); } /// Export all logs, and return the [File] object containing the log file. diff --git a/lib/core/system/modules.dart b/lib/core/system/modules.dart index bea838cb..5c585719 100644 --- a/lib/core/system/modules.dart +++ b/lib/core/system/modules.dart @@ -184,7 +184,7 @@ extension LunaModuleExtension on LunaModule { case LunaModule.NZBGET: return 'NZBGet is a binary downloader, which downloads files from Usenet based on information given in nzb-files.'; case LunaModule.RADARR: return 'Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. It can also be configured to automatically upgrade the quality of existing files in the library when a better quality format becomes available.'; case LunaModule.SABNZBD: return 'SABnzbd is a multi-platform binary newsgroup downloader. The program works in the background and simplifies the downloading verifying and extracting of files from Usenet.'; - case LunaModule.SEARCH: return '${Constants.APPLICATION_NAME} currently supports all indexers that support the newznab protocol, including NZBHydra2.'; + case LunaModule.SEARCH: return 'LunaSea currently supports all indexers that support the newznab protocol, including NZBHydra2.'; case LunaModule.SETTINGS: return null; case LunaModule.SONARR: return 'Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.'; case LunaModule.TAUTULLI: return 'Tautulli is an application that you can run alongside your Plex Media Server to monitor activity and track various statistics. Most importantly, these statistics include what has been watched, who watched it, when and where they watched it, and how it was watched.'; diff --git a/lib/core/models/state/state.dart b/lib/core/system/state.dart similarity index 100% rename from lib/core/models/state/state.dart rename to lib/core/system/state.dart diff --git a/lib/core/ui.dart b/lib/core/ui.dart index 72df90fb..d69d775a 100644 --- a/lib/core/ui.dart +++ b/lib/core/ui.dart @@ -1,21 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +export 'ui/appbar.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'; export 'ui/containers.dart'; -export 'ui/description_block.dart'; -export 'ui/dialogs.dart'; +export 'ui/controllers.dart'; +export 'ui/decoration.dart'; export 'ui/divider.dart'; export 'ui/drawer.dart'; -export 'ui/expandable.dart'; -export 'ui/floating_action_button.dart'; -export 'ui/icon.dart'; +export 'ui/header.dart'; +export 'ui/highlighted_node.dart'; +export 'ui/icons.dart'; export 'ui/input_bar.dart'; -export 'ui/listview.dart'; +export 'ui/invalid_route.dart'; +export 'ui/list_tile.dart'; +export 'ui/list_view.dart'; export 'ui/loader.dart'; -export 'ui/messages.dart'; -export 'ui/navigation_bar.dart'; +export 'ui/message.dart'; export 'ui/network_image.dart'; +export 'ui/popup_menu_button.dart'; export 'ui/refresh_indicator.dart'; export 'ui/shape.dart'; +export 'ui/sliver_sticky_header.dart'; export 'ui/snackbar.dart'; +export 'ui/switch.dart'; export 'ui/table.dart'; export 'ui/text.dart'; +export 'ui/theme.dart'; + +class LunaUI { + // Text Constants + static const String TEXT_ARROW_LEFT = '←'; + static const String TEXT_ARROW_RIGHT = '→'; + static const String TEXT_BULLET = '•'; + static const String TEXT_ELLIPSIS = '…'; + static const String TEXT_EMDASH = '—'; + // Font-Related + static const double FONT_SIZE_APP_BAR = 18.0; + static const double FONT_SIZE_BUTTON = 14.0; + static const double FONT_SIZE_GRAPH_LEGEND = 10.0; + static const double FONT_SIZE_HEADER = 18.0; + static const double FONT_SIZE_HIGHLIGHTED_NODE = 12.0; + static const double FONT_SIZE_MESSAGES = 16.0; + static const double FONT_SIZE_NAVIGATION_BAR = 13.0; + static const double FONT_SIZE_SUBHEADER = 12.0; + static const double FONT_SIZE_SUBTITLE = 13.0; + static const double FONT_SIZE_TITLE = 16.0; + static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600; + // UI-Related + static const int ANIMATION_SPEED = 250; + static const int ANIMATION_IMAGE_FADE_IN_SPEED = 125; + static const double BORDER_RADIUS = 10.0; + static const double ELEVATION = 0.0; + static const EdgeInsets MARGIN_CARD = const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0); + // Border-Related + static bool get shouldUseBorder => LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data; + static ShapeBorder get shapeBorder => shouldUseBorder ? LunaShapeBorder.roundedWithBorder() : LunaShapeBorder.rounded(); +} diff --git a/lib/core/luna_ui/appbar.dart b/lib/core/ui/appbar.dart similarity index 100% rename from lib/core/luna_ui/appbar.dart rename to lib/core/ui/appbar.dart diff --git a/lib/core/luna_ui/bottom_modal_sheet.dart b/lib/core/ui/bottom_modal_sheet.dart similarity index 100% rename from lib/core/luna_ui/bottom_modal_sheet.dart rename to lib/core/ui/bottom_modal_sheet.dart diff --git a/lib/core/luna_ui/bottom_navigation_bar.dart b/lib/core/ui/bottom_navigation_bar.dart similarity index 100% rename from lib/core/luna_ui/bottom_navigation_bar.dart rename to lib/core/ui/bottom_navigation_bar.dart diff --git a/lib/core/luna_ui/bottom_navigation_bar/badge.dart b/lib/core/ui/bottom_navigation_bar/badge.dart similarity index 100% rename from lib/core/luna_ui/bottom_navigation_bar/badge.dart rename to lib/core/ui/bottom_navigation_bar/badge.dart diff --git a/lib/core/luna_ui/bottom_navigation_bar/bottom_navigation_bar.dart b/lib/core/ui/bottom_navigation_bar/bottom_navigation_bar.dart similarity index 100% rename from lib/core/luna_ui/bottom_navigation_bar/bottom_navigation_bar.dart rename to lib/core/ui/bottom_navigation_bar/bottom_navigation_bar.dart diff --git a/lib/core/ui/button.dart b/lib/core/ui/button.dart index f4f488d7..47d72a74 100644 --- a/lib/core/ui/button.dart +++ b/lib/core/ui/button.dart @@ -1,2 +1,146 @@ -export 'button/button.dart'; -export 'button/button_slim.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:lunasea/core.dart'; + +enum _BUTTON_SIZE { + REGULAR, + SLIM, +} + +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; + + 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, + 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( + 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, + ), + textAlign: TextAlign.center, + ); + } +} \ No newline at end of file diff --git a/lib/core/ui/card.dart b/lib/core/ui/card.dart index 9502976c..007952fd 100644 --- a/lib/core/ui/card.dart +++ b/lib/core/ui/card.dart @@ -1,2 +1,25 @@ -export 'card/card.dart'; -export 'card/card_tile.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaCard extends Card { + LunaCard({ + Key key, + @required BuildContext context, + @required Widget child, + EdgeInsets margin = LunaUI.MARGIN_CARD, + Color color, + Decoration decoration, + double height, + }) : super( + key: key, + child: Container(child: child, decoration: decoration, height: height), + margin: margin, + color: color == null ? Theme.of(context).primaryColor : color, + shape: LunaUI.shapeBorder, + elevation: LunaUI.ELEVATION, + clipBehavior: Clip.antiAlias, + ) { + assert(context != null); + assert(child != null); + } +} diff --git a/lib/core/luna_ui/colors.dart b/lib/core/ui/colors.dart similarity index 100% rename from lib/core/luna_ui/colors.dart rename to lib/core/ui/colors.dart diff --git a/lib/core/ui/containers.dart b/lib/core/ui/containers.dart index f4b62f99..d6d516d7 100644 --- a/lib/core/ui/containers.dart +++ b/lib/core/ui/containers.dart @@ -1 +1 @@ -export 'containers/row.dart'; +export 'containers/button_container.dart'; diff --git a/lib/core/luna_ui/containers/button_container.dart b/lib/core/ui/containers/button_container.dart similarity index 100% rename from lib/core/luna_ui/containers/button_container.dart rename to lib/core/ui/containers/button_container.dart diff --git a/lib/core/luna_ui/controllers.dart b/lib/core/ui/controllers.dart similarity index 100% rename from lib/core/luna_ui/controllers.dart rename to lib/core/ui/controllers.dart diff --git a/lib/core/luna_ui/controllers/page_controller.dart b/lib/core/ui/controllers/page_controller.dart similarity index 94% rename from lib/core/luna_ui/controllers/page_controller.dart rename to lib/core/ui/controllers/page_controller.dart index 7f951d90..dac3619a 100644 --- a/lib/core/luna_ui/controllers/page_controller.dart +++ b/lib/core/ui/controllers/page_controller.dart @@ -7,7 +7,7 @@ class LunaPageController extends PageController { LunaPageController({ int initialPage, }) : super( - initialPage: initialPage, + initialPage: initialPage ?? 0, ) { this?.addListener(_focusListener); } diff --git a/lib/core/luna_ui/decoration.dart b/lib/core/ui/decoration.dart similarity index 100% rename from lib/core/luna_ui/decoration.dart rename to lib/core/ui/decoration.dart diff --git a/lib/core/luna_ui/decoration/card_decoration.dart b/lib/core/ui/decoration/card_decoration.dart similarity index 100% rename from lib/core/luna_ui/decoration/card_decoration.dart rename to lib/core/ui/decoration/card_decoration.dart diff --git a/lib/core/ui/divider.dart b/lib/core/ui/divider.dart index 8789f3e3..17331404 100644 --- a/lib/core/ui/divider.dart +++ b/lib/core/ui/divider.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class LSDivider extends StatelessWidget { - @override - Widget build(BuildContext context) => Divider( +class LunaDivider extends Divider { + LunaDivider() : super( thickness: 1.0, color: LunaColours.splash, indent: 60.0, diff --git a/lib/core/ui/drawer/drawer.dart b/lib/core/ui/drawer/drawer.dart index 4d82b04a..2ceface8 100644 --- a/lib/core/ui/drawer/drawer.dart +++ b/lib/core/ui/drawer/drawer.dart @@ -2,100 +2,68 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/wake_on_lan.dart'; -class LSDrawer extends StatelessWidget { +class LunaDrawer extends StatelessWidget { final String page; - LSDrawer({ + LunaDrawer({ @required this.page, }); @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), - builder: (context, lunaBox, widget) { - return ValueListenableBuilder( + Widget build(BuildContext context) { + return LunaDatabaseValue.ENABLED_PROFILE.listen( + builder: (context, lunaBox, widget) => ValueListenableBuilder( valueListenable: Database.indexersBox.listenable(), - builder: (context, indexerBox, widget) { - ProfileHiveObject profile = Database.profilesBox.get(lunaBox.get(LunaDatabaseValue.ENABLED_PROFILE.key)); - return Drawer( - child: ListView( - children: _getDrawerEntries(context, profile, (indexerBox as Box).length > 0), - padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0), - physics: ClampingScrollPhysics(), - ), - ); - } - ); - } - ); + builder: (context, indexerBox, widget) => Drawer( + child: ListView( + children: _getDrawerEntries(context), + padding: EdgeInsets.only(bottom: 8.0), + physics: ClampingScrollPhysics(), + ), + ), + ), + ); + } - List _getDrawerEntries(BuildContext context, ProfileHiveObject profile, bool showIndexerSearch) { + List _getDrawerEntries(BuildContext context) { + List automation = Database.currentProfileObject.enabledAutomationModules; + List clients = Database.currentProfileObject.enabledClientModules; + List monitoring = Database.currentProfileObject.enabledMonitoringModules; + bool showSearch = Database.indexersBox.length > 0; return [ - LSDrawerHeader(), - _buildEntry( - context: context, - icon: LunaModule.DASHBOARD.icon, - title: LunaModule.DASHBOARD.name, - route: LunaModule.DASHBOARD.route, + LunaDrawerHeader(), + _buildEntry(context: context, module: LunaModule.DASHBOARD), + _buildEntry(context: context, module: LunaModule.SETTINGS), + LunaDivider(), + if(showSearch) _buildEntry(context: context, module: LunaModule.SEARCH), + if(LunaModule.WAKE_ON_LAN.enabled) _buildWakeOnLAN(context), + if(automation.length != 0) ...List.generate( + automation.length, + (index) => _buildEntry(context: context, module: automation[index]), ), - _buildEntry( - context: context, - icon: LunaModule.SETTINGS.icon, - title: LunaModule.SETTINGS.name, - route: LunaModule.SETTINGS.route, + if(clients.length != 0) ...List.generate( + clients.length, + (index) => _buildEntry(context: context, module: clients[index]), ), - LSDivider(), - if(showIndexerSearch) _buildEntry( - context: context, - icon: LunaModule.SEARCH.icon, - title: LunaModule.SEARCH.name, - route: LunaModule.SEARCH.route, - ), - if(LunaModule.WAKE_ON_LAN.enabled) _buildWakeOnLAN(context: context), - ...List.generate( - Database.currentProfileObject.enabledAutomationModules.length, - (index) => _buildEntry( - context: context, - route: Database.currentProfileObject.enabledAutomationModules[index].route, - icon: Database.currentProfileObject.enabledAutomationModules[index].icon, - title: Database.currentProfileObject.enabledAutomationModules[index].name, - ), - ), - ...List.generate( - Database.currentProfileObject.enabledClientModules.length, - (index) => _buildEntry( - context: context, - route: Database.currentProfileObject.enabledClientModules[index].route, - icon: Database.currentProfileObject.enabledClientModules[index].icon, - title: Database.currentProfileObject.enabledClientModules[index].name, - ), - ), - ...List.generate( - Database.currentProfileObject.enabledMonitoringModules.length, - (index) => _buildEntry( - context: context, - route: Database.currentProfileObject.enabledMonitoringModules[index].route, - icon: Database.currentProfileObject.enabledMonitoringModules[index].icon, - title: Database.currentProfileObject.enabledMonitoringModules[index].name, - ), + if(monitoring.length != 0) ...List.generate( + monitoring.length, + (index) => _buildEntry(context: context, module: monitoring[index]), ), ]; } Widget _buildEntry({ @required BuildContext context, - @required IconData icon, - @required String title, - @required String route, + @required LunaModule module, }) { - bool currentPage = page == title.toLowerCase(); + bool currentPage = page == module.key.toLowerCase(); return ListTile( - leading: LSIcon( - icon: icon, + leading: Icon( + module.icon, color: currentPage ? LunaColours.accent : Colors.white, ), title: Text( - title, + module.name, style: TextStyle( color: currentPage ? LunaColours.accent @@ -105,17 +73,15 @@ class LSDrawer extends StatelessWidget { ), onTap: () async { Navigator.of(context).pop(); - if(!currentPage) LunaState.navigatorKey.currentState.pushNamedAndRemoveUntil(route, (Route route) => false); + if(!currentPage) module.launch(); }, contentPadding: EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0), ); } - Widget _buildWakeOnLAN({ - @required BuildContext context, - }) { + Widget _buildWakeOnLAN(BuildContext context) { return ListTile( - leading: LSIcon(icon: LunaModule.WAKE_ON_LAN.icon), + leading: Icon(LunaModule.WAKE_ON_LAN.icon), title: Text( LunaModule.WAKE_ON_LAN.name, style: TextStyle( @@ -124,22 +90,23 @@ class LSDrawer extends StatelessWidget { ), ), onTap: () async { - WakeOnLANAPI _api = WakeOnLANAPI.from(Database.currentProfileObject); - await _api.wake() - .then((_) => LSSnackBar( + WakeOnLANAPI api = WakeOnLANAPI.fromProfile(); + await api.wake() + .then((_) => showLunaSuccessSnackBar( context: context, title: 'Machine is Waking Up...', message: 'Magic packet successfully sent', - type: SNACKBAR_TYPE.success, )) - .catchError((_) => LSSnackBar( - context: context, - title: 'Failed to Wake Machine', - message: 'Magic packet failed to send', - type: SNACKBAR_TYPE.failure, - )); + .catchError((error, stack) { + LunaLogger().error('Failed to wake machine', error, stack); + return showLunaErrorSnackBar( + context: context, + title: 'Failed to Wake Machine', + error: error, + ); + }); }, contentPadding: EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0), ); } -} \ No newline at end of file +} diff --git a/lib/core/ui/drawer/drawer_header.dart b/lib/core/ui/drawer/drawer_header.dart index 02ca797c..df6a46f5 100644 --- a/lib/core/ui/drawer/drawer_header.dart +++ b/lib/core/ui/drawer/drawer_header.dart @@ -2,58 +2,58 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; -// ignore: non_constant_identifier_names -Widget LSDrawerHeader() => UserAccountsDrawerHeader( - accountName: StreamBuilder( - stream: LunaFirebaseAuth.instance.authStateChanges(), - builder: (context, snapshot) => LSTitle(text: LunaFirebaseAuth().email ?? Constants.APPLICATION_NAME), - ), - accountEmail: ValueListenableBuilder( - valueListenable: Database.lunaSeaBox.listenable(keys: [LunaDatabaseValue.ENABLED_PROFILE.key]), - builder: (context, lunaBox, widget) => ValueListenableBuilder( - valueListenable: Database.profilesBox.listenable(), - builder: (context, profilesBox, widget) => Padding( - child: LunaPopupMenuButton( - tooltip: 'Change Profiles', - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - LSSubtitle( - text: LunaDatabaseValue.ENABLED_PROFILE.data, - ), - LSIcon( - icon: Icons.arrow_drop_down, - color: Colors.white70, - size: Constants.UI_FONT_SIZE_HEADER, - ), - ], - ), - onSelected: (result) { - HapticFeedback.selectionClick(); - LunaProfile().safelyChangeProfiles(result); - }, - itemBuilder: (context) { - return >[for(String profile in (profilesBox as Box).keys) PopupMenuItem( - value: profile, - child: Text( - profile, - style: TextStyle( - fontSize: Constants.UI_FONT_SIZE_SUBTITLE, +class LunaDrawerHeader extends UserAccountsDrawerHeader { + LunaDrawerHeader() : super( + accountName: StreamBuilder( + stream: LunaFirebaseAuth.instance.authStateChanges(), + builder: (context, snapshot) => LunaText.title(text: LunaFirebaseAuth().email ?? 'LunaSea'), + ), + accountEmail: LunaDatabaseValue.ENABLED_PROFILE.listen( + builder: (context, lunaBox, widget) => ValueListenableBuilder( + valueListenable: Database.profilesBox.listenable(), + builder: (context, profilesBox, widget) => Padding( + child: LunaPopupMenuButton( + tooltip: 'Change Profiles', + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + LunaText.subtitle( + text: LunaDatabaseValue.ENABLED_PROFILE.data, ), - ), - )]; - }, + Icon( + Icons.arrow_drop_down, + color: Colors.white70, + size: LunaUI.FONT_SIZE_HEADER, + ), + ], + ), + onSelected: (result) { + HapticFeedback.selectionClick(); + LunaProfile().safelyChangeProfiles(result); + }, + itemBuilder: (context) { + return >[for(String profile in (profilesBox as Box).keys) PopupMenuItem( + value: profile, + child: Text( + profile, + style: TextStyle( + fontSize: LunaUI.FONT_SIZE_SUBTITLE, + ), + ), + )]; + }, + ), + padding: EdgeInsets.only(right: 12.0), ), - padding: EdgeInsets.only(right: 12.0), ), ), - ), - decoration: BoxDecoration( - color: LunaColours.accent, - image: DecorationImage( - image: AssetImage('assets/branding/icon_drawer.png'), - colorFilter: ColorFilter.mode(LunaColours.primary.withOpacity(0.15), BlendMode.dstATop), - fit: BoxFit.cover, + decoration: BoxDecoration( + color: LunaColours.accent, + image: DecorationImage( + image: AssetImage('assets/branding/icon_drawer.png'), + colorFilter: ColorFilter.mode(LunaColours.primary.withOpacity(0.15), BlendMode.dstATop), + fit: BoxFit.cover, + ), ), - ), -); + ); +} diff --git a/lib/core/luna_ui/header.dart b/lib/core/ui/header.dart similarity index 100% rename from lib/core/luna_ui/header.dart rename to lib/core/ui/header.dart diff --git a/lib/core/luna_ui/highlighted_node.dart b/lib/core/ui/highlighted_node.dart similarity index 100% rename from lib/core/luna_ui/highlighted_node.dart rename to lib/core/ui/highlighted_node.dart diff --git a/lib/core/luna_ui/icons.dart b/lib/core/ui/icons.dart similarity index 100% rename from lib/core/luna_ui/icons.dart rename to lib/core/ui/icons.dart diff --git a/lib/core/luna_ui/icons/icon_button.dart b/lib/core/ui/icons/icon_button.dart similarity index 100% rename from lib/core/luna_ui/icons/icon_button.dart rename to lib/core/ui/icons/icon_button.dart diff --git a/lib/core/luna_ui/icons/icon_custom.dart b/lib/core/ui/icons/icon_custom.dart similarity index 100% rename from lib/core/luna_ui/icons/icon_custom.dart rename to lib/core/ui/icons/icon_custom.dart diff --git a/lib/core/ui/input_bar.dart b/lib/core/ui/input_bar.dart index 741b6448..9900809a 100644 --- a/lib/core/ui/input_bar.dart +++ b/lib/core/ui/input_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -class LSTextInputBar extends StatefulWidget { +class LunaTextInputBar extends StatefulWidget { final TextEditingController controller; final TextInputAction action; final TextInputType keyboardType; @@ -11,12 +11,12 @@ class LSTextInputBar extends StatefulWidget { final bool autofocus; final bool obscureText; final bool isFormField; - final void Function(String, bool) onChanged; + final void Function(String) onChanged; final void Function(String) onSubmitted; final String Function(String) validator; final EdgeInsets margin; - LSTextInputBar({ + LunaTextInputBar({ @required this.controller, this.onChanged, this.onSubmitted, @@ -24,19 +24,21 @@ class LSTextInputBar extends StatefulWidget { this.autofillHints, this.action = TextInputAction.search, this.keyboardType = TextInputType.text, - this.labelText = 'Search...', + this.labelText, this.labelIcon = Icons.search, this.margin = LunaUI.MARGIN_CARD, this.autofocus = false, this.obscureText = false, this.isFormField = false, - }); + }) { + assert(controller != null); + } @override State createState() => _State(); } -class _State extends State { +class _State extends State { bool _isFocused = false; @override @@ -57,7 +59,7 @@ class _State extends State { ); InputDecoration get _sharedInputDecoration => InputDecoration( - labelText: widget.labelText, + labelText: widget.labelText ?? 'lunasea.SearchTextBar'.tr(), labelStyle: TextStyle( color: Colors.white54, decoration: TextDecoration.none, @@ -70,7 +72,10 @@ class _State extends State { color: LunaColours.accent, size: 24.0, ), - onTap: () => widget?.onChanged('', true), + onTap: widget.onChanged == null ? null : () { + widget.controller.text = ''; + widget.onChanged(''); + } ), opacity: !_isFocused || widget.controller.text == '' ? 0.0 : 1.0, duration: Duration(milliseconds: 200), @@ -98,7 +103,7 @@ class _State extends State { autocorrect: false, keyboardType: widget.keyboardType, validator: widget?.validator, - onChanged: widget.onChanged == null ? null : (value) => widget.onChanged(value, false), + onChanged: widget.onChanged == null ? null : widget.onChanged, onFieldSubmitted: widget?.onSubmitted, ); @@ -113,7 +118,7 @@ class _State extends State { obscureText: widget.obscureText, autocorrect: false, keyboardType: widget.keyboardType, - onChanged: widget.onChanged == null ? null : (value) => widget.onChanged(value, false), + onChanged: widget.onChanged == null ? null : widget.onChanged, onSubmitted: widget?.onSubmitted, ); -} \ No newline at end of file +} diff --git a/lib/core/luna_ui/invalid_route.dart b/lib/core/ui/invalid_route.dart similarity index 100% rename from lib/core/luna_ui/invalid_route.dart rename to lib/core/ui/invalid_route.dart diff --git a/lib/core/luna_ui/list_tile.dart b/lib/core/ui/list_tile.dart similarity index 100% rename from lib/core/luna_ui/list_tile.dart rename to lib/core/ui/list_tile.dart diff --git a/lib/core/luna_ui/list_tile/list_tile.dart b/lib/core/ui/list_tile/list_tile.dart similarity index 100% rename from lib/core/luna_ui/list_tile/list_tile.dart rename to lib/core/ui/list_tile/list_tile.dart diff --git a/lib/core/luna_ui/list_view.dart b/lib/core/ui/list_view.dart similarity index 100% rename from lib/core/luna_ui/list_view.dart rename to lib/core/ui/list_view.dart diff --git a/lib/core/luna_ui/list_view/list_view.dart b/lib/core/ui/list_view/list_view.dart similarity index 100% rename from lib/core/luna_ui/list_view/list_view.dart rename to lib/core/ui/list_view/list_view.dart diff --git a/lib/core/luna_ui/list_view/list_view_builder.dart b/lib/core/ui/list_view/list_view_builder.dart similarity index 100% rename from lib/core/luna_ui/list_view/list_view_builder.dart rename to lib/core/ui/list_view/list_view_builder.dart diff --git a/lib/core/luna_ui/list_view/list_view_modal.dart b/lib/core/ui/list_view/list_view_modal.dart similarity index 100% rename from lib/core/luna_ui/list_view/list_view_modal.dart rename to lib/core/ui/list_view/list_view_modal.dart diff --git a/lib/core/luna_ui/list_view/paged_list_view.dart b/lib/core/ui/list_view/paged_list_view.dart similarity index 100% rename from lib/core/luna_ui/list_view/paged_list_view.dart rename to lib/core/ui/list_view/paged_list_view.dart diff --git a/lib/core/ui/loader.dart b/lib/core/ui/loader.dart index 63d0e569..fc9cc497 100644 --- a/lib/core/ui/loader.dart +++ b/lib/core/ui/loader.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:lunasea/core.dart'; -class LSLoader extends StatelessWidget { +class LunaLoader extends StatelessWidget { final double size; final Color color; - LSLoader({ + LunaLoader({ Key key, this.size = 25.0, this.color, diff --git a/lib/core/luna_ui/message.dart b/lib/core/ui/message.dart similarity index 100% rename from lib/core/luna_ui/message.dart rename to lib/core/ui/message.dart diff --git a/lib/core/luna_ui/message/message.dart b/lib/core/ui/message/message.dart similarity index 93% rename from lib/core/luna_ui/message/message.dart rename to lib/core/ui/message/message.dart index 908ca589..5950bf55 100644 --- a/lib/core/luna_ui/message/message.dart +++ b/lib/core/ui/message/message.dart @@ -45,7 +45,7 @@ class LunaMessage extends StatelessWidget { return LunaMessage( key: key, text: text, - buttonText: 'Go Back', + buttonText: 'lunasea.GoBack'.tr(), onTap: () => Navigator.of(context).pop(), useSafeArea: useSafeArea, ); @@ -60,8 +60,8 @@ class LunaMessage extends StatelessWidget { assert(onTap != null); return LunaMessage( key: key, - text: 'An Error Has Occurred', - buttonText: 'Try Again', + text: 'lunasea.AnErrorHasOccurred'.tr(), + buttonText: 'lunasea.TryAgain'.tr(), onTap: onTap, useSafeArea: useSafeArea, ); @@ -78,8 +78,8 @@ class LunaMessage extends StatelessWidget { assert(context != null); return LunaMessage( key: key, - text: '$module Is Not Enabled', - buttonText: 'Return to Dashboard', + text: 'lunasea.ModuleIsNotEnabled'.tr(args: [module]), + buttonText: 'lunasea.ReturnToDashboard'.tr(), onTap: () async => LunaModule.DASHBOARD.launch(), useSafeArea: useSafeArea, ); diff --git a/lib/core/ui/network_image.dart b/lib/core/ui/network_image.dart index 43534516..634b8612 100644 --- a/lib/core/ui/network_image.dart +++ b/lib/core/ui/network_image.dart @@ -1,38 +1,42 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -// ignore: non_constant_identifier_names -Widget LSNetworkImage({ - @required double height, - @required double width, - @required String url, - @required String placeholder, - Map headers, - bool roundCorners = true, -}) => ClipRRect( - child: Container( - child: Stack( - fit: StackFit.expand, - children: [ - Image.asset(placeholder), - if(url != null) FadeInImage( - height: height, - width: width, - fadeInDuration: Duration(milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED), - fadeOutDuration: Duration(milliseconds: 1), - placeholder: AssetImage(placeholder), - fit: BoxFit.cover, - image: NetworkImage( - url, - headers: headers, +class LunaNetworkImage extends ClipRRect { + LunaNetworkImage({ + double height, + double width, + String url, + @required String placeholderAsset, + Map headers, + bool roundCorners = true, + }) : super( + child: Container( + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + placeholderAsset, + height: height, + width: width, ), - ), - ], + if(url != null && url.isNotEmpty) FadeInImage( + height: height, + width: width, + fadeInDuration: Duration(milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED), + fadeOutDuration: Duration(milliseconds: 1), + placeholder: AssetImage(placeholderAsset), + fit: BoxFit.cover, + image: NetworkImage(url, headers: headers?.cast()), + ), + ], + ), + height: height, + width: width, ), - height: height, - width: width, - ), - borderRadius: roundCorners - ? BorderRadius.circular(Constants.UI_BORDER_RADIUS) - : null, -); \ No newline at end of file + borderRadius: roundCorners ? BorderRadius.circular(LunaUI.BORDER_RADIUS) : null, + ) { + // assert(height != null); + // assert(width != null); + assert(placeholderAsset != null); + } +} diff --git a/lib/core/luna_ui/popup_menu_button.dart b/lib/core/ui/popup_menu_button.dart similarity index 100% rename from lib/core/luna_ui/popup_menu_button.dart rename to lib/core/ui/popup_menu_button.dart diff --git a/lib/core/ui/refresh_indicator.dart b/lib/core/ui/refresh_indicator.dart index a9f344ce..af0d67c5 100644 --- a/lib/core/ui/refresh_indicator.dart +++ b/lib/core/ui/refresh_indicator.dart @@ -1,28 +1,15 @@ import 'package:flutter/material.dart'; -class LSRefreshIndicator extends StatefulWidget { - final Widget child; - final Function onRefresh; - final GlobalKey refreshKey; - - LSRefreshIndicator({ - @required this.child, - @required this.onRefresh, - @required this.refreshKey, - }); - - @override - State createState() => _State(); +class LunaRefreshIndicator extends RefreshIndicator { + LunaRefreshIndicator({ + GlobalKey key, + @required BuildContext context, + @required Future Function() onRefresh, + @required Widget child, + }) : super( + key: key, + backgroundColor: Theme.of(context).primaryColor, + onRefresh: onRefresh, + child: child, + ); } - -class _State extends State { - @override - Widget build(BuildContext context) { - return RefreshIndicator( - key: widget.refreshKey, - backgroundColor: Theme.of(context).primaryColor, - onRefresh: widget.onRefresh, - child: widget.child, - ); - } -} \ No newline at end of file diff --git a/lib/core/ui/shape.dart b/lib/core/ui/shape.dart index 462e6be3..7d369d31 100644 --- a/lib/core/ui/shape.dart +++ b/lib/core/ui/shape.dart @@ -1,19 +1,14 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; -// ignore: non_constant_identifier_names -ShapeBorder LSRoundedShape({ - double borderRadius = Constants.UI_BORDER_RADIUS, -}) => RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius), -); +class LunaShapeBorder extends RoundedRectangleBorder { + LunaShapeBorder._internal({ + bool useBorder = false, + }) : super( + borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS), + side: useBorder ? BorderSide(color: Colors.white12) : BorderSide.none, + ); -// ignore: non_constant_identifier_names -ShapeBorder LSRoundedShapeWithBorder({ - double borderRadius = Constants.UI_BORDER_RADIUS, -}) => RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius), - side: BorderSide( - color: Colors.white12, - ), -); + factory LunaShapeBorder.rounded() => LunaShapeBorder._internal(); + factory LunaShapeBorder.roundedWithBorder() => LunaShapeBorder._internal(useBorder: true); +} diff --git a/lib/core/luna_ui/sliver_sticky_header.dart b/lib/core/ui/sliver_sticky_header.dart similarity index 100% rename from lib/core/luna_ui/sliver_sticky_header.dart rename to lib/core/ui/sliver_sticky_header.dart diff --git a/lib/core/ui/snackbar.dart b/lib/core/ui/snackbar.dart index 84285668..b4d4b8f5 100644 --- a/lib/core/ui/snackbar.dart +++ b/lib/core/ui/snackbar.dart @@ -1,90 +1,4 @@ -import 'package:flutter/material.dart'; -import 'package:flash/flash.dart'; -import 'package:lunasea/core.dart'; - -enum SNACKBAR_TYPE { - success, - failure, - info, -} - -const _LEFT_PADDING = 8.0; - -// ignore: non_constant_identifier_names -Future LSSnackBar({ - @required BuildContext context, - @required String title, - String message = LunaLogger.CHECK_LOGS_MESSAGE, - Duration duration, - SNACKBAR_TYPE type = SNACKBAR_TYPE.info, - bool showButton = false, - String buttonText = 'view', - Function buttonOnPressed, -}) async { - if(showButton) assert(buttonOnPressed != null); - Color color; - IconData icon; - switch(type) { - case SNACKBAR_TYPE.failure: color = LunaColours.red; icon = Icons.error_outline; break; - case SNACKBAR_TYPE.success: color = LunaColours.accent; icon = Icons.check_circle_outline; break; - case SNACKBAR_TYPE.info: color = LunaColours.blue; icon = Icons.info_outline; break; - } - showFlash( - context: context, - duration: duration != null - ? duration - : showButton - ? Duration(seconds: 4) - : Duration(seconds: 2), - builder: (context, controller) => Flash( - backgroundColor: Theme.of(context).primaryColor, - controller: controller, - style: FlashStyle.floating, - boxShadows: [BoxShadow(blurRadius: 6.0, spreadRadius: 4.0, color: Colors.black.withOpacity(0.10))], - horizontalDismissDirection: HorizontalDismissDirection.horizontal, - margin: EdgeInsets.all(8.0), - borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - borderColor: LunaDatabaseValue.THEME_AMOLED.data && LunaDatabaseValue.THEME_AMOLED_BORDER.data - ? Colors.white12 - : Colors.transparent, - child: FlashBar( - title: Text( - title, - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - fontSize: Constants.UI_FONT_SIZE_TITLE, - ), - ), - message: Text( - message, - style: TextStyle( - color: Colors.white70, - fontSize: Constants.UI_FONT_SIZE_SUBTITLE, - ), - ), - icon: Padding( - child: LSIconButton( - icon: icon, - color: color, - ), - padding: EdgeInsets.only(left: _LEFT_PADDING), - ), - primaryAction: showButton - ? TextButton( - child: Text( - buttonText.toUpperCase(), - style: TextStyle( - fontWeight: LunaUI.FONT_WEIGHT_BOLD, - color: LunaColours.accent, - ), - ), - onPressed: () { - controller.dismiss(); - buttonOnPressed(); - }, - ) - : null, - ), - ), - ); -} +export 'snackbar/snackbar.dart'; +export 'snackbar/snackbar_error.dart'; +export 'snackbar/snackbar_info.dart'; +export 'snackbar/snackbar_success.dart'; diff --git a/lib/core/luna_ui/snackbar/snackbar.dart b/lib/core/ui/snackbar/snackbar.dart similarity index 100% rename from lib/core/luna_ui/snackbar/snackbar.dart rename to lib/core/ui/snackbar/snackbar.dart diff --git a/lib/core/luna_ui/snackbar/snackbar_error.dart b/lib/core/ui/snackbar/snackbar_error.dart similarity index 100% rename from lib/core/luna_ui/snackbar/snackbar_error.dart rename to lib/core/ui/snackbar/snackbar_error.dart diff --git a/lib/core/luna_ui/snackbar/snackbar_info.dart b/lib/core/ui/snackbar/snackbar_info.dart similarity index 100% rename from lib/core/luna_ui/snackbar/snackbar_info.dart rename to lib/core/ui/snackbar/snackbar_info.dart diff --git a/lib/core/luna_ui/snackbar/snackbar_success.dart b/lib/core/ui/snackbar/snackbar_success.dart similarity index 100% rename from lib/core/luna_ui/snackbar/snackbar_success.dart rename to lib/core/ui/snackbar/snackbar_success.dart diff --git a/lib/core/luna_ui/switch.dart b/lib/core/ui/switch.dart similarity index 100% rename from lib/core/luna_ui/switch.dart rename to lib/core/ui/switch.dart diff --git a/lib/core/ui/table.dart b/lib/core/ui/table.dart index 59e9bf2b..5d4c60a9 100644 --- a/lib/core/ui/table.dart +++ b/lib/core/ui/table.dart @@ -1,2 +1,2 @@ -export 'table/block.dart'; -export 'table/content.dart'; +export 'table/table_card.dart'; +export 'table/table_content.dart'; diff --git a/lib/core/luna_ui/table/table_card.dart b/lib/core/ui/table/table_card.dart similarity index 100% rename from lib/core/luna_ui/table/table_card.dart rename to lib/core/ui/table/table_card.dart diff --git a/lib/core/luna_ui/table/table_content.dart b/lib/core/ui/table/table_content.dart similarity index 100% rename from lib/core/luna_ui/table/table_content.dart rename to lib/core/ui/table/table_content.dart diff --git a/lib/core/ui/text.dart b/lib/core/ui/text.dart index 271a8863..b3bd0cb4 100644 --- a/lib/core/ui/text.dart +++ b/lib/core/ui/text.dart @@ -1,4 +1,67 @@ -export 'text/header.dart'; -export 'text/text_highlighted.dart'; -export 'text/text_title.dart'; -export 'text/text_subtitle.dart'; +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; + +class LunaText extends Text { + /// Create a new [Text] widget. + LunaText({ + @required String text, + Key key, + int maxLines, + TextOverflow overflow, + bool softWrap, + TextStyle style, + TextAlign textAlign, + }) : super( + text, + key: key, + maxLines: maxLines, + overflow: overflow, + softWrap: softWrap, + style: style, + textAlign: textAlign, + ); + + /// Create a [LunaText] widget with the styling pre-assigned to be a LunaSea title. + factory LunaText.title({ + Key key, + @required String text, + int maxLines = 1, + bool softWrap = false, + TextAlign textAlign = TextAlign.start, + Color color = Colors.white, + bool darken = false, + }) => LunaText( + text: text, + key: key, + maxLines: maxLines, + overflow: TextOverflow.fade, + softWrap: softWrap, + textAlign: textAlign, + style: TextStyle( + color: darken ? color.withOpacity(0.30) : color, + fontWeight: LunaUI.FONT_WEIGHT_BOLD, + fontSize: LunaUI.FONT_SIZE_TITLE, + ), + ); + + /// Create a [LunaText] widget with the styling pre-assigned to be a LunaSea subtitle. + factory LunaText.subtitle({ + Key key, + @required String text, + int maxLines = 1, + bool softWrap = false, + TextAlign textAlign = TextAlign.start, + Color color = Colors.white70, + bool darken = false, + }) => LunaText( + key: key, + text: text, + softWrap: softWrap, + maxLines: maxLines, + textAlign: textAlign, + style: TextStyle( + color: darken ? color.withOpacity(0.30) : color, + fontSize: LunaUI.FONT_SIZE_SUBTITLE, + ), + ); +} diff --git a/lib/core/luna_ui/theme.dart b/lib/core/ui/theme.dart similarity index 100% rename from lib/core/luna_ui/theme.dart rename to lib/core/ui/theme.dart diff --git a/lib/core/utilities/changelog.dart b/lib/core/utilities/changelog.dart index 268a2e8e..66b1ca7f 100644 --- a/lib/core/utilities/changelog.dart +++ b/lib/core/utilities/changelog.dart @@ -12,7 +12,7 @@ class LunaChangelog { .then((package) { if(AlertsDatabaseValue.CHANGELOG.data != package.buildNumber) { AlertsDatabaseValue.CHANGELOG.put(package.buildNumber); - showChangelog(); + showChangelog(package.version, package.buildNumber); } }) .catchError((error, stack) { @@ -21,7 +21,7 @@ class LunaChangelog { } /// Load the changelog asset and show a bottom modal sheet with the changelog. - Future showChangelog() async { + Future showChangelog(String version, String buildNumber) async { await DefaultAssetBundle.of(LunaState.navigatorKey.currentContext).loadString('assets/changelog.json') .then((data) { _Changelog changelog = _Changelog.fromJson(json.decode(data)); @@ -29,7 +29,7 @@ class LunaChangelog { context: LunaState.navigatorKey.currentContext, builder: (context) => LunaListViewModal( children: [ - LunaHeader(text: '${changelog.version} (${changelog.build})', subtitle: changelog.motd), + LunaHeader(text: '$version ($buildNumber)', subtitle: changelog.motd), if((changelog.changesNew?.length ?? 0) != 0) LunaHeader(text: 'New'), if((changelog.changesNew?.length ?? 0) != 0) LunaTableCard( content: List.generate( @@ -79,7 +79,6 @@ class LunaChangelog { class _Changelog { String motd; String version; - String build; List<_Change> changesNew; List<_Change> changesTweaks; List<_Change> changesFixes; @@ -88,7 +87,6 @@ class _Changelog { _Changelog changelog = _Changelog(); changelog.motd = json['motd'] ?? ''; changelog.version = json['version'] ?? ''; - changelog.build = json['build'] ?? ''; changelog.changesNew = json['new'] == null ? [] : (json['new'] as List).map<_Change>((change) => _Change.fromJson(change)).toList(); changelog.changesTweaks = json['tweaks'] == null ? [] : (json['tweaks'] as List).map<_Change>((change) => _Change.fromJson(change)).toList(); changelog.changesFixes = json['fixes'] == null ? [] : (json['fixes'] as List).map<_Change>((change) => _Change.fromJson(change)).toList(); diff --git a/lib/main.dart b/lib/main.dart index 6c5bdb74..a3d74ae6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,19 +3,26 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:lunasea/core.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; /// LunaSea Entry Point: Initialize & Run Application /// /// Runs app in Sentry guarded zone to attempt to capture fatal (crashing) errors -Future main() async => await SentryFlutter.init( - (options) => options - ..dsn = LunaLogger.SENTRY_DSN, - appRunner: () async { - await _init(); - runApp(LunaBIOS()); - } -); +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await _init(); + runZonedGuarded( + () => runApp( + EasyLocalization( + supportedLocales: LunaLocalization().supportedLocales, + path: LunaLocalization().fileDirectory, + fallbackLocale: LunaLocalization().fallbackLocale, + useFallbackTranslations: true, + child: LunaBIOS(), + ), + ), + (error, stack) => LunaFirebaseCrashlytics.instance.recordError, + ); +} /// Initializes LunaSea before running the BIOS Widget. /// @@ -39,13 +46,14 @@ Future _init() async { statusBarColor: Colors.transparent, )); //LunaSea initialization + await Database().initialize(); + await LunaFirebase().initialize(); LunaLogger().initialize(); LunaNetworking().initialize(); LunaImageCache().initialize(); LunaRouter().intialize(); - await LunaFirebase().initialize(); await LunaInAppPurchases().initialize(); - await Database().initialize(); + await LunaLocalization().initialize(); } class LunaBIOS extends StatefulWidget { @@ -100,13 +108,15 @@ class _State extends State { ]), builder: (context, box, _) { return MaterialApp( + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, routes: LunaRouter().routes, onGenerateRoute: LunaRouter.router.generator, + navigatorKey: LunaState.navigatorKey, darkTheme: LunaTheme().activeTheme(), theme: LunaTheme().activeTheme(), - title: Constants.APPLICATION_NAME, - navigatorKey: LunaState.navigatorKey, - navigatorObservers: [ SentryNavigatorObserver() ], + title: 'LunaSea', ); }, ), diff --git a/lib/modules/dashboard/routes/home.dart b/lib/modules/dashboard/routes/home.dart index 5dd5ae35..2ec38d59 100644 --- a/lib/modules/dashboard/routes/home.dart +++ b/lib/modules/dashboard/routes/home.dart @@ -48,10 +48,10 @@ class _State extends State { } } - Widget get _drawer => LSDrawer(page: LunaModule.DASHBOARD.key); + Widget get _drawer => LunaDrawer(page: LunaModule.DASHBOARD.key); Widget get _appBar => LunaAppBar( - title: Constants.APPLICATION_NAME, + title: 'LunaSea', useDrawer: true, actions: Database.currentProfileObject.anyAutomationEnabled ? [ diff --git a/lib/modules/lidarr/routes/lidarr.dart b/lib/modules/lidarr/routes/lidarr.dart index 48971347..63f07883 100644 --- a/lib/modules/lidarr/routes/lidarr.dart +++ b/lib/modules/lidarr/routes/lidarr.dart @@ -54,7 +54,7 @@ class _State extends State { ), ); - Widget get _drawer => LSDrawer(page: LunaModule.LIDARR.key); + Widget get _drawer => LunaDrawer(page: LunaModule.LIDARR.key); Widget get _bottomNavigationBar => LidarrNavigationBar(pageController: _pageController); diff --git a/lib/modules/nzbget/routes/nzbget.dart b/lib/modules/nzbget/routes/nzbget.dart index ae79a72f..4c58bae2 100644 --- a/lib/modules/nzbget/routes/nzbget.dart +++ b/lib/modules/nzbget/routes/nzbget.dart @@ -54,7 +54,7 @@ class _State extends State { ), ); - Widget get _drawer => LSDrawer(page: 'nzbget'); + Widget get _drawer => LunaDrawer(page: LunaModule.NZBGET.key); Widget get _bottomNavigationBar => NZBGetNavigationBar(pageController: _pageController); diff --git a/lib/modules/radarr/core/api_helper.dart b/lib/modules/radarr/core/api_helper.dart index 0b35b696..2e950202 100644 --- a/lib/modules/radarr/core/api_helper.dart +++ b/lib/modules/radarr/core/api_helper.dart @@ -96,7 +96,6 @@ class RadarrAPIHelper { return movie; }) .catchError((error, stack) { - print(error); LunaLogger().error('Failed to add movie (tmdbId: ${movie.tmdbId})', error, stack); if(showSnackbar) showLunaErrorSnackBar(title: 'Failed to Add Movie', error: error); return null; diff --git a/lib/modules/radarr/core/database.dart b/lib/modules/radarr/core/database.dart index d579d39b..4a69b988 100644 --- a/lib/modules/radarr/core/database.dart +++ b/lib/modules/radarr/core/database.dart @@ -41,6 +41,7 @@ class RadarrDatabase extends LunaModuleDatabase { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: @@ -71,6 +72,7 @@ class RadarrDatabase extends LunaModuleDatabase { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: @@ -96,6 +98,7 @@ class RadarrDatabase extends LunaModuleDatabase { case 'RADARR_ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID': return RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID; case 'RADARR_ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID': return RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID; case 'RADARR_ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID': return RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID; + case 'RADARR_ADD_MOVIE_DEFAULT_TAGS': return RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS; case 'RADARR_ADD_DISCOVER_USE_SUGGESTIONS': return RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS; case 'RADARR_REMOVE_MOVIE_IMPORT_LIST': return RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST; case 'RADARR_REMOVE_MOVIE_DELETE_FILES': return RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES; @@ -120,6 +123,7 @@ enum RadarrDatabaseValue { ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID, ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID, ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID, + ADD_MOVIE_DEFAULT_TAGS, ADD_DISCOVER_USE_SUGGESTIONS, REMOVE_MOVIE_IMPORT_LIST, REMOVE_MOVIE_DELETE_FILES, @@ -143,6 +147,7 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: return 'RADARR_ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID'; case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: return 'RADARR_ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID'; case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: return 'RADARR_ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID'; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: return 'RADARR_ADD_MOVIE_DEFAULT_TAGS'; case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: return 'RADARR_ADD_DISCOVER_USE_SUGGESTIONS'; case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: return 'RADARR_REMOVE_MOVIE_IMPORT_LIST'; case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: return 'RADARR_REMOVE_MOVIE_DELETE_FILES'; @@ -168,6 +173,7 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: return _box.get(this.key, defaultValue: null); case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: return _box.get(this.key, defaultValue: null); case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: return _box.get(this.key, defaultValue: 'announced'); + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: return _box.get(this.key, defaultValue: []); case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: return _box.get(this.key, defaultValue: true); case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: return _box.get(this.key, defaultValue: false); case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: return _box.get(this.key, defaultValue: false); @@ -179,24 +185,25 @@ extension RadarrDatabaseValueExtension on RadarrDatabaseValue { void put(dynamic value) { final box = Database.lunaSeaBox; switch(this) { - case RadarrDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_SORTING_MOVIES: if(value.runtimeType == RadarrMoviesSorting) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES: if(value.runtimeType == RadarrMoviesFilter) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_SORTING_RELEASES: if(value.runtimeType == RadarrReleasesSorting) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES: if(value.runtimeType == RadarrReleasesFilter) box.put(this.key, value); return; - case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MONITORED_STATE: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: if(value.runtimeType == int) box.put(this.key, value); return; - case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: if(value.runtimeType == String) box.put(this.key, value); return; - case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: if(value.runtimeType == bool) box.put(this.key, value); return; - case RadarrDatabaseValue.CONTENT_PAGE_SIZE: if(value.runtimeType == int) box.put(this.key, value); return; + case RadarrDatabaseValue.NAVIGATION_INDEX: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.NAVIGATION_INDEX_MOVIE_DETAILS: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.NAVIGATION_INDEX_ADD_MOVIE: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.NAVIGATION_INDEX_SYSTEM_STATUS: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_SORTING_MOVIES: if(value is RadarrMoviesSorting) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_SORTING_MOVIES_ASCENDING: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_FILTERING_MOVIES: if(value is RadarrMoviesFilter) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_SORTING_RELEASES: if(value is RadarrReleasesSorting) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.DEFAULT_FILTERING_RELEASES: if(value is RadarrReleasesFilter) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MONITORED_STATE: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_ROOT_FOLDER_ID: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_QUALITY_PROFILE_ID: if(value is int) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_MINIMUM_AVAILABILITY_ID: if(value is String) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS: if(value is List) box.put(this.key, value); return; + case RadarrDatabaseValue.ADD_DISCOVER_USE_SUGGESTIONS: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.REMOVE_MOVIE_DELETE_FILES: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.REMOVE_MOVIE_IMPORT_LIST: if(value is bool) box.put(this.key, value); return; + case RadarrDatabaseValue.CONTENT_PAGE_SIZE: if(value is int) box.put(this.key, value); return; } LunaLogger().warning('RadarrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid RadarrDatabaseValue: ${this?.toString() ?? 'null'}'); } diff --git a/lib/modules/radarr/core/dialogs.dart b/lib/modules/radarr/core/dialogs.dart index 70a1a444..12fe6fca 100644 --- a/lib/modules/radarr/core/dialogs.dart +++ b/lib/modules/radarr/core/dialogs.dart @@ -15,7 +15,7 @@ class RadarrDialogs { await LSDialog.dialog( context: context, - title: 'Radarr Settings', + title: 'lunasea.Settings'.tr(), content: List.generate( RadarrGlobalSettingsType.values.length, (index) => LSDialog.tile( @@ -247,7 +247,7 @@ class RadarrDialogs { await LSDialog.dialog( context: context, - title: 'Minimum Availability', + title: 'radarr.MinimumAvailability'.tr(), content: List.generate( RadarrAvailability.values.length, (index) => LSDialog.tile( @@ -274,7 +274,7 @@ class RadarrDialogs { await LSDialog.dialog( context: context, - title: 'Quality Profile', + title: 'radarr.QualityProfile'.tr(), content: List.generate( profiles.length, (index) => LSDialog.tile( @@ -403,7 +403,7 @@ class RadarrDialogs { await LSDialog.dialog( context: context, - title: 'Root Folder', + title: 'radarr.RootFolder'.tr(), content: List.generate( folders.length, (index) => LSDialog.tile( diff --git a/lib/modules/radarr/core/extensions/radarr_event_type.dart b/lib/modules/radarr/core/extensions/radarr_event_type.dart index 7294dcb7..8e19e1b4 100644 --- a/lib/modules/radarr/core/extensions/radarr_event_type.dart +++ b/lib/modules/radarr/core/extensions/radarr_event_type.dart @@ -45,13 +45,13 @@ extension lunaRadarrEventType on RadarrEventType { String lunaReadable(RadarrHistoryRecord record) { switch(this) { - case RadarrEventType.GRABBED: return 'Grabbed from ${record.data['indexer'] ?? LunaUI.TEXT_EMDASH}'; - case RadarrEventType.DOWNLOAD_FAILED: return 'Download Failed'; - case RadarrEventType.DOWNLOAD_FOLDER_IMPORTED: return 'Movie Imported (${record?.quality?.quality?.name ?? LunaUI.TEXT_EMDASH})'; - case RadarrEventType.DOWNLOAD_IGNORED: return 'Download Ignored'; - case RadarrEventType.MOVIE_FILE_DELETED: return 'Movie File Deleted'; - case RadarrEventType.MOVIE_FILE_RENAMED: return 'Movie File Renamed'; - case RadarrEventType.MOVIE_FOLDER_IMPORTED: return 'Movie Imported (${record?.quality?.quality?.name ?? LunaUI.TEXT_EMDASH})'; + case RadarrEventType.GRABBED: return 'radarr.GrabbedFrom'.tr(args: [record.data['indexer'] ?? LunaUI.TEXT_EMDASH]); + case RadarrEventType.DOWNLOAD_FAILED: return 'radarr.DownloadFailed'.tr(); + case RadarrEventType.DOWNLOAD_FOLDER_IMPORTED: return 'radarr.MovieImported'.tr(args: [record?.quality?.quality?.name ?? LunaUI.TEXT_EMDASH]); + case RadarrEventType.DOWNLOAD_IGNORED: return 'radarr.DownloadIgnored'.tr(); + case RadarrEventType.MOVIE_FILE_DELETED: return 'radarr.MovieFileDeleted'.tr(); + case RadarrEventType.MOVIE_FILE_RENAMED: return 'radarr.MovieFileRenamed'.tr(); + case RadarrEventType.MOVIE_FOLDER_IMPORTED: return 'radarr.MovieImported'.tr(args: [record?.quality?.quality?.name ?? LunaUI.TEXT_EMDASH]); } return null; } diff --git a/lib/modules/radarr/core/state.dart b/lib/modules/radarr/core/state.dart index c196532a..2ba6b417 100644 --- a/lib/modules/radarr/core/state.dart +++ b/lib/modules/radarr/core/state.dart @@ -15,8 +15,6 @@ class RadarrState extends LunaModuleState { _rootFolders = null; _qualityProfiles = null; _tags = null; - // Reset search query fields - _moviesSearchQuery = ''; // Reinitialize resetProfile(); if(_enabled) { diff --git a/lib/modules/radarr/core/types/filter_movies.dart b/lib/modules/radarr/core/types/filter_movies.dart index 71000abc..84a6f2a4 100644 --- a/lib/modules/radarr/core/types/filter_movies.dart +++ b/lib/modules/radarr/core/types/filter_movies.dart @@ -34,12 +34,12 @@ extension RadarrMoviesFilterExtension on RadarrMoviesFilter { String get readable { switch(this) { - case RadarrMoviesFilter.ALL: return 'All'; - case RadarrMoviesFilter.MONITORED: return 'Monitored'; - case RadarrMoviesFilter.UNMONITORED: return 'Unmonitored'; - case RadarrMoviesFilter.MISSING: return 'Missing'; - case RadarrMoviesFilter.WANTED: return 'Wanted'; - case RadarrMoviesFilter.CUTOFF_UNMET: return 'Cutoff Unmet'; + case RadarrMoviesFilter.ALL: return 'radarr.All'.tr(); + case RadarrMoviesFilter.MONITORED: return 'radarr.Monitored'.tr(); + case RadarrMoviesFilter.UNMONITORED: return 'radarr.Unmonitored'.tr(); + case RadarrMoviesFilter.MISSING: return 'radarr.Missing'.tr(); + case RadarrMoviesFilter.WANTED: return 'radarr.Wanted'.tr(); + case RadarrMoviesFilter.CUTOFF_UNMET: return 'radarr.CutoffUnmet'.tr(); default: return null; } } diff --git a/lib/modules/radarr/core/types/filter_releases.dart b/lib/modules/radarr/core/types/filter_releases.dart index 6fb6799a..8a95b7c6 100644 --- a/lib/modules/radarr/core/types/filter_releases.dart +++ b/lib/modules/radarr/core/types/filter_releases.dart @@ -25,9 +25,9 @@ extension RadarrReleasesFilterExtension on RadarrReleasesFilter { String get readable { switch(this) { - case RadarrReleasesFilter.ALL: return 'All'; - case RadarrReleasesFilter.APPROVED: return 'Approved'; - case RadarrReleasesFilter.REJECTED: return 'Rejected'; + case RadarrReleasesFilter.ALL: return 'radarr.All'.tr(); + case RadarrReleasesFilter.APPROVED: return 'radarr.Approved'.tr(); + case RadarrReleasesFilter.REJECTED: return 'radarr.Rejected'.tr(); default: return null; } } diff --git a/lib/modules/radarr/core/types/settings_global.dart b/lib/modules/radarr/core/types/settings_global.dart index 3c05b8ea..abbfcfcd 100644 --- a/lib/modules/radarr/core/types/settings_global.dart +++ b/lib/modules/radarr/core/types/settings_global.dart @@ -24,11 +24,11 @@ extension RadarrGlobalSettingsTypeExtension on RadarrGlobalSettingsType { String get name { switch(this) { - case RadarrGlobalSettingsType.WEB_GUI: return 'View Web GUI'; - case RadarrGlobalSettingsType.UPDATE_LIBRARY: return 'Update Library'; - case RadarrGlobalSettingsType.RUN_RSS_SYNC: return 'Run RSS Sync'; - case RadarrGlobalSettingsType.SEARCH_ALL_MISSING: return 'Search All Missing'; - case RadarrGlobalSettingsType.BACKUP_DATABASE: return 'Backup Database'; + case RadarrGlobalSettingsType.WEB_GUI: return 'radarr.ViewWebGUI'.tr(); + case RadarrGlobalSettingsType.UPDATE_LIBRARY: return 'radarr.UpdateLibrary'.tr(); + case RadarrGlobalSettingsType.RUN_RSS_SYNC: return 'radarr.RunRSSSync'.tr(); + case RadarrGlobalSettingsType.SEARCH_ALL_MISSING: return 'radarr.SearchAllMissing'.tr(); + case RadarrGlobalSettingsType.BACKUP_DATABASE: return 'radarr.BackupDatabase'.tr(); } throw Exception('Invalid RadarrGlobalSettingsType'); } diff --git a/lib/modules/radarr/core/types/settings_movie.dart b/lib/modules/radarr/core/types/settings_movie.dart index dc34e38f..43e2ce9e 100644 --- a/lib/modules/radarr/core/types/settings_movie.dart +++ b/lib/modules/radarr/core/types/settings_movie.dart @@ -22,10 +22,10 @@ extension RadarrMovieSettingsTypeExtension on RadarrMovieSettingsType { String name(RadarrMovie movie) { switch(this) { - case RadarrMovieSettingsType.MONITORED: return movie.monitored ? 'Unmonitor Movie' : 'Monitor Movie'; - case RadarrMovieSettingsType.EDIT: return 'Edit Movie'; - case RadarrMovieSettingsType.REFRESH: return 'Refresh Movie'; - case RadarrMovieSettingsType.DELETE: return 'Remove Movie'; + case RadarrMovieSettingsType.MONITORED: return movie.monitored ? 'radarr.UnmonitorMovie'.tr() : 'radarr.MonitorMovie'.tr(); + case RadarrMovieSettingsType.EDIT: return 'radarr.EditMovie'.tr(); + case RadarrMovieSettingsType.REFRESH: return 'radarr.RefreshMovie'.tr(); + case RadarrMovieSettingsType.DELETE: return 'radarr.RemoveMovie'.tr(); } throw Exception('Invalid RadarrMovieSettingsType'); } diff --git a/lib/modules/radarr/core/types/sorting_movies.dart b/lib/modules/radarr/core/types/sorting_movies.dart index 31a6a622..bdb2de0b 100644 --- a/lib/modules/radarr/core/types/sorting_movies.dart +++ b/lib/modules/radarr/core/types/sorting_movies.dart @@ -40,14 +40,14 @@ extension RadarrMoviesSortingExtension on RadarrMoviesSorting { String get readable { switch(this) { - case RadarrMoviesSorting.ALPHABETICAL: return 'Alphabetical'; - case RadarrMoviesSorting.DATE_ADDED: return 'Date Added'; - case RadarrMoviesSorting.QUALITY_PROFILE: return 'Quality Profile'; - case RadarrMoviesSorting.RUNTIME: return 'Runtime'; - case RadarrMoviesSorting.SIZE: return 'Size'; - case RadarrMoviesSorting.STUDIO: return 'Studio'; - case RadarrMoviesSorting.YEAR: return 'Year'; - case RadarrMoviesSorting.MIN_AVAILABILITY: return 'Minimum Availability'; + case RadarrMoviesSorting.ALPHABETICAL: return 'radarr.Alphabetical'.tr(); + case RadarrMoviesSorting.DATE_ADDED: return 'radarr.DateAdded'.tr(); + case RadarrMoviesSorting.QUALITY_PROFILE: return 'radarr.QualityProfile'.tr(); + case RadarrMoviesSorting.RUNTIME: return 'radarr.Runtime'.tr(); + case RadarrMoviesSorting.SIZE: return 'radarr.Size'.tr(); + case RadarrMoviesSorting.STUDIO: return 'radarr.Studio'.tr(); + case RadarrMoviesSorting.YEAR: return 'radarr.Year'.tr(); + case RadarrMoviesSorting.MIN_AVAILABILITY: return 'radarr.MinimumAvailability'.tr(); default: return null; } } diff --git a/lib/modules/radarr/core/types/sorting_releases.dart b/lib/modules/radarr/core/types/sorting_releases.dart index 93a59f66..139874cc 100644 --- a/lib/modules/radarr/core/types/sorting_releases.dart +++ b/lib/modules/radarr/core/types/sorting_releases.dart @@ -34,12 +34,12 @@ extension RadarrReleasesSortingExtension on RadarrReleasesSorting { String get readable { switch(this) { - case RadarrReleasesSorting.AGE: return 'Age'; - case RadarrReleasesSorting.ALPHABETICAL: return 'Alphabetical'; - case RadarrReleasesSorting.SEEDERS: return 'Seeders'; - case RadarrReleasesSorting.WEIGHT: return 'Weight'; - case RadarrReleasesSorting.TYPE: return 'Type'; - case RadarrReleasesSorting.SIZE: return 'Size'; + case RadarrReleasesSorting.AGE: return 'radarr.Age'.tr(); + case RadarrReleasesSorting.ALPHABETICAL: return 'radarr.Alphabetical'.tr(); + case RadarrReleasesSorting.SEEDERS: return 'radarr.Seeders'.tr(); + case RadarrReleasesSorting.WEIGHT: return 'radarr.Weight'.tr(); + case RadarrReleasesSorting.TYPE: return 'radarr.Type'.tr(); + case RadarrReleasesSorting.SIZE: return 'radarr.Size'.tr(); default: return null; } } diff --git a/lib/modules/radarr/routes/add_movie/route.dart b/lib/modules/radarr/routes/add_movie/route.dart index a3ed3ff0..bd7d9f76 100644 --- a/lib/modules/radarr/routes/add_movie/route.dart +++ b/lib/modules/radarr/routes/add_movie/route.dart @@ -53,7 +53,7 @@ class _State extends State<_RadarrAddMovieRoute> { Widget _appBar() { return LunaAppBar( - title: 'Add Movie', + title: 'radarr.AddMovie'.tr(), pageController: _pageController, scrollControllers: RadarrAddMovieNavigationBar.scrollControllers, ); @@ -61,11 +61,11 @@ class _State extends State<_RadarrAddMovieRoute> { Widget _body() { return ChangeNotifierProvider( - create: (context) => RadarrAddMovieState(context, _arguments.query), + create: (context) => RadarrAddMovieState(context, _arguments?.query ?? ''), builder: (context, _) => PageView( controller: _pageController, children: [ - RadarrAddMovieSearchPage(), + RadarrAddMovieSearchPage(autofocusSearchBar: (_arguments?.query ?? '').isEmpty && _pageController.initialPage == 0), RadarrAddMovieDiscoverPage(), ], ), diff --git a/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart b/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart index 5cf2bb1f..a9c8ecff 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/discovery_results_tile.dart @@ -48,7 +48,7 @@ class _State extends State { TextSpan _subtitle2() { String summary; if(widget.movie.overview == null || widget.movie.overview.isEmpty) { - summary = 'No summary is available'; + summary = 'radarr.NoSummaryIsAvailable'.tr(); } else { summary = '${widget.movie.overview}\n'; } @@ -60,7 +60,7 @@ class _State extends State { Future _onTap() async { if(widget.onTapShowOverview) { - LunaDialogs().textPreview(context, widget.movie.title, widget.movie.overview ?? 'No summary is available.'); + LunaDialogs().textPreview(context, widget.movie.title, widget.movie.overview ?? 'radarr.NoSummaryIsAvailable'.tr()); } else { RadarrAddMovieDetailsRouter().navigateTo(context, movie: widget.movie, isDiscovery: true); } diff --git a/lib/modules/radarr/routes/add_movie/widgets/navigation_bar.dart b/lib/modules/radarr/routes/add_movie/widgets/navigation_bar.dart index ce3e63ba..6325033d 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/navigation_bar.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/navigation_bar.dart @@ -3,10 +3,18 @@ import 'package:lunasea/core.dart'; class RadarrAddMovieNavigationBar extends StatelessWidget { final PageController pageController; - static const List icons = [Icons.search, Icons.whatshot]; - static const List titles = ['Search', 'Discover']; static List scrollControllers = List.generate(icons.length, (_) => ScrollController()); + static const List icons = [ + Icons.search, + Icons.whatshot, + ]; + + static List get titles => [ + 'radarr.Search'.tr(), + 'radarr.Discover'.tr(), + ]; + RadarrAddMovieNavigationBar({ Key key, @required this.pageController, diff --git a/lib/modules/radarr/routes/add_movie/widgets/page_discover.dart b/lib/modules/radarr/routes/add_movie/widgets/page_discover.dart index 9faebe0a..36adcbe8 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/page_discover.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/page_discover.dart @@ -61,8 +61,8 @@ class _State extends State with AutomaticKeepAliveCl Widget _list(List movies, List discovery, List exclusions) { List _filtered = _filterAndSort(movies, discovery, exclusions); if(_filtered.length == 0) return LunaMessage( - text: 'No Movies Found', - buttonText: 'Refresh', + text: 'radarr.NoMoviesFound'.tr(), + buttonText: 'lunasea.Refresh'.tr(), onTap: _refreshKey.currentState.show, ); return LunaListViewBuilder( diff --git a/lib/modules/radarr/routes/add_movie/widgets/page_search.dart b/lib/modules/radarr/routes/add_movie/widgets/page_search.dart index f6e1d418..0b61bb0c 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/page_search.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/page_search.dart @@ -3,6 +3,13 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/modules/radarr.dart'; class RadarrAddMovieSearchPage extends StatefulWidget { + final bool autofocusSearchBar; + + RadarrAddMovieSearchPage({ + Key key, + @required this.autofocusSearchBar, + }) : super(key: key); + @override State createState() => _State(); } @@ -37,6 +44,7 @@ class _State extends State with AutomaticKeepAliveClie return LunaAppBar.empty( child: RadarrAddMovieSearchSearchBar( query: context.read().searchQuery, + autofocus: widget.autofocusSearchBar, ), height: 62.0, ); @@ -79,7 +87,7 @@ class _State extends State with AutomaticKeepAliveClie } Widget _list(List movies, List results, List exclusions) { - if((results?.length ?? 0) == 0) return LunaListView(children: [LunaMessage.inList(text: 'No Results Found')], controller: null); + if((results?.length ?? 0) == 0) return LunaListView(children: [LunaMessage.inList(text: 'radarr.NoResultsFound'.tr())], controller: null); return LunaListViewBuilder( controller: RadarrAddMovieNavigationBar.scrollControllers[0], itemCount: results.length, diff --git a/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart b/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart index e5d7e5cc..ac59faf2 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/search_results_tile.dart @@ -54,7 +54,7 @@ class _State extends State { TextSpan _subtitle2() { String summary; if(widget.movie.overview == null || widget.movie.overview.isEmpty) { - summary = 'No summary is available'; + summary = 'radarr.NoSummaryIsAvailable'.tr()+'\n'; } else { summary = '${widget.movie.overview}\n'; } @@ -66,7 +66,7 @@ class _State extends State { Future _onTap() async { if(widget.onTapShowOverview) { - LunaDialogs().textPreview(context, widget.movie.title, widget.movie.overview ?? 'No summary is available.'); + LunaDialogs().textPreview(context, widget.movie.title, widget.movie.overview ?? 'radarr.NoSummaryIsAvailable'.tr()); } else if(widget.exists) { RadarrMoviesDetailsRouter().navigateTo(context, movieId: widget.movie.id ?? -1); } else { diff --git a/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart b/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart index 4525b022..8dc71cf2 100644 --- a/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart +++ b/lib/modules/radarr/routes/add_movie/widgets/search_search_bar.dart @@ -4,10 +4,12 @@ import 'package:lunasea/modules/radarr.dart'; class RadarrAddMovieSearchSearchBar extends StatefulWidget implements PreferredSizeWidget { final String query; + final bool autofocus; RadarrAddMovieSearchSearchBar({ Key key, @required this.query, + @required this.autofocus, }) : super(key: key); @override @@ -35,7 +37,7 @@ class _State extends State { child: Consumer( builder: (context, state, _) => LunaTextInputBar( controller: _controller, - autofocus: context.read().searchQuery?.isEmpty ?? true, + autofocus: widget.autofocus, onChanged: (value) => context.read().searchQuery = value, onSubmitted: (value) { if(value.isNotEmpty) context.read().fetchLookup(context); diff --git a/lib/modules/radarr/routes/add_movie_details/route.dart b/lib/modules/radarr/routes/add_movie_details/route.dart index 5b2d6746..b954b4e2 100644 --- a/lib/modules/radarr/routes/add_movie_details/route.dart +++ b/lib/modules/radarr/routes/add_movie_details/route.dart @@ -57,8 +57,8 @@ class _State extends State<_RadarrMoviesAddDetailsRoute> with LunaLoadCallbackMi Widget build(BuildContext context) { _RadarrAddMovieDetailsArguments arguments = ModalRoute.of(context).settings.arguments; if(arguments == null || arguments.movie == null) return LunaInvalidRoute( - title: 'Add Movie', - message: 'Movie Not Found', + title: 'radarr.AddMovie'.tr(), + message: 'radarr.MovieNotFound'.tr(), ); return Scaffold( key: _scaffoldKey, @@ -69,7 +69,7 @@ class _State extends State<_RadarrMoviesAddDetailsRoute> with LunaLoadCallbackMi Widget _appBar() { return LunaAppBar( - title: 'Add Movie', + title: 'radarr.AddMovie'.tr(), scrollControllers: [scrollController], ); } diff --git a/lib/modules/radarr/routes/add_movie_details/state.dart b/lib/modules/radarr/routes/add_movie_details/state.dart index 197d18c5..548f9bce 100644 --- a/lib/modules/radarr/routes/add_movie_details/state.dart +++ b/lib/modules/radarr/routes/add_movie_details/state.dart @@ -18,7 +18,7 @@ class RadarrAddMovieDetailsState extends ChangeNotifier { assert(rootFolders != null); assert(qualityProfiles != null); assert(tags != null); - _tags = []; + _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, @@ -75,6 +75,7 @@ class RadarrAddMovieDetailsState extends ChangeNotifier { set tags(List tags) { assert(tags != null); _tags = tags; + RadarrDatabaseValue.ADD_MOVIE_DEFAULT_TAGS.put((tags ?? []).map((tag) => tag.id).toList()); notifyListeners(); } diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/button_add_movie.dart b/lib/modules/radarr/routes/add_movie_details/widgets/button_add_movie.dart index 81e75bc3..bfc3c6db 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/button_add_movie.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/button_add_movie.dart @@ -16,7 +16,7 @@ class RadarrAddMovieDetailsAddButton extends StatelessWidget { @override Widget build(BuildContext context) { return LunaButton( - text: searchOnAdd ? 'Add + Search' : 'Add', + text: searchOnAdd ? 'radarr.AddMovieAndSearch'.tr() : 'radarr.AddMovie'.tr(), backgroundColor: searchOnAdd ? LunaColours.orange : LunaColours.accent, onTap: () async => _onTap(context), loadingState: context.watch().state, diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart index e95384f4..6fb7e1d4 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_minimum_availability.dart @@ -7,7 +7,7 @@ class RadarrAddMovieDetailsMinimumAvailabilityTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Minimum Availability'), + title: LunaText.title(text: 'radarr.MinimumAvailability'.tr()), subtitle: Selector( selector: (_, state) => state.availability, builder: (context, availability, _) => LunaText.subtitle(text: availability?.readable ?? Constants.TEXT_EMDASH), diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart index fd4dce41..019c15f1 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_monitored.dart @@ -7,8 +7,8 @@ class RadarrAddMovieDetailsMonitoredTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Monitored'), - subtitle: LunaText.subtitle(text: 'Monitor movie for new releases'), + title: LunaText.title(text: 'radarr.Monitored'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.MonitoredDescription'.tr()), trailing: Selector( selector: (_, state) => state.monitored, builder: (context, monitored, _) => LunaSwitch( diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart index c2f5f7a3..b69e125b 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_quality_profile.dart @@ -7,7 +7,7 @@ class RadarrAddMovieDetailsQualityProfileTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Quality Profile'), + title: LunaText.title(text: 'radarr.QualityProfile'.tr()), subtitle: Selector( selector: (_, state) => state.qualityProfile, builder: (context, profile, _) => LunaText.subtitle(text: profile?.name ?? Constants.TEXT_EMDASH), diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart index ea872bd7..0a4fe022 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_root_folder.dart @@ -7,7 +7,7 @@ class RadarrAddMovieDetailsRootFolderTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Root Folder'), + title: LunaText.title(text: 'radarr.RootFolder'.tr()), subtitle: Selector( selector: (_, state) => state.rootFolder, builder: (context, folder, _) => LunaText.subtitle(text: folder?.path ?? Constants.TEXT_EMDASH), diff --git a/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart b/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart index 29b0365b..fd991329 100644 --- a/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart +++ b/lib/modules/radarr/routes/add_movie_details/widgets/tile_tags.dart @@ -7,7 +7,7 @@ class RadarrAddMovieDetailsTagsTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Tags'), + title: LunaText.title(text: 'radarr.Tags'.tr()), subtitle: LunaText.subtitle(text: context.watch().tags.length == 0 ? LunaUI.TEXT_EMDASH : context.watch().tags.map((e) => e.label).join(', ')), diff --git a/lib/modules/radarr/routes/catalogue/route.dart b/lib/modules/radarr/routes/catalogue/route.dart index cb7d180b..a1ed0343 100644 --- a/lib/modules/radarr/routes/catalogue/route.dart +++ b/lib/modules/radarr/routes/catalogue/route.dart @@ -70,8 +70,8 @@ class _State extends State with AutomaticKeepAliveClientMi Widget _movies(List movies, List qualityProfiles) { if((movies?.length ?? 0) == 0) return LunaMessage( - text: 'No Movies Found', - buttonText: 'Refresh', + text: 'radarr.NoMoviesFound'.tr(), + buttonText: 'lunasea.Refresh'.tr(), onTap: _refreshKey.currentState.show, ); return Selector( @@ -81,13 +81,13 @@ class _State extends State with AutomaticKeepAliveClientMi if((_filtered?.length ?? 0) == 0) return LunaListView( controller: RadarrNavigationBar.scrollControllers[0], children: [ - LunaMessage.inList(text: 'No Movies Found'), + LunaMessage.inList(text: 'radarr.NoMoviesFound'.tr()), LunaButtonContainer( children: [ LunaButton( text: query.length > 20 - ? 'Search For "${query.substring(0, 20)}${LunaUI.TEXT_ELLIPSIS}"' - : 'Search For "$query"', + ? 'radarr.SearchFor'.tr(args: ['"${query.substring(0, 20)}${LunaUI.TEXT_ELLIPSIS}"']) + : 'radarr.SearchFor'.tr(args: ['"$query"']), onTap: () async => RadarrAddMovieRouter().navigateTo(context, query: query ?? ''), ), ], diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart index 997cdf5e..de48c13b 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_filter_button.dart @@ -21,7 +21,7 @@ class _State extends State { context: context, child: Consumer( builder: (context, state, _) => LunaPopupMenuButton( - tooltip: 'Filter Catalogue', + tooltip: 'radarr.FilterCatalogue'.tr(), icon: Icons.filter_alt_outlined, onSelected: (result) { state.moviesFilterType = result; diff --git a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart index e1217804..db86a021 100644 --- a/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart +++ b/lib/modules/radarr/routes/catalogue/widgets/search_bar_sort_button.dart @@ -21,7 +21,7 @@ class _State extends State { context: context, child: Consumer( builder: (context, state, _) => LunaPopupMenuButton( - tooltip: 'Sort Catalogue', + tooltip: 'radarr.SortCatalogue'.tr(), icon: Icons.sort, onSelected: (result) { if(state.moviesSortType == result) { diff --git a/lib/modules/radarr/routes/edit_movie/route.dart b/lib/modules/radarr/routes/edit_movie/route.dart index bb60b0bb..3ede04dc 100644 --- a/lib/modules/radarr/routes/edit_movie/route.dart +++ b/lib/modules/radarr/routes/edit_movie/route.dart @@ -46,7 +46,10 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L @override Widget build(BuildContext context) { - if(widget.movieId <= 0) return LunaInvalidRoute(title: 'Edit Movie', message: 'Movie Not Found'); + if(widget.movieId <= 0) return LunaInvalidRoute( + title: 'radarr.EditMovie'.tr(), + message: 'radarr.MovieNotFound'.tr(), + ); return Scaffold( key: _scaffoldKey, appBar: _appBar(), @@ -57,7 +60,7 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L Widget _appBar() { return LunaAppBar( scrollControllers: [scrollController], - title: 'Edit Movie', + title: 'radarr.EditMovie'.tr(), ); } @@ -87,7 +90,10 @@ class _State extends State<_RadarrMoviesEditRoute> with LunaLoadCallbackMixin, L return ChangeNotifierProvider( create: (_) => RadarrMoviesEditState(movie: movie, qualityProfiles: qualityProfiles, tags: tags), builder: (context, _) { - if(context.watch().state == LunaLoadingState.ERROR) return LunaMessage.goBack(context: context, text: 'An Error Has Occurred'); + if(context.watch().state == LunaLoadingState.ERROR) return LunaMessage.goBack( + context: context, + text: 'lunasea.AnErrorHasOccurred'.tr(), + ); return LunaListView( controller: scrollController, children: [ diff --git a/lib/modules/radarr/routes/edit_movie/widgets/button_update_movie.dart b/lib/modules/radarr/routes/edit_movie/widgets/button_update_movie.dart index 648fc25c..6aa5fd97 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/button_update_movie.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/button_update_movie.dart @@ -15,7 +15,7 @@ class RadarrMoviesEditUpdateMovieButton extends StatelessWidget { return LunaButtonContainer( children: [ LunaButton( - text: 'Update Movie', + text: 'radarr.UpdateMovie'.tr(), loadingState: context.watch().state, onTap: () async { context.read().state = LunaLoadingState.ACTIVE; diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart index d08b69ca..c1b5c328 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_monitored.dart @@ -7,8 +7,8 @@ class RadarrMoviesEditMonitoredTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Monitored'), - subtitle: LunaText.subtitle(text: 'Monitor movie for new releases'), + title: LunaText.title(text: 'radarr.Monitored'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.MonitoredDescription'.tr()), trailing: LunaSwitch( value: context.watch().monitored, onChanged: (value) => context.read().monitored = value, diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart index c553a6d1..2690714a 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_path.dart @@ -7,11 +7,11 @@ class RadarrMoviesEditPathTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Movie Path'), + title: LunaText.title(text: 'radarr.MoviePath'.tr()), subtitle: LunaText.subtitle(text: context.watch().path ?? Constants.TEXT_EMDASH), trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), onTap: () async { - Tuple2 _values = await LunaDialogs().editText(context, 'Movie Path', prefill: context.read().path); + Tuple2 _values = await LunaDialogs().editText(context, 'radarr.MoviePath'.tr(), prefill: context.read().path); if(_values.item1) context.read().path = _values.item2; }, ); diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart index 3b501dcb..f41defc2 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_quality_profile.dart @@ -14,7 +14,7 @@ class RadarrMoviesEditQualityProfileTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Quality Profile'), + title: LunaText.title(text: 'radarr.QualityProfile'.tr()), subtitle: LunaText.subtitle(text: context.watch().qualityProfile?.name ?? Constants.TEXT_EMDASH), trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), onTap: () async { diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart index f31b8a07..4b87bdae 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_radarr_availability.dart @@ -7,7 +7,7 @@ class RadarrMoviesEditMinimumAvailabilityTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Minimum Availability'), + title: LunaText.title(text: 'radarr.MinimumAvailability'.tr()), subtitle: LunaText.subtitle(text: context.watch().availability.readable), trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), onTap: () async { diff --git a/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart b/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart index 79c82850..52d19317 100644 --- a/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart +++ b/lib/modules/radarr/routes/edit_movie/widgets/tile_tags.dart @@ -7,9 +7,9 @@ class RadarrMoviesEditTagsTile extends StatelessWidget { Widget build(BuildContext context) { return LunaListTile( context: context, - title: LunaText.title(text: 'Tags'), + title: LunaText.title(text: 'radarr.Tags'.tr()), subtitle: LunaText.subtitle(text: context.watch().tags.length == 0 - ? 'Not Set' + ? LunaUI.TEXT_EMDASH : context.watch().tags.map((e) => e.label).join(', ')), trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded), onTap: () async => await RadarrDialogs().setEditTags(context), diff --git a/lib/modules/radarr/routes/history/route.dart b/lib/modules/radarr/routes/history/route.dart index e8110c64..dc1542b8 100644 --- a/lib/modules/radarr/routes/history/route.dart +++ b/lib/modules/radarr/routes/history/route.dart @@ -56,7 +56,7 @@ class _State extends State<_RadarrHistoryRoute> with LunaScrollControllerMixin { Widget _appBar() { return LunaAppBar( - title: 'History', + title: 'radarr.History'.tr(), scrollControllers: [scrollController], ); } @@ -84,7 +84,7 @@ class _State extends State<_RadarrHistoryRoute> with LunaScrollControllerMixin { pagingController: _pagingController, scrollController: scrollController, listener: _fetchPage, - noItemsFoundMessage: 'No History Found', + noItemsFoundMessage: 'radarr.NoHistoryFound'.tr(), itemBuilder: (context, history, index) { RadarrMovie _movie = movies.firstWhere((movie) => movie.id == history.movieId, orElse: () => null); return RadarrHistoryTile(history: history, title: _movie?.title); diff --git a/lib/modules/radarr/routes/missing/route.dart b/lib/modules/radarr/routes/missing/route.dart index 49e1514f..b7c0f860 100644 --- a/lib/modules/radarr/routes/missing/route.dart +++ b/lib/modules/radarr/routes/missing/route.dart @@ -59,8 +59,8 @@ class _State extends State with AutomaticKeepAliveClientMixi Widget _list(List movies, List qualityProfiles) { if((movies?.length ?? 0) == 0) return LunaMessage( - text: 'No Movies Found', - buttonText: 'Refresh', + text: 'radarr.NoMoviesFound'.tr(), + buttonText: 'lunasea.Refresh'.tr(), onTap: _refreshKey.currentState.show, ); return LunaListViewBuilder( diff --git a/lib/modules/radarr/routes/missing/widgets/missing_tile.dart b/lib/modules/radarr/routes/missing/widgets/missing_tile.dart index 6f630044..caa3aa34 100644 --- a/lib/modules/radarr/routes/missing/widgets/missing_tile.dart +++ b/lib/modules/radarr/routes/missing/widgets/missing_tile.dart @@ -68,7 +68,7 @@ class _State extends State { fontWeight: LunaUI.FONT_WEIGHT_BOLD, color: LunaColours.red, ), - text: _days == null ? 'Released' : _days == 'Today' ? 'Released Today' : 'Released $_days Ago' + text: _days == null ? 'radarr.Released'.tr() : _days == 'Today' ? 'radarr.ReleasedToday'.tr() : 'Released $_days Ago' ); } diff --git a/lib/modules/radarr/routes/more/route.dart b/lib/modules/radarr/routes/more/route.dart index 1afa6aaa..bd7a902e 100644 --- a/lib/modules/radarr/routes/more/route.dart +++ b/lib/modules/radarr/routes/more/route.dart @@ -28,29 +28,29 @@ class _State extends State with AutomaticKeepAliveClientMixin { children: [ LunaListTile( context: context, - title: LunaText.title(text: 'History'), - subtitle: LunaText.subtitle(text: 'View Recent Activity'), + title: LunaText.title(text: 'radarr.History'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.HistoryDescription'.tr()), trailing: LunaIconButton(icon: CustomIcons.history, color: LunaColours.list(0)), onTap: () async => RadarrHistoryRouter().navigateTo(context), ), LunaListTile( context: context, - title: LunaText.title(text: 'Queue'), - subtitle: LunaText.subtitle(text: 'View Active & Queued Content'), + title: LunaText.title(text: 'radarr.Queue'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.QueueDescription'.tr()), trailing: LunaIconButton(icon: Icons.queue, color: LunaColours.list(1)), onTap: () async => RadarrQueueRouter().navigateTo(context), ), LunaListTile( context: context, - title: LunaText.title(text: 'System Status'), - subtitle: LunaText.subtitle(text: 'System Status & Disk Space'), + title: LunaText.title(text: 'radarr.SystemStatus'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.SystemStatusDescription'.tr()), trailing: LunaIconButton(icon: CustomIcons.monitoring, color: LunaColours.list(2)), onTap: () async => RadarrSystemStatusRouter().navigateTo(context), ), LunaListTile( context: context, - title: LunaText.title(text: 'Tags'), - subtitle: LunaText.subtitle(text: 'Manage Your Tags'), + title: LunaText.title(text: 'radarr.Tags'.tr()), + subtitle: LunaText.subtitle(text: 'radarr.TagsDescription'.tr()), trailing: LunaIconButton(icon: Icons.style, color: LunaColours.list(3)), onTap: () async => RadarrTagsRouter().navigateTo(context), ), diff --git a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_links_section.dart b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_links_section.dart index 9534b543..31cad81a 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/overview_movie_links_section.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/overview_movie_links_section.dart @@ -18,45 +18,52 @@ class RadarrMovieDetailsOverviewLinksSection extends StatelessWidget { context: context, child: InkWell( child: Padding( - child: Image.asset( - 'assets/images/services/imdb.png', - height: 21.0, - ), - padding: EdgeInsets.all(18.0), + child: Image.asset('assets/images/services/imdb.png'), + padding: EdgeInsets.all(12.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), onTap: () async => await movie?.imdbId?.lunaOpenIMDB(), ), + height: 50.0, margin: EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), ), if(movie.tmdbId != null && movie.tmdbId != 0) LunaCard( context: context, child: InkWell( child: Padding( - child: Image.asset( - 'assets/images/services/themoviedb.png', - height: 22.0, - ), - padding: EdgeInsets.all(17.0), + child: Image.asset('assets/images/services/themoviedb.png'), + padding: EdgeInsets.all(12.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), onTap: () async => await movie?.tmdbId?.toString()?.lunaOpenTheMovieDBMovie(), ), + height: 50.0, + margin: EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + ), + if(movie.tmdbId != null && movie.tmdbId != 0) LunaCard( + context: context, + child: InkWell( + child: Padding( + child: Image.asset('assets/images/services/trakt.png'), + padding: EdgeInsets.all(12.0), + ), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + onTap: () async => await movie?.tmdbId?.toString()?.lunaOpenTraktMovie(), + ), + height: 50.0, margin: EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), ), if(movie.youTubeTrailerId != null && movie.youTubeTrailerId.isNotEmpty) LunaCard( context: context, child: InkWell( child: Padding( - child: Image.asset( - 'assets/images/services/youtube.png', - height: 17.0, - ), - padding: EdgeInsets.all(20.0), + child: Image.asset('assets/images/services/youtube.png'), + padding: EdgeInsets.all(12.0), ), borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), onTap: () async => await movie?.youTubeTrailerId?.lunaOpenYouTube(), ), + height: 50.0, margin: EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), ), ], diff --git a/lib/modules/radarr/routes/movie_details/widgets/page_overview.dart b/lib/modules/radarr/routes/movie_details/widgets/page_overview.dart index c8a0cd5b..98486ddb 100644 --- a/lib/modules/radarr/routes/movie_details/widgets/page_overview.dart +++ b/lib/modules/radarr/routes/movie_details/widgets/page_overview.dart @@ -35,13 +35,13 @@ class _State extends State with AutomaticKeepAli controller: RadarrMovieDetailsNavigationBar.scrollControllers[0], children: [ RadarrMovieDetailsOverviewDescriptionTile(movie: widget.movie), + RadarrMovieDetailsOverviewLinksSection(movie: widget.movie), RadarrMovieDetailsOverviewDownloadButtons(movie: widget.movie), RadarrMovieDetailsOverviewInformationBlock( movie: widget.movie, qualityProfile: widget.qualityProfile, tags: widget.tags, ), - RadarrMovieDetailsOverviewLinksSection(movie: widget.movie), ], ), ), diff --git a/lib/modules/radarr/routes/queue/route.dart b/lib/modules/radarr/routes/queue/route.dart index 831e53e3..337c2800 100644 --- a/lib/modules/radarr/routes/queue/route.dart +++ b/lib/modules/radarr/routes/queue/route.dart @@ -22,13 +22,13 @@ class _State extends State<_RadarrQueueRoute> { return Scaffold( key: _scaffoldKey, appBar: _appBar(), - body: LunaMessage.goBack(context: context, text: 'Coming Soon'), + body: LunaMessage.goBack(context: context, text: 'lunasea.ComingSoon'.tr()), ); } Widget _appBar() { return LunaAppBar( - title: 'Queue', + title: 'radarr.Queue'.tr(), ); } } diff --git a/lib/modules/radarr/routes/radarr/route.dart b/lib/modules/radarr/routes/radarr/route.dart index 4b1b2217..9de2a087 100644 --- a/lib/modules/radarr/routes/radarr/route.dart +++ b/lib/modules/radarr/routes/radarr/route.dart @@ -48,7 +48,7 @@ class _State extends State<_RadarrHomeRoute> { return false; } - Widget _drawer() => LSDrawer(page: 'radarr'); + Widget _drawer() => LunaDrawer(page: LunaModule.RADARR.key); Widget _bottomNavigationBar() { if(context.read().enabled) return RadarrNavigationBar(pageController: _pageController); diff --git a/lib/modules/radarr/routes/radarr/widgets/navigation_bar.dart b/lib/modules/radarr/routes/radarr/widgets/navigation_bar.dart index f2f489d2..7c388fe1 100644 --- a/lib/modules/radarr/routes/radarr/widgets/navigation_bar.dart +++ b/lib/modules/radarr/routes/radarr/widgets/navigation_bar.dart @@ -3,9 +3,21 @@ import 'package:lunasea/core.dart'; class RadarrNavigationBar extends StatelessWidget { final PageController pageController; - static const List icons = [CustomIcons.movies, CustomIcons.upcoming, CustomIcons.calendar_missing, Icons.more_horiz]; - static const List titles = ['Movies', 'Upcoming', 'Missing', 'More']; - static List scrollControllers = List.generate(titles.length, (_) => ScrollController()); + static List scrollControllers = List.generate(icons.length, (_) => ScrollController()); + + static const List icons = [ + CustomIcons.movies, + CustomIcons.upcoming, + CustomIcons.calendar_missing, + Icons.more_horiz, + ]; + + static List get titles => [ + 'radarr.Movies'.tr(), + 'radarr.Upcoming'.tr(), + 'radarr.Missing'.tr(), + 'radarr.More'.tr(), + ]; RadarrNavigationBar({ Key key, diff --git a/lib/modules/radarr/routes/upcoming/route.dart b/lib/modules/radarr/routes/upcoming/route.dart index aff307fb..b7e36b51 100644 --- a/lib/modules/radarr/routes/upcoming/route.dart +++ b/lib/modules/radarr/routes/upcoming/route.dart @@ -63,8 +63,8 @@ class _State extends State with AutomaticKeepAliveClientMix List qualityProfiles, ) { if((movies?.length ?? 0) == 0) return LunaMessage( - text: 'No Movies Found', - buttonText: 'Refresh', + text: 'radarr.NoMoviesFound'.tr(), + buttonText: 'lunasea.Refresh'.tr(), onTap: _refreshKey.currentState.show, ); return LunaListViewBuilder( diff --git a/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart b/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart index 92bd52da..38f6cd53 100644 --- a/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart +++ b/lib/modules/radarr/routes/upcoming/widgets/upcoming_tile.dart @@ -84,8 +84,20 @@ class _State extends State { color: color, ), children: [ - if(type == 'release') TextSpan(text: _days == null ? 'Availability Unknown' : _days == 'Today' ? 'Available Today' : 'Available in $_days'), - if(type == 'cinema') TextSpan(text: _days == null ? 'Cinema Date Unknown' : _days == 'Today' ? 'In Cinemas Today' : 'In Cinemas in $_days'), + if(type == 'release') TextSpan( + text: _days == null + ? 'radarr.AvailabilityUnknown'.tr() + : _days == 'Today' + ? 'radarr.AvailableToday'.tr() + : 'radarr.AvailableIn'.tr(args: [_days]), + ), + if(type == 'cinema') TextSpan( + text: _days == null + ? 'radarr.CinemaDateUnknown'.tr() + : _days == 'Today' + ? 'radarr.InCinemasToday'.tr() + : 'radarr.InCinemasIn'.tr(args: [_days]), + ), if(type == 'unknown') TextSpan(text: _days), ], ); diff --git a/lib/modules/sabnzbd/routes/sabnzbd.dart b/lib/modules/sabnzbd/routes/sabnzbd.dart index 5a71fdce..b6450cd1 100644 --- a/lib/modules/sabnzbd/routes/sabnzbd.dart +++ b/lib/modules/sabnzbd/routes/sabnzbd.dart @@ -54,7 +54,7 @@ class _State extends State { ), ); - Widget get _drawer => LSDrawer(page: 'sabnzbd'); + Widget get _drawer => LunaDrawer(page: LunaModule.SABNZBD.key); Widget get _bottomNavigationBar => SABnzbdNavigationBar(pageController: _pageController); diff --git a/lib/modules/search/core/api/api.dart b/lib/modules/search/core/api/api.dart index 398290c6..aef97dce 100644 --- a/lib/modules/search/core/api/api.dart +++ b/lib/modules/search/core/api/api.dart @@ -1,8 +1,9 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:xml_parser/xml_parser.dart'; +//import 'package:xml_parser/xml_parser.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/modules/search.dart'; +import 'package:xml/xml.dart'; class NewznabAPI { final Map _values; @@ -48,70 +49,59 @@ class NewznabAPI { } Future> getCategories() async { - try { - Response response = await _dio.get( - '', - queryParameters: { - 't': 'caps', - }, - ); - XmlDocument _xml = XmlDocument.fromString(response.data); - List _results = []; - for(XmlElement _category in _xml?.root?.getChild('categories')?.getChildren('Category') ?? []) { - NewznabCategoryData _data = NewznabCategoryData( - id: int.tryParse(_category.attributes.firstWhere((attr) => attr.name.toString() == 'id')?.value) ?? -1, - name: _category.attributes.firstWhere((attr) => attr.name.toString() == 'name')?.value ?? 'Unknown Category', - ); - for(XmlElement _subcategory in _category?.getChildren('subcat') ?? []) { - _data.subcategories.add(NewznabSubcategoryData( - id: int.tryParse(_subcategory.attributes.firstWhere((attr) => attr.name.toString() == 'id')?.value) ?? -1, - name: _subcategory.attributes.firstWhere((attr) => attr.name.toString() == 'name')?.value ?? 'Unknown Subcategory', + Response response = await _dio.get( + '', + queryParameters: { + 't': 'caps', + }, + ); + List results = []; + XmlDocument xml = XmlDocument.parse(response.data); + xml.getElement('caps').getElement('categories').findElements('category').forEach((cat) { + String catName = cat.getAttribute('name'); + int catId = int.tryParse(cat.getAttribute('id') ?? 'noid'); + if((catName?.isNotEmpty ?? false) && (catId != null)) { + NewznabCategoryData data = NewznabCategoryData(id: catId, name: catName); + cat.findElements('subcat').forEach((subcat) { + String subcatName = subcat.getAttribute('name'); + int subcatId = int.tryParse(subcat.getAttribute('id') ?? 'noid'); + if((subcatName?.isNotEmpty ?? false) && (subcatId != null)) data.subcategories.add(NewznabSubcategoryData( + id: subcatId, + name: subcatName, )); - } - _results.add(_data); + }); + results.add(data); } - return _results; - } on DioError catch (error, stack) { - logError('Unable to fetch categories', error, stack); - return Future.error(error); - } catch (error, stack) { - logError('Unable to fetch categories', error, stack); - return Future.error(error); - } + }); + return results; } Future> getResults({ @required int categoryId, @required String query, int offset = 0 }) async { - try { - Response response = await _dio.get( - '', - queryParameters: { - 't': 'search', - if(categoryId != -1) 'cat': categoryId, - if(query != '') 'q': query, - 'limit': 100, - 'extended': 1, - 'offset': 100*offset, - }, + Response response = await _dio.get( + '', + queryParameters: { + 't': 'search', + if(categoryId != -1) 'cat': categoryId, + if(query != '') 'q': query, + 'limit': 10, + 'extended': 1, + 'offset': 10*offset, + }, + ); + List results = []; + XmlDocument xml = XmlDocument.parse(response.data); + xml.getElement('rss')?.getElement('channel')?.findElements('item')?.forEach((item) { + int size = int.tryParse(item.getElement('enclosure')?.getAttribute('length') ?? 'nosize'); + NewznabResultData data = NewznabResultData( + title: item.getElement('title')?.innerText ?? 'Unknown Title', + category: item.getElement('category')?.innerText ?? 'Unknown Category', + size: size ?? 0, + linkComments: item.getElement('comments')?.innerText ?? '', + linkDownload: item.getElement('link')?.innerText ?? '', + date: item.getElement('pubDate')?.innerText ?? 'Unknown Date', ); - XmlDocument _xml = XmlDocument.fromString(response.data); - List _results = []; - for(XmlElement _item in _xml?.root?.getChild('channel')?.getChildren('item') ?? []) { - _results.add(NewznabResultData( - title: _item.getChild('title')?.text ?? 'Unknown Result', - category: _item.getChild('category')?.text ?? 'Unknown Category', - size: int.tryParse(_item.getChild('enclosure')?.getAttribute('length')) ?? 0, - linkComments: _item?.getChild('comments')?.text ?? '', - linkDownload: _item?.getChild('link')?.text ?? '', - date: _item?.getChild('pubDate')?.text ?? 'Unknown Date', - )); - } - return _results; - } on DioError catch (error, stack) { - logError('Unable to fetch results', error, stack); - return Future.error(error); - } catch (error, stack) { - logError('Unable to fetch results', error, stack); - return Future.error(error); - } + results.add(data); + }); + return results; } } diff --git a/lib/modules/search/core/api/data/result.dart b/lib/modules/search/core/api/data/result.dart index b0774114..0ce8457f 100644 --- a/lib/modules/search/core/api/data/result.dart +++ b/lib/modules/search/core/api/data/result.dart @@ -22,10 +22,17 @@ class NewznabResultData { DateTime get dateObject { try { DateFormat _format = DateFormat("EEE, dd MMM yyyy hh:mm:ss"); - int _offset = int.tryParse(date.substring(date.length - 5)); - DateTime _date = _format.parseUtc(date); + String _dateFixed = [ + date.substring(0, 3), + '.', + date.substring(3, 11), + '.', + date.substring(11, date.length), + ].join(); + int _offset = int.tryParse(_dateFixed.substring(_dateFixed.length - 5)); + DateTime _date = _format.parseUtc(_dateFixed); if(_offset != null) _date = _date.add(Duration(hours: (-(_offset/100).round()))); - return _date.toLocal(); + return _date.toLocal().isAfter(DateTime.now()) ? DateTime.now() : _date.toLocal(); } catch (e) {} return null; } diff --git a/lib/modules/search/routes/categories.dart b/lib/modules/search/routes/categories.dart index 0ff12595..b59eed5b 100644 --- a/lib/modules/search/routes/categories.dart +++ b/lib/modules/search/routes/categories.dart @@ -52,7 +52,10 @@ class _State extends State { builder: (context, snapshot) { switch(snapshot.connectionState) { case ConnectionState.done: { - if(snapshot.hasError || snapshot.data == null) return LSErrorMessage(onTapHandler: () => _refresh()); + if(snapshot.hasError || snapshot.data == null) { + LunaLogger().error('Unable to fetch categories', snapshot.error, snapshot.stackTrace); + return LSErrorMessage(onTapHandler: () => _refresh()); + } return _list(snapshot.data); } case ConnectionState.none: diff --git a/lib/modules/search/routes/indexers.dart b/lib/modules/search/routes/indexers.dart index 46017677..d40862eb 100644 --- a/lib/modules/search/routes/indexers.dart +++ b/lib/modules/search/routes/indexers.dart @@ -42,7 +42,7 @@ class _State extends State { title: 'Search', ); - Widget get _drawer => LSDrawer(page: 'search'); + Widget get _drawer => LunaDrawer(page: LunaModule.SEARCH.key); Widget get _body => LSListView( children: [..._indexerList], diff --git a/lib/modules/search/routes/results.dart b/lib/modules/search/routes/results.dart index 627604df..5a202d1f 100644 --- a/lib/modules/search/routes/results.dart +++ b/lib/modules/search/routes/results.dart @@ -57,7 +57,10 @@ class _State extends State { builder: (context, snapshot) { switch(snapshot.connectionState) { case ConnectionState.done: { - if(snapshot.hasError || snapshot.data == null) return LSErrorMessage(onTapHandler: () => _refresh()); + if(snapshot.hasError || snapshot.data == null) { + LunaLogger().error('Unable to fetch results', snapshot.error, snapshot.stackTrace); + return LSErrorMessage(onTapHandler: () => _refresh()); + } _results = snapshot.data; return _list; } diff --git a/lib/modules/search/routes/search.dart b/lib/modules/search/routes/search.dart index 19d65b0f..a19e0567 100644 --- a/lib/modules/search/routes/search.dart +++ b/lib/modules/search/routes/search.dart @@ -48,6 +48,7 @@ class _State extends State { switch(snapshot.connectionState) { case ConnectionState.done: { if(snapshot.hasError || snapshot.data == null) { + LunaLogger().error('Unable to fetch results', snapshot.error, snapshot.stackTrace); _data = _error; } else { _results = snapshot.data; diff --git a/lib/modules/settings/core/dialogs.dart b/lib/modules/settings/core/dialogs.dart index 250fb44f..15c9571a 100644 --- a/lib/modules/settings/core/dialogs.dart +++ b/lib/modules/settings/core/dialogs.dart @@ -27,7 +27,7 @@ class SettingsDialogs { ], content: [ LSDialog.textContent( - text: 'Are you sure you want to sign out of your ${Constants.APPLICATION_NAME} account?' + text: 'Are you sure you want to sign out of your LunaSea account?' ), ], contentPadding: LSDialog.textDialogContentPadding(), @@ -530,6 +530,34 @@ class SettingsDialogs { return Tuple2(_flag, _browser); } + Future> changeLanguage(BuildContext context) async { + List languages = LunaLocalization().supportedLanguages; + bool _flag = false; + LunaLanguage _language; + + void _setValues(bool flag, LunaLanguage language) { + _flag = flag; + _language = language; + Navigator.of(context, rootNavigator: true).pop(); + } + + await LSDialog.dialog( + context: context, + title: 'Language', + content: List.generate( + languages.length, + (index) => LSDialog.tile( + icon: Icons.language_rounded, + iconColor: LunaColours.list(index), + text: languages[index].name, + onTap: () => _setValues(true, languages[index]), + ), + ), + contentPadding: LSDialog.listDialogContentPadding(), + ); + return Tuple2(_flag, _language); + } + static Future> editCalendarStartingDay(BuildContext context) async { bool _flag = false; CalendarStartingDay _startingDate; @@ -828,7 +856,7 @@ class SettingsDialogs { return [_flag, _textController.text]; } - Future disableSentryWarning(BuildContext context) async { + Future disableCrashlyticsWarning(BuildContext context) async { bool _flag = false; void _setValues(bool flag) { @@ -838,11 +866,11 @@ class SettingsDialogs { await LSDialog.dialog( context: context, - title: 'Sentry', + title: 'Firebase Crashlytics', buttons: [ LSDialog.button( text: 'Website', - onPressed: () => Constants.URL_SENTRY.lunaOpenGenericLink(), + onPressed: () => Constants.URL_CRASHLYTICS.lunaOpenGenericLink(), textColor: LunaColours.accent, ), LSDialog.button( @@ -859,10 +887,11 @@ class SettingsDialogs { color: LunaColours.red, fontSize: LSDialog.SUBBODY_SIZE, ), - LSDialog.textSpanContent(text: 'Sentry is an open-source platform used for capturing crashes and errors.\n\n'), - LSDialog.textSpanContent(text: 'To reserve your right to privacy, I have added the option to disable Sentry error tracking, but please know that these errors and stacktraces are incredibly useful for catching and pinpointing bugs!\n\n'), + LSDialog.textSpanContent(text: 'Firebase Crashlytics is a tool used for capturing crashes and errors.\n\n'), + LSDialog.textSpanContent(text: 'To reserve your right to privacy, you have the option to disable error and crash tracking, but please know that these errors and stacktraces are incredibly useful for catching and pinpointing bugs!\n\n'), + LSDialog.textSpanContent(text: 'A link to the Firebase website is available below for more information to help make an informed decision.\n\n'), LSDialog.bolded( - text: 'A link to their website is available below for more information to help make an informed decision.', + text: 'A restart of LunaSea is required for the changes to take effect.', color: LunaColours.accent, fontSize: LSDialog.SUBBODY_SIZE, ), @@ -999,7 +1028,7 @@ class SettingsDialogs { }, ), ], - content: [LSDialog.textContent(text: '${Constants.APPLICATION_NAME} offers a free account to backup your configuration to the cloud, with additional features coming in the future!')], + content: [LSDialog.textContent(text: 'LunaSea offers a free account to backup your configuration to the cloud, with additional features coming in the future!')], contentPadding: LSDialog.textDialogContentPadding(), ); } diff --git a/lib/modules/settings/routes/account/pages/signed_in.dart b/lib/modules/settings/routes/account/pages/signed_in.dart index 271be87f..443444f1 100644 --- a/lib/modules/settings/routes/account/pages/signed_in.dart +++ b/lib/modules/settings/routes/account/pages/signed_in.dart @@ -37,7 +37,7 @@ class _State extends State { onTap: () async { bool result = await SettingsDialogs().confirmAccountSignOut(context); if(result) LunaFirebaseAuth().signOut() - .then((_) => showLunaSuccessSnackBar(context: context, title: 'Signed Out', message: 'Signed out of your ${Constants.APPLICATION_NAME} account')) + .then((_) => showLunaSuccessSnackBar(context: context, title: 'Signed Out', message: 'Signed out of your LunaSea account')) .catchError((error, stack) { LunaLogger().error('Failed to sign out', error, stack); showLunaErrorSnackBar(context: context, title: 'Failed to Sign Out', error: error); diff --git a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart index e9fd85e1..77243812 100644 --- a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart @@ -113,7 +113,7 @@ class _State extends State<_SettingsConfigurationLidarrRoute> with LunaScrollCon .then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'Lidarr is ready to use with ${Constants.APPLICATION_NAME}', + message: 'Lidarr is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart index ae5eafe8..d2e13c67 100644 --- a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart @@ -133,7 +133,7 @@ class _State extends State<_SettingsConfigurationNZBGetRoute> with LunaScrollCon .then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'NZBGet is ready to use with ${Constants.APPLICATION_NAME}', + message: 'NZBGet is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/configuration_quickactions.dart b/lib/modules/settings/routes/configuration_quickactions.dart index 847bc311..c10a3770 100644 --- a/lib/modules/settings/routes/configuration_quickactions.dart +++ b/lib/modules/settings/routes/configuration_quickactions.dart @@ -36,7 +36,7 @@ class _State extends State<_SettingsConfigurationQuickActionsRoute> with LunaScr context, 'Quick Actions', [ - 'Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing ${Constants.APPLICATION_NAME}\'s icon.', + 'Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing LunaSea\'s icon.', 'A limited number of quick actions can be set at a time, and enabling more than your launcher can support will have no effect.' ].join('\n\n'), ), diff --git a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart index 08ef1046..5ffb9a71 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart @@ -113,7 +113,7 @@ class _State extends State<_SettingsConfigurationRadarrRoute> with LunaScrollCon .system.status().then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'Radarr is ready to use with ${Constants.APPLICATION_NAME}', + message: 'Radarr is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart b/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart index 6e820b2c..6bf50277 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/default_sorting.dart @@ -116,11 +116,7 @@ class _State extends State<_SettingsConfigurationRadarrRoute> with LunaScrollCon onTap: () async { List titles = RadarrReleasesSorting.values.map((e) => e.readable).toList(); Tuple2 values = await RadarrDialogs().setDefaultSortingOrFiltering(context, titles: titles); - if(values.item1) { - RadarrDatabaseValue.DEFAULT_SORTING_RELEASES.put(RadarrReleasesSorting.values[values.item2]); - context.read().moviesSortType = RadarrDatabaseValue.DEFAULT_SORTING_RELEASES.data; - context.read().moviesSortAscending = RadarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING.data; - } + if(values.item1) RadarrDatabaseValue.DEFAULT_SORTING_RELEASES.put(RadarrReleasesSorting.values[values.item2]); }, ), ); diff --git a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart index 6847c31f..b84de25f 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart @@ -113,7 +113,7 @@ class _State extends State<_SettingsConfigurationSABnzbdRoute> with LunaScrollCo .then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'SABnzbd is ready to use with ${Constants.APPLICATION_NAME}', + message: 'SABnzbd is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart index 7b40c22e..6837a0a9 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart @@ -130,7 +130,7 @@ class _State extends State<_SettingsConfigurationSonarrRoute> with LunaScrollCon .system.getStatus().then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'Sonarr is ready to use with ${Constants.APPLICATION_NAME}', + message: 'Sonarr is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart index 1d1d8e58..03f0e106 100644 --- a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart @@ -113,7 +113,7 @@ class _State extends State<_SettingsConfigurationTautulliRoute> with LunaScrollC .miscellaneous.arnold().then((_) => showLunaSuccessSnackBar( context: context, title: 'Connected Successfully', - message: 'Tautulli is ready to use with ${Constants.APPLICATION_NAME}', + message: 'Tautulli is ready to use with LunaSea', )).catchError((error, trace) { LunaLogger().error('Connection Test Failed', error, trace); showLunaErrorSnackBar( diff --git a/lib/modules/settings/routes/donations/pages/thank_you.dart b/lib/modules/settings/routes/donations/pages/thank_you.dart index b83f3857..b56200a3 100644 --- a/lib/modules/settings/routes/donations/pages/thank_you.dart +++ b/lib/modules/settings/routes/donations/pages/thank_you.dart @@ -40,7 +40,7 @@ class _State extends State<_SettingsDonationsThankYouRoute> with LunaScrollContr text: 'Thank You', subtitle: [ "Thank you for supporting the open-source community!", - "Donations are never expected or assumed, so thank you for giving back and helping ${Constants.APPLICATION_NAME} remain free, forever!", + "Donations are never expected or assumed, so thank you for giving back and helping LunaSea remain free, forever!", ].join("\n\n"), ), ], diff --git a/lib/modules/settings/routes/profiles.dart b/lib/modules/settings/routes/profiles.dart index aa89f30f..4f58c636 100644 --- a/lib/modules/settings/routes/profiles.dart +++ b/lib/modules/settings/routes/profiles.dart @@ -35,9 +35,9 @@ class _State extends State<_SettingsProfilesRoute> with LunaScrollControllerMixi context, 'Profiles', [ - 'Profiles allow you to add multiple instances of modules into ${Constants.APPLICATION_NAME}.', + 'Profiles allow you to add multiple instances of modules into LunaSea.', 'Newznab indexer searching is enabled and shared across all profiles.', - 'You can switch between profiles in the main navigation drawer of ${Constants.APPLICATION_NAME}.', + 'You can switch between profiles in the main navigation drawer of LunaSea.', ].join('\n\n'), ), ), diff --git a/lib/modules/settings/routes/resources.dart b/lib/modules/settings/routes/resources.dart index ce662309..43c2db92 100644 --- a/lib/modules/settings/routes/resources.dart +++ b/lib/modules/settings/routes/resources.dart @@ -80,10 +80,17 @@ class _State extends State<_SettingsResourcesRoute> with LunaScrollControllerMix trailing: LunaIconButton(icon: Icons.developer_board), onTap: () async => await Constants.URL_TESTFLIGHT.lunaOpenGenericLink() ), + LunaListTile( + context: context, + title: LunaText.title(text: 'Weblate'), + subtitle: LunaText.subtitle(text: 'Help Localize LunaSea'), + trailing: LunaIconButton(icon: Icons.translate), + onTap: () async => await Constants.URL_WEBLATE.lunaOpenGenericLink(), + ), LunaListTile( context: context, title: LunaText.title(text: 'Website'), - subtitle: LunaText.subtitle(text: 'Visit ${Constants.APPLICATION_NAME}\'s Website'), + subtitle: LunaText.subtitle(text: 'Visit LunaSea\'s Website'), trailing: LunaIconButton(icon: CustomIcons.home), onTap: () async => await Constants.URL_WEBSITE.lunaOpenGenericLink(), ), diff --git a/lib/modules/settings/routes/settings.dart b/lib/modules/settings/routes/settings.dart index 9b226f84..fa9989f9 100644 --- a/lib/modules/settings/routes/settings.dart +++ b/lib/modules/settings/routes/settings.dart @@ -37,7 +37,7 @@ class _State extends State<_SettingsHomeRoute> with LunaScrollControllerMixin { return false; } - Widget _drawer() => LSDrawer(page: LunaModule.SETTINGS.key); + Widget _drawer() => LunaDrawer(page: LunaModule.SETTINGS.key); Widget _appBar() { return LunaAppBar( @@ -54,14 +54,14 @@ class _State extends State<_SettingsHomeRoute> with LunaScrollControllerMixin { LunaListTile( context: context, title: LunaText.title(text: 'Account'), - subtitle: LunaText.subtitle(text: 'Your ${Constants.APPLICATION_NAME} Account'), + subtitle: LunaText.subtitle(text: 'Your LunaSea Account'), trailing: LunaIconButton(icon: Icons.account_circle), onTap: () async => SettingsAccountRouter().navigateTo(context), ), LunaListTile( context: context, title: LunaText.title(text: 'Configuration'), - subtitle: LunaText.subtitle(text: 'Configure & Setup ${Constants.APPLICATION_NAME}'), + subtitle: LunaText.subtitle(text: 'Configure & Setup LunaSea'), trailing: LunaIconButton(icon: Icons.device_hub), onTap: () async => SettingsConfigurationRouter().navigateTo(context), ), diff --git a/lib/modules/settings/routes/system/route.dart b/lib/modules/settings/routes/system/route.dart index 2c9cd982..cb6007b0 100644 --- a/lib/modules/settings/routes/system/route.dart +++ b/lib/modules/settings/routes/system/route.dart @@ -45,6 +45,7 @@ class _State extends State<_SettingsSystemRoute> with LunaScrollControllerMixin SettingsSystemBackupRestoreBackupTile(), SettingsSystemBackupRestoreRestoreTile(), LunaDivider(), + _language(), _enableSentry(), _clearConfiguration(), ], @@ -63,11 +64,24 @@ class _State extends State<_SettingsSystemRoute> with LunaScrollControllerMixin ), subtitle: LunaText.subtitle(text: 'View Recent Changes'), trailing: LunaIconButton(icon: Icons.system_update), - onTap: LunaChangelog().showChangelog, + onTap: () async => LunaChangelog().showChangelog(snapshot.data.version, snapshot.data.buildNumber), ), ); } + Widget _language() { + return LunaListTile( + context: context, + title: LunaText.title(text: 'Language'), + subtitle: LunaText.subtitle(text: LunaLanguage.ENGLISH.fromLocale(context.locale)?.name ?? LunaUI.TEXT_EMDASH), + trailing: LunaIconButton(icon: Icons.language), + onTap: () async { + Tuple2 result = await SettingsDialogs().changeLanguage(context); + if(result.item1) result.item2.use(context); + }, + ); + } + Widget _logs() { return LunaListTile( context: context, @@ -82,13 +96,19 @@ class _State extends State<_SettingsSystemRoute> with LunaScrollControllerMixin return LunaDatabaseValue.ENABLED_SENTRY.listen( builder: (context, box, _) => LunaListTile( context: context, - title: LunaText.title(text: 'Sentry'), + title: LunaText.title(text: 'Firebase Crashlytics'), subtitle: LunaText.subtitle(text: 'Crash and Error Tracking'), trailing: LunaSwitch( value: LunaDatabaseValue.ENABLED_SENTRY.data, onChanged: (value) async { - bool result = await SettingsDialogs().disableSentryWarning(context); - if(result) LunaDatabaseValue.ENABLED_SENTRY.put(value); + LunaDatabaseValue enabled = LunaDatabaseValue.ENABLED_SENTRY; + if(enabled.data) { + bool result = await SettingsDialogs().disableCrashlyticsWarning(context); + if(result) enabled.put(value); + } else { + enabled.put(value); + } + LunaFirebaseCrashlytics().setEnabledState(); } ), ), diff --git a/lib/modules/sonarr.dart b/lib/modules/sonarr.dart index 4e70cd9d..e2ca6d9a 100644 --- a/lib/modules/sonarr.dart +++ b/lib/modules/sonarr.dart @@ -1,3 +1,3 @@ export 'package:sonarr/sonarr.dart'; export 'sonarr/core.dart'; -export 'sonarr/modules.dart'; +export 'sonarr/routes.dart'; diff --git a/lib/modules/sonarr/core/database.dart b/lib/modules/sonarr/core/database.dart index 78b1c0df..44d70bb3 100644 --- a/lib/modules/sonarr/core/database.dart +++ b/lib/modules/sonarr/core/database.dart @@ -33,6 +33,7 @@ class SonarrDatabase extends LunaModuleDatabase { case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS: case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: @@ -62,7 +63,8 @@ class SonarrDatabase extends LunaModuleDatabase { case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: - case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS: case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: case SonarrDatabaseValue.QUEUE_REFRESH_RATE: case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: value.put(config[key]); break; @@ -85,6 +87,7 @@ class SonarrDatabase extends LunaModuleDatabase { case 'SONARR_ADD_SERIES_DEFAULT_LANGUAGE_PROFILE': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE; case 'SONARR_ADD_SERIES_DEFAULT_QUALITY_PROFILE': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE; case 'SONARR_ADD_SERIES_DEFAULT_ROOT_FOLDER': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER; + case 'SONARR_ADD_SERIES_DEFAULT_TAGS': return SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS; case 'SONARR_DEFAULT_SORTING_SERIES': return SonarrDatabaseValue.DEFAULT_SORTING_SERIES; case 'SONARR_DEFAULT_SORTING_RELEASES': return SonarrDatabaseValue.DEFAULT_SORTING_RELEASES; case 'SONARR_DEFAULT_SORTING_SERIES_ASCENDING': return SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING; @@ -104,6 +107,7 @@ enum SonarrDatabaseValue { ADD_SERIES_DEFAULT_LANGUAGE_PROFILE, ADD_SERIES_DEFAULT_QUALITY_PROFILE, ADD_SERIES_DEFAULT_ROOT_FOLDER, + ADD_SERIES_DEFAULT_TAGS, DEFAULT_SORTING_SERIES, DEFAULT_SORTING_RELEASES, DEFAULT_SORTING_SERIES_ASCENDING, @@ -128,6 +132,7 @@ extension SonarrDatabaseValueExtension on SonarrDatabaseValue { case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: return 'SONARR_ADD_SERIES_DEFAULT_LANGUAGE_PROFILE'; case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: return 'SONARR_ADD_SERIES_DEFAULT_QUALITY_PROFILE'; case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: return 'SONARR_ADD_SERIES_DEFAULT_ROOT_FOLDER'; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS: return 'SONARR_ADD_SERIES_DEFAULT_TAGS'; case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: return 'SONARR_DEFAULT_SORTING_SERIES'; case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: return 'SONARR_DEFAULT_SORTING_RELEASES'; case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: return 'SONARR_DEFAULT_SORTING_SERIES_ASCENDING'; @@ -151,6 +156,7 @@ extension SonarrDatabaseValueExtension on SonarrDatabaseValue { case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE : return _box.get(this.key, defaultValue: null); case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE : return _box.get(this.key, defaultValue: null); case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER : return _box.get(this.key, defaultValue: null); + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS: return _box.get(this.key, defaultValue: []); case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: return _box.get(this.key, defaultValue: SonarrSeriesSorting.ALPHABETICAL); case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: return _box.get(this.key, defaultValue: SonarrReleasesSorting.WEIGHT); case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: return _box.get(this.key, defaultValue: true); @@ -162,22 +168,23 @@ extension SonarrDatabaseValueExtension on SonarrDatabaseValue { void put(dynamic value) { final box = Database.lunaSeaBox; switch(this) { - case SonarrDatabaseValue.NAVIGATION_INDEX: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED: if(value.runtimeType == bool) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS: if(value.runtimeType == bool) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE: if(value.runtimeType == String) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS: if(value.runtimeType == SonarrMonitorStatus) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: if(value.runtimeType == SonarrSeriesSorting) box.put(this.key, value); return; - case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: if(value.runtimeType == SonarrReleasesSorting) box.put(this.key, value); return; - case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); return; - case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: if(value.runtimeType == bool) box.put(this.key, value); return; - case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.QUEUE_REFRESH_RATE: if(value.runtimeType == int) box.put(this.key, value); return; - case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: if(value.runtimeType == int) box.put(this.key, value); return; + case SonarrDatabaseValue.NAVIGATION_INDEX: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.NAVIGATION_INDEX_SERIES_DETAILS: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED: if(value is bool) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS: if(value is bool) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_SERIES_TYPE: if(value is String) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITOR_STATUS: if(value is SonarrMonitorStatus) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_LANGUAGE_PROFILE: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_QUALITY_PROFILE: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_ROOT_FOLDER: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS: if(value is List) box.put(this.key, value); return; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES: if(value is SonarrSeriesSorting) box.put(this.key, value); return; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES: if(value is SonarrReleasesSorting) box.put(this.key, value); return; + case SonarrDatabaseValue.DEFAULT_SORTING_SERIES_ASCENDING: if(value is bool) box.put(this.key, value); return; + case SonarrDatabaseValue.DEFAULT_SORTING_RELEASES_ASCENDING: if(value is bool) box.put(this.key, value); return; + case SonarrDatabaseValue.UPCOMING_FUTURE_DAYS: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.QUEUE_REFRESH_RATE: if(value is int) box.put(this.key, value); return; + case SonarrDatabaseValue.CONTENT_LOAD_LENGTH: if(value is int) box.put(this.key, value); return; } LunaLogger().warning('SonarrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid SonarrDatabaseValue: ${this?.toString() ?? 'null'}'); }//=> Database.lunaSeaBox.put(this.key, value); diff --git a/lib/modules/sonarr/modules.dart b/lib/modules/sonarr/modules.dart deleted file mode 100644 index ca432133..00000000 --- a/lib/modules/sonarr/modules.dart +++ /dev/null @@ -1,13 +0,0 @@ -export 'modules/history.dart'; -export 'modules/missing.dart'; -export 'modules/queue.dart'; -export 'modules/releases.dart'; -export 'modules/season_details.dart'; -export 'modules/series.dart'; -export 'modules/series_add.dart'; -export 'modules/series_add_details.dart'; -export 'modules/series_details.dart'; -export 'modules/series_edit.dart'; -export 'modules/sonarr.dart'; -export 'modules/tags.dart'; -export 'modules/upcoming.dart'; diff --git a/lib/modules/sonarr/routes.dart b/lib/modules/sonarr/routes.dart new file mode 100644 index 00000000..3f0c44ee --- /dev/null +++ b/lib/modules/sonarr/routes.dart @@ -0,0 +1,13 @@ +export 'routes/history.dart'; +export 'routes/missing.dart'; +export 'routes/queue.dart'; +export 'routes/releases.dart'; +export 'routes/season_details.dart'; +export 'routes/series.dart'; +export 'routes/series_add.dart'; +export 'routes/series_add_details.dart'; +export 'routes/series_details.dart'; +export 'routes/series_edit.dart'; +export 'routes/sonarr.dart'; +export 'routes/tags.dart'; +export 'routes/upcoming.dart'; diff --git a/lib/modules/sonarr/modules/history.dart b/lib/modules/sonarr/routes/history.dart similarity index 100% rename from lib/modules/sonarr/modules/history.dart rename to lib/modules/sonarr/routes/history.dart diff --git a/lib/modules/sonarr/modules/history/route.dart b/lib/modules/sonarr/routes/history/route.dart similarity index 100% rename from lib/modules/sonarr/modules/history/route.dart rename to lib/modules/sonarr/routes/history/route.dart diff --git a/lib/modules/sonarr/modules/history/widgets.dart b/lib/modules/sonarr/routes/history/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/history/widgets.dart rename to lib/modules/sonarr/routes/history/widgets.dart diff --git a/lib/modules/sonarr/modules/history/widgets/history_tile.dart b/lib/modules/sonarr/routes/history/widgets/history_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/history/widgets/history_tile.dart rename to lib/modules/sonarr/routes/history/widgets/history_tile.dart diff --git a/lib/modules/sonarr/modules/missing.dart b/lib/modules/sonarr/routes/missing.dart similarity index 100% rename from lib/modules/sonarr/modules/missing.dart rename to lib/modules/sonarr/routes/missing.dart diff --git a/lib/modules/sonarr/modules/missing/route.dart b/lib/modules/sonarr/routes/missing/route.dart similarity index 100% rename from lib/modules/sonarr/modules/missing/route.dart rename to lib/modules/sonarr/routes/missing/route.dart diff --git a/lib/modules/sonarr/modules/missing/widgets.dart b/lib/modules/sonarr/routes/missing/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/missing/widgets.dart rename to lib/modules/sonarr/routes/missing/widgets.dart diff --git a/lib/modules/sonarr/modules/missing/widgets/missing_tile.dart b/lib/modules/sonarr/routes/missing/widgets/missing_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/missing/widgets/missing_tile.dart rename to lib/modules/sonarr/routes/missing/widgets/missing_tile.dart diff --git a/lib/modules/sonarr/modules/queue.dart b/lib/modules/sonarr/routes/queue.dart similarity index 100% rename from lib/modules/sonarr/modules/queue.dart rename to lib/modules/sonarr/routes/queue.dart diff --git a/lib/modules/sonarr/modules/queue/route.dart b/lib/modules/sonarr/routes/queue/route.dart similarity index 100% rename from lib/modules/sonarr/modules/queue/route.dart rename to lib/modules/sonarr/routes/queue/route.dart diff --git a/lib/modules/sonarr/modules/queue/widgets.dart b/lib/modules/sonarr/routes/queue/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/queue/widgets.dart rename to lib/modules/sonarr/routes/queue/widgets.dart diff --git a/lib/modules/sonarr/modules/queue/widgets/queue_tile.dart b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/queue/widgets/queue_tile.dart rename to lib/modules/sonarr/routes/queue/widgets/queue_tile.dart diff --git a/lib/modules/sonarr/modules/releases.dart b/lib/modules/sonarr/routes/releases.dart similarity index 100% rename from lib/modules/sonarr/modules/releases.dart rename to lib/modules/sonarr/routes/releases.dart diff --git a/lib/modules/sonarr/modules/releases/route.dart b/lib/modules/sonarr/routes/releases/route.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/route.dart rename to lib/modules/sonarr/routes/releases/route.dart diff --git a/lib/modules/sonarr/modules/releases/widgets.dart b/lib/modules/sonarr/routes/releases/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/widgets.dart rename to lib/modules/sonarr/routes/releases/widgets.dart diff --git a/lib/modules/sonarr/modules/releases/widgets/appbar.dart b/lib/modules/sonarr/routes/releases/widgets/appbar.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/widgets/appbar.dart rename to lib/modules/sonarr/routes/releases/widgets/appbar.dart diff --git a/lib/modules/sonarr/modules/releases/widgets/appbar_filter_button.dart b/lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/widgets/appbar_filter_button.dart rename to lib/modules/sonarr/routes/releases/widgets/appbar_filter_button.dart diff --git a/lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart b/lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/widgets/appbar_sort_button.dart rename to lib/modules/sonarr/routes/releases/widgets/appbar_sort_button.dart diff --git a/lib/modules/sonarr/modules/releases/widgets/tile_release.dart b/lib/modules/sonarr/routes/releases/widgets/tile_release.dart similarity index 100% rename from lib/modules/sonarr/modules/releases/widgets/tile_release.dart rename to lib/modules/sonarr/routes/releases/widgets/tile_release.dart diff --git a/lib/modules/sonarr/modules/season_details.dart b/lib/modules/sonarr/routes/season_details.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details.dart rename to lib/modules/sonarr/routes/season_details.dart diff --git a/lib/modules/sonarr/modules/season_details/route.dart b/lib/modules/sonarr/routes/season_details/route.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/route.dart rename to lib/modules/sonarr/routes/season_details/route.dart diff --git a/lib/modules/sonarr/modules/season_details/widgets.dart b/lib/modules/sonarr/routes/season_details/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/widgets.dart rename to lib/modules/sonarr/routes/season_details/widgets.dart diff --git a/lib/modules/sonarr/modules/season_details/widgets/all_seasons.dart b/lib/modules/sonarr/routes/season_details/widgets/all_seasons.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/widgets/all_seasons.dart rename to lib/modules/sonarr/routes/season_details/widgets/all_seasons.dart diff --git a/lib/modules/sonarr/modules/season_details/widgets/season.dart b/lib/modules/sonarr/routes/season_details/widgets/season.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/widgets/season.dart rename to lib/modules/sonarr/routes/season_details/widgets/season.dart diff --git a/lib/modules/sonarr/modules/season_details/widgets/season_header.dart b/lib/modules/sonarr/routes/season_details/widgets/season_header.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/widgets/season_header.dart rename to lib/modules/sonarr/routes/season_details/widgets/season_header.dart diff --git a/lib/modules/sonarr/modules/season_details/widgets/tile_episode.dart b/lib/modules/sonarr/routes/season_details/widgets/tile_episode.dart similarity index 100% rename from lib/modules/sonarr/modules/season_details/widgets/tile_episode.dart rename to lib/modules/sonarr/routes/season_details/widgets/tile_episode.dart diff --git a/lib/modules/sonarr/modules/series.dart b/lib/modules/sonarr/routes/series.dart similarity index 100% rename from lib/modules/sonarr/modules/series.dart rename to lib/modules/sonarr/routes/series.dart diff --git a/lib/modules/sonarr/modules/series/route.dart b/lib/modules/sonarr/routes/series/route.dart similarity index 100% rename from lib/modules/sonarr/modules/series/route.dart rename to lib/modules/sonarr/routes/series/route.dart diff --git a/lib/modules/sonarr/modules/series/widgets.dart b/lib/modules/sonarr/routes/series/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/series/widgets.dart rename to lib/modules/sonarr/routes/series/widgets.dart diff --git a/lib/modules/sonarr/modules/series/widgets/search_bar.dart b/lib/modules/sonarr/routes/series/widgets/search_bar.dart similarity index 100% rename from lib/modules/sonarr/modules/series/widgets/search_bar.dart rename to lib/modules/sonarr/routes/series/widgets/search_bar.dart diff --git a/lib/modules/sonarr/modules/series/widgets/search_bar_filter_button.dart b/lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart similarity index 100% rename from lib/modules/sonarr/modules/series/widgets/search_bar_filter_button.dart rename to lib/modules/sonarr/routes/series/widgets/search_bar_filter_button.dart diff --git a/lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart b/lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart similarity index 100% rename from lib/modules/sonarr/modules/series/widgets/search_bar_sort_button.dart rename to lib/modules/sonarr/routes/series/widgets/search_bar_sort_button.dart diff --git a/lib/modules/sonarr/modules/series/widgets/series_tile.dart b/lib/modules/sonarr/routes/series/widgets/series_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/series/widgets/series_tile.dart rename to lib/modules/sonarr/routes/series/widgets/series_tile.dart diff --git a/lib/modules/sonarr/modules/series_add.dart b/lib/modules/sonarr/routes/series_add.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add.dart rename to lib/modules/sonarr/routes/series_add.dart diff --git a/lib/modules/sonarr/modules/series_add/route.dart b/lib/modules/sonarr/routes/series_add/route.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add/route.dart rename to lib/modules/sonarr/routes/series_add/route.dart diff --git a/lib/modules/sonarr/modules/series_add/widgets.dart b/lib/modules/sonarr/routes/series_add/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add/widgets.dart rename to lib/modules/sonarr/routes/series_add/widgets.dart diff --git a/lib/modules/sonarr/modules/series_add/widgets/appbar.dart b/lib/modules/sonarr/routes/series_add/widgets/appbar.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add/widgets/appbar.dart rename to lib/modules/sonarr/routes/series_add/widgets/appbar.dart diff --git a/lib/modules/sonarr/modules/series_add/widgets/search_results.dart b/lib/modules/sonarr/routes/series_add/widgets/search_results.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add/widgets/search_results.dart rename to lib/modules/sonarr/routes/series_add/widgets/search_results.dart diff --git a/lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart b/lib/modules/sonarr/routes/series_add/widgets/search_results_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add/widgets/search_results_tile.dart rename to lib/modules/sonarr/routes/series_add/widgets/search_results_tile.dart diff --git a/lib/modules/sonarr/modules/series_add_details.dart b/lib/modules/sonarr/routes/series_add_details.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details.dart rename to lib/modules/sonarr/routes/series_add_details.dart diff --git a/lib/modules/sonarr/modules/series_add_details/route.dart b/lib/modules/sonarr/routes/series_add_details/route.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/route.dart rename to lib/modules/sonarr/routes/series_add_details/route.dart diff --git a/lib/modules/sonarr/modules/series_add_details/state.dart b/lib/modules/sonarr/routes/series_add_details/state.dart similarity index 94% rename from lib/modules/sonarr/modules/series_add_details/state.dart rename to lib/modules/sonarr/routes/series_add_details/state.dart index ec788075..3f461b3c 100644 --- a/lib/modules/sonarr/modules/series_add_details/state.dart +++ b/lib/modules/sonarr/routes/series_add_details/state.dart @@ -11,7 +11,7 @@ class SonarrSeriesAddDetailsState extends ChangeNotifier { @required List tags, }) { _series = series; - _tags = []; + _tags = (tags ?? []).where((tag) => ((SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS.data as List) ?? []).contains(tag.id)).toList(); _monitored = SonarrDatabaseValue.ADD_SERIES_DEFAULT_MONITORED.data ?? true; _useSeasonFolders = SonarrDatabaseValue.ADD_SERIES_DEFAULT_USE_SEASON_FOLDERS.data ?? true; _seriesType = SonarrSeriesType.values.firstWhere( @@ -102,6 +102,7 @@ class SonarrSeriesAddDetailsState extends ChangeNotifier { set tags(List tags) { assert(tags != null); _tags = tags; + SonarrDatabaseValue.ADD_SERIES_DEFAULT_TAGS.put((tags ?? []).map((tag) => tag.id).toList()); notifyListeners(); } } diff --git a/lib/modules/sonarr/modules/series_add_details/widgets.dart b/lib/modules/sonarr/routes/series_add_details/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets.dart rename to lib/modules/sonarr/routes/series_add_details/widgets.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart b/lib/modules/sonarr/routes/series_add_details/widgets/appbar_link_action.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/appbar_link_action.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/appbar_link_action.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart b/lib/modules/sonarr/routes/series_add_details/widgets/button_add_series.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/button_add_series.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/button_add_series.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_language_profile.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_language_profile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_language_profile.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_language_profile.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_monitor_status.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_monitor_status.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_monitor_status.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_monitor_status.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_monitored.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_monitored.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_monitored.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_monitored.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_quality_profile.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_quality_profile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_quality_profile.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_quality_profile.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_root_folder.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_root_folder.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_root_folder.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_root_folder.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_series_type.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_series_type.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_series_type.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_tags.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_tags.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_tags.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_tags.dart diff --git a/lib/modules/sonarr/modules/series_add_details/widgets/tile_use_season_folders.dart b/lib/modules/sonarr/routes/series_add_details/widgets/tile_use_season_folders.dart similarity index 100% rename from lib/modules/sonarr/modules/series_add_details/widgets/tile_use_season_folders.dart rename to lib/modules/sonarr/routes/series_add_details/widgets/tile_use_season_folders.dart diff --git a/lib/modules/sonarr/modules/series_details.dart b/lib/modules/sonarr/routes/series_details.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details.dart rename to lib/modules/sonarr/routes/series_details.dart diff --git a/lib/modules/sonarr/modules/series_details/route.dart b/lib/modules/sonarr/routes/series_details/route.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/route.dart rename to lib/modules/sonarr/routes/series_details/route.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets.dart b/lib/modules/sonarr/routes/series_details/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets.dart rename to lib/modules/sonarr/routes/series_details/widgets.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart b/lib/modules/sonarr/routes/series_details/widgets/appbar_edit_action.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/appbar_edit_action.dart rename to lib/modules/sonarr/routes/series_details/widgets/appbar_edit_action.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/appbar_settings_action.dart b/lib/modules/sonarr/routes/series_details/widgets/appbar_settings_action.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/appbar_settings_action.dart rename to lib/modules/sonarr/routes/series_details/widgets/appbar_settings_action.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/navigation_bar.dart b/lib/modules/sonarr/routes/series_details/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/navigation_bar.dart rename to lib/modules/sonarr/routes/series_details/widgets/navigation_bar.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/overview.dart b/lib/modules/sonarr/routes/series_details/widgets/overview.dart similarity index 66% rename from lib/modules/sonarr/modules/series_details/widgets/overview.dart rename to lib/modules/sonarr/routes/series_details/widgets/overview.dart index ebb64026..154729bd 100644 --- a/lib/modules/sonarr/modules/series_details/widgets/overview.dart +++ b/lib/modules/sonarr/routes/series_details/widgets/overview.dart @@ -24,8 +24,8 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { Widget build(BuildContext context) => LSListView( children: [ _description(context), + _links(), _information(context), - _links, ], ); @@ -98,56 +98,66 @@ class SonarrSeriesDetailsOverview extends StatelessWidget { ), ); - Widget get _links => LSContainerRow( - children: [ - if(series.imdbId != '') Expanded( - child: LSCard( - child: InkWell( - child: Padding( - child: Image.asset( - 'assets/images/services/imdb.png', - height: 21.0, + Widget _links() { + return LSContainerRow( + children: [ + if(series.imdbId != '') Expanded( + child: LSCard( + child: InkWell( + child: Padding( + child: Image.asset('assets/images/services/imdb.png'), + padding: EdgeInsets.all(12.0), ), - padding: EdgeInsets.all(18.0), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + onTap: () async => await series?.imdbId?.lunaOpenIMDB(), ), - borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.imdbId?.lunaOpenIMDB(), + height: 50.0, + reducedMargin: true, ), - reducedMargin: true, ), - ), - if(series.tvdbId != 0) Expanded( - child: LSCard( - child: InkWell( - child: Padding( - child: Image.asset( - 'assets/images/services/thetvdb.png', - height: 23.0, + if(series.tvdbId != 0) Expanded( + child: LSCard( + child: InkWell( + child: Padding( + child: Image.asset('assets/images/services/trakt.png'), + padding: EdgeInsets.all(12.0), ), - padding: EdgeInsets.all(17.0), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + onTap: () async => await series?.tvdbId?.toString()?.lunaOpenTraktSeries(), ), - borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.tvdbId?.toString()?.lunaOpenTheTVDB(), + height: 50.0, + reducedMargin: true, ), - reducedMargin: true, ), - ), - if(series.tvMazeId != 0) Expanded( - child: LSCard( - child: InkWell( - child: Padding( - child: Image.asset( - 'assets/images/services/tvmaze.png', - height: 21.0, + if(series.tvdbId != 0) Expanded( + child: LSCard( + child: InkWell( + child: Padding( + child: Image.asset('assets/images/services/thetvdb.png'), + padding: EdgeInsets.all(12.0), ), - padding: EdgeInsets.all(18.0), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + onTap: () async => await series?.tvdbId?.toString()?.lunaOpenTheTVDB(), ), - borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), - onTap: () async => await series?.tvMazeId?.toString()?.lunaOpenTVMaze(), + height: 50.0, + reducedMargin: true, ), - reducedMargin: true, ), - ), - ], - ); + if(series.tvMazeId != 0) Expanded( + child: LSCard( + child: InkWell( + child: Padding( + child: Image.asset('assets/images/services/tvmaze.png'), + padding: EdgeInsets.all(12.0), + ), + borderRadius: BorderRadius.circular(Constants.UI_BORDER_RADIUS), + onTap: () async => await series?.tvMazeId?.toString()?.lunaOpenTVMaze(), + ), + height: 50.0, + reducedMargin: true, + ), + ), + ], + ); + } } diff --git a/lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart b/lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/season_all_tile.dart rename to lib/modules/sonarr/routes/series_details/widgets/season_all_tile.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/season_list.dart b/lib/modules/sonarr/routes/series_details/widgets/season_list.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/season_list.dart rename to lib/modules/sonarr/routes/series_details/widgets/season_list.dart diff --git a/lib/modules/sonarr/modules/series_details/widgets/season_tile.dart b/lib/modules/sonarr/routes/series_details/widgets/season_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_details/widgets/season_tile.dart rename to lib/modules/sonarr/routes/series_details/widgets/season_tile.dart diff --git a/lib/modules/sonarr/modules/series_edit.dart b/lib/modules/sonarr/routes/series_edit.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit.dart rename to lib/modules/sonarr/routes/series_edit.dart diff --git a/lib/modules/sonarr/modules/series_edit/route.dart b/lib/modules/sonarr/routes/series_edit/route.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/route.dart rename to lib/modules/sonarr/routes/series_edit/route.dart diff --git a/lib/modules/sonarr/modules/series_edit/state.dart b/lib/modules/sonarr/routes/series_edit/state.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/state.dart rename to lib/modules/sonarr/routes/series_edit/state.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets.dart b/lib/modules/sonarr/routes/series_edit/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets.dart rename to lib/modules/sonarr/routes/series_edit/widgets.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart b/lib/modules/sonarr/routes/series_edit/widgets/button_update_series.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/button_update_series.dart rename to lib/modules/sonarr/routes/series_edit/widgets/button_update_series.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_language_profile.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_language_profile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_language_profile.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_language_profile.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_monitored.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_monitored.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_monitored.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_monitored.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_quality_profile.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_quality_profile.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_quality_profile.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_quality_profile.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_series_path.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_series_path.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_series_path.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_series_type.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_series_type.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_series_type.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_tags.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_tags.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_tags.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_tags.dart diff --git a/lib/modules/sonarr/modules/series_edit/widgets/tile_use_season_folders.dart b/lib/modules/sonarr/routes/series_edit/widgets/tile_use_season_folders.dart similarity index 100% rename from lib/modules/sonarr/modules/series_edit/widgets/tile_use_season_folders.dart rename to lib/modules/sonarr/routes/series_edit/widgets/tile_use_season_folders.dart diff --git a/lib/modules/sonarr/modules/sonarr.dart b/lib/modules/sonarr/routes/sonarr.dart similarity index 100% rename from lib/modules/sonarr/modules/sonarr.dart rename to lib/modules/sonarr/routes/sonarr.dart diff --git a/lib/modules/sonarr/modules/sonarr/route.dart b/lib/modules/sonarr/routes/sonarr/route.dart similarity index 97% rename from lib/modules/sonarr/modules/sonarr/route.dart rename to lib/modules/sonarr/routes/sonarr/route.dart index b18d22f8..901bb06d 100644 --- a/lib/modules/sonarr/modules/sonarr/route.dart +++ b/lib/modules/sonarr/routes/sonarr/route.dart @@ -47,7 +47,7 @@ class _State extends State<_SonarrHomeRoute> { return false; } - Widget get _drawer => LSDrawer(page: 'sonarr'); + Widget get _drawer => LunaDrawer(page: LunaModule.SONARR.key); Widget get _bottomNavigationBar => SonarrNavigationBar(pageController: _pageController); diff --git a/lib/modules/sonarr/modules/sonarr/widgets.dart b/lib/modules/sonarr/routes/sonarr/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/sonarr/widgets.dart rename to lib/modules/sonarr/routes/sonarr/widgets.dart diff --git a/lib/modules/sonarr/modules/sonarr/widgets/appbar_add_series_action.dart b/lib/modules/sonarr/routes/sonarr/widgets/appbar_add_series_action.dart similarity index 100% rename from lib/modules/sonarr/modules/sonarr/widgets/appbar_add_series_action.dart rename to lib/modules/sonarr/routes/sonarr/widgets/appbar_add_series_action.dart diff --git a/lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart b/lib/modules/sonarr/routes/sonarr/widgets/appbar_global_settings_action.dart similarity index 100% rename from lib/modules/sonarr/modules/sonarr/widgets/appbar_global_settings_action.dart rename to lib/modules/sonarr/routes/sonarr/widgets/appbar_global_settings_action.dart diff --git a/lib/modules/sonarr/modules/sonarr/widgets/navigation_bar.dart b/lib/modules/sonarr/routes/sonarr/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/sonarr/modules/sonarr/widgets/navigation_bar.dart rename to lib/modules/sonarr/routes/sonarr/widgets/navigation_bar.dart diff --git a/lib/modules/sonarr/modules/tags.dart b/lib/modules/sonarr/routes/tags.dart similarity index 100% rename from lib/modules/sonarr/modules/tags.dart rename to lib/modules/sonarr/routes/tags.dart diff --git a/lib/modules/sonarr/modules/tags/route.dart b/lib/modules/sonarr/routes/tags/route.dart similarity index 100% rename from lib/modules/sonarr/modules/tags/route.dart rename to lib/modules/sonarr/routes/tags/route.dart diff --git a/lib/modules/sonarr/modules/tags/widgets.dart b/lib/modules/sonarr/routes/tags/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/tags/widgets.dart rename to lib/modules/sonarr/routes/tags/widgets.dart diff --git a/lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart b/lib/modules/sonarr/routes/tags/widgets/appbar_action_add_tag.dart similarity index 100% rename from lib/modules/sonarr/modules/tags/widgets/appbar_action_add_tag.dart rename to lib/modules/sonarr/routes/tags/widgets/appbar_action_add_tag.dart diff --git a/lib/modules/sonarr/modules/tags/widgets/tag_tile.dart b/lib/modules/sonarr/routes/tags/widgets/tag_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/tags/widgets/tag_tile.dart rename to lib/modules/sonarr/routes/tags/widgets/tag_tile.dart diff --git a/lib/modules/sonarr/modules/upcoming.dart b/lib/modules/sonarr/routes/upcoming.dart similarity index 100% rename from lib/modules/sonarr/modules/upcoming.dart rename to lib/modules/sonarr/routes/upcoming.dart diff --git a/lib/modules/sonarr/modules/upcoming/route.dart b/lib/modules/sonarr/routes/upcoming/route.dart similarity index 100% rename from lib/modules/sonarr/modules/upcoming/route.dart rename to lib/modules/sonarr/routes/upcoming/route.dart diff --git a/lib/modules/sonarr/modules/upcoming/widgets.dart b/lib/modules/sonarr/routes/upcoming/widgets.dart similarity index 100% rename from lib/modules/sonarr/modules/upcoming/widgets.dart rename to lib/modules/sonarr/routes/upcoming/widgets.dart diff --git a/lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart b/lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart similarity index 100% rename from lib/modules/sonarr/modules/upcoming/widgets/upcoming_tile.dart rename to lib/modules/sonarr/routes/upcoming/widgets/upcoming_tile.dart diff --git a/lib/modules/tautulli.dart b/lib/modules/tautulli.dart index b960d394..9cdf9683 100644 --- a/lib/modules/tautulli.dart +++ b/lib/modules/tautulli.dart @@ -1,3 +1,3 @@ export 'package:tautulli/tautulli.dart'; export 'tautulli/core.dart'; -export 'tautulli/modules.dart'; +export 'tautulli/routes.dart'; diff --git a/lib/modules/tautulli/modules.dart b/lib/modules/tautulli/modules.dart deleted file mode 100644 index 3141d492..00000000 --- a/lib/modules/tautulli/modules.dart +++ /dev/null @@ -1,27 +0,0 @@ -export 'modules/activity.dart'; -export 'modules/activity_details.dart'; -export 'modules/check_for_updates.dart'; -export 'modules/graphs.dart'; -export 'modules/graphs_play_by_period.dart'; -export 'modules/graphs_stream_information.dart'; -export 'modules/history.dart'; -export 'modules/history_details.dart'; -export 'modules/ipaddress_details.dart'; -export 'modules/libraries.dart'; -export 'modules/libraries_details.dart'; -export 'modules/logs.dart'; -export 'modules/logs_logins.dart'; -export 'modules/logs_newsletters.dart'; -export 'modules/logs_notifications.dart'; -export 'modules/logs_plex_media_scanner.dart'; -export 'modules/logs_tautulli.dart'; -export 'modules/logs_plex_media_server.dart'; -export 'modules/media_details.dart'; -export 'modules/more.dart'; -export 'modules/recently_added.dart'; -export 'modules/search.dart'; -export 'modules/statistics.dart'; -export 'modules/synced_items.dart'; -export 'modules/tautulli.dart'; -export 'modules/users.dart'; -export 'modules/users_details.dart'; diff --git a/lib/modules/tautulli/routes.dart b/lib/modules/tautulli/routes.dart new file mode 100644 index 00000000..ffb9a679 --- /dev/null +++ b/lib/modules/tautulli/routes.dart @@ -0,0 +1,27 @@ +export 'routes/activity.dart'; +export 'routes/activity_details.dart'; +export 'routes/check_for_updates.dart'; +export 'routes/graphs.dart'; +export 'routes/graphs_play_by_period.dart'; +export 'routes/graphs_stream_information.dart'; +export 'routes/history.dart'; +export 'routes/history_details.dart'; +export 'routes/ipaddress_details.dart'; +export 'routes/libraries.dart'; +export 'routes/libraries_details.dart'; +export 'routes/logs.dart'; +export 'routes/logs_logins.dart'; +export 'routes/logs_newsletters.dart'; +export 'routes/logs_notifications.dart'; +export 'routes/logs_plex_media_scanner.dart'; +export 'routes/logs_tautulli.dart'; +export 'routes/logs_plex_media_server.dart'; +export 'routes/media_details.dart'; +export 'routes/more.dart'; +export 'routes/recently_added.dart'; +export 'routes/search.dart'; +export 'routes/statistics.dart'; +export 'routes/synced_items.dart'; +export 'routes/tautulli.dart'; +export 'routes/users.dart'; +export 'routes/users_details.dart'; diff --git a/lib/modules/tautulli/modules/activity.dart b/lib/modules/tautulli/routes/activity.dart similarity index 100% rename from lib/modules/tautulli/modules/activity.dart rename to lib/modules/tautulli/routes/activity.dart diff --git a/lib/modules/tautulli/modules/activity/route.dart b/lib/modules/tautulli/routes/activity/route.dart similarity index 100% rename from lib/modules/tautulli/modules/activity/route.dart rename to lib/modules/tautulli/routes/activity/route.dart diff --git a/lib/modules/tautulli/modules/activity/widgets.dart b/lib/modules/tautulli/routes/activity/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/activity/widgets.dart rename to lib/modules/tautulli/routes/activity/widgets.dart diff --git a/lib/modules/tautulli/modules/activity/widgets/activity_status.dart b/lib/modules/tautulli/routes/activity/widgets/activity_status.dart similarity index 100% rename from lib/modules/tautulli/modules/activity/widgets/activity_status.dart rename to lib/modules/tautulli/routes/activity/widgets/activity_status.dart diff --git a/lib/modules/tautulli/modules/activity/widgets/activity_tile.dart b/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/activity/widgets/activity_tile.dart rename to lib/modules/tautulli/routes/activity/widgets/activity_tile.dart diff --git a/lib/modules/tautulli/modules/activity_details.dart b/lib/modules/tautulli/routes/activity_details.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details.dart rename to lib/modules/tautulli/routes/activity_details.dart diff --git a/lib/modules/tautulli/modules/activity_details/route.dart b/lib/modules/tautulli/routes/activity_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/route.dart rename to lib/modules/tautulli/routes/activity_details/route.dart diff --git a/lib/modules/tautulli/modules/activity_details/widgets.dart b/lib/modules/tautulli/routes/activity_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/widgets.dart rename to lib/modules/tautulli/routes/activity_details/widgets.dart diff --git a/lib/modules/tautulli/modules/activity_details/widgets/information.dart b/lib/modules/tautulli/routes/activity_details/widgets/information.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/widgets/information.dart rename to lib/modules/tautulli/routes/activity_details/widgets/information.dart diff --git a/lib/modules/tautulli/modules/activity_details/widgets/metadata.dart b/lib/modules/tautulli/routes/activity_details/widgets/metadata.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/widgets/metadata.dart rename to lib/modules/tautulli/routes/activity_details/widgets/metadata.dart diff --git a/lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart b/lib/modules/tautulli/routes/activity_details/widgets/terminate_session.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/widgets/terminate_session.dart rename to lib/modules/tautulli/routes/activity_details/widgets/terminate_session.dart diff --git a/lib/modules/tautulli/modules/activity_details/widgets/user.dart b/lib/modules/tautulli/routes/activity_details/widgets/user.dart similarity index 100% rename from lib/modules/tautulli/modules/activity_details/widgets/user.dart rename to lib/modules/tautulli/routes/activity_details/widgets/user.dart diff --git a/lib/modules/tautulli/modules/check_for_updates.dart b/lib/modules/tautulli/routes/check_for_updates.dart similarity index 100% rename from lib/modules/tautulli/modules/check_for_updates.dart rename to lib/modules/tautulli/routes/check_for_updates.dart diff --git a/lib/modules/tautulli/modules/check_for_updates/route.dart b/lib/modules/tautulli/routes/check_for_updates/route.dart similarity index 100% rename from lib/modules/tautulli/modules/check_for_updates/route.dart rename to lib/modules/tautulli/routes/check_for_updates/route.dart diff --git a/lib/modules/tautulli/modules/check_for_updates/widgets.dart b/lib/modules/tautulli/routes/check_for_updates/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/check_for_updates/widgets.dart rename to lib/modules/tautulli/routes/check_for_updates/widgets.dart diff --git a/lib/modules/tautulli/modules/check_for_updates/widgets/pms_tile.dart b/lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/check_for_updates/widgets/pms_tile.dart rename to lib/modules/tautulli/routes/check_for_updates/widgets/pms_tile.dart diff --git a/lib/modules/tautulli/modules/check_for_updates/widgets/tautulli_tile.dart b/lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/check_for_updates/widgets/tautulli_tile.dart rename to lib/modules/tautulli/routes/check_for_updates/widgets/tautulli_tile.dart diff --git a/lib/modules/tautulli/modules/graphs.dart b/lib/modules/tautulli/routes/graphs.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs.dart rename to lib/modules/tautulli/routes/graphs.dart diff --git a/lib/modules/tautulli/modules/graphs/route.dart b/lib/modules/tautulli/routes/graphs/route.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs/route.dart rename to lib/modules/tautulli/routes/graphs/route.dart diff --git a/lib/modules/tautulli/modules/graphs/widgets.dart b/lib/modules/tautulli/routes/graphs/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs/widgets.dart rename to lib/modules/tautulli/routes/graphs/widgets.dart diff --git a/lib/modules/tautulli/modules/graphs/widgets/navigation_bar.dart b/lib/modules/tautulli/routes/graphs/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs/widgets/navigation_bar.dart rename to lib/modules/tautulli/routes/graphs/widgets/navigation_bar.dart diff --git a/lib/modules/tautulli/modules/graphs/widgets/type_button.dart b/lib/modules/tautulli/routes/graphs/widgets/type_button.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs/widgets/type_button.dart rename to lib/modules/tautulli/routes/graphs/widgets/type_button.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period.dart b/lib/modules/tautulli/routes/graphs_play_by_period.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period.dart rename to lib/modules/tautulli/routes/graphs_play_by_period.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/route.dart b/lib/modules/tautulli/routes/graphs_play_by_period/route.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/route.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/route.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets/daily_play_count_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets/daily_play_count_graph.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets/daily_play_count_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_day_of_week_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_top_platforms_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets/play_count_by_top_users_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart b/lib/modules/tautulli/routes/graphs_play_by_period/widgets/plays_by_month_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_play_by_period/widgets/plays_by_month_graph.dart rename to lib/modules/tautulli/routes/graphs_play_by_period/widgets/plays_by_month_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information.dart b/lib/modules/tautulli/routes/graphs_stream_information.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information.dart rename to lib/modules/tautulli/routes/graphs_stream_information.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/route.dart b/lib/modules/tautulli/routes/graphs_stream_information/route.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/route.dart rename to lib/modules/tautulli/routes/graphs_stream_information/route.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets/daily_stream_type_breakdown_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_platform_stream_type_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_source_resolution_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_stream_resolution_graph.dart diff --git a/lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart b/lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart similarity index 100% rename from lib/modules/tautulli/modules/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart rename to lib/modules/tautulli/routes/graphs_stream_information/widgets/play_count_by_user_stream_type_graph.dart diff --git a/lib/modules/tautulli/modules/history.dart b/lib/modules/tautulli/routes/history.dart similarity index 100% rename from lib/modules/tautulli/modules/history.dart rename to lib/modules/tautulli/routes/history.dart diff --git a/lib/modules/tautulli/modules/history/route.dart b/lib/modules/tautulli/routes/history/route.dart similarity index 100% rename from lib/modules/tautulli/modules/history/route.dart rename to lib/modules/tautulli/routes/history/route.dart diff --git a/lib/modules/tautulli/modules/history/widgets.dart b/lib/modules/tautulli/routes/history/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/history/widgets.dart rename to lib/modules/tautulli/routes/history/widgets.dart diff --git a/lib/modules/tautulli/modules/history/widgets/history_tile.dart b/lib/modules/tautulli/routes/history/widgets/history_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/history/widgets/history_tile.dart rename to lib/modules/tautulli/routes/history/widgets/history_tile.dart diff --git a/lib/modules/tautulli/modules/history_details.dart b/lib/modules/tautulli/routes/history_details.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details.dart rename to lib/modules/tautulli/routes/history_details.dart diff --git a/lib/modules/tautulli/modules/history_details/route.dart b/lib/modules/tautulli/routes/history_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details/route.dart rename to lib/modules/tautulli/routes/history_details/route.dart diff --git a/lib/modules/tautulli/modules/history_details/widgets.dart b/lib/modules/tautulli/routes/history_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details/widgets.dart rename to lib/modules/tautulli/routes/history_details/widgets.dart diff --git a/lib/modules/tautulli/modules/history_details/widgets/information.dart b/lib/modules/tautulli/routes/history_details/widgets/information.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details/widgets/information.dart rename to lib/modules/tautulli/routes/history_details/widgets/information.dart diff --git a/lib/modules/tautulli/modules/history_details/widgets/metadata.dart b/lib/modules/tautulli/routes/history_details/widgets/metadata.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details/widgets/metadata.dart rename to lib/modules/tautulli/routes/history_details/widgets/metadata.dart diff --git a/lib/modules/tautulli/modules/history_details/widgets/user.dart b/lib/modules/tautulli/routes/history_details/widgets/user.dart similarity index 100% rename from lib/modules/tautulli/modules/history_details/widgets/user.dart rename to lib/modules/tautulli/routes/history_details/widgets/user.dart diff --git a/lib/modules/tautulli/modules/ipaddress_details.dart b/lib/modules/tautulli/routes/ipaddress_details.dart similarity index 100% rename from lib/modules/tautulli/modules/ipaddress_details.dart rename to lib/modules/tautulli/routes/ipaddress_details.dart diff --git a/lib/modules/tautulli/modules/ipaddress_details/route.dart b/lib/modules/tautulli/routes/ipaddress_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/ipaddress_details/route.dart rename to lib/modules/tautulli/routes/ipaddress_details/route.dart diff --git a/lib/modules/tautulli/modules/ipaddress_details/widgets.dart b/lib/modules/tautulli/routes/ipaddress_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/ipaddress_details/widgets.dart rename to lib/modules/tautulli/routes/ipaddress_details/widgets.dart diff --git a/lib/modules/tautulli/modules/ipaddress_details/widgets/geolocation_tile.dart b/lib/modules/tautulli/routes/ipaddress_details/widgets/geolocation_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/ipaddress_details/widgets/geolocation_tile.dart rename to lib/modules/tautulli/routes/ipaddress_details/widgets/geolocation_tile.dart diff --git a/lib/modules/tautulli/modules/ipaddress_details/widgets/whois_tile.dart b/lib/modules/tautulli/routes/ipaddress_details/widgets/whois_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/ipaddress_details/widgets/whois_tile.dart rename to lib/modules/tautulli/routes/ipaddress_details/widgets/whois_tile.dart diff --git a/lib/modules/tautulli/modules/libraries.dart b/lib/modules/tautulli/routes/libraries.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries.dart rename to lib/modules/tautulli/routes/libraries.dart diff --git a/lib/modules/tautulli/modules/libraries/route.dart b/lib/modules/tautulli/routes/libraries/route.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries/route.dart rename to lib/modules/tautulli/routes/libraries/route.dart diff --git a/lib/modules/tautulli/modules/libraries/widgets.dart b/lib/modules/tautulli/routes/libraries/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries/widgets.dart rename to lib/modules/tautulli/routes/libraries/widgets.dart diff --git a/lib/modules/tautulli/modules/libraries/widgets/library_tile.dart b/lib/modules/tautulli/routes/libraries/widgets/library_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries/widgets/library_tile.dart rename to lib/modules/tautulli/routes/libraries/widgets/library_tile.dart diff --git a/lib/modules/tautulli/modules/libraries_details.dart b/lib/modules/tautulli/routes/libraries_details.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details.dart rename to lib/modules/tautulli/routes/libraries_details.dart diff --git a/lib/modules/tautulli/modules/libraries_details/route.dart b/lib/modules/tautulli/routes/libraries_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/route.dart rename to lib/modules/tautulli/routes/libraries_details/route.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets.dart b/lib/modules/tautulli/routes/libraries_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets.dart rename to lib/modules/tautulli/routes/libraries_details/widgets.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/information.dart b/lib/modules/tautulli/routes/libraries_details/widgets/information.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/information.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/information.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart b/lib/modules/tautulli/routes/libraries_details/widgets/information_details.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/information_details.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/information_details.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart b/lib/modules/tautulli/routes/libraries_details/widgets/information_global_stats.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/information_global_stats.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/information_global_stats.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/navigation_bar.dart b/lib/modules/tautulli/routes/libraries_details/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/navigation_bar.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/navigation_bar.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/user_stats.dart b/lib/modules/tautulli/routes/libraries_details/widgets/user_stats.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/user_stats.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/user_stats.dart diff --git a/lib/modules/tautulli/modules/libraries_details/widgets/user_stats_tile.dart b/lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/libraries_details/widgets/user_stats_tile.dart rename to lib/modules/tautulli/routes/libraries_details/widgets/user_stats_tile.dart diff --git a/lib/modules/tautulli/modules/logs.dart b/lib/modules/tautulli/routes/logs.dart similarity index 100% rename from lib/modules/tautulli/modules/logs.dart rename to lib/modules/tautulli/routes/logs.dart diff --git a/lib/modules/tautulli/modules/logs/route.dart b/lib/modules/tautulli/routes/logs/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/route.dart rename to lib/modules/tautulli/routes/logs/route.dart diff --git a/lib/modules/tautulli/modules/logs/widgets.dart b/lib/modules/tautulli/routes/logs/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets.dart rename to lib/modules/tautulli/routes/logs/widgets.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/logins_tile.dart b/lib/modules/tautulli/routes/logs/widgets/logins_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/logins_tile.dart rename to lib/modules/tautulli/routes/logs/widgets/logins_tile.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/newsletters_tile.dart b/lib/modules/tautulli/routes/logs/widgets/newsletters_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/newsletters_tile.dart rename to lib/modules/tautulli/routes/logs/widgets/newsletters_tile.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/notifications_tile.dart b/lib/modules/tautulli/routes/logs/widgets/notifications_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/notifications_tile.dart rename to lib/modules/tautulli/routes/logs/widgets/notifications_tile.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/plex_media_scanner.dart b/lib/modules/tautulli/routes/logs/widgets/plex_media_scanner.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/plex_media_scanner.dart rename to lib/modules/tautulli/routes/logs/widgets/plex_media_scanner.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/plex_media_server_tile.dart b/lib/modules/tautulli/routes/logs/widgets/plex_media_server_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/plex_media_server_tile.dart rename to lib/modules/tautulli/routes/logs/widgets/plex_media_server_tile.dart diff --git a/lib/modules/tautulli/modules/logs/widgets/tautulli_tile.dart b/lib/modules/tautulli/routes/logs/widgets/tautulli_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs/widgets/tautulli_tile.dart rename to lib/modules/tautulli/routes/logs/widgets/tautulli_tile.dart diff --git a/lib/modules/tautulli/modules/logs_logins.dart b/lib/modules/tautulli/routes/logs_logins.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_logins.dart rename to lib/modules/tautulli/routes/logs_logins.dart diff --git a/lib/modules/tautulli/modules/logs_logins/route.dart b/lib/modules/tautulli/routes/logs_logins/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_logins/route.dart rename to lib/modules/tautulli/routes/logs_logins/route.dart diff --git a/lib/modules/tautulli/modules/logs_logins/widgets.dart b/lib/modules/tautulli/routes/logs_logins/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_logins/widgets.dart rename to lib/modules/tautulli/routes/logs_logins/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_logins/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_logins/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/logs_newsletters.dart b/lib/modules/tautulli/routes/logs_newsletters.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_newsletters.dart rename to lib/modules/tautulli/routes/logs_newsletters.dart diff --git a/lib/modules/tautulli/modules/logs_newsletters/route.dart b/lib/modules/tautulli/routes/logs_newsletters/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_newsletters/route.dart rename to lib/modules/tautulli/routes/logs_newsletters/route.dart diff --git a/lib/modules/tautulli/modules/logs_newsletters/widgets.dart b/lib/modules/tautulli/routes/logs_newsletters/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_newsletters/widgets.dart rename to lib/modules/tautulli/routes/logs_newsletters/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_newsletters/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_newsletters/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/logs_notifications.dart b/lib/modules/tautulli/routes/logs_notifications.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_notifications.dart rename to lib/modules/tautulli/routes/logs_notifications.dart diff --git a/lib/modules/tautulli/modules/logs_notifications/route.dart b/lib/modules/tautulli/routes/logs_notifications/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_notifications/route.dart rename to lib/modules/tautulli/routes/logs_notifications/route.dart diff --git a/lib/modules/tautulli/modules/logs_notifications/widgets.dart b/lib/modules/tautulli/routes/logs_notifications/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_notifications/widgets.dart rename to lib/modules/tautulli/routes/logs_notifications/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_notifications/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_notifications/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_scanner.dart b/lib/modules/tautulli/routes/logs_plex_media_scanner.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_scanner.dart rename to lib/modules/tautulli/routes/logs_plex_media_scanner.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart b/lib/modules/tautulli/routes/logs_plex_media_scanner/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_scanner/route.dart rename to lib/modules/tautulli/routes/logs_plex_media_scanner/route.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_scanner/widgets.dart b/lib/modules/tautulli/routes/logs_plex_media_scanner/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_scanner/widgets.dart rename to lib/modules/tautulli/routes/logs_plex_media_scanner/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_scanner/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_plex_media_scanner/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_scanner/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_plex_media_scanner/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_server.dart b/lib/modules/tautulli/routes/logs_plex_media_server.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_server.dart rename to lib/modules/tautulli/routes/logs_plex_media_server.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_server/route.dart b/lib/modules/tautulli/routes/logs_plex_media_server/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_server/route.dart rename to lib/modules/tautulli/routes/logs_plex_media_server/route.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_server/widgets.dart b/lib/modules/tautulli/routes/logs_plex_media_server/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_server/widgets.dart rename to lib/modules/tautulli/routes/logs_plex_media_server/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_plex_media_server/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_plex_media_server/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_plex_media_server/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_plex_media_server/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/logs_tautulli.dart b/lib/modules/tautulli/routes/logs_tautulli.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_tautulli.dart rename to lib/modules/tautulli/routes/logs_tautulli.dart diff --git a/lib/modules/tautulli/modules/logs_tautulli/route.dart b/lib/modules/tautulli/routes/logs_tautulli/route.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_tautulli/route.dart rename to lib/modules/tautulli/routes/logs_tautulli/route.dart diff --git a/lib/modules/tautulli/modules/logs_tautulli/widgets.dart b/lib/modules/tautulli/routes/logs_tautulli/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_tautulli/widgets.dart rename to lib/modules/tautulli/routes/logs_tautulli/widgets.dart diff --git a/lib/modules/tautulli/modules/logs_tautulli/widgets/log_tile.dart b/lib/modules/tautulli/routes/logs_tautulli/widgets/log_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/logs_tautulli/widgets/log_tile.dart rename to lib/modules/tautulli/routes/logs_tautulli/widgets/log_tile.dart diff --git a/lib/modules/tautulli/modules/media_details.dart b/lib/modules/tautulli/routes/media_details.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details.dart rename to lib/modules/tautulli/routes/media_details.dart diff --git a/lib/modules/tautulli/modules/media_details/route.dart b/lib/modules/tautulli/routes/media_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/route.dart rename to lib/modules/tautulli/routes/media_details/route.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets.dart b/lib/modules/tautulli/routes/media_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets.dart rename to lib/modules/tautulli/routes/media_details/widgets.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/history.dart b/lib/modules/tautulli/routes/media_details/widgets/history.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/history.dart rename to lib/modules/tautulli/routes/media_details/widgets/history.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/metadata.dart rename to lib/modules/tautulli/routes/media_details/widgets/metadata.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata_header.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/metadata_header.dart rename to lib/modules/tautulli/routes/media_details/widgets/metadata_header.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata_metadata.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/metadata_metadata.dart rename to lib/modules/tautulli/routes/media_details/widgets/metadata_metadata.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart b/lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/metadata_summary.dart rename to lib/modules/tautulli/routes/media_details/widgets/metadata_summary.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/navigation_bar.dart b/lib/modules/tautulli/routes/media_details/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/navigation_bar.dart rename to lib/modules/tautulli/routes/media_details/widgets/navigation_bar.dart diff --git a/lib/modules/tautulli/modules/media_details/widgets/switcher_buttons.dart b/lib/modules/tautulli/routes/media_details/widgets/switcher_buttons.dart similarity index 100% rename from lib/modules/tautulli/modules/media_details/widgets/switcher_buttons.dart rename to lib/modules/tautulli/routes/media_details/widgets/switcher_buttons.dart diff --git a/lib/modules/tautulli/modules/more.dart b/lib/modules/tautulli/routes/more.dart similarity index 100% rename from lib/modules/tautulli/modules/more.dart rename to lib/modules/tautulli/routes/more.dart diff --git a/lib/modules/tautulli/modules/more/route.dart b/lib/modules/tautulli/routes/more/route.dart similarity index 100% rename from lib/modules/tautulli/modules/more/route.dart rename to lib/modules/tautulli/routes/more/route.dart diff --git a/lib/modules/tautulli/modules/more/widgets.dart b/lib/modules/tautulli/routes/more/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets.dart rename to lib/modules/tautulli/routes/more/widgets.dart diff --git a/lib/modules/tautulli/modules/more/widgets/check_for_updates_tile.dart b/lib/modules/tautulli/routes/more/widgets/check_for_updates_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/check_for_updates_tile.dart rename to lib/modules/tautulli/routes/more/widgets/check_for_updates_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/graphs_tile.dart b/lib/modules/tautulli/routes/more/widgets/graphs_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/graphs_tile.dart rename to lib/modules/tautulli/routes/more/widgets/graphs_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/libraries_tile.dart b/lib/modules/tautulli/routes/more/widgets/libraries_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/libraries_tile.dart rename to lib/modules/tautulli/routes/more/widgets/libraries_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/logs_tile.dart b/lib/modules/tautulli/routes/more/widgets/logs_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/logs_tile.dart rename to lib/modules/tautulli/routes/more/widgets/logs_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/recently_added_tile.dart b/lib/modules/tautulli/routes/more/widgets/recently_added_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/recently_added_tile.dart rename to lib/modules/tautulli/routes/more/widgets/recently_added_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/search_tile.dart b/lib/modules/tautulli/routes/more/widgets/search_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/search_tile.dart rename to lib/modules/tautulli/routes/more/widgets/search_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/statistics_tile.dart b/lib/modules/tautulli/routes/more/widgets/statistics_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/statistics_tile.dart rename to lib/modules/tautulli/routes/more/widgets/statistics_tile.dart diff --git a/lib/modules/tautulli/modules/more/widgets/synced_items_tile.dart b/lib/modules/tautulli/routes/more/widgets/synced_items_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/more/widgets/synced_items_tile.dart rename to lib/modules/tautulli/routes/more/widgets/synced_items_tile.dart diff --git a/lib/modules/tautulli/modules/recently_added.dart b/lib/modules/tautulli/routes/recently_added.dart similarity index 100% rename from lib/modules/tautulli/modules/recently_added.dart rename to lib/modules/tautulli/routes/recently_added.dart diff --git a/lib/modules/tautulli/modules/recently_added/route.dart b/lib/modules/tautulli/routes/recently_added/route.dart similarity index 100% rename from lib/modules/tautulli/modules/recently_added/route.dart rename to lib/modules/tautulli/routes/recently_added/route.dart diff --git a/lib/modules/tautulli/modules/recently_added/widgets.dart b/lib/modules/tautulli/routes/recently_added/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/recently_added/widgets.dart rename to lib/modules/tautulli/routes/recently_added/widgets.dart diff --git a/lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart b/lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/recently_added/widgets/content_tile.dart rename to lib/modules/tautulli/routes/recently_added/widgets/content_tile.dart diff --git a/lib/modules/tautulli/modules/search.dart b/lib/modules/tautulli/routes/search.dart similarity index 100% rename from lib/modules/tautulli/modules/search.dart rename to lib/modules/tautulli/routes/search.dart diff --git a/lib/modules/tautulli/modules/search/route.dart b/lib/modules/tautulli/routes/search/route.dart similarity index 100% rename from lib/modules/tautulli/modules/search/route.dart rename to lib/modules/tautulli/routes/search/route.dart diff --git a/lib/modules/tautulli/modules/search/widgets.dart b/lib/modules/tautulli/routes/search/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/search/widgets.dart rename to lib/modules/tautulli/routes/search/widgets.dart diff --git a/lib/modules/tautulli/modules/search/widgets/search_appbar.dart b/lib/modules/tautulli/routes/search/widgets/search_appbar.dart similarity index 100% rename from lib/modules/tautulli/modules/search/widgets/search_appbar.dart rename to lib/modules/tautulli/routes/search/widgets/search_appbar.dart diff --git a/lib/modules/tautulli/modules/search/widgets/search_result_tile.dart b/lib/modules/tautulli/routes/search/widgets/search_result_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/search/widgets/search_result_tile.dart rename to lib/modules/tautulli/routes/search/widgets/search_result_tile.dart diff --git a/lib/modules/tautulli/modules/search/widgets/search_results.dart b/lib/modules/tautulli/routes/search/widgets/search_results.dart similarity index 100% rename from lib/modules/tautulli/modules/search/widgets/search_results.dart rename to lib/modules/tautulli/routes/search/widgets/search_results.dart diff --git a/lib/modules/tautulli/modules/statistics.dart b/lib/modules/tautulli/routes/statistics.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics.dart rename to lib/modules/tautulli/routes/statistics.dart diff --git a/lib/modules/tautulli/modules/statistics/route.dart b/lib/modules/tautulli/routes/statistics/route.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/route.dart rename to lib/modules/tautulli/routes/statistics/route.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets.dart b/lib/modules/tautulli/routes/statistics/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets.dart rename to lib/modules/tautulli/routes/statistics/widgets.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/media_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/media_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/media_tile.dart rename to lib/modules/tautulli/routes/statistics/widgets/media_tile.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/platform_tile.dart rename to lib/modules/tautulli/routes/statistics/widgets/platform_tile.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/recently_watched_tile.dart rename to lib/modules/tautulli/routes/statistics/widgets/recently_watched_tile.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/stream_tile.dart rename to lib/modules/tautulli/routes/statistics/widgets/stream_tile.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart b/lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/time_range_button.dart rename to lib/modules/tautulli/routes/statistics/widgets/time_range_button.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/type_button.dart b/lib/modules/tautulli/routes/statistics/widgets/type_button.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/type_button.dart rename to lib/modules/tautulli/routes/statistics/widgets/type_button.dart diff --git a/lib/modules/tautulli/modules/statistics/widgets/user_tile.dart b/lib/modules/tautulli/routes/statistics/widgets/user_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/statistics/widgets/user_tile.dart rename to lib/modules/tautulli/routes/statistics/widgets/user_tile.dart diff --git a/lib/modules/tautulli/modules/synced_items.dart b/lib/modules/tautulli/routes/synced_items.dart similarity index 100% rename from lib/modules/tautulli/modules/synced_items.dart rename to lib/modules/tautulli/routes/synced_items.dart diff --git a/lib/modules/tautulli/modules/synced_items/route.dart b/lib/modules/tautulli/routes/synced_items/route.dart similarity index 100% rename from lib/modules/tautulli/modules/synced_items/route.dart rename to lib/modules/tautulli/routes/synced_items/route.dart diff --git a/lib/modules/tautulli/modules/synced_items/widgets.dart b/lib/modules/tautulli/routes/synced_items/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/synced_items/widgets.dart rename to lib/modules/tautulli/routes/synced_items/widgets.dart diff --git a/lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart b/lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/synced_items/widgets/synced_item_tile.dart rename to lib/modules/tautulli/routes/synced_items/widgets/synced_item_tile.dart diff --git a/lib/modules/tautulli/modules/tautulli.dart b/lib/modules/tautulli/routes/tautulli.dart similarity index 100% rename from lib/modules/tautulli/modules/tautulli.dart rename to lib/modules/tautulli/routes/tautulli.dart diff --git a/lib/modules/tautulli/modules/tautulli/route.dart b/lib/modules/tautulli/routes/tautulli/route.dart similarity index 97% rename from lib/modules/tautulli/modules/tautulli/route.dart rename to lib/modules/tautulli/routes/tautulli/route.dart index 28be747a..2f2ebf01 100644 --- a/lib/modules/tautulli/modules/tautulli/route.dart +++ b/lib/modules/tautulli/routes/tautulli/route.dart @@ -60,7 +60,7 @@ class _State extends State<_TautulliHomeRoute> { return false; } - Widget get _drawer => LSDrawer(page: 'tautulli'); + Widget get _drawer => LunaDrawer(page: LunaModule.TAUTULLI.key); Widget get _bottomNavigationBar => TautulliNavigationBar(pageController: _pageController); diff --git a/lib/modules/tautulli/modules/tautulli/widgets.dart b/lib/modules/tautulli/routes/tautulli/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/tautulli/widgets.dart rename to lib/modules/tautulli/routes/tautulli/widgets.dart diff --git a/lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart b/lib/modules/tautulli/routes/tautulli/widgets/global_settings.dart similarity index 100% rename from lib/modules/tautulli/modules/tautulli/widgets/global_settings.dart rename to lib/modules/tautulli/routes/tautulli/widgets/global_settings.dart diff --git a/lib/modules/tautulli/modules/tautulli/widgets/navigation_bar.dart b/lib/modules/tautulli/routes/tautulli/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/tautulli/modules/tautulli/widgets/navigation_bar.dart rename to lib/modules/tautulli/routes/tautulli/widgets/navigation_bar.dart diff --git a/lib/modules/tautulli/modules/users.dart b/lib/modules/tautulli/routes/users.dart similarity index 100% rename from lib/modules/tautulli/modules/users.dart rename to lib/modules/tautulli/routes/users.dart diff --git a/lib/modules/tautulli/modules/users/route.dart b/lib/modules/tautulli/routes/users/route.dart similarity index 100% rename from lib/modules/tautulli/modules/users/route.dart rename to lib/modules/tautulli/routes/users/route.dart diff --git a/lib/modules/tautulli/modules/users/widgets.dart b/lib/modules/tautulli/routes/users/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/users/widgets.dart rename to lib/modules/tautulli/routes/users/widgets.dart diff --git a/lib/modules/tautulli/modules/users/widgets/user_tile.dart b/lib/modules/tautulli/routes/users/widgets/user_tile.dart similarity index 100% rename from lib/modules/tautulli/modules/users/widgets/user_tile.dart rename to lib/modules/tautulli/routes/users/widgets/user_tile.dart diff --git a/lib/modules/tautulli/modules/users_details.dart b/lib/modules/tautulli/routes/users_details.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details.dart rename to lib/modules/tautulli/routes/users_details.dart diff --git a/lib/modules/tautulli/modules/users_details/route.dart b/lib/modules/tautulli/routes/users_details/route.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/route.dart rename to lib/modules/tautulli/routes/users_details/route.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets.dart b/lib/modules/tautulli/routes/users_details/widgets.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets.dart rename to lib/modules/tautulli/routes/users_details/widgets.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets/history.dart b/lib/modules/tautulli/routes/users_details/widgets/history.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets/history.dart rename to lib/modules/tautulli/routes/users_details/widgets/history.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart b/lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets/ip_addresses.dart rename to lib/modules/tautulli/routes/users_details/widgets/ip_addresses.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets/navigation_bar.dart b/lib/modules/tautulli/routes/users_details/widgets/navigation_bar.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets/navigation_bar.dart rename to lib/modules/tautulli/routes/users_details/widgets/navigation_bar.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets/profile.dart b/lib/modules/tautulli/routes/users_details/widgets/profile.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets/profile.dart rename to lib/modules/tautulli/routes/users_details/widgets/profile.dart diff --git a/lib/modules/tautulli/modules/users_details/widgets/synced_items.dart b/lib/modules/tautulli/routes/users_details/widgets/synced_items.dart similarity index 100% rename from lib/modules/tautulli/modules/users_details/widgets/synced_items.dart rename to lib/modules/tautulli/routes/users_details/widgets/synced_items.dart diff --git a/lib/modules/wake_on_lan/core/api.dart b/lib/modules/wake_on_lan/core/api.dart index 1ad7d089..137801ee 100644 --- a/lib/modules/wake_on_lan/core/api.dart +++ b/lib/modules/wake_on_lan/core/api.dart @@ -1,29 +1,31 @@ +import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:wake_on_lan/wake_on_lan.dart'; class WakeOnLANAPI { - final Map _values; + final bool enabled; + final String broadcastAddress; + final String macAddress; - WakeOnLANAPI._internal(this._values); - factory WakeOnLANAPI.from(ProfileHiveObject profile) => WakeOnLANAPI._internal(profile.getWakeOnLAN()); + WakeOnLANAPI._internal({ + @required this.enabled, + @required this.broadcastAddress, + @required this.macAddress, + }); + + factory WakeOnLANAPI.fromProfile() { + ProfileHiveObject profile = Database.currentProfileObject; + return WakeOnLANAPI._internal( + enabled: profile.wakeOnLANEnabled, + broadcastAddress: profile.wakeOnLANBroadcastAddress, + macAddress: profile.wakeOnLANMACAddress, + ); + } - void logError(String text, Object error, StackTrace trace) => LunaLogger().error('Wake on LAN: $text', error, trace); - - bool get enabled => _values['enabled']; - String get broadcastAddress => _values['broadcastAddress']; - String get macAddress => _values['MACAddress']; - - Future testConnection() async => true; - - Future wake() async { - try { - IPv4Address ipv4 = IPv4Address.from(broadcastAddress); - MACAddress mac = MACAddress.from(macAddress); - await WakeOnLAN.from(ipv4, mac).wake(); - return true; - } catch (error, stack) { - logError('Failed to wake machine', error, stack); - return Future.error(error); - } + Future wake() async { + IPv4Address ipv4 = IPv4Address.from(broadcastAddress); + MACAddress mac = MACAddress.from(macAddress); + await WakeOnLAN.from(ipv4, mac).wake(); + return true; } } diff --git a/localization/lunasea/en.json b/localization/lunasea/en.json new file mode 100644 index 00000000..78a5f4d6 --- /dev/null +++ b/localization/lunasea/en.json @@ -0,0 +1,11 @@ +{ + "lunasea.AnErrorHasOccurred": "An Error Has Occurred", + "lunasea.ComingSoon": "Coming Soon", + "lunasea.GoBack": "Go Back", + "lunasea.ModuleIsNotEnabled": "{} Is Not Enabled", + "lunasea.Refresh": "Refresh", + "lunasea.ReturnToDashboard": "Return to Dashboard", + "lunasea.SearchTextBar": "Search…", + "lunasea.Settings": "Settings", + "lunasea.TryAgain": "Try Again" +} diff --git a/localization/lunasea/fr.json b/localization/lunasea/fr.json new file mode 100644 index 00000000..d9503b00 --- /dev/null +++ b/localization/lunasea/fr.json @@ -0,0 +1,11 @@ +{ + "lunasea.AnErrorHasOccurred": "Une erreur s'est produite", + "lunasea.ComingSoon": "Prochainement", + "lunasea.GoBack": "Retour en arrière", + "lunasea.ModuleIsNotEnabled": "{} n'est pas activé", + "lunasea.Refresh": "Rafraichir", + "lunasea.ReturnToDashboard": "Retour au tableau de bord", + "lunasea.SearchTextBar": "Rechercher…", + "lunasea.Settings": "Réglages", + "lunasea.TryAgain": "Réessayer" +} diff --git a/localization/lunasea/nb-NO.json b/localization/lunasea/nb-NO.json new file mode 100644 index 00000000..85b3d824 --- /dev/null +++ b/localization/lunasea/nb-NO.json @@ -0,0 +1,6 @@ +{ + "lunasea.AnErrorHasOccurred": "En feil har inntruffet", + "lunasea.Refresh": "Gjenoppfrisk", + "lunasea.SearchTextBar": "Søk …", + "lunasea.Settings": "Innstillinger" +} diff --git a/localization/lunasea/sv.json b/localization/lunasea/sv.json new file mode 100644 index 00000000..0589d4a4 --- /dev/null +++ b/localization/lunasea/sv.json @@ -0,0 +1,6 @@ +{ + "lunasea.AnErrorHasOccurred": "Ett fel har inträffat", + "lunasea.Refresh": "Uppdatera", + "lunasea.SearchTextBar": "Sök…", + "lunasea.Settings": "Inställningar" +} diff --git a/localization/radarr/en.json b/localization/radarr/en.json new file mode 100644 index 00000000..c45ffda4 --- /dev/null +++ b/localization/radarr/en.json @@ -0,0 +1,73 @@ +{ + "radarr.Age": "Age", + "radarr.All": "All", + "radarr.AddMovie": "Add Movie", + "radarr.AddMovieAndSearch": "Add + Search", + "radarr.Alphabetical": "Alphabetical", + "radarr.Approved": "Approved", + "radarr.AvailabilityUnknown": "Availability Unknown", + "radarr.AvailableIn": "Available in {}", + "radarr.AvailableToday": "Available Today", + "radarr.BackupDatabase": "Backup Database", + "radarr.CinemaDateUnknown": "Cinema Date Unknown", + "radarr.CutoffUnmet": "Cutoff Unmet", + "radarr.DateAdded": "Date Added", + "radarr.Discover": "Discover", + "radarr.DownloadFailed": "Download Failed", + "radarr.DownloadIgnored": "Download Ignored", + "radarr.EditMovie": "Edit Movie", + "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.MinimumAvailability": "Minimum Availability", + "radarr.Missing": "Missing", + "radarr.Monitored": "Monitored", + "radarr.MonitoredDescription": "Monitor for new releases", + "radarr.MonitorMovie": "Monitor Movie", + "radarr.More": "More", + "radarr.Movies": "Movies", + "radarr.MovieFileDeleted": "Movie File Deleted", + "radarr.MovieFileRenamed": "Movie File Renamed", + "radarr.MovieImported": "Movie Imported ({})", + "radarr.MovieNotFound": "Movie Not Found", + "radarr.MoviePath": "Movie Path", + "radarr.NoHistoryFound": "No History Found", + "radarr.NoMoviesFound": "No Movies Found", + "radarr.NoResultsFound": "No Results Found", + "radarr.NoSummaryIsAvailable": "No summary is available.", + "radarr.QualityProfile": "Quality Profile", + "radarr.Queue": "Queue", + "radarr.QueueDescription": "View Active & Queued Content", + "radarr.RefreshMovie": "Refresh Movie", + "radarr.Rejected": "Rejected", + "radarr.Released": "Released", + "radarr.ReleasedToday": "Released Today", + "radarr.RemoveMovie": "Remove Movie", + "radarr.RootFolder": "Root Folder", + "radarr.Runtime": "Runtime", + "radarr.RunRSSSync": "Run RSS Sync", + "radarr.Search": "Search", + "radarr.SearchAllMissing": "Search All Missing", + "radarr.SearchFor": "Search for {}", + "radarr.Seeders": "Seeders", + "radarr.Size": "Size", + "radarr.SortCatalogue": "Sort Catalogue", + "radarr.Studio": "Studio", + "radarr.SystemStatus": "System Status", + "radarr.SystemStatusDescription": "System Status & Disk Space", + "radarr.Tags": "Tags", + "radarr.TagsDescription": "Manage Your Tags", + "radarr.Type": "Type", + "radarr.Unmonitored": "Unmonitored", + "radarr.UnmonitorMovie": "Unmonitor Movie", + "radarr.Upcoming": "Upcoming", + "radarr.UpdateLibrary": "Update Library", + "radarr.UpdateMovie": "Update Movie", + "radarr.ViewWebGUI": "View Web GUI", + "radarr.Wanted": "Wanted", + "radarr.Weight": "Weight", + "radarr.Year": "Year" +} diff --git a/localization/radarr/fr.json b/localization/radarr/fr.json new file mode 100644 index 00000000..094659a8 --- /dev/null +++ b/localization/radarr/fr.json @@ -0,0 +1,73 @@ +{ + "radarr.AddMovie": "Ajouter un film", + "radarr.AddMovieAndSearch": "Ajouter + Rechercher", + "radarr.Age": "Âge", + "radarr.All": "Tout", + "radarr.Alphabetical": "Alphabétique", + "radarr.Approved": "Approuvé", + "radarr.AvailabilityUnknown": "Disponibilité inconnue", + "radarr.AvailableIn": "Disponible dans {}", + "radarr.AvailableToday": "Disponible aujourd'hui", + "radarr.BackupDatabase": "Sauvegarder la base de donnée", + "radarr.CinemaDateUnknown": "Date de sortie au cinéma inconnue", + "radarr.CutoffUnmet": "Qualité préférée non atteinte", + "radarr.DateAdded": "Date ajoutée", + "radarr.Discover": "Découvrir", + "radarr.DownloadFailed": "Echec du téléchargement", + "radarr.DownloadIgnored": "Télécharger Ignoré", + "radarr.EditMovie": "Modifier le film", + "radarr.FilterCatalogue": "Catalogue des filtres", + "radarr.GrabbedFrom": "Récupéré depuis {}", + "radarr.History": "Historique", + "radarr.HistoryDescription": "Voir les activités récentes", + "radarr.InCinemasIn": "Dans les cinémas en {}", + "radarr.InCinemasToday": "Au cinéma aujourd'hui", + "radarr.MinimumAvailability": "Disponibilité minimale", + "radarr.Missing": "Manquant", + "radarr.MonitorMovie": "Surveiller le film", + "radarr.Monitored": "Surveillé", + "radarr.MonitoredDescription": "Surveiller les nouvelles versions", + "radarr.More": "Plus", + "radarr.MovieFileDeleted": "Fichier vidéo supprimé", + "radarr.MovieFileRenamed": "Fichier vidéo renommé", + "radarr.MovieImported": "Film importé ({})", + "radarr.MovieNotFound": "Film non trouvé", + "radarr.MoviePath": "Chemin du film", + "radarr.Movies": "Films", + "radarr.NoHistoryFound": "Pas d'historique trouvé", + "radarr.NoMoviesFound": "Aucun film trouvé", + "radarr.NoResultsFound": "Aucun résultat", + "radarr.NoSummaryIsAvailable": "Aucun résumé n'est disponible.", + "radarr.QualityProfile": "Profil de la qualité", + "radarr.Queue": "File d'attente", + "radarr.QueueDescription": "Voir le contenu actif et en attente", + "radarr.RefreshMovie": "Rafraichir le film", + "radarr.Rejected": "Rejeté", + "radarr.Released": "Sortie", + "radarr.ReleasedToday": "Sortie aujourd'hui", + "radarr.RemoveMovie": "Supprimer le film", + "radarr.RootFolder": "Dossier racine", + "radarr.RunRSSSync": "Lancer la synchronisation RSS", + "radarr.Runtime": "Durée", + "radarr.Search": "Rechercher", + "radarr.SearchAllMissing": "Rechercher tous les manquants", + "radarr.SearchFor": "Rechercher {}", + "radarr.Seeders": "Seeders", + "radarr.Size": "Taille", + "radarr.SortCatalogue": "Trier le catalogue", + "radarr.Studio": "Studio", + "radarr.SystemStatus": "État du système", + "radarr.SystemStatusDescription": "État du système et espace disque", + "radarr.Tags": "Tags", + "radarr.TagsDescription": "Gérer vos tags", + "radarr.Type": "Type", + "radarr.UnmonitorMovie": "Ne plus surveiller le film", + "radarr.Unmonitored": "Non surveillé", + "radarr.Upcoming": "À venir", + "radarr.UpdateLibrary": "Mettre à jour la bibliothèque", + "radarr.UpdateMovie": "Mettre à jour le film", + "radarr.ViewWebGUI": "Voir l'interface web", + "radarr.Wanted": "Recherché", + "radarr.Weight": "Poids", + "radarr.Year": "Année" +} diff --git a/localization/radarr/nb-NO.json b/localization/radarr/nb-NO.json new file mode 100644 index 00000000..4e1e76c7 --- /dev/null +++ b/localization/radarr/nb-NO.json @@ -0,0 +1,33 @@ +{ + "radarr.AddMovie": "Legg til film", + "radarr.AddMovieAndSearch": "Legg til + søk", + "radarr.Discover": "Oppdag", + "radarr.EditMovie": "Rediger fil", + "radarr.FilterCatalogue": "Filtrer katalog", + "radarr.History": "Historikk", + "radarr.HistoryDescription": "Vis nylig historikk", + "radarr.MinimumAvailability": "Minimumstilgjengelighet", + "radarr.Missing": "Mangler", + "radarr.Monitored": "På oppsynsliste", + "radarr.MonitoredDescription": "Hold oppsyn med nye utgivelser", + "radarr.More": "Mer", + "radarr.MovieNotFound": "Fant ikke filmen", + "radarr.MoviePath": "Filmsti", + "radarr.Movies": "Filmer", + "radarr.NoMoviesFound": "Fant ingen filmer", + "radarr.NoResultsFound": "Resultatløst", + "radarr.NoSummaryIsAvailable": "Mangler sammendrag.", + "radarr.QualityProfile": "Kvalitetsprofil", + "radarr.Queue": "Kø", + "radarr.QueueDescription": "Vis aktivt og køplassert innhold", + "radarr.RootFolder": "Rotmappe", + "radarr.Search": "Søk", + "radarr.SearchFor": "Søkk etter {}", + "radarr.SortCatalogue": "Sorter katelog", + "radarr.SystemStatus": "Systemstatus", + "radarr.SystemStatusDescription": "Systemstatus og diskplass", + "radarr.Tags": "Etiketter", + "radarr.TagsDescription": "Håndter dine etiketter", + "radarr.Upcoming": "Kommende", + "radarr.UpdateMovie": "Oppdater film" +} diff --git a/localization/radarr/sv.json b/localization/radarr/sv.json new file mode 100644 index 00000000..32921f81 --- /dev/null +++ b/localization/radarr/sv.json @@ -0,0 +1,33 @@ +{ + "radarr.AddMovie": "Lägg till Film", + "radarr.AddMovieAndSearch": "Lägg till + Sök", + "radarr.Discover": "Utforska", + "radarr.EditMovie": "Redigera Film", + "radarr.FilterCatalogue": "Filterkatalog", + "radarr.History": "Historik", + "radarr.HistoryDescription": "Visa Senaste Aktivitet", + "radarr.MinimumAvailability": "Minsta Tillgänglighet", + "radarr.Missing": "Saknas", + "radarr.Monitored": "Bevakad", + "radarr.MonitoredDescription": "Bevaka för nya utgåvor", + "radarr.More": "Mer", + "radarr.MovieNotFound": "Film Hittades Inte", + "radarr.MoviePath": "Film Sökväg", + "radarr.Movies": "Filmer", + "radarr.NoMoviesFound": "Inga Filmer Funna", + "radarr.NoResultsFound": "Inga Resultat Funna", + "radarr.NoSummaryIsAvailable": "Ingen sammanfattning finns tillgänglig.", + "radarr.QualityProfile": "Kvalitetsprofil", + "radarr.Queue": "Kö", + "radarr.QueueDescription": "Visa Aktivt & Köat Innehåll", + "radarr.RootFolder": "Rotmapp", + "radarr.Search": "Sök", + "radarr.SearchFor": "Sök efter {}", + "radarr.SortCatalogue": "Sortera Katalog", + "radarr.SystemStatus": "Systemstatus", + "radarr.SystemStatusDescription": "Systemstatus & Disk Utrymme", + "radarr.Tags": "Taggar", + "radarr.TagsDescription": "Hantera Dina Taggar", + "radarr.Upcoming": "Kommande", + "radarr.UpdateMovie": "Uppdatera Film" +} diff --git a/pubspec.lock b/pubspec.lock index 1b3a448a..99ff5636 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,35 +7,28 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "14.0.0" + version: "17.0.0" analyzer: dependency: "direct dev" description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.41.2" - archive: - dependency: transitive - description: - name: archive - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.13" + version: "1.1.0" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "2.0.0" asn1lib: dependency: transitive description: name: asn1lib url: "https://pub.dartlang.org" source: hosted - version: "0.8.1" + version: "0.9.0-nullsafety.0" async: dependency: transitive description: @@ -56,14 +49,14 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.6.2" + version: "1.6.3" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.5" + version: "0.4.6" build_daemon: dependency: transitive description: @@ -77,21 +70,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.5.3" + version: "1.5.4" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.11.1" + version: "1.11.5" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.7" + version: "6.1.10" built_collection: dependency: transitive description: @@ -147,21 +140,21 @@ packages: name: cloud_firestore url: "https://pub.dartlang.org" source: hosted - version: "0.16.0+1" + version: "1.0.0" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "4.0.0" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web url: "https://pub.dartlang.org" source: hosted - version: "0.3.0+2" + version: "1.0.0" code_builder: dependency: transitive description: @@ -182,84 +175,70 @@ packages: name: connectivity_plus url: "https://pub.dartlang.org" source: hosted - version: "0.8.1" + version: "1.0.0" connectivity_plus_linux: dependency: transitive description: name: connectivity_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "0.3.1" + version: "1.0.0" connectivity_plus_macos: dependency: transitive description: name: connectivity_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" + version: "1.0.0" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" + version: "1.0.0" connectivity_plus_web: dependency: transitive description: name: connectivity_plus_web url: "https://pub.dartlang.org" source: hosted - version: "0.6.1" + version: "1.0.0" connectivity_plus_windows: dependency: transitive description: name: connectivity_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "1.0.0" convert: dependency: "direct main" description: name: convert url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "3.0.0" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" - dapackages: - dependency: "direct dev" - description: - name: dapackages - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" + version: "3.0.0" dart_style: dependency: transitive description: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.12" - dartx: - dependency: transitive - description: - name: dartx - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.0" + version: "1.3.14" dbus: dependency: transitive description: name: dbus url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.2.1" dio: dependency: "direct main" description: @@ -267,20 +246,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.10" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + easy_logger: + dependency: transitive + description: + name: easy_logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" encrypt: dependency: "direct main" description: name: encrypt url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "5.0.0-beta.1" equatable: dependency: transitive description: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "1.2.6" + version: "2.0.0" expandable: dependency: "direct main" description: @@ -301,105 +294,119 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.0.0" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.1" + version: "6.1.0" file_picker: dependency: "direct main" description: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "2.1.6" + version: "3.0.0" firebase_auth: dependency: "direct main" description: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "0.20.1" + version: "1.0.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "4.0.0" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "0.3.3" + version: "1.0.0" firebase_core: dependency: "direct main" description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "0.7.0" + version: "1.0.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "4.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web url: "https://pub.dartlang.org" source: hosted - version: "0.2.1+3" + version: "1.0.0" + firebase_crashlytics: + dependency: "direct main" + description: + name: firebase_crashlytics + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + firebase_crashlytics_platform_interface: + dependency: transitive + description: + name: firebase_crashlytics_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" firebase_messaging: dependency: "direct main" description: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "8.0.0-dev.15" + version: "9.0.0" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.0-dev.10" + version: "2.0.0" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.0-dev.6" + version: "1.0.0" firebase_storage: dependency: "direct main" description: name: firebase_storage url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "8.0.0" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" firebase_storage_web: dependency: transitive description: name: firebase_storage_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+3" + version: "1.0.0" fixnum: dependency: transitive description: @@ -413,7 +420,7 @@ packages: name: fl_chart url: "https://pub.dartlang.org" source: hosted - version: "0.12.3" + version: "0.20.1" flash: dependency: "direct main" description: @@ -427,40 +434,31 @@ packages: name: fluro url: "https://pub.dartlang.org" source: hosted - version: "1.7.8" + version: "2.0.3" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_launcher_icons: - dependency: "direct dev" - description: - name: flutter_launcher_icons - url: "https://pub.dartlang.org" - source: hosted - version: "0.8.1" - flutter_native_splash: - dependency: "direct dev" - description: - name: flutter_native_splash - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "1.0.11" + version: "2.0.0" flutter_spinkit: dependency: "direct main" description: name: flutter_spinkit url: "https://pub.dartlang.org" source: hosted - version: "4.1.2+1" + version: "5.0.0" flutter_sticky_header: dependency: "direct main" description: @@ -479,7 +477,7 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" google_nav_bar: dependency: "direct main" description: @@ -500,35 +498,28 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "1.4.4+1" + version: "2.0.0" hive_flutter: dependency: "direct main" description: name: hive_flutter url: "https://pub.dartlang.org" source: hosted - version: "0.3.1" + version: "1.0.0" hive_generator: dependency: "direct dev" description: name: hive_generator url: "https://pub.dartlang.org" source: hosted - version: "0.8.2" - html_character_entities: - dependency: transitive - description: - name: html_character_entities - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.2+4" + version: "1.0.0" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.2" + version: "0.13.0" http_multi_server: dependency: transitive description: @@ -537,47 +528,40 @@ packages: source: hosted version: "2.2.0" http_parser: - dependency: transitive + dependency: "direct overridden" description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.4" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.19" + version: "4.0.0" in_app_purchase: dependency: "direct main" description: name: in_app_purchase url: "https://pub.dartlang.org" source: hosted - version: "0.3.5+1" + version: "0.5.0" infinite_scroll_pagination: dependency: "direct main" description: name: infinite_scroll_pagination url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "3.0.0" intl: dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.17.0" io: dependency: transitive description: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.5" js: dependency: transitive description: @@ -591,7 +575,7 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "3.1.1" + version: "4.0.0" logging: dependency: transitive description: @@ -619,7 +603,7 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.7" + version: "1.0.0" modal_bottom_sheet: dependency: "direct main" description: @@ -640,70 +624,56 @@ packages: name: network_info_plus url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "1.0.0" network_info_plus_linux: dependency: transitive description: name: network_info_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "1.0.0" network_info_plus_macos: dependency: transitive description: name: network_info_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "1.0.0" network_info_plus_platform_interface: dependency: transitive description: name: network_info_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "1.0.0" network_info_plus_web: dependency: transitive description: name: network_info_plus_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "1.0.0" network_info_plus_windows: dependency: transitive description: name: network_info_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.1" - node_io: - dependency: transitive - description: - name: node_io - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.0.0" package_config: dependency: transitive description: name: package_config url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "2.0.0" package_info: dependency: "direct main" description: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.3+4" + version: "2.0.0" path: dependency: transitive description: @@ -717,56 +687,56 @@ packages: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.4.1+1" + version: "0.5.0-nullsafety.0" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.2.0-nullsafety.0" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.27" + version: "2.0.1" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+2" + version: "2.0.0" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+8" + version: "2.0.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+3" + version: "2.0.0" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.10.0" + version: "1.11.0" percent_indicator: dependency: "direct main" description: @@ -774,27 +744,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.9+1" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - url: "https://pub.dartlang.org" - source: hosted - version: "5.1.0+2" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "4.0.2" platform: dependency: transitive description: @@ -808,14 +764,14 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.0" pointycastle: dependency: transitive description: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "3.0.0-nullsafety.2" pool: dependency: transitive description: @@ -829,14 +785,14 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "3.0.13" + version: "4.1.0" provider: dependency: "direct main" description: name: provider url: "https://pub.dartlang.org" source: hosted - version: "4.3.3" + version: "5.0.0" pub_semver: dependency: transitive description: @@ -857,7 +813,7 @@ packages: name: quick_actions url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+10" + version: "0.5.0" quiver: dependency: transitive description: @@ -871,84 +827,105 @@ packages: name: radarr url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" - recursive_regex: - dependency: transitive - description: - name: recursive_regex - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.3+2" + version: "1.1.4+1" sembast: dependency: transitive description: name: sembast url: "https://pub.dartlang.org" source: hosted - version: "2.4.10+1" - sentry: - dependency: transitive - description: - name: sentry - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.5" - sentry_flutter: - dependency: "direct main" - description: - name: sentry_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.5" - service_worker: - dependency: transitive - description: - name: service_worker - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.4" + version: "2.4.10+4" share_plus: dependency: "direct main" description: name: share_plus url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" share_plus_linux: dependency: transitive description: name: share_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "2.0.0" + share_plus_macos: + dependency: transitive + description: + name: share_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" share_plus_web: dependency: transitive description: name: share_plus_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "2.0.0" share_plus_windows: dependency: transitive description: name: share_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "2.0.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "0.7.9" + version: "1.0.0" shelf_web_socket: dependency: transitive description: @@ -974,14 +951,14 @@ packages: name: sliver_tools url: "https://pub.dartlang.org" source: hosted - version: "0.1.10+1" + version: "0.2.0-nullsafety" sonarr: dependency: "direct main" description: name: sonarr url: "https://pub.dartlang.org" source: hosted - version: "1.1.0+1" + version: "1.1.1+1" source_gen: dependency: transitive description: @@ -1030,7 +1007,7 @@ packages: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "2.2.0+2" + version: "3.0.0" table_calendar: dependency: "direct main" description: @@ -1044,7 +1021,7 @@ packages: name: tautulli url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.2.2+1" term_glyph: dependency: transitive description: @@ -1052,13 +1029,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" - time: - dependency: transitive - description: - name: time - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" timing: dependency: transitive description: @@ -1086,49 +1056,49 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.7.10" + version: "6.0.2" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+4" + version: "2.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+9" + version: "2.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "2.0.2" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+3" + version: "2.0.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "2.0.0" uuid: dependency: "direct main" description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.2.2" + version: "3.0.1" value_layout_builder: dependency: transitive description: @@ -1149,7 +1119,7 @@ packages: name: wake_on_lan url: "https://pub.dartlang.org" source: hosted - version: "1.1.1+6" + version: "1.1.2+1" watcher: dependency: transitive description: @@ -1170,28 +1140,21 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.4+1" + version: "2.0.0" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.2.0" xml: - dependency: transitive + dependency: "direct main" description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "4.5.1" - xml_parser: - dependency: "direct main" - description: - name: xml_parser - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.2" + version: "5.0.2" xxtea: dependency: transitive description: @@ -1205,7 +1168,7 @@ packages: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.12.0-0.0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index ae491cfc..4bde5a71 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,84 +1,83 @@ name: lunasea description: Self-Hosted Controller -version: 5.0.0+50000010 +version: 5.0.0+50000011 publish_to: 'none' environment: sdk: ">=2.7.0 <3.0.0" dependencies: badges: ^1.1.6 - cloud_firestore: ^0.16.0+1 - connectivity_plus: ^0.8.1 - convert: ^2.1.1 + cloud_firestore: ^1.0.0 + connectivity_plus: ^1.0.0 + convert: ^3.0.0 dio: ^3.0.10 - encrypt: ^4.1.0 + easy_localization: ^3.0.0 + encrypt: ^5.0.0-beta.1 expandable: ^4.1.4 - file_picker: ^2.1.6 - firebase_core: ^0.7.0 - firebase_auth: ^0.20.1 - firebase_messaging: ^8.0.0-dev.15 - firebase_storage: ^7.0.0 + file_picker: ^3.0.0 + firebase_auth: ^1.0.0 + firebase_core: ^1.0.0 + firebase_crashlytics: ^1.0.0 + firebase_messaging: ^9.0.0 + firebase_storage: ^8.0.0 flash: ^1.3.2 - fluro: ^1.7.8 + fluro: ^2.0.3 flutter: sdk: flutter - flutter_spinkit: ^4.1.2+1 + flutter_spinkit: ^5.0.0 flutter_sticky_header: ^0.5.0 - fl_chart: ^0.12.3 + fl_chart: ^0.20.1 f_logs: ^1.3.0-alpha-02 google_nav_bar: ^4.0.2 - hive: ^1.4.4+1 - hive_flutter: ^0.3.1 - in_app_purchase: ^0.3.5+1 - infinite_scroll_pagination: ^2.3.0 - intl: ^0.16.1 + hive: ^2.0.0 + hive_flutter: ^1.0.0 + in_app_purchase: ^0.5.0 + infinite_scroll_pagination: ^3.0.0 + intl: ^0.17.0 modal_bottom_sheet: ^1.0.0+1 - network_info_plus: ^0.1.2 - package_info: ^0.4.3+4 - path_provider: ^1.6.27 + network_info_plus: ^1.0.0 + package_info: ^2.0.0 + path_provider: ^2.0.1 percent_indicator: ^2.1.9+1 - permission_handler: ^5.1.0+2 - provider: ^4.3.3 - quick_actions: ^0.4.0+10 - sentry_flutter: ^4.0.5 - share_plus: ^1.2.0 + provider: ^5.0.0 + quick_actions: ^0.5.0 + share_plus: ^2.0.0 stack_trace: ^1.10.0 table_calendar: ^2.3.3 tuple: ^1.0.3 - url_launcher: ^5.7.10 - uuid: ^2.2.2 - xml_parser: ^0.1.2 + url_launcher: ^6.0.2 + uuid: ^3.0.1 + xml: ^5.0.2 # Comet.Tools Packages - radarr: ^1.1.3 - sonarr: ^1.1.0+1 - tautulli: ^1.2.1 - wake_on_lan: ^1.1.1+6 + radarr: ^1.1.4+1 + sonarr: ^1.1.1+1 + tautulli: ^1.2.2+1 + wake_on_lan: ^1.1.2+1 + +dependency_overrides: + http_parser: ^4.0.0 + intl: ^0.17.0 + path_provider: ^2.0.1 dev_dependencies: - analyzer: ^0.41.2 - build_runner: ^1.11.1 - dapackages: ^1.4.0 - flutter_launcher_icons: ^0.8.1 - flutter_native_splash: ^0.3.0 - hive_generator: ^0.8.2 + analyzer: ^1.1.0 + build_runner: ^1.11.5 + hive_generator: ^1.0.0 flutter: uses-material-design: true assets: - # Changelog + # System Assets - assets/changelog.json - # Colors + - assets/localization/ + - assets/branding/ + # Other - assets/images/colors/ - # Automation - assets/images/lidarr/ - assets/images/radarr/ - assets/images/sonarr/ - # Monitoring - assets/images/tautulli/ - # Services - assets/images/services/ - # Branding - - assets/branding/ fonts: - family: CustomIcons fonts: @@ -97,6 +96,4 @@ flutter_native_splash: scripts: build: flutter packages pub run build_runner build --delete-conflicting-outputs - icons: flutter pub run flutter_launcher_icons:main - splash: flutter pub pub run flutter_native_splash:create - outdated: flutter pub pub run dapackages:dapackages.dart pubspec.yaml \ No newline at end of file + build_localizations: dart ./scripts/build_localizations.dart diff --git a/scripts/build_aab b/scripts/build/aab.sh similarity index 96% rename from scripts/build_aab rename to scripts/build/aab.sh index 0d7bf421..9b79182b 100755 --- a/scripts/build_aab +++ b/scripts/build/aab.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cd .. +cd ../.. # Remove old builds rm -rf output/*.aab # Clean and build diff --git a/scripts/build_apk b/scripts/build/apk.sh similarity index 98% rename from scripts/build_apk rename to scripts/build/apk.sh index 234a44e5..8ff1fb35 100755 --- a/scripts/build_apk +++ b/scripts/build/apk.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cd .. +cd ../.. # Remove old builds rm -rf output/*.apk # Clean and build diff --git a/scripts/build_ipa b/scripts/build/ipa.sh similarity index 97% rename from scripts/build_ipa rename to scripts/build/ipa.sh index 6cc45a04..e61df06c 100755 --- a/scripts/build_ipa +++ b/scripts/build/ipa.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cd .. +cd ../.. # Remove old builds rm -rf build rm -rf output/*.ipa diff --git a/scripts/build_localizations.dart b/scripts/build_localizations.dart new file mode 100644 index 00000000..e77592e3 --- /dev/null +++ b/scripts/build_localizations.dart @@ -0,0 +1,62 @@ +import 'dart:convert'; +import 'dart:io'; + +/// Simple dart script to concat all module's localization files into a single file for loading into LunaSea. +/// +/// This script is designed to be run from the root of the project, using derry: `derry run build_localizations`. +void main() { + // Check and delete any found prepared localizations + Directory _assets = Directory('assets/localization'); + if(_assets.existsSync()) _assets.deleteSync(recursive: true); + // Go through each localization file + Directory _localization = Directory('localization'); + _localization.listSync().forEach((folder) { + // Ensure that the filesystem entity is a folder, and finds any files within + if(FileSystemEntity.isDirectorySync(folder.path)) { + Directory module = Directory(folder.path); + print('Processing Module: ${module.path.substring(module.path.lastIndexOf('/')+1)}'); + module.listSync().forEach((language) { + print('-> Adding language: ${language.path.substring(language.path.lastIndexOf('/')+1)}...'); + // Ensures that the filesystem entity is a JSON file + if(FileSystemEntity.isFileSync(language.path) && language.path.endsWith('.json')) { + // Create the file (if it does not exist), append language data to it + String name = language.path.split('/').last; + String path = '${_assets.path}/$name'; + _createFile(path); + File file = File(language.path); + _writeFile(path, jsonDecode(file.readAsStringSync())); + } + }); + print(''); + } + }); +} + +void _writeFile(String path, Map data) { + // Read the current data in the file + File file = File(path); + Map fileData = jsonDecode(file.readAsStringSync()); + // Ensure the file exists + if(file.existsSync()) { + print('---> Writing localization strings...'); + // Add all the localization strings to the file, and write back the string version of the map. + + fileData.addAll(data); + JsonEncoder encoder = JsonEncoder.withIndent(' '); + file.writeAsStringSync(encoder.convert(fileData), mode: FileMode.write, flush: true); + } else { + print('---> Failed to find file. Skipping...'); + return; + } +} + +void _createFile(String path) { + File file = File(path); + if(file.existsSync()) { + print('---> Found file. Skipping...'); + return; + } + print('---> Created empty file...'); + file.createSync(recursive: true); + file.writeAsStringSync('{}', flush: true); +}