diff --git a/.gitignore b/.gitignore index 69bed392..a23a8a27 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ LunaSea.ipa ios/build/ ExportOptions.plist api_responses/ +pre_archive diff --git a/assets/CustomIcons.ttf b/assets/CustomIcons.ttf new file mode 100644 index 00000000..0d473a6d Binary files /dev/null and b/assets/CustomIcons.ttf differ diff --git a/assets/custom_icons/CustomIcons.ttf b/assets/custom_icons/CustomIcons.ttf deleted file mode 100644 index 55459853..00000000 Binary files a/assets/custom_icons/CustomIcons.ttf and /dev/null differ diff --git a/assets/custom_icons/config.json b/assets/custom_icons/config.json deleted file mode 100644 index 86089f2f..00000000 --- a/assets/custom_icons/config.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "CustomIcons", - "css_prefix_text": "", - "css_use_suffix": false, - "hinting": true, - "units_per_em": 1000, - "ascent": 850, - "glyphs": [ - { - "uid": "0f79e958e1ed08e14ccbd7369b5d4440", - "css": "sabnzbd", - "code": 59392, - "src": "custom_icons", - "selected": true, - "svg": { - "path": "M252.3 109.8C249.8 110.8 244.6 114.4 240.8 117.8L233.8 124.1 233.8 289.9 233.8 455.6 178.7 455.2C119 454.9 113.8 456 107.7 470.7 101.1 486.8 105.8 497.6 130.9 523.4L154 547.2 146.1 558.9 138.1 570.6 139.6 639.9C140.4 678 141 712 140.9 715.4 140.9 718.8 140.7 735.8 140.4 753.3L140 785.1 150 794.1 160.1 803.1 265.4 802.9C323.4 802.7 373.5 801.5 376.9 800.2 387.1 796.1 397.1 803.7 438.9 847.4 498 909.1 496.9 909.1 555.9 848.1 602.4 799.9 607.1 796.3 617.8 800.3 630.7 805.2 829.7 803.8 839.6 798.7 854.8 790.8 855.4 786.1 855.4 674.1L855.4 570.1 848 559.2 840.7 548.4 861.8 527.4C893.6 495.9 898.6 479.8 882.3 462.3 876.1 455.6 873.9 455.4 820.2 455.4L764.6 455.4 764.6 290.8 764.6 126.3 756.1 117.8 747.6 109.2 502.2 108.7C367.3 108.4 254.8 108.9 252.3 109.8M735.4 310.8L735.4 483.1 798.7 483.1 862 483.1 828.8 516.9 795.7 550.8 765.5 550.8 735.4 550.8 735.3 519.2C735.3 471.7 735.4 471.8 666.3 473.8 601.8 475.8 603.1 474.9 603.1 520.3L603.1 550.8 493.1 550.9C432.6 551 342.2 551 292.3 550.9L201.5 550.7 169.2 517.7 136.9 484.6 199.2 483.8 261.5 483 261.5 310.7 261.5 138.5 498.5 138.5 735.4 138.5 735.4 310.8M708.4 539.2L709.2 576.9 766.9 577.8 824.6 578.6 824.6 675.5 824.6 772.3 727.7 772.3 630.8 772.3 630.8 636.9 630.8 501.5 669.1 501.5 707.5 501.5 708.4 539.2M363.1 598.5L363.1 618.5 306.2 618.5 249.2 618.5 249.2 636.9 249.2 655.4 306.2 655.4 363.1 655.4 363.1 713.8 363.1 772.3 266.2 772.3 169.2 772.3 169.2 752.4 169.2 732.4 226.9 731.6 284.6 730.8 285.5 713.1 286.5 695.4 227.8 695.4 169.2 695.4 169.2 636.9 169.2 578.5 266.2 578.5 363.1 578.5 363.1 598.5M593.8 675.4L593.8 772.3 496.9 772.3 400 772.3 400 713.8 400 655.4 456.9 655.4 513.8 655.4 513.8 636.9 513.8 618.5 456.9 618.5 400 618.5 400 598.5 400 578.5 496.9 578.5 593.8 578.5 593.8 675.4M708.4 674.6L709.2 730.8 726.9 731.7 744.6 732.6 744.6 675.5 744.6 618.5 726.1 618.5 707.6 618.5 708.4 674.6M480 713.8L480 732.3 496.9 732.3 513.8 732.3 513.8 713.8 513.8 695.4 496.9 695.4 480 695.4 480 713.8M529.2 831.9L497.7 863.8 475.8 840.5C463.7 827.7 449.7 813.3 444.7 808.6L435.6 800 498.1 800 560.6 800 529.2 831.9", - "width": 1000 - }, - "search": [ - "sabnzbd" - ] - }, - { - "uid": "f08ba9f0e8f9b426a92efcaa59c1e932", - "css": "reddit-alien", - "code": 62081, - "src": "fontawesome" - }, - { - "uid": "0f6a2573a7b6df911ed199bb63717e27", - "css": "github-circled", - "code": 61595, - "src": "fontawesome" - }, - { - "uid": "bbf307b0e5e61f59d838e83455cabecc", - "css": "nzbget", - "code": 59394, - "src": "custom_icons", - "selected": true, - "svg": { - "path": "M412.9 68.7C131 119.7-26.3 441.8 104.3 701 250.5 991.1 661.2 1026.6 852 765.6 1089.5 440.7 810-3.1 412.9 68.7M599.6 117C892.5 197.1 993.1 558.1 783.4 776.7 591 977.3 257.7 922.7 138.8 671.2 38 457.9 147.6 200.5 371.5 124.8 435.9 103.1 536.1 99.6 599.6 117M421.9 294.9L421.9 324.2 502 324.2 582 324.2 582 294.9 582 265.6 502 265.6 421.9 265.6 421.9 294.9M421.9 380.9L421.9 410.2 502 410.2 582 410.2 582 380.9 582 351.6 502 351.6 421.9 351.6 421.9 380.9M421.4 438.4C421.2 441.1 421.3 461.4 421.8 483.4L422.6 523.4 367.4 523.4C321.3 523.4 312.6 524.3 315.2 528.9 320.1 537.4 499.3 718.8 502.8 718.8 506.7 718.8 695.3 530.3 695.3 526.3 695.3 524.7 669.9 523.4 638.8 523.4L582.3 523.4 581.2 479.5 580.1 435.5 501 434.5C439.2 433.7 421.8 434.5 421.4 438.4", - "width": 1000 - }, - "search": [ - "nzbget" - ] - } - ] -} \ No newline at end of file diff --git a/assets/custom_icons/images/nzbget.png b/assets/custom_icons/images/nzbget.png deleted file mode 100644 index dab63b48..00000000 Binary files a/assets/custom_icons/images/nzbget.png and /dev/null differ diff --git a/assets/custom_icons/images/nzbget.svg b/assets/custom_icons/images/nzbget.svg deleted file mode 100644 index e2683a3b..00000000 --- a/assets/custom_icons/images/nzbget.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/custom_icons/images/sabnzbd.png b/assets/custom_icons/images/sabnzbd.png deleted file mode 100644 index 5011834b..00000000 Binary files a/assets/custom_icons/images/sabnzbd.png and /dev/null differ diff --git a/assets/custom_icons/images/sabnzbd.svg b/assets/custom_icons/images/sabnzbd.svg deleted file mode 100644 index e3eac6bc..00000000 --- a/assets/custom_icons/images/sabnzbd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/core.dart b/lib/core.dart new file mode 100644 index 00000000..ea3d9616 --- /dev/null +++ b/lib/core.dart @@ -0,0 +1,4 @@ +export 'package:lunasea/core/app_state.dart'; +export 'package:lunasea/core/constants.dart'; +export 'package:lunasea/core/extensions.dart'; +export 'package:lunasea/core/reducers.dart'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart new file mode 100644 index 00000000..3f787767 --- /dev/null +++ b/lib/core/app_state.dart @@ -0,0 +1,10 @@ +import 'package:redux/redux.dart'; + +final appReducer = combineReducers([ + +]); + +class AppState { + AppState(); + factory AppState.initialState() => AppState(); +} diff --git a/lib/system/constants.dart b/lib/core/constants.dart similarity index 95% rename from lib/system/constants.dart rename to lib/core/constants.dart index 812dd243..712c5279 100644 --- a/lib/system/constants.dart +++ b/lib/core/constants.dart @@ -29,12 +29,6 @@ class Constants { "fixes": ['Unable to fetch changes'], "tweaks": ['Unable to fetch changes'], }]; - static const Map LOWEST_VERSION = { - 'major': 0, - 'minor': 0, - 'revision': 0, - 'code': 0, - }; //Filesystem static const FILESYSTEM_INVALID = '<>'; static const ENCRYPTION_FAILURE = '<>'; diff --git a/lib/core/extensions.dart b/lib/core/extensions.dart new file mode 100644 index 00000000..705b17ba --- /dev/null +++ b/lib/core/extensions.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/core/extensions/bytes.dart'; +export 'package:lunasea/core/extensions/datetime.dart'; +export 'package:lunasea/core/extensions/functions.dart'; diff --git a/lib/core/extensions/bytes.dart b/lib/core/extensions/bytes.dart new file mode 100644 index 00000000..3ffc300c --- /dev/null +++ b/lib/core/extensions/bytes.dart @@ -0,0 +1,13 @@ +import 'package:lunasea/core.dart'; + +extension BytesExtension on int { + String toStringFromBytes({ int decimals = 2 }) { + int position = 0; + double size = this.toDouble(); + while(size > 1024) { + size /= 1024; + position++; + } + return '${size.toStringAsFixed(decimals)} ${Constants.BYTE_SIZES[position]}'; + } +} diff --git a/lib/core/extensions/datetime.dart b/lib/core/extensions/datetime.dart new file mode 100644 index 00000000..0a80cb8d --- /dev/null +++ b/lib/core/extensions/datetime.dart @@ -0,0 +1,5 @@ +extension DateTimeExtension on DateTime { + DateTime round() { + return DateTime(this.year, this.month, this.day); + } +} diff --git a/lib/system/functions.dart b/lib/core/extensions/functions.dart similarity index 99% rename from lib/system/functions.dart rename to lib/core/extensions/functions.dart index c123d9ad..3deb47d6 100644 --- a/lib/system/functions.dart +++ b/lib/core/extensions/functions.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; import 'package:url_launcher/url_launcher.dart'; class Functions { diff --git a/lib/core/reducers.dart b/lib/core/reducers.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/logic.dart b/lib/logic.dart new file mode 100644 index 00000000..d92cd3d6 --- /dev/null +++ b/lib/logic.dart @@ -0,0 +1,5 @@ +export 'package:lunasea/logic/abstracts.dart'; +export 'package:lunasea/logic/automation.dart'; +export 'package:lunasea/logic/clients.dart'; +export 'package:lunasea/logic/calendar.dart'; +export 'package:lunasea/logic/system.dart'; diff --git a/lib/logic/abstracts.dart b/lib/logic/abstracts.dart new file mode 100644 index 00000000..f85ebd65 --- /dev/null +++ b/lib/logic/abstracts.dart @@ -0,0 +1,9 @@ +export 'package:lunasea/logic/abstracts/calendar.dart'; + +abstract class API { + void logWarning(String methodName, String text); + void logError(String methodName, String text, Object error); + Future testConnection(List values); +} + +abstract class Entry {} diff --git a/lib/logic/abstracts/calendar.dart b/lib/logic/abstracts/calendar.dart new file mode 100644 index 00000000..eaf425e1 --- /dev/null +++ b/lib/logic/abstracts/calendar.dart @@ -0,0 +1 @@ +export 'package:lunasea/logic/abstracts/calendar/entry.dart'; diff --git a/lib/logic/abstracts/calendar/entry.dart b/lib/logic/abstracts/calendar/entry.dart new file mode 100644 index 00000000..0e294bb1 --- /dev/null +++ b/lib/logic/abstracts/calendar/entry.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/logic/abstracts.dart'; + +abstract class CalendarEntry implements Entry { + int id; + String title; + + TextSpan get subtitle; + IconButton get trailing; + String get bannerURI; + Future enterContent(BuildContext context); + + CalendarEntry( + this.id, + this.title, + ); +} diff --git a/lib/logic/automation.dart b/lib/logic/automation.dart new file mode 100644 index 00000000..a44040dc --- /dev/null +++ b/lib/logic/automation.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/logic/automation/lidarr.dart'; +export 'package:lunasea/logic/automation/radarr.dart'; +export 'package:lunasea/logic/automation/sonarr.dart'; diff --git a/lib/logic/automation/lidarr/api.dart b/lib/logic/automation/lidarr/api.dart index 852a2248..80c17382 100644 --- a/lib/logic/automation/lidarr/api.dart +++ b/lib/logic/automation/lidarr/api.dart @@ -1,8 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/logger.dart'; class LidarrAPI { LidarrAPI._(); @@ -214,13 +213,13 @@ class LidarrAPI { return null; } - static Future removeArtist(int artistID) async { + static Future removeArtist(int artistID, { deleteFiles = false }) async { List values = Values.lidarrValues; if(values[0] == false) { return false; } try { - String uri = '${values[1]}/api/v1/artist/$artistID?apikey=${values[2]}'; + String uri = '${values[1]}/api/v1/artist/$artistID?apikey=${values[2]}&deleteFiles=$deleteFiles'; http.Response response = await http.delete( Uri.encodeFull(uri), ); diff --git a/lib/logic/automation/lidarr/entry/album.dart b/lib/logic/automation/lidarr/entry/album.dart index 1e50c293..84932839 100644 --- a/lib/logic/automation/lidarr/entry/album.dart +++ b/lib/logic/automation/lidarr/entry/album.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; import 'package:intl/intl.dart'; class LidarrAlbumEntry { diff --git a/lib/logic/automation/lidarr/entry/catalogue.dart b/lib/logic/automation/lidarr/entry/catalogue.dart index 562ece21..1e3dd696 100644 --- a/lib/logic/automation/lidarr/entry/catalogue.dart +++ b/lib/logic/automation/lidarr/entry/catalogue.dart @@ -1,5 +1,5 @@ -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; class LidarrCatalogueEntry { String title; @@ -46,7 +46,7 @@ class LidarrCatalogueEntry { } String get subtitle { - return '$albums\t•\t$tracks\n${Functions.bytesToReadable(sizeOnDisk)}'; + return '$albums\t•\t$tracks\n${sizeOnDisk.toStringFromBytes()}'; } String get tracks { diff --git a/lib/logic/automation/lidarr/entry/history.dart b/lib/logic/automation/lidarr/entry/history.dart index 73aa2c4b..d440fe17 100644 --- a/lib/logic/automation/lidarr/entry/history.dart +++ b/lib/logic/automation/lidarr/entry/history.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; abstract class LidarrHistoryEntry { String title; diff --git a/lib/logic/automation/lidarr/entry/missing.dart b/lib/logic/automation/lidarr/entry/missing.dart index dfdb44e2..b567b8b4 100644 --- a/lib/logic/automation/lidarr/entry/missing.dart +++ b/lib/logic/automation/lidarr/entry/missing.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; class LidarrMissingEntry { String title; diff --git a/lib/logic/automation/lidarr/entry/track.dart b/lib/logic/automation/lidarr/entry/track.dart index 6b054dd8..a8ceafa9 100644 --- a/lib/logic/automation/lidarr/entry/track.dart +++ b/lib/logic/automation/lidarr/entry/track.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class LidarrTrackEntry { String title; diff --git a/lib/logic/automation/radarr/api.dart b/lib/logic/automation/radarr/api.dart index 2d16924c..6713935d 100644 --- a/lib/logic/automation/radarr/api.dart +++ b/lib/logic/automation/radarr/api.dart @@ -1,8 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/logger.dart'; class RadarrAPI { RadarrAPI._(); @@ -245,13 +244,13 @@ class RadarrAPI { return false; } - static Future removeMovie(int id) async { + static Future removeMovie(int id, {bool deleteFiles = false }) async { List values = Values.radarrValues; if(values[0] == false) { return false; } try { - String uri = '${values[1]}/api/movie/$id?apikey=${values[2]}'; + String uri = '${values[1]}/api/movie/$id?apikey=${values[2]}&deleteFiles=$deleteFiles'; http.Response response = await http.delete( Uri.encodeFull(uri), ); @@ -470,29 +469,65 @@ class RadarrAPI { Uri.encodeFull(uri), ); if(response.statusCode == 200) { - List _availableEntries = []; + List _entries = []; + List body = json.decode(response.body); + for(var entry in body) { + if(!entry['downloaded'] && entry['monitored'] && entry['status'] == 'released') { + var _quality = entry['qualityProfileId'] != null ? _qualities[entry['qualityProfileId']] : null; + _entries.add( + RadarrMissingEntry( + entry['id'] ?? -1, + entry['title'] ?? 'Unknown Title', + entry['sortTitle'] ?? 'Unknown Title', + entry['studio'] ?? 'Unknown Studio', + entry['physicalRelease'] ?? '', + entry['inCinemas'] ?? '', + _quality != null ? _quality.name : '', + entry['year'] ?? 0, + entry['runtime'] ?? 0, + entry['status'] ?? 'Unknown Status', + ), + ); + } + } + //Sort the list by the appropriate dates + _entries.sort((a,b) { + if(a.physicalReleaseObject == null) return 1; + if(b.physicalReleaseObject == null) return -1; + return b.physicalReleaseObject.compareTo(a.physicalReleaseObject); + }); + return _entries; + } else { + logError('getMissing', ' HTTP Status Code (${response.statusCode})', null); + } + } + } catch (e) { + logError('getMissing', 'Failed to fetch missing movies', e); + return null; + } + logWarning('getMissing', 'Failed to fetch missing movies'); + return null; + } + + static Future> getUpcoming() async { + List values = Values.radarrValues; + if(values[0] == false) { + return null; + } + try { + Map _qualities = await getQualityProfiles(); + if(_qualities != null) { + String uri = '${values[1]}/api/movie?apikey=${values[2]}'; + http.Response response = await http.get( + Uri.encodeFull(uri), + ); + if(response.statusCode == 200) { List _cinemaEntries = []; List _announcedEntries = []; List body = json.decode(response.body); for(var entry in body) { if(!entry['downloaded'] && entry['monitored']) { var _quality = entry['qualityProfileId'] != null ? _qualities[entry['qualityProfileId']] : null; - if(entry['status'] == 'released') { - _availableEntries.add( - RadarrMissingEntry( - entry['id'] ?? -1, - entry['title'] ?? 'Unknown Title', - entry['sortTitle'] ?? 'Unknown Title', - entry['studio'] ?? 'Unknown Studio', - entry['physicalRelease'] ?? '', - entry['inCinemas'] ?? '', - _quality != null ? _quality.name : '', - entry['year'] ?? 0, - entry['runtime'] ?? 0, - entry['status'] ?? 'Unknown Status', - ), - ); - } if(entry['status'] == 'inCinemas') { _cinemaEntries.add( RadarrMissingEntry( @@ -527,12 +562,7 @@ class RadarrAPI { } } } - //Sort the lists by the appropriate dates - _availableEntries.sort((a,b) { - if(a.physicalReleaseObject == null) return 1; - if(b.physicalReleaseObject == null) return -1; - return b.physicalReleaseObject.compareTo(a.physicalReleaseObject); - }); + //Sort entries _cinemaEntries.sort((a,b) { if(a.physicalReleaseObject == null) return 1; if(b.physicalReleaseObject == null) return -1; @@ -543,16 +573,16 @@ class RadarrAPI { if(b.inCinemasObject == null) return -1; return a.inCinemasObject.compareTo(b.inCinemasObject); }); - return [_availableEntries, _cinemaEntries, _announcedEntries].expand((x) => x).toList(); + return [_cinemaEntries, _announcedEntries].expand((x) => x).toList(); } else { - logError('getMissing', ' HTTP Status Code (${response.statusCode})', null); + logError('getUpcoming', ' HTTP Status Code (${response.statusCode})', null); } } } catch (e) { - logError('getMissing', 'Failed to fetch missing movies', e); + logError('getUpcoming', 'Failed to fetch upcoming movies', e); return null; } - logWarning('getMissing', 'Failed to fetch missing movies'); + logWarning('getUpcoming', 'Failed to fetch upcoming movies'); return null; } diff --git a/lib/logic/automation/radarr/entry/catalogue.dart b/lib/logic/automation/radarr/entry/catalogue.dart index eddfb462..48666ca8 100644 --- a/lib/logic/automation/radarr/entry/catalogue.dart +++ b/lib/logic/automation/radarr/entry/catalogue.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:intl/intl.dart'; class RadarrCatalogueEntry { diff --git a/lib/logic/automation/radarr/entry/history.dart b/lib/logic/automation/radarr/entry/history.dart index 5198555a..838500e3 100644 --- a/lib/logic/automation/radarr/entry/history.dart +++ b/lib/logic/automation/radarr/entry/history.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; abstract class RadarrHistoryEntry { String movieTitle; diff --git a/lib/logic/automation/radarr/entry/missing.dart b/lib/logic/automation/radarr/entry/missing.dart index 390ae991..46e5453e 100644 --- a/lib/logic/automation/radarr/entry/missing.dart +++ b/lib/logic/automation/radarr/entry/missing.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; class RadarrMissingEntry { String title; diff --git a/lib/logic/automation/radarr/entry/upcoming.dart b/lib/logic/automation/radarr/entry/upcoming.dart index fd36e3c7..d37db242 100644 --- a/lib/logic/automation/radarr/entry/upcoming.dart +++ b/lib/logic/automation/radarr/entry/upcoming.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; class RadarrUpcomingEntry { String title; diff --git a/lib/logic/automation/sonarr/api.dart b/lib/logic/automation/sonarr/api.dart index d2bb0381..851af911 100644 --- a/lib/logic/automation/sonarr/api.dart +++ b/lib/logic/automation/sonarr/api.dart @@ -1,9 +1,8 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/logic/automation/sonarr.dart'; import 'package:intl/intl.dart'; -import 'package:lunasea/system/logger.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/logic/automation/sonarr.dart'; class SonarrAPI { SonarrAPI._(); @@ -93,13 +92,13 @@ class SonarrAPI { return false; } - static Future removeSeries(int seriesID) async { + static Future removeSeries(int seriesID, { deleteFiles = false }) async { List values = Values.sonarrValues; if(values[0] == false) { return false; } try { - String uri = '${values[1]}/api/series/$seriesID?apikey=${values[2]}'; + String uri = '${values[1]}/api/series/$seriesID?apikey=${values[2]}&deleteFiles=$deleteFiles'; http.Response response = await http.delete( Uri.encodeFull(uri), ); @@ -578,6 +577,7 @@ class SonarrAPI { entry['episodeNumber'] ?? 0, entry['airDateUtc'] ?? '', entry['id'] ?? -1, + entry['episodeFileId'] ?? -1, entry['monitored'] ?? false, entry['hasFile'] ?? false, quality ?? 'Unknown Quality', @@ -1075,4 +1075,64 @@ class SonarrAPI { logWarning('downloadRelease', 'Failed to download release ($guid)'); return false; } + + static Future toggleEpisodeMonitored(int episodeID, bool status) async { + List values = Values.sonarrValues; + if(values[0] == false) { + return false; + } + try { + String uriGet = '${values[1]}/api/episode/$episodeID?apikey=${values[2]}'; + String uriPut = '${values[1]}/api/episode?apikey=${values[2]}'; + http.Response response = await http.get( + Uri.encodeFull(uriGet), + ); + if(response.statusCode == 200) { + Map body = json.decode(response.body); + body['monitored'] = status; + response = await http.put( + Uri.encodeFull(uriPut), + body: json.encode(body), + headers: { + "Content-Type": "application/json", + }, + ); + if(response.statusCode == 202) { + return true; + } else { + logError('toggleSeasonMonitored', ' HTTP Status Code (${response.statusCode})', null); + } + } else { + logError('toggleSeasonMonitored', ' HTTP Status Code (${response.statusCode})', null); + } + } catch (e) { + logError('toggleEpisodeMonitored', 'Failed to toggle episode monitored state ($episodeID, $status)', e); + return false; + } + logWarning('toggleEpisodeMonitored', 'Failed to toggle episode monitored state ($episodeID, $status)'); + return false; + } + + static Future deleteEpisodeFile(int episodeFileID) async { + List values = Values.sonarrValues; + if(values[0] == false) { + return false; + } + try { + String uri = '${values[1]}/api/episodefile/$episodeFileID?apikey=${values[2]}'; + http.Response response = await http.delete( + Uri.encodeFull(uri), + ); + if(response.statusCode == 200) { + return true; + } else { + logError('deleteEpisodeFile', ' HTTP Status Code (${response.statusCode})', null); + } + } catch (e) { + logError('deleteEpisodeFile', 'Failed to delete episode file ($episodeFileID)', e); + return false; + } + logWarning('deleteEpisodeFile', 'Failed to delete episode file ($episodeFileID)'); + return false; + } } diff --git a/lib/logic/automation/sonarr/entry/catalogue.dart b/lib/logic/automation/sonarr/entry/catalogue.dart index 0643a70a..4159db1d 100644 --- a/lib/logic/automation/sonarr/entry/catalogue.dart +++ b/lib/logic/automation/sonarr/entry/catalogue.dart @@ -1,6 +1,6 @@ -import 'package:lunasea/configuration/values.dart'; import 'package:intl/intl.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; class SonarrCatalogueEntry { String title; diff --git a/lib/logic/automation/sonarr/entry/episode.dart b/lib/logic/automation/sonarr/entry/episode.dart index 344f442c..ce1b13c9 100644 --- a/lib/logic/automation/sonarr/entry/episode.dart +++ b/lib/logic/automation/sonarr/entry/episode.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; import 'package:intl/intl.dart'; class SonarrEpisodeEntry { @@ -10,6 +10,7 @@ class SonarrEpisodeEntry { int seasonNumber; int episodeNumber; int episodeID; + int episodeFileID; int size; bool isMonitored; bool hasFile; @@ -23,6 +24,7 @@ class SonarrEpisodeEntry { this.episodeNumber, this.airDate, this.episodeID, + this.episodeFileID, this.isMonitored, this.hasFile, this.quality, diff --git a/lib/logic/automation/sonarr/entry/history.dart b/lib/logic/automation/sonarr/entry/history.dart index f21065d3..b2ae182a 100644 --- a/lib/logic/automation/sonarr/entry/history.dart +++ b/lib/logic/automation/sonarr/entry/history.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; abstract class SonarrHistoryEntry { String seriesTitle; diff --git a/lib/logic/automation/sonarr/entry/missing.dart b/lib/logic/automation/sonarr/entry/missing.dart index 578f8464..c2558df8 100644 --- a/lib/logic/automation/sonarr/entry/missing.dart +++ b/lib/logic/automation/sonarr/entry/missing.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/system.dart'; class SonarrMissingEntry { String showTitle; diff --git a/lib/logic/automation/sonarr/entry/search.dart b/lib/logic/automation/sonarr/entry/search.dart index 9b4b3f31..d187be64 100644 --- a/lib/logic/automation/sonarr/entry/search.dart +++ b/lib/logic/automation/sonarr/entry/search.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class SonarrSearchEntry { String title; diff --git a/lib/logic/automation/sonarr/entry/upcoming.dart b/lib/logic/automation/sonarr/entry/upcoming.dart index aced820d..6c479ceb 100644 --- a/lib/logic/automation/sonarr/entry/upcoming.dart +++ b/lib/logic/automation/sonarr/entry/upcoming.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; class SonarrUpcomingEntry { String seriesTitle; diff --git a/lib/logic/calendar.dart b/lib/logic/calendar.dart new file mode 100644 index 00000000..3ebfd5da --- /dev/null +++ b/lib/logic/calendar.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/logic/calendar/api.dart'; +export 'package:lunasea/logic/calendar/entry.dart'; diff --git a/lib/logic/calendar/api.dart b/lib/logic/calendar/api.dart new file mode 100644 index 00000000..8991c303 --- /dev/null +++ b/lib/logic/calendar/api.dart @@ -0,0 +1,145 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/logic/abstracts.dart'; +import 'package:lunasea/logic/calendar.dart'; + +class CalendarAPI extends API { + @override + void logError(String methodName, String text, Object error) { + Logger.error('package:lunasea/logic/home/api.dart', methodName, 'Home: $text', error, StackTrace.current); + } + + @override + void logWarning(String methodName, String text) { + Logger.warning('package:lunasea/logic/home/api.dart', methodName, 'Home: $text'); + } + + Future> getUpcoming(DateTime today) async { + Map _upcoming = {}; + await _getLidarrUpcoming(_upcoming, today); + await _getRadarrUpcoming(_upcoming, today); + await _getSonarrUpcoming(_upcoming, today); + return _upcoming; + } + + Future _getLidarrUpcoming(Map map, DateTime today, { int startOffset = 7, int endOffset = 60 }) async { + try { + List values = Values.lidarrValues; + if(values[0]) { + String start = DateFormat('y-MM-dd').format(today.subtract(Duration(days: startOffset))); + String end = DateFormat('y-MM-dd').format(today.add(Duration(days: endOffset))); + String uri = '${values[1]}/api/v1/calendar?apikey=${values[2]}&start=$start&end=$end'; + http.Response response = await http.get(Uri.encodeFull(uri)); + if(response.statusCode == 200) { + List body = json.decode(response.body); + if(body.length > 0) { + for(var entry in body) { + DateTime date = DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal()?.round(); + if(date != null) { + List day = map[date] ?? []; + day.add(CalendarLidarrEntry( + id: entry['id'] ?? 0, + title: entry['artist']['artistName'] ?? 'Unknown Artist', + albumTitle: entry['title'] ?? 'Unknown Album Title', + artistId: entry['artist']['id'] ?? 0, + hasAllFiles: (entry['statistics'] != null ? entry['statistics']['percentOfTracks'] ?? 0 : 0) == 100, + )); + map[date] = day; + } + } + } + } else { + logError('_getLidarrUpcoming', ' HTTP Status Code (${response.statusCode})', null); + } + } + } catch (e) { + logError('_getLidarrUpcoming', 'Failed to fetch Lidarr upcoming content', e); + return; + } + } + + Future _getRadarrUpcoming(Map map, DateTime today, { int startOffset = 7, int endOffset = 60 }) async { + try { + List values = Values.radarrValues; + if(values[0]) { + String start = DateFormat('y-MM-dd').format(today.subtract(Duration(days: startOffset))); + String end = DateFormat('y-MM-dd').format(today.add(Duration(days: endOffset))); + String uri = '${values[1]}/api/calendar?apikey=${values[2]}&start=$start&end=$end'; + http.Response response = await http.get(Uri.encodeFull(uri)); + if(response.statusCode == 200) { + List body = json.decode(response.body); + if(body.length > 0) { + for(var entry in body) { + DateTime date = DateTime.tryParse(entry['physicalRelease'] ?? '')?.toLocal()?.round(); + if(date != null) { + List day = map[date] ?? []; + day.add(CalendarRadarrEntry( + id: entry['id'] ?? 0, + title: entry['title'] ?? 'Unknown Title', + hasFile: entry['hasFile'] ?? false, + fileQualityProfile: entry['hasFile'] ? entry['movieFile']['quality']['quality']['name'] : '', + year: entry['year'] ?? 0, + runtime: entry['runtime'] ?? 0, + )); + map[date] = day; + } + } + } + } else { + logError('_getRadarrUpcoming', ' HTTP Status Code (${response.statusCode})', null); + } + } + } catch (e) { + logError('_getRadarrUpcoming', 'Failed to fetch Radarr upcoming content', e); + return; + } + } + + Future _getSonarrUpcoming(Map map, DateTime today, { int startOffset = 7, int endOffset = 60 }) async { + try { + List values = Values.sonarrValues; + if(values[0]) { + String start = DateFormat('y-MM-dd').format(today.subtract(Duration(days: startOffset))); + String end = DateFormat('y-MM-dd').format(today.add(Duration(days: endOffset))); + String uri = '${values[1]}/api/calendar?apikey=${values[2]}&start=$start&end=$end'; + http.Response response = await http.get(Uri.encodeFull(uri)); + if(response.statusCode == 200) { + List body = json.decode(response.body); + if(body.length > 0) { + for(var entry in body) { + DateTime date = DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal()?.round(); + if(date != null) { + List day = map[date] ?? []; + day.add(CalendarSonarrEntry( + id: entry['id'] ?? 0, + seriesID: entry['seriesId'] ?? 0, + title: entry['series']['title'] ?? 'Unknown Series', + episodeTitle: entry['title'] ?? 'Unknown Episode Title', + seasonNumber: entry['seasonNumber'] ?? -1, + episodeNumber: entry['episodeNumber'] ?? -1, + airTime: entry['airDateUtc'] ?? '', + hasFile: entry['hasFile'] ?? false, + fileQualityProfile: entry['hasFile'] ? entry['episodeFile']['quality']['quality']['name'] : '', + )); + map[date] = day; + } + } + } + } else { + logError('_getSonarrUpcoming', ' HTTP Status Code (${response.statusCode})', null); + } + } + } catch (e) { + logError('_getSonarrUpcoming', 'Failed to fetch Sonarr upcoming content', e); + return; + } + } + + @override + Future testConnection(List values) async { + return true; + } +} \ No newline at end of file diff --git a/lib/logic/calendar/entry.dart b/lib/logic/calendar/entry.dart new file mode 100644 index 00000000..04dc268b --- /dev/null +++ b/lib/logic/calendar/entry.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/logic/calendar/entry/lidarr.dart'; +export 'package:lunasea/logic/calendar/entry/radarr.dart'; +export 'package:lunasea/logic/calendar/entry/sonarr.dart'; diff --git a/lib/logic/calendar/entry/lidarr.dart b/lib/logic/calendar/entry/lidarr.dart new file mode 100644 index 00000000..ab1b3f61 --- /dev/null +++ b/lib/logic/calendar/entry/lidarr.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/artist.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/logic/abstracts.dart'; +import 'package:lunasea/widgets/ui.dart'; + +class CalendarLidarrEntry extends CalendarEntry { + String albumTitle; + int artistId; + bool hasAllFiles; + + CalendarLidarrEntry({ + @required int id, + @required String title, + @required this.albumTitle, + @required this.artistId, + @required this.hasAllFiles, + }) : super(id, title); + + @override + String get bannerURI { + List values = Values.lidarrValues; + return '${values[1]}/api/v1/MediaCover/Artist/$artistId/banner.jpg?apikey=${values[2]}'; + } + + @override + TextSpan get subtitle => TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: albumTitle, + style: TextStyle( + fontStyle: FontStyle.italic, + ), + ), + if(!hasAllFiles) TextSpan( + text: '\nNot Downloaded', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), + ), + if(hasAllFiles) TextSpan( + text: '\nDownloaded', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(Constants.ACCENT_COLOR), + ), + ) + ], + ); + + @override + Future enterContent(BuildContext context) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => LidarrArtistDetails(entry: null, artistID: artistId), + ), + ); + } + + @override + IconButton get trailing => IconButton( + icon: Elements.getIcon(Icons.arrow_forward_ios), + onPressed: null, + ); +} \ No newline at end of file diff --git a/lib/logic/calendar/entry/radarr.dart b/lib/logic/calendar/entry/radarr.dart new file mode 100644 index 00000000..393243ed --- /dev/null +++ b/lib/logic/calendar/entry/radarr.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/routes/radarr/subpages/details/movie.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/logic/abstracts.dart'; + +class CalendarRadarrEntry extends CalendarEntry { + bool hasFile; + String fileQualityProfile; + int year; + int runtime; + + CalendarRadarrEntry({ + @required int id, + @required String title, + @required this.hasFile, + @required this.fileQualityProfile, + @required this.year, + @required this.runtime, + }): super(id, title); + + String get runtimeString { + if(runtime == null || runtime == 0) { + return ''; + } + return Functions.runtimeReadable(runtime, withDot: true); + } + + @override + TextSpan get subtitle => TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: '$year$runtimeString', + ), + if(!hasFile) TextSpan( + text: '\nNot Downloaded', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), + ), + if(hasFile) TextSpan( + text: '\nDownloaded ($fileQualityProfile)', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(Constants.ACCENT_COLOR), + ), + ) + ], + ); + + @override + String get bannerURI { + List values = Values.radarrValues; + return '${values[1]}/api/MediaCover/$id/fanart-360.jpg?apikey=${values[2]}'; + } + + @override + Future enterContent(BuildContext context) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => RadarrMovieDetails(entry: null, movieID: id), + ), + ); + } + + @override + IconButton get trailing => IconButton( + icon: Elements.getIcon(Icons.arrow_forward_ios), + onPressed: null, + ); +} \ No newline at end of file diff --git a/lib/logic/calendar/entry/sonarr.dart b/lib/logic/calendar/entry/sonarr.dart new file mode 100644 index 00000000..a810b29d --- /dev/null +++ b/lib/logic/calendar/entry/sonarr.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/show.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/logic/abstracts.dart'; + +class CalendarSonarrEntry extends CalendarEntry { + String episodeTitle; + int seasonNumber; + int episodeNumber; + int seriesID; + String airTime; + bool hasFile; + String fileQualityProfile; + + CalendarSonarrEntry({ + @required int id, + @required String title, + @required this.episodeTitle, + @required this.seasonNumber, + @required this.episodeNumber, + @required this.seriesID, + @required this.airTime, + @required this.hasFile, + @required this.fileQualityProfile, + }) : super(id, title); + + @override + TextSpan get subtitle => TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: 'Season $seasonNumber Episode $episodeNumber: ', + ), + TextSpan( + text: episodeTitle, + style: TextStyle( + fontStyle: FontStyle.italic, + ), + ), + if(!hasFile) TextSpan( + text: '\nNot Downloaded', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), + ), + if(hasFile) TextSpan( + text: '\nDownloaded ($fileQualityProfile)', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(Constants.ACCENT_COLOR), + ), + ) + ], + ); + + @override + String get bannerURI { + List values = Values.sonarrValues; + return '${values[1]}/api/mediacover/$seriesID/banner-70.jpg?apikey=${values[2]}'; + } + + @override + Future enterContent(BuildContext context) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SonarrShowDetails(entry: null, seriesID: seriesID), + ), + ); + } + + @override + IconButton get trailing => IconButton( + icon: Text( + airTimeString, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ), + onPressed: null, + ); + + DateTime get airTimeObject { + return DateTime.tryParse(airTime)?.toLocal(); + } + + String get airTimeString { + if(airTimeObject != null) { + return DateFormat('KK:mm\na').format(airTimeObject); + } + return 'N/A'; + } +} \ No newline at end of file diff --git a/lib/logic/clients.dart b/lib/logic/clients.dart new file mode 100644 index 00000000..09f07d2b --- /dev/null +++ b/lib/logic/clients.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/logic/clients/nzbget.dart'; +export 'package:lunasea/logic/clients/sabnzbd.dart'; diff --git a/lib/logic/clients/nzbget/api.dart b/lib/logic/clients/nzbget/api.dart index 2f8079b1..1f7bfe91 100644 --- a/lib/logic/clients/nzbget/api.dart +++ b/lib/logic/clients/nzbget/api.dart @@ -1,9 +1,8 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/logic/clients/nzbget/entry.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/logger.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/logic/clients/nzbget.dart'; class NZBGetAPI { NZBGetAPI._(); @@ -16,14 +15,10 @@ class NZBGetAPI { Logger.error('package:lunasea/logic/clients/nzbget/api.dart', methodName, 'NZBGet: $text', error, StackTrace.current); } - static String getURL(String uri) { - return Uri.encodeFull('$uri/jsonrpc'); - } - - static Map getHeader(String username, String password) { - return { - 'authorization': 'Basic ' + base64Encode(utf8.encode('$username:$password')), - }; + static String getURL(String uri, String username, String password) { + return (username != null && username != '' && password != null && password != '') + ? Uri.encodeFull('$uri/$username:$password/jsonrpc') + : Uri.encodeFull('$uri/jsonrpc'); } static String getBody(String method, {List params = Constants.EMPTY_LIST}) { @@ -38,8 +33,7 @@ class NZBGetAPI { static Future testConnection(List values) async { try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('version'), ); if(response.statusCode == 200) { @@ -81,8 +75,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('status'), ); if(response.statusCode == 200) { @@ -114,8 +107,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('status'), ); if(response.statusCode == 200) { @@ -151,8 +143,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'log', params: [ @@ -193,8 +184,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('listgroups'), ); if(response.statusCode == 200) { @@ -239,8 +229,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'history', params: [hidden], @@ -284,8 +273,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('pausedownload'), ); if(response.statusCode == 200) { @@ -312,8 +300,7 @@ class NZBGetAPI { try { if(await pauseQueue()) { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'scheduleresume', params: [minutes*60], @@ -343,8 +330,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('resumedownload'), ); if(response.statusCode == 200) { @@ -370,8 +356,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -404,8 +389,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -438,8 +422,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -472,8 +455,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -506,8 +488,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -540,8 +521,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -574,8 +554,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -608,8 +587,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -642,8 +620,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -676,8 +653,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -710,8 +686,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody('config'), ); if(response.statusCode == 200) { @@ -750,8 +725,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'editqueue', params: [ @@ -784,8 +758,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'append', params: [ @@ -826,8 +799,7 @@ class NZBGetAPI { try { String dataBase64 = utf8.fuse(base64).encode(data); http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'append', params: [ @@ -867,8 +839,7 @@ class NZBGetAPI { } try { http.Response response = await http.post( - getURL(values[1]), - headers: getHeader(values[2], values[3]), + getURL(values[1], values[2], values[3]), body: getBody( 'rate', params: [limit], diff --git a/lib/logic/clients/nzbget/entry/history.dart b/lib/logic/clients/nzbget/entry/history.dart index 8a4629dd..199e9d6f 100644 --- a/lib/logic/clients/nzbget/entry/history.dart +++ b/lib/logic/clients/nzbget/entry/history.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class NZBGetHistoryEntry { int id; diff --git a/lib/logic/clients/nzbget/entry/queue.dart b/lib/logic/clients/nzbget/entry/queue.dart index 8124c093..0519938e 100644 --- a/lib/logic/clients/nzbget/entry/queue.dart +++ b/lib/logic/clients/nzbget/entry/queue.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class NZBGetQueueEntry { int id; diff --git a/lib/logic/clients/nzbget/entry/statistics.dart b/lib/logic/clients/nzbget/entry/statistics.dart index b2d8957c..e044bec4 100644 --- a/lib/logic/clients/nzbget/entry/statistics.dart +++ b/lib/logic/clients/nzbget/entry/statistics.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class NZBGetStatisticsEntry { int freeSpaceHigh; diff --git a/lib/logic/clients/nzbget/entry/status.dart b/lib/logic/clients/nzbget/entry/status.dart index 83866be7..027aec21 100644 --- a/lib/logic/clients/nzbget/entry/status.dart +++ b/lib/logic/clients/nzbget/entry/status.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class NZBGetStatusEntry { bool paused; diff --git a/lib/logic/clients/sabnzbd/api.dart b/lib/logic/clients/sabnzbd/api.dart index a899f2b5..5abf6acb 100644 --- a/lib/logic/clients/sabnzbd/api.dart +++ b/lib/logic/clients/sabnzbd/api.dart @@ -1,8 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/logic/clients/sabnzbd/entry.dart'; -import 'package:lunasea/system/logger.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/logic/clients/sabnzbd.dart'; class SABnzbdAPI { SABnzbdAPI._(); diff --git a/lib/logic/clients/sabnzbd/entry/history.dart b/lib/logic/clients/sabnzbd/entry/history.dart index 326aa68a..b4e46030 100644 --- a/lib/logic/clients/sabnzbd/entry/history.dart +++ b/lib/logic/clients/sabnzbd/entry/history.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class SABnzbdHistoryEntry { String nzoId; diff --git a/lib/logic/clients/sabnzbd/entry/statistics.dart b/lib/logic/clients/sabnzbd/entry/statistics.dart index bef0967d..0e6b71ca 100644 --- a/lib/logic/clients/sabnzbd/entry/statistics.dart +++ b/lib/logic/clients/sabnzbd/entry/statistics.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class SABnzbdStatisticsEntry { List servers; diff --git a/lib/logic/clients/sabnzbd/entry/status.dart b/lib/logic/clients/sabnzbd/entry/status.dart index 3a43d760..1aee153e 100644 --- a/lib/logic/clients/sabnzbd/entry/status.dart +++ b/lib/logic/clients/sabnzbd/entry/status.dart @@ -1,4 +1,4 @@ -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/core.dart'; class SABnzbdStatusEntry { bool paused; diff --git a/lib/logic/system.dart b/lib/logic/system.dart new file mode 100644 index 00000000..178784ca --- /dev/null +++ b/lib/logic/system.dart @@ -0,0 +1 @@ +export 'package:lunasea/logic/system/version.dart'; diff --git a/lib/logic/system/version.dart b/lib/logic/system/version.dart index 3052310a..b5eec7aa 100644 --- a/lib/logic/system/version.dart +++ b/lib/logic/system/version.dart @@ -1,21 +1,9 @@ import 'package:http/http.dart' as http; import 'dart:convert'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class System { System._(); - - static Future getLatestVersion() async { - try { - http.Response response = await http.get( - Uri.encodeFull('https://www.lunasea.app/version.json'), - ); - if(response.statusCode == 200) { - return json.decode(response.body); - } - } catch (e) {} - return Constants.LOWEST_VERSION; - } static Future getChangelog() async { try { diff --git a/lib/main.dart b/lib/main.dart index d74517d7..94d1b633 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/pages/pages.dart'; -import 'package:lunasea/configuration/configuration.dart'; -import 'package:lunasea/system/logger.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:redux/redux.dart'; +import 'package:lunasea/routes.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -17,17 +18,25 @@ void main() async { } class _BIOS extends StatelessWidget { + final Store store = Store( + appReducer, + initialState: AppState.initialState(), + ); + @override Widget build(BuildContext context) { - return MaterialApp( - title: 'LunaSea', - debugShowCheckedModeBanner: false, - routes: _setRoute(), - theme: _setTheme(), + return StoreProvider( + store: store, + child: MaterialApp( + title: 'LunaSea', + debugShowCheckedModeBanner: false, + routes: _setRoutes(), + theme: _setTheme(), + ), ); } - Map _setRoute() { + Map _setRoutes() { return { '/': (BuildContext context) => Home(), '/settings': (BuildContext context) => Settings(), diff --git a/lib/pages/home/sections.dart b/lib/pages/home/sections.dart deleted file mode 100644 index 05c26094..00000000 --- a/lib/pages/home/sections.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'package:lunasea/pages/home/sections/summary.dart'; -export 'package:lunasea/pages/home/sections/quickaccess.dart'; diff --git a/lib/pages/lidarr/subpages.dart b/lib/pages/lidarr/subpages.dart deleted file mode 100644 index 552f3486..00000000 --- a/lib/pages/lidarr/subpages.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'package:lunasea/pages/lidarr/subpages/catalogue/catalogue.dart'; -export 'package:lunasea/pages/lidarr/subpages/history/history.dart'; -export 'package:lunasea/pages/lidarr/subpages/missing/missing.dart'; diff --git a/lib/pages/nzbget/subpages.dart b/lib/pages/nzbget/subpages.dart deleted file mode 100644 index d8f7e6c2..00000000 --- a/lib/pages/nzbget/subpages.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'package:lunasea/pages/nzbget/subpages/history/history.dart'; -export 'package:lunasea/pages/nzbget/subpages/queue/queue.dart'; diff --git a/lib/pages/pages.dart b/lib/pages/pages.dart deleted file mode 100644 index dd3ef7c1..00000000 --- a/lib/pages/pages.dart +++ /dev/null @@ -1,7 +0,0 @@ -export 'package:lunasea/pages/home/home.dart'; -export 'package:lunasea/pages/settings/settings.dart'; -export 'package:lunasea/pages/lidarr/lidarr.dart'; -export 'package:lunasea/pages/nzbget/nzbget.dart'; -export 'package:lunasea/pages/radarr/radarr.dart'; -export 'package:lunasea/pages/sonarr/sonarr.dart'; -export 'package:lunasea/pages/sabnzbd/sabnzbd.dart'; diff --git a/lib/pages/radarr/subpages.dart b/lib/pages/radarr/subpages.dart deleted file mode 100644 index e090fa34..00000000 --- a/lib/pages/radarr/subpages.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'package:lunasea/pages/radarr/subpages/catalogue/catalogue.dart'; -export 'package:lunasea/pages/radarr/subpages/history/history.dart'; -export 'package:lunasea/pages/radarr/subpages/missing/missing.dart'; -export 'package:lunasea/pages/radarr/subpages/details/movie.dart'; diff --git a/lib/pages/radarr/subpages/details/tabs/tabs.dart b/lib/pages/radarr/subpages/details/tabs/tabs.dart deleted file mode 100644 index b9f84320..00000000 --- a/lib/pages/radarr/subpages/details/tabs/tabs.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'package:lunasea/pages/radarr/subpages/details/tabs/files.dart'; -export 'package:lunasea/pages/radarr/subpages/details/tabs/overview.dart'; -export 'package:lunasea/pages/radarr/subpages/details/tabs/releases.dart'; diff --git a/lib/pages/sabnzbd/subpages.dart b/lib/pages/sabnzbd/subpages.dart deleted file mode 100644 index 839f1ba7..00000000 --- a/lib/pages/sabnzbd/subpages.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'package:lunasea/pages/sabnzbd/subpages/history/history.dart'; -export 'package:lunasea/pages/sabnzbd/subpages/queue/queue.dart'; diff --git a/lib/pages/settings/subpages.dart b/lib/pages/settings/subpages.dart deleted file mode 100644 index a7727f4b..00000000 --- a/lib/pages/settings/subpages.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'package:lunasea/pages/settings/subpages/general/general.dart'; -export 'package:lunasea/pages/settings/subpages/monitoring/monitoring.dart'; -export 'package:lunasea/pages/settings/subpages/automation/automation.dart'; -export 'package:lunasea/pages/settings/subpages/clients/clients.dart'; \ No newline at end of file diff --git a/lib/pages/settings/subpages/automation/tabs/tabs.dart b/lib/pages/settings/subpages/automation/tabs/tabs.dart deleted file mode 100644 index ba87563d..00000000 --- a/lib/pages/settings/subpages/automation/tabs/tabs.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'package:lunasea/pages/settings/subpages/automation/tabs/lidarr.dart'; -export 'package:lunasea/pages/settings/subpages/automation/tabs/radarr.dart'; -export 'package:lunasea/pages/settings/subpages/automation/tabs/sonarr.dart'; diff --git a/lib/pages/settings/subpages/clients/tabs/tabs.dart b/lib/pages/settings/subpages/clients/tabs/tabs.dart deleted file mode 100644 index aa7d40e2..00000000 --- a/lib/pages/settings/subpages/clients/tabs/tabs.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'package:lunasea/pages/settings/subpages/clients/tabs/nzbget.dart'; -export 'package:lunasea/pages/settings/subpages/clients/tabs/sabnzbd.dart'; diff --git a/lib/pages/settings/subpages/general/tabs/tabs.dart b/lib/pages/settings/subpages/general/tabs/tabs.dart deleted file mode 100644 index 47a30255..00000000 --- a/lib/pages/settings/subpages/general/tabs/tabs.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'package:lunasea/pages/settings/subpages/general/tabs/backup_restore.dart'; -export 'package:lunasea/pages/settings/subpages/general/tabs/logs.dart'; -export 'package:lunasea/pages/settings/subpages/general/tabs/lunasea.dart'; -export 'package:lunasea/pages/settings/subpages/general/tabs/profile.dart'; diff --git a/lib/pages/sonarr/subpages.dart b/lib/pages/sonarr/subpages.dart deleted file mode 100644 index ee5c2332..00000000 --- a/lib/pages/sonarr/subpages.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'package:lunasea/pages/sonarr/subpages/catalogue/catalogue.dart'; -export 'package:lunasea/pages/sonarr/subpages/history/history.dart'; -export 'package:lunasea/pages/sonarr/subpages/missing/missing.dart'; -export 'package:lunasea/pages/sonarr/subpages/upcoming/upcoming.dart'; -export 'package:lunasea/pages/sonarr/subpages/details/season.dart'; -export 'package:lunasea/pages/sonarr/subpages/details/show.dart'; diff --git a/lib/routes.dart b/lib/routes.dart new file mode 100644 index 00000000..52c67917 --- /dev/null +++ b/lib/routes.dart @@ -0,0 +1,7 @@ +export 'package:lunasea/routes/home/home.dart'; +export 'package:lunasea/routes/settings/settings.dart'; +export 'package:lunasea/routes/lidarr/lidarr.dart'; +export 'package:lunasea/routes/nzbget/nzbget.dart'; +export 'package:lunasea/routes/radarr/radarr.dart'; +export 'package:lunasea/routes/sonarr/sonarr.dart'; +export 'package:lunasea/routes/sabnzbd/sabnzbd.dart'; diff --git a/lib/routes/home/home.dart b/lib/routes/home/home.dart new file mode 100644 index 00000000..23f42fd7 --- /dev/null +++ b/lib/routes/home/home.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/home/subpages.dart'; + +class Home extends StatefulWidget { + @override + State createState() { + return _State(); + } +} + +class _State extends State { + final _scaffoldKey = GlobalKey(); + int _currIndex = 0; + + final List _children = [ + Summary(), + Calendar(), + ]; + + final List _titles = [ + 'Summary', + 'Calendar', + ]; + + final List _icons = [ + Icon(CustomIcons.home), + Icon(CustomIcons.calendar) + ]; + + void _navOnTap(int index) { + if(mounted) { + setState(() { + _currIndex = index; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: Stack( + children: [ + for(int i=0; i < _children.length; i++) + Offstage( + offstage: _currIndex != i, + child: TickerMode( + enabled: _currIndex == i, + child: _children[i], + ), + ) + ], + ), + appBar: Navigation.getAppBar('LunaSea', context), + drawer: Navigation.getDrawer('home', context), + bottomNavigationBar: Navigation.getBottomNavigationBar(_currIndex, _icons, _titles, _navOnTap), + ); + } +} diff --git a/lib/routes/home/subpages.dart b/lib/routes/home/subpages.dart new file mode 100644 index 00000000..6762dba5 --- /dev/null +++ b/lib/routes/home/subpages.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/routes/home/subpages/summary/summary.dart'; +export 'package:lunasea/routes/home/subpages/calendar/calendar.dart'; diff --git a/lib/routes/home/subpages/calendar/calendar.dart b/lib/routes/home/subpages/calendar/calendar.dart new file mode 100644 index 00000000..2f20a269 --- /dev/null +++ b/lib/routes/home/subpages/calendar/calendar.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/logic.dart'; +import 'package:lunasea/widgets/pages/home.dart'; +import 'package:lunasea/widgets/ui.dart' as UI; + +class Calendar extends StatefulWidget { + @override + State createState() => _State(); +} + +class _State extends State { + final _refreshIndicatorKey = GlobalKey(); + DateTime _today; + Map _events; + bool _loading = true; + + @override + initState() { + super.initState(); + Future.delayed(Duration(milliseconds: 200)).then((_) { + if(mounted) { + _refreshIndicatorKey?.currentState?.show(); + } + }); + } + + Future _handleRefresh() async { + if(mounted) setState(() { + _loading = true; + }); + CalendarAPI api = CalendarAPI(); + _today = DateTime.now().round(); + _events = await api.getUpcoming(_today); + if(mounted) setState(() { + _loading = false; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: RefreshIndicator( + key: _refreshIndicatorKey, + onRefresh: _handleRefresh, + backgroundColor: Color(Constants.SECONDARY_COLOR), + child: _loading + ? UI.Loading() + : _events == null + ? UI.ConnectionError(onTapHandler: () { + _refreshIndicatorKey?.currentState?.show(); + }) + : CalendarWidget( + events: _events, + today: _today, + ), + ), + ); + } +} diff --git a/lib/routes/home/subpages/summary/sections.dart b/lib/routes/home/subpages/summary/sections.dart new file mode 100644 index 00000000..51933934 --- /dev/null +++ b/lib/routes/home/subpages/summary/sections.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/routes/home/subpages/summary/sections/summary.dart'; +export 'package:lunasea/routes/home/subpages/summary/sections/quickaccess.dart'; diff --git a/lib/pages/home/sections/quickaccess.dart b/lib/routes/home/subpages/summary/sections/quickaccess.dart similarity index 88% rename from lib/pages/home/sections/quickaccess.dart rename to lib/routes/home/subpages/summary/sections/quickaccess.dart index 04e69842..cac1cc45 100644 --- a/lib/pages/home/sections/quickaccess.dart +++ b/lib/routes/home/subpages/summary/sections/quickaccess.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; List buildQuickAccess(BuildContext context, List services) { List serviceList = [ @@ -8,7 +8,7 @@ List buildQuickAccess(BuildContext context, List services) { child: Card( child: InkWell( child: Padding( - child: Elements.getIcon(Icons.music_note, color: Colors.blue), + child: Elements.getIcon(CustomIcons.music, color: Colors.blue), padding: EdgeInsets.symmetric(vertical: 16.0), ), onTap: () async { @@ -24,7 +24,7 @@ List buildQuickAccess(BuildContext context, List services) { child: Card( child: InkWell( child: Padding( - child: Elements.getIcon(Icons.movie, color: Color(Constants.ACCENT_COLOR)), + child: Elements.getIcon(CustomIcons.movies, color: Color(Constants.ACCENT_COLOR)), padding: EdgeInsets.symmetric(vertical: 16.0), ), onTap: () async { @@ -40,7 +40,7 @@ List buildQuickAccess(BuildContext context, List services) { child: Card( child: InkWell( child: Padding( - child: Elements.getIcon(Icons.live_tv, color: Colors.orange), + child: Elements.getIcon(CustomIcons.television, color: Colors.orange), padding: EdgeInsets.symmetric(vertical: 16.0), ), onTap: () async { @@ -88,7 +88,7 @@ List buildQuickAccess(BuildContext context, List services) { child: Card( child: InkWell( child: Padding( - child: Elements.getIcon(Icons.settings, color: Colors.red), + child: Elements.getIcon(CustomIcons.settings, color: Colors.red), padding: EdgeInsets.symmetric(vertical: 16.0), ), onTap: () async { @@ -102,7 +102,7 @@ List buildQuickAccess(BuildContext context, List services) { ), ]; List chunkedList = [Elements.getHeader('Quick Access')]; - List chunks = chunkifyServices(serviceList); + List chunks = chunkifyServices(serviceList, chunkSize: serviceList.length <= 4 ? 4 : (serviceList.length/2).ceil()); chunkedList.addAll(chunks); return chunkedList; } diff --git a/lib/pages/home/sections/summary.dart b/lib/routes/home/subpages/summary/sections/summary.dart similarity index 99% rename from lib/pages/home/sections/summary.dart rename to lib/routes/home/subpages/summary/sections/summary.dart index a8d9eab2..b2894670 100644 --- a/lib/pages/home/sections/summary.dart +++ b/lib/routes/home/subpages/summary/sections/summary.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; List buildSummary(BuildContext context, List services, int sonarr, int radarr, int lidarr) { return [ diff --git a/lib/pages/home/home.dart b/lib/routes/home/subpages/summary/summary.dart similarity index 81% rename from lib/pages/home/home.dart rename to lib/routes/home/subpages/summary/summary.dart index 3c742d23..7f0e11d4 100644 --- a/lib/pages/home/home.dart +++ b/lib/routes/home/subpages/summary/summary.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/pages/home/sections.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/routes/home/subpages/summary/sections.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/logic/automation.dart'; -class Home extends StatefulWidget { +class Summary extends StatefulWidget { @override - State createState() { + State createState() { return _State(); } } -class _State extends State { +class _State extends State { final _refreshIndicatorKey = GlobalKey(); List _services = []; int _lidarrCount; @@ -108,10 +106,6 @@ class _State extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: Navigation.getAppBar('LunaSea', context), - drawer: Navigation.getDrawer('home', context), - body: _buildBody(), - ); + return _buildBody(); } } \ No newline at end of file diff --git a/lib/pages/lidarr/lidarr.dart b/lib/routes/lidarr/lidarr.dart similarity index 93% rename from lib/pages/lidarr/lidarr.dart rename to lib/routes/lidarr/lidarr.dart index 93da1d18..f6095a06 100644 --- a/lib/pages/lidarr/lidarr.dart +++ b/lib/routes/lidarr/lidarr.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/logic/automation/lidarr/api.dart'; -import 'package:lunasea/pages/lidarr/subpages.dart'; -import 'package:lunasea/pages/lidarr/subpages/catalogue/addartist/search.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages.dart'; +import 'package:lunasea/routes/lidarr/subpages/catalogue/addartist/search.dart'; class Lidarr extends StatefulWidget { @override @@ -36,9 +35,9 @@ class _State extends State { ]; final List _icons = [ - Icon(Icons.music_note), - Icon(Icons.event_busy), - Icon(Icons.history) + Icon(CustomIcons.music), + Icon(CustomIcons.calendar_missing), + Icon(CustomIcons.history) ]; void _navOnTap(int index) { diff --git a/lib/routes/lidarr/subpages.dart b/lib/routes/lidarr/subpages.dart new file mode 100644 index 00000000..8cb62f21 --- /dev/null +++ b/lib/routes/lidarr/subpages.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/routes/lidarr/subpages/catalogue/catalogue.dart'; +export 'package:lunasea/routes/lidarr/subpages/history/history.dart'; +export 'package:lunasea/routes/lidarr/subpages/missing/missing.dart'; diff --git a/lib/pages/lidarr/subpages/catalogue/addartist/details.dart b/lib/routes/lidarr/subpages/catalogue/addartist/details.dart similarity index 98% rename from lib/pages/lidarr/subpages/catalogue/addartist/details.dart rename to lib/routes/lidarr/subpages/catalogue/addartist/details.dart index 39fa26af..8fd2ed2a 100644 --- a/lib/pages/lidarr/subpages/catalogue/addartist/details.dart +++ b/lib/routes/lidarr/subpages/catalogue/addartist/details.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrArtistSearchDetails extends StatefulWidget { final LidarrSearchEntry entry; diff --git a/lib/pages/lidarr/subpages/catalogue/addartist/search.dart b/lib/routes/lidarr/subpages/catalogue/addartist/search.dart similarity index 97% rename from lib/pages/lidarr/subpages/catalogue/addartist/search.dart rename to lib/routes/lidarr/subpages/catalogue/addartist/search.dart index d5bc5556..07ca9ba0 100644 --- a/lib/pages/lidarr/subpages/catalogue/addartist/search.dart +++ b/lib/routes/lidarr/subpages/catalogue/addartist/search.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/catalogue/addartist/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages/catalogue/addartist/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrArtistSearch extends StatefulWidget { @override diff --git a/lib/pages/lidarr/subpages/catalogue/catalogue.dart b/lib/routes/lidarr/subpages/catalogue/catalogue.dart similarity index 91% rename from lib/pages/lidarr/subpages/catalogue/catalogue.dart rename to lib/routes/lidarr/subpages/catalogue/catalogue.dart index 2f073f90..c7817bd2 100644 --- a/lib/pages/lidarr/subpages/catalogue/catalogue.dart +++ b/lib/routes/lidarr/subpages/catalogue/catalogue.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/artist.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/edit.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/artist.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/edit.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Catalogue extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -167,7 +167,7 @@ class _State extends State with TickerProviderStateMixin { _searchController.text = ''; } } - setState(() {}); + if(mounted) setState(() {}); } Widget _noEntries(String message) { @@ -343,12 +343,25 @@ class _State extends State with TickerProviderStateMixin { case 'remove_artist': { values = await LidarrDialogs.showDeleteArtistPrompt(context); if(values[0]) { - if(await LidarrAPI.removeArtist(entry.artistID)) { - Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); - widget.refreshIndicatorKey?.currentState?.show(); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await LidarrAPI.removeArtist(entry.artistID, deleteFiles: true)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await LidarrAPI.removeArtist(entry.artistID)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } + } break; } @@ -380,7 +393,7 @@ class _State extends State with TickerProviderStateMixin { Future _enterArtist(LidarrCatalogueEntry entry, int index) async { final result = await Navigator.of(context).push( MaterialPageRoute( - builder: (context) => LidarrArtistDetails(entry: entry), + builder: (context) => LidarrArtistDetails(entry: entry, artistID: entry.artistID), ), ); //Handle the result diff --git a/lib/pages/lidarr/subpages/details/album.dart b/lib/routes/lidarr/subpages/details/album.dart similarity index 96% rename from lib/pages/lidarr/subpages/details/album.dart rename to lib/routes/lidarr/subpages/details/album.dart index 618247ce..596c92f0 100644 --- a/lib/pages/lidarr/subpages/details/album.dart +++ b/lib/routes/lidarr/subpages/details/album.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/search.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/system/functions.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/search.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrAlbumDetails extends StatefulWidget { final String title; diff --git a/lib/pages/lidarr/subpages/details/artist.dart b/lib/routes/lidarr/subpages/details/artist.dart similarity index 91% rename from lib/pages/lidarr/subpages/details/artist.dart rename to lib/routes/lidarr/subpages/details/artist.dart index 2b01a3f3..0b71de60 100644 --- a/lib/pages/lidarr/subpages/details/artist.dart +++ b/lib/routes/lidarr/subpages/details/artist.dart @@ -1,18 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/album.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/edit.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/album.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/edit.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrArtistDetails extends StatefulWidget { final LidarrCatalogueEntry entry; + final int artistID; LidarrArtistDetails({ Key key, - this.entry, + @required this.entry, + @required this.artistID, }) : super(key: key); @override @@ -29,6 +30,7 @@ class _State extends State { 'Albums', ]; List _albumEntries; + bool _loading = false; bool _loadingAlbums = true; bool _hideUnmonitored = false; @@ -40,7 +42,17 @@ class _State extends State { @override void initState() { super.initState(); - _refreshData(); + if(entry == null) { + _needFetch(); + } + _refreshArtist(); + _refreshAlbums(); + } + + void _needFetch() { + if(mounted) setState(() { + _loading = true; + }); } @override @@ -50,21 +62,31 @@ class _State extends State { initialIndex: 1, child: Scaffold( key: _scaffoldKey, - body: _buildPage(), + body: _loading ? + Notifications.centeredMessage('Loading...') : + entry == null ? + Notifications.centeredMessage('Connection Error') : + _buildPage(), ), ); } - Future _refreshData() async { + Future _refreshArtist() async { + LidarrCatalogueEntry _entry = await LidarrAPI.getArtist(widget.artistID); + _entry ??= entry; + entry = _entry; + if(mounted) setState(() { + _loading = false; + }); + } + + Future _refreshAlbums() async { if(mounted) { setState(() { _loadingAlbums = true; }); } - LidarrCatalogueEntry _entry = await LidarrAPI.getArtist(entry.artistID); - _entry ??= entry; - entry = _entry; - _albumEntries = await LidarrAPI.getArtistAlbums(entry.artistID); + _albumEntries = await LidarrAPI.getArtistAlbums(widget.artistID); if(mounted) { setState(() { _loadingAlbums = false; @@ -144,11 +166,23 @@ class _State extends State { case 'remove_artist': { values = await LidarrDialogs.showDeleteArtistPrompt(context); if(values[0]) { - if(await LidarrAPI.removeArtist(entry.artistID)) { - Navigator.of(context).pop('remove_artist'); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await LidarrAPI.removeArtist(entry.artistID, deleteFiles: true)) { + Navigator.of(context).pop('remove_artist'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await LidarrAPI.removeArtist(entry.artistID)) { + Navigator.of(context).pop('remove_artist'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } + } break; } @@ -632,6 +666,6 @@ class _State extends State { } } } - _refreshData(); + _refreshAlbums(); } } \ No newline at end of file diff --git a/lib/pages/lidarr/subpages/details/edit.dart b/lib/routes/lidarr/subpages/details/edit.dart similarity index 99% rename from lib/pages/lidarr/subpages/details/edit.dart rename to lib/routes/lidarr/subpages/details/edit.dart index 79d7fc3f..580dd678 100644 --- a/lib/pages/lidarr/subpages/details/edit.dart +++ b/lib/routes/lidarr/subpages/details/edit.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrEditArtist extends StatefulWidget { final LidarrCatalogueEntry entry; diff --git a/lib/pages/lidarr/subpages/details/release.dart b/lib/routes/lidarr/subpages/details/release.dart similarity index 98% rename from lib/pages/lidarr/subpages/details/release.dart rename to lib/routes/lidarr/subpages/details/release.dart index c14ee4e0..256dca87 100644 --- a/lib/pages/lidarr/subpages/details/release.dart +++ b/lib/routes/lidarr/subpages/details/release.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrReleaseInfo extends StatefulWidget { final LidarrReleaseEntry entry; diff --git a/lib/pages/lidarr/subpages/details/search.dart b/lib/routes/lidarr/subpages/details/search.dart similarity index 75% rename from lib/pages/lidarr/subpages/details/search.dart rename to lib/routes/lidarr/subpages/details/search.dart index e5fc6f8b..894c1df8 100644 --- a/lib/pages/lidarr/subpages/details/search.dart +++ b/lib/routes/lidarr/subpages/details/search.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/release.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/release.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LidarrAlbumSearch extends StatefulWidget { final int albumID; @@ -87,12 +86,38 @@ class _State extends State { return Card( child: ListTile( title: Elements.getTitle(release.title), - subtitle: Elements.getSubtitle( - '${Functions.toCapitalize(release.protocol)}' + - '${release.isTorrent ? "\t•\t${release.seeders}/${release.leechers}" : ''}' + - '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n' + - '${release.quality ?? 'Unknown'}\t•\t'+ - '${Functions.bytesToReadable(release.size) ?? 'Unknown'}' + subtitle: RichText( + text: TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: '${Functions.toCapitalize(release.protocol)}', + style: TextStyle( + color: release.isTorrent ? Colors.orange : Color(Constants.ACCENT_COLOR), + fontWeight: FontWeight.bold, + ), + ), + if(release.isTorrent) TextSpan( + text: '${release.isTorrent ? " (${release.seeders}/${release.leechers})" : ''}', + style: TextStyle( + color: Colors.orange, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n', + ), + TextSpan( + text: '${release.quality ?? 'Unknown'}\t•\t', + ), + TextSpan( + text: '${Functions.bytesToReadable(release.size) ?? 'Unknown'}', + ), + ] + ), ), trailing: InkWell( child: IconButton( diff --git a/lib/pages/lidarr/subpages/history/history.dart b/lib/routes/lidarr/subpages/history/history.dart similarity index 97% rename from lib/pages/lidarr/subpages/history/history.dart rename to lib/routes/lidarr/subpages/history/history.dart index 57d60e64..452c001c 100644 --- a/lib/pages/lidarr/subpages/history/history.dart +++ b/lib/routes/lidarr/subpages/history/history.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class History extends StatefulWidget { final GlobalKey refreshIndicatorKey; diff --git a/lib/pages/lidarr/subpages/missing/missing.dart b/lib/routes/lidarr/subpages/missing/missing.dart similarity index 91% rename from lib/pages/lidarr/subpages/missing/missing.dart rename to lib/routes/lidarr/subpages/missing/missing.dart index 813f1025..552789c1 100644 --- a/lib/pages/lidarr/subpages/missing/missing.dart +++ b/lib/routes/lidarr/subpages/missing/missing.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/pages/lidarr/subpages/details/album.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/album.dart'; +import 'package:lunasea/routes/lidarr/subpages/details/artist.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Missing extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -127,6 +128,9 @@ class _State extends State { onTap: () async { _enterAlbum(entry); }, + onLongPress: () async { + _enterArtist(entry); + }, ), decoration: BoxDecoration( image: DecorationImage( @@ -162,4 +166,12 @@ class _State extends State { switch(result) { } } + + Future _enterArtist(LidarrMissingEntry entry) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => LidarrArtistDetails(entry: null, artistID: entry.artistID), + ), + ); + } } diff --git a/lib/pages/nzbget/nzbget.dart b/lib/routes/nzbget/nzbget.dart similarity index 91% rename from lib/pages/nzbget/nzbget.dart rename to lib/routes/nzbget/nzbget.dart index 03116451..ec708014 100644 --- a/lib/pages/nzbget/nzbget.dart +++ b/lib/routes/nzbget/nzbget.dart @@ -1,13 +1,12 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/pages/nzbget/subpages.dart'; -import 'package:lunasea/pages/nzbget/subpages/statistics/statistics.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/nzbget/subpages.dart'; +import 'package:lunasea/routes/nzbget/subpages/statistics/statistics.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGet extends StatefulWidget { @override @@ -38,8 +37,8 @@ class _State extends State { int _currIndex = 0; final List _icons = [ - Icon(Icons.queue), - Icon(Icons.history), + Icon(CustomIcons.queue), + Icon(CustomIcons.history), ]; void _navOnTap(int index) { @@ -107,7 +106,7 @@ class _State extends State { ), appBar: _buildAppBar(), drawer: Navigation.getDrawer('nzbget', context), - bottomNavigationBar: _buildBottomNavigationBar(), + bottomNavigationBar: Navigation.getBottomNavigationBar(_currIndex, _icons, _titles, _navOnTap), ); } @@ -166,23 +165,6 @@ class _State extends State { ); } - BottomNavigationBar _buildBottomNavigationBar() { - return BottomNavigationBar( - currentIndex: _currIndex, - type: BottomNavigationBarType.fixed, - fixedColor: Color(Constants.ACCENT_COLOR), - elevation: 0.0, - onTap: _navOnTap, - items: [ - for(int i =0; i<_icons.length; i++) - BottomNavigationBarItem( - icon: _icons[i], - title: Text(_titles[i]), - ) - ], - ); - } - Future _handlePopup(BuildContext context) async { List values = await NZBGetDialogs.showSettingsPrompt(context); if(values[0]) { diff --git a/lib/routes/nzbget/subpages.dart b/lib/routes/nzbget/subpages.dart new file mode 100644 index 00000000..b2070257 --- /dev/null +++ b/lib/routes/nzbget/subpages.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/routes/nzbget/subpages/history/history.dart'; +export 'package:lunasea/routes/nzbget/subpages/queue/queue.dart'; diff --git a/lib/pages/nzbget/subpages/history/details.dart b/lib/routes/nzbget/subpages/history/details.dart similarity index 99% rename from lib/pages/nzbget/subpages/history/details.dart rename to lib/routes/nzbget/subpages/history/details.dart index 49676c65..c2cae3e2 100644 --- a/lib/pages/nzbget/subpages/history/details.dart +++ b/lib/routes/nzbget/subpages/history/details.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGetHistoryDetails extends StatefulWidget { final NZBGetHistoryEntry entry; diff --git a/lib/pages/nzbget/subpages/history/history.dart b/lib/routes/nzbget/subpages/history/history.dart similarity index 98% rename from lib/pages/nzbget/subpages/history/history.dart rename to lib/routes/nzbget/subpages/history/history.dart index 51b05b1b..6f8c8f5a 100644 --- a/lib/pages/nzbget/subpages/history/history.dart +++ b/lib/routes/nzbget/subpages/history/history.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/pages/nzbget/subpages/history/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/nzbget/subpages/history/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGetHistory extends StatefulWidget { final GlobalKey scaffoldKey; diff --git a/lib/pages/nzbget/subpages/queue/queue.dart b/lib/routes/nzbget/subpages/queue/queue.dart similarity index 98% rename from lib/pages/nzbget/subpages/queue/queue.dart rename to lib/routes/nzbget/subpages/queue/queue.dart index 7c653bbb..69697150 100644 --- a/lib/pages/nzbget/subpages/queue/queue.dart +++ b/lib/routes/nzbget/subpages/queue/queue.dart @@ -1,13 +1,10 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/flutter/reorderable_list.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/system/ui/dialog.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/logic/clients/nzbget.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGetQueue extends StatefulWidget { final GlobalKey scaffoldKey; @@ -128,7 +125,7 @@ class _State extends State with TickerProviderStateMixin { if(_values[1] == -1) { _values = await NZBGetDialogs.showCustomPauseForPrompt(context); if(_values[0]) { - if(await NZBGetAPI.pauseQueueFor(_values[1])) { + if(await NZBGetAPI.pauseQueueFor(_values[1]) && mounted) { setState(() { _paused = true; }); @@ -138,7 +135,7 @@ class _State extends State with TickerProviderStateMixin { } } } else { - if(await NZBGetAPI.pauseQueueFor(_values[1])) { + if(await NZBGetAPI.pauseQueueFor(_values[1]) && mounted) { setState(() { _paused = true; }); diff --git a/lib/pages/nzbget/subpages/statistics/statistics.dart b/lib/routes/nzbget/subpages/statistics/statistics.dart similarity index 99% rename from lib/pages/nzbget/subpages/statistics/statistics.dart rename to lib/routes/nzbget/subpages/statistics/statistics.dart index a628652c..4083e4d2 100644 --- a/lib/pages/nzbget/subpages/statistics/statistics.dart +++ b/lib/routes/nzbget/subpages/statistics/statistics.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGetStatistics extends StatefulWidget { @override diff --git a/lib/pages/radarr/radarr.dart b/lib/routes/radarr/radarr.dart similarity index 89% rename from lib/pages/radarr/radarr.dart rename to lib/routes/radarr/radarr.dart index e7b05b94..c2b22a04 100644 --- a/lib/pages/radarr/radarr.dart +++ b/lib/routes/radarr/radarr.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/radarr/api.dart'; -import 'package:lunasea/pages/radarr/subpages/catalogue/addmovie/search.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/radarr/subpages.dart'; +import 'package:lunasea/routes/radarr/subpages/catalogue/addmovie/search.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/radarr/subpages.dart'; class Radarr extends StatefulWidget { @override @@ -19,26 +18,30 @@ class _State extends State { GlobalKey(), GlobalKey(), GlobalKey(), + GlobalKey(), ]; final _scaffoldKey = GlobalKey(); int _currIndex = 0; final List _children = [ Catalogue(refreshIndicatorKey: _refreshKeys[0]), - Missing(refreshIndicatorKey: _refreshKeys[1]), - History(refreshIndicatorKey: _refreshKeys[2]), + Upcoming(refreshIndicatorKey: _refreshKeys[1]), + Missing(refreshIndicatorKey: _refreshKeys[2]), + History(refreshIndicatorKey: _refreshKeys[3]), ]; final List _titles = [ 'Catalogue', + 'Upcoming', 'Missing', 'History', ]; final List _icons = [ - Icon(Icons.movie), - Icon(Icons.event_busy), - Icon(Icons.history) + Icon(CustomIcons.movies), + Icon(CustomIcons.upcoming), + Icon(CustomIcons.calendar_missing), + Icon(CustomIcons.history) ]; void _navOnTap(int index) { diff --git a/lib/routes/radarr/subpages.dart b/lib/routes/radarr/subpages.dart new file mode 100644 index 00000000..0a4d6292 --- /dev/null +++ b/lib/routes/radarr/subpages.dart @@ -0,0 +1,4 @@ +export 'package:lunasea/routes/radarr/subpages/catalogue/catalogue.dart'; +export 'package:lunasea/routes/radarr/subpages/history/history.dart'; +export 'package:lunasea/routes/radarr/subpages/missing/missing.dart'; +export 'package:lunasea/routes/radarr/subpages/upcoming/upcoming.dart'; diff --git a/lib/pages/radarr/subpages/catalogue/addmovie/details.dart b/lib/routes/radarr/subpages/catalogue/addmovie/details.dart similarity index 98% rename from lib/pages/radarr/subpages/catalogue/addmovie/details.dart rename to lib/routes/radarr/subpages/catalogue/addmovie/details.dart index 9b71c9a9..63febe05 100644 --- a/lib/pages/radarr/subpages/catalogue/addmovie/details.dart +++ b/lib/routes/radarr/subpages/catalogue/addmovie/details.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrMovieSearchDetails extends StatefulWidget { final RadarrSearchEntry entry; diff --git a/lib/pages/radarr/subpages/catalogue/addmovie/search.dart b/lib/routes/radarr/subpages/catalogue/addmovie/search.dart similarity index 97% rename from lib/pages/radarr/subpages/catalogue/addmovie/search.dart rename to lib/routes/radarr/subpages/catalogue/addmovie/search.dart index 38cc0890..2a85f518 100644 --- a/lib/pages/radarr/subpages/catalogue/addmovie/search.dart +++ b/lib/routes/radarr/subpages/catalogue/addmovie/search.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/pages/radarr/subpages/catalogue/addmovie/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/radarr/subpages/catalogue/addmovie/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrMovieSearch extends StatefulWidget { @override diff --git a/lib/pages/radarr/subpages/catalogue/catalogue.dart b/lib/routes/radarr/subpages/catalogue/catalogue.dart similarity index 92% rename from lib/pages/radarr/subpages/catalogue/catalogue.dart rename to lib/routes/radarr/subpages/catalogue/catalogue.dart index 566e16a7..2cf5a329 100644 --- a/lib/pages/radarr/subpages/catalogue/catalogue.dart +++ b/lib/routes/radarr/subpages/catalogue/catalogue.dart @@ -2,11 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/pages/radarr/subpages.dart'; -import 'package:lunasea/pages/radarr/subpages/details/edit.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/radarr/subpages/details/movie.dart'; +import 'package:lunasea/routes/radarr/subpages/details/edit.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Catalogue extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -168,7 +167,7 @@ class _State extends State with TickerProviderStateMixin { _searchController.text = ''; } } - setState(() {}); + if(mounted) setState(() {}); } Widget _noEntries(String message) { @@ -389,11 +388,23 @@ class _State extends State with TickerProviderStateMixin { case 'remove_movie': { values = await RadarrDialogs.showDeleteMoviePrompt(context); if(values[0]) { - if(await RadarrAPI.removeMovie(entry.movieID)) { - Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); - widget.refreshIndicatorKey?.currentState?.show(); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await RadarrAPI.removeMovie(entry.movieID, deleteFiles: true)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await RadarrAPI.removeMovie(entry.movieID)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } } break; diff --git a/lib/pages/radarr/subpages/details/edit.dart b/lib/routes/radarr/subpages/details/edit.dart similarity index 99% rename from lib/pages/radarr/subpages/details/edit.dart rename to lib/routes/radarr/subpages/details/edit.dart index 793f168a..503c182a 100644 --- a/lib/pages/radarr/subpages/details/edit.dart +++ b/lib/routes/radarr/subpages/details/edit.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrEditMovie extends StatefulWidget { final RadarrCatalogueEntry entry; diff --git a/lib/pages/radarr/subpages/details/movie.dart b/lib/routes/radarr/subpages/details/movie.dart similarity index 83% rename from lib/pages/radarr/subpages/details/movie.dart rename to lib/routes/radarr/subpages/details/movie.dart index df9a01c5..c6301a35 100644 --- a/lib/pages/radarr/subpages/details/movie.dart +++ b/lib/routes/radarr/subpages/details/movie.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/pages/radarr/subpages/details/edit.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/pages/radarr/subpages/details/tabs/tabs.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/radarr/subpages/details/edit.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/routes/radarr/subpages/details/tabs/tabs.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrMovieDetails extends StatefulWidget { final RadarrCatalogueEntry entry; @@ -145,10 +145,21 @@ class _State extends State { case 'remove_movie': { values = await RadarrDialogs.showDeleteMoviePrompt(context); if(values[0]) { - if(await RadarrAPI.removeMovie(entry.movieID)) { - Navigator.of(context).pop('movie_deleted'); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await RadarrAPI.removeMovie(entry.movieID, deleteFiles: true)) { + Navigator.of(context).pop('movie_deleted'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await RadarrAPI.removeMovie(entry.movieID)) { + Navigator.of(context).pop('movie_deleted'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } } break; diff --git a/lib/pages/radarr/subpages/details/release.dart b/lib/routes/radarr/subpages/details/release.dart similarity index 98% rename from lib/pages/radarr/subpages/details/release.dart rename to lib/routes/radarr/subpages/details/release.dart index 80fa0865..d94fd9e0 100644 --- a/lib/pages/radarr/subpages/details/release.dart +++ b/lib/routes/radarr/subpages/details/release.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrReleaseInfo extends StatefulWidget { final RadarrReleaseEntry entry; diff --git a/lib/pages/radarr/subpages/details/tabs/files.dart b/lib/routes/radarr/subpages/details/tabs/files.dart similarity index 97% rename from lib/pages/radarr/subpages/details/tabs/files.dart rename to lib/routes/radarr/subpages/details/tabs/files.dart index 08999293..0d2c27c1 100644 --- a/lib/pages/radarr/subpages/details/tabs/files.dart +++ b/lib/routes/radarr/subpages/details/tabs/files.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; Widget buildFiles(RadarrCatalogueEntry entry, GlobalKey scaffoldKey, Function refreshData, BuildContext context) { return Scrollbar( diff --git a/lib/pages/radarr/subpages/details/tabs/overview.dart b/lib/routes/radarr/subpages/details/tabs/overview.dart similarity index 99% rename from lib/pages/radarr/subpages/details/tabs/overview.dart rename to lib/routes/radarr/subpages/details/tabs/overview.dart index 5d68282d..aeb0d9f5 100644 --- a/lib/pages/radarr/subpages/details/tabs/overview.dart +++ b/lib/routes/radarr/subpages/details/tabs/overview.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; Widget buildOverview(RadarrCatalogueEntry entry, BuildContext context) { return Scrollbar( diff --git a/lib/pages/radarr/subpages/details/tabs/releases.dart b/lib/routes/radarr/subpages/details/tabs/releases.dart similarity index 76% rename from lib/pages/radarr/subpages/details/tabs/releases.dart rename to lib/routes/radarr/subpages/details/tabs/releases.dart index 02d3a89f..f7d12bf4 100644 --- a/lib/pages/radarr/subpages/details/tabs/releases.dart +++ b/lib/routes/radarr/subpages/details/tabs/releases.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/pages/radarr/subpages/details/release.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/radarr/subpages/details/release.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class RadarrReleases extends StatefulWidget { final GlobalKey scaffoldKey; @@ -93,12 +93,38 @@ class _State extends State { return Card( child: ListTile( title: Elements.getTitle(release.title), - subtitle: Elements.getSubtitle( - '${Functions.toCapitalize(release.protocol)}' + - '${release.isTorrent ? "\t•\t${release.seeders}/${release.leechers}" : ''}' + - '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n' + - '${release.quality ?? 'Unknown'}\t•\t'+ - '${Functions.bytesToReadable(release.size) ?? 'Unknown'}' + subtitle: RichText( + text: TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: '${Functions.toCapitalize(release.protocol)}', + style: TextStyle( + color: release.isTorrent ? Colors.orange : Color(Constants.ACCENT_COLOR), + fontWeight: FontWeight.bold, + ), + ), + if(release.isTorrent) TextSpan( + text: '${release.isTorrent ? " (${release.seeders}/${release.leechers})" : ''}', + style: TextStyle( + color: Colors.orange, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n', + ), + TextSpan( + text: '${release.quality ?? 'Unknown'}\t•\t', + ), + TextSpan( + text: '${Functions.bytesToReadable(release.size) ?? 'Unknown'}', + ), + ] + ), ), trailing: InkWell( child: IconButton( diff --git a/lib/routes/radarr/subpages/details/tabs/tabs.dart b/lib/routes/radarr/subpages/details/tabs/tabs.dart new file mode 100644 index 00000000..3a6f29df --- /dev/null +++ b/lib/routes/radarr/subpages/details/tabs/tabs.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/routes/radarr/subpages/details/tabs/files.dart'; +export 'package:lunasea/routes/radarr/subpages/details/tabs/overview.dart'; +export 'package:lunasea/routes/radarr/subpages/details/tabs/releases.dart'; diff --git a/lib/pages/radarr/subpages/history/history.dart b/lib/routes/radarr/subpages/history/history.dart similarity index 97% rename from lib/pages/radarr/subpages/history/history.dart rename to lib/routes/radarr/subpages/history/history.dart index 10fb69e5..1df27d42 100644 --- a/lib/pages/radarr/subpages/history/history.dart +++ b/lib/routes/radarr/subpages/history/history.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class History extends StatefulWidget { final GlobalKey refreshIndicatorKey; diff --git a/lib/pages/radarr/subpages/missing/missing.dart b/lib/routes/radarr/subpages/missing/missing.dart similarity index 97% rename from lib/pages/radarr/subpages/missing/missing.dart rename to lib/routes/radarr/subpages/missing/missing.dart index 5424788e..07dba689 100644 --- a/lib/pages/radarr/subpages/missing/missing.dart +++ b/lib/routes/radarr/subpages/missing/missing.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/pages/radarr/subpages.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/radarr/subpages/details/movie.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Missing extends StatefulWidget { final GlobalKey refreshIndicatorKey; diff --git a/lib/routes/radarr/subpages/upcoming/upcoming.dart b/lib/routes/radarr/subpages/upcoming/upcoming.dart new file mode 100644 index 00000000..05b99785 --- /dev/null +++ b/lib/routes/radarr/subpages/upcoming/upcoming.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_advanced_networkimage/provider.dart'; +import 'package:lunasea/logic/automation/radarr.dart'; +import 'package:lunasea/routes/radarr/subpages/details/movie.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; + +class Upcoming extends StatefulWidget { + final GlobalKey refreshIndicatorKey; + + Upcoming({ + Key key, + @required this.refreshIndicatorKey, + }) : super(key: key); + + @override + State createState() { + return _State(); + } +} + +class _State extends State { + final _scaffoldKey = GlobalKey(); + bool _loading = true; + List _entries = []; + + @override + void initState() { + super.initState(); + Future.delayed(Duration(milliseconds: 200)).then((_) { + if(mounted) { + widget.refreshIndicatorKey?.currentState?.show(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: RefreshIndicator( + key: widget.refreshIndicatorKey, + backgroundColor: Color(Constants.SECONDARY_COLOR), + onRefresh: _handleRefresh, + child: _loading ? + Notifications.centeredMessage('Loading...') : + _entries == null ? + Notifications.centeredMessage('Connection Error', showBtn: true, btnMessage: 'Refresh', onTapHandler: () {widget.refreshIndicatorKey?.currentState?.show();}) : + _entries.length == 0 ? + Notifications.centeredMessage('No Upcoming Movies', showBtn: true, btnMessage: 'Refresh', onTapHandler: () {widget.refreshIndicatorKey?.currentState?.show();}) : + _buildList(), + ), + ); + } + + Future _handleRefresh() async { + if(mounted) { + setState(() { + _loading = true; + }); + } + _entries = await RadarrAPI.getUpcoming(); + if(mounted) { + setState(() { + _loading = false; + }); + } + } + + Widget _buildList() { + return Scrollbar( + child: ListView.builder( + itemCount: _entries.length, + itemBuilder: (context, index) { + return _buildEntry(_entries[index]); + }, + padding: Elements.getListViewPadding(), + physics: AlwaysScrollableScrollPhysics(), + ), + ); + } + + Widget _buildEntry(RadarrMissingEntry entry) { + return Card( + child: Container( + child: ListTile( + title: Elements.getTitle(entry.title), + subtitle: RichText( + text: TextSpan( + style: TextStyle( + color: Colors.white70, + letterSpacing: Constants.LETTER_SPACING, + ), + children: entry.subtitle, + ), + ), + trailing: IconButton( + icon: Elements.getIcon(Icons.search), + tooltip: 'Search', + onPressed: () async { + if(await RadarrAPI.searchMissingMovies([entry.movieID])) { + Notifications.showSnackBar(_scaffoldKey, 'Searching for ${entry.title}...'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to search for ${entry.title}'); + } + }, + ), + contentPadding: Elements.getContentPadding(), + onTap: () async { + _enterMovie(entry); + }, + ), + decoration: BoxDecoration( + image: DecorationImage( + image: AdvancedNetworkImage( + entry.posterURI(), + useDiskCache: true, + loadFailedCallback: () {}, + fallbackAssetImage: 'assets/images/secondary_color.png', + retryLimit: 1, + ), + colorFilter: new ColorFilter.mode(Color(Constants.SECONDARY_COLOR).withOpacity(0.20), BlendMode.dstATop), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(4.0)), + ), + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ); + } + + Future _enterMovie(RadarrMissingEntry entry) async { + final result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => RadarrMovieDetails(entry: null, movieID: entry.movieID,), + ), + ); + //Handle the result + switch(result) { + case 'movie_deleted': { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + break; + } + } + } +} \ No newline at end of file diff --git a/lib/pages/sabnzbd/sabnzbd.dart b/lib/routes/sabnzbd/sabnzbd.dart similarity index 92% rename from lib/pages/sabnzbd/sabnzbd.dart rename to lib/routes/sabnzbd/sabnzbd.dart index a57bf5de..36383f65 100644 --- a/lib/pages/sabnzbd/sabnzbd.dart +++ b/lib/routes/sabnzbd/sabnzbd.dart @@ -1,14 +1,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/clients/sabnzbd.dart'; import 'package:lunasea/logic/clients/sabnzbd/entry.dart'; -import 'package:lunasea/pages/sabnzbd/subpages.dart'; -import 'package:lunasea/pages/sabnzbd/subpages/statistics/statistics.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sabnzbd/subpages.dart'; +import 'package:lunasea/routes/sabnzbd/subpages/statistics/statistics.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbd extends StatefulWidget { @override @@ -40,8 +39,8 @@ class _State extends State { ]; final List _icons = [ - Icon(Icons.queue), - Icon(Icons.history), + Icon(CustomIcons.queue), + Icon(CustomIcons.history), ]; void _navOnTap(int index) { @@ -109,7 +108,7 @@ class _State extends State { ), appBar: _buildAppBar(), drawer: Navigation.getDrawer('sabnzbd', context), - bottomNavigationBar: _buildBottomNavigationBar(), + bottomNavigationBar: Navigation.getBottomNavigationBar(_currIndex, _icons, _titles, _navOnTap), ); } @@ -168,23 +167,6 @@ class _State extends State { ); } - BottomNavigationBar _buildBottomNavigationBar() { - return BottomNavigationBar( - currentIndex: _currIndex, - type: BottomNavigationBarType.fixed, - fixedColor: Color(Constants.ACCENT_COLOR), - elevation: 0.0, - onTap: _navOnTap, - items: [ - for(int i =0; i<_icons.length; i++) - BottomNavigationBarItem( - icon: _icons[i], - title: Text(_titles[i]), - ) - ], - ); - } - Future _handlePopup(BuildContext context) async { List values = await SABnzbdDialogs.showSettingsPrompt(context); if(values[0]) { diff --git a/lib/routes/sabnzbd/subpages.dart b/lib/routes/sabnzbd/subpages.dart new file mode 100644 index 00000000..1ee1be03 --- /dev/null +++ b/lib/routes/sabnzbd/subpages.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/routes/sabnzbd/subpages/history/history.dart'; +export 'package:lunasea/routes/sabnzbd/subpages/queue/queue.dart'; diff --git a/lib/pages/sabnzbd/subpages/history/details.dart b/lib/routes/sabnzbd/subpages/history/details.dart similarity index 99% rename from lib/pages/sabnzbd/subpages/history/details.dart rename to lib/routes/sabnzbd/subpages/history/details.dart index 2fa3a6c4..7aeb2145 100644 --- a/lib/pages/sabnzbd/subpages/history/details.dart +++ b/lib/routes/sabnzbd/subpages/history/details.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/sabnzbd.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbdHistoryDetails extends StatefulWidget { final SABnzbdHistoryEntry entry; diff --git a/lib/pages/sabnzbd/subpages/history/history.dart b/lib/routes/sabnzbd/subpages/history/history.dart similarity index 98% rename from lib/pages/sabnzbd/subpages/history/history.dart rename to lib/routes/sabnzbd/subpages/history/history.dart index ad869b28..63de16fb 100644 --- a/lib/pages/sabnzbd/subpages/history/history.dart +++ b/lib/routes/sabnzbd/subpages/history/history.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:lunasea/logic/clients/sabnzbd.dart'; -import 'package:lunasea/pages/sabnzbd/subpages/history/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sabnzbd/subpages/history/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbdHistory extends StatefulWidget { final GlobalKey scaffoldKey; diff --git a/lib/pages/sabnzbd/subpages/queue/queue.dart b/lib/routes/sabnzbd/subpages/queue/queue.dart similarity index 97% rename from lib/pages/sabnzbd/subpages/queue/queue.dart rename to lib/routes/sabnzbd/subpages/queue/queue.dart index 1ed9efad..65c811e1 100644 --- a/lib/pages/sabnzbd/subpages/queue/queue.dart +++ b/lib/routes/sabnzbd/subpages/queue/queue.dart @@ -1,13 +1,10 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:lunasea/logic/clients/sabnzbd.dart'; -import 'package:lunasea/logic/clients/sabnzbd/entry.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/flutter/reorderable_list.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; +import 'package:lunasea/logic/clients/sabnzbd.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbdQueue extends StatefulWidget { final GlobalKey scaffoldKey; @@ -170,7 +167,7 @@ class _State extends State { if(_values[1] == -1) { _values = await SABnzbdDialogs.showCustomPauseForPrompt(context); if(_values[0]) { - if(await SABnzbdAPI.pauseQueueFor(_values[1])) { + if(await SABnzbdAPI.pauseQueueFor(_values[1]) && mounted) { setState(() { _paused = true; }); @@ -180,7 +177,7 @@ class _State extends State { } } } else { - if(await SABnzbdAPI.pauseQueueFor(_values[1])) { + if(await SABnzbdAPI.pauseQueueFor(_values[1]) && mounted) { setState(() { _paused = true; }); diff --git a/lib/pages/sabnzbd/subpages/statistics/statistics.dart b/lib/routes/sabnzbd/subpages/statistics/statistics.dart similarity index 98% rename from lib/pages/sabnzbd/subpages/statistics/statistics.dart rename to lib/routes/sabnzbd/subpages/statistics/statistics.dart index 96573461..2cc49120 100644 --- a/lib/pages/sabnzbd/subpages/statistics/statistics.dart +++ b/lib/routes/sabnzbd/subpages/statistics/statistics.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/sabnzbd.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbdServerStatusStatistics extends StatefulWidget { @override diff --git a/lib/pages/settings/settings.dart b/lib/routes/settings/settings.dart similarity index 76% rename from lib/pages/settings/settings.dart rename to lib/routes/settings/settings.dart index 8b5a9691..f05f6d67 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/routes/settings/settings.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/settings/subpages.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/settings/subpages.dart'; class Settings extends StatefulWidget { @override @@ -16,22 +16,21 @@ class _State extends State { General(), Automation(), Clients(), - Monitoring(), + Indexers(), ]; final List _titles = [ 'General', 'Automation', 'Clients', - 'Monitoring', + 'Indexers', ]; final List _icons = [ - Icon(Icons.person), - Icon(Icons.layers), - Icon(Icons.file_download), - Icon(Icons.multiline_chart), - + Icon(CustomIcons.user), + Icon(CustomIcons.layers), + Icon(CustomIcons.clients), + Icon(CustomIcons.rss), ]; void _navOnTap(int index) { diff --git a/lib/routes/settings/subpages.dart b/lib/routes/settings/subpages.dart new file mode 100644 index 00000000..da1f03eb --- /dev/null +++ b/lib/routes/settings/subpages.dart @@ -0,0 +1,5 @@ +export 'package:lunasea/routes/settings/subpages/general/general.dart'; +export 'package:lunasea/routes/settings/subpages/monitoring/monitoring.dart'; +export 'package:lunasea/routes/settings/subpages/automation/automation.dart'; +export 'package:lunasea/routes/settings/subpages/clients/clients.dart'; +export 'package:lunasea/routes/settings/subpages/indexers/indexers.dart'; diff --git a/lib/pages/settings/subpages/automation/automation.dart b/lib/routes/settings/subpages/automation/automation.dart similarity index 86% rename from lib/pages/settings/subpages/automation/automation.dart rename to lib/routes/settings/subpages/automation/automation.dart index 03fcfdee..d2c6ca32 100644 --- a/lib/pages/settings/subpages/automation/automation.dart +++ b/lib/routes/settings/subpages/automation/automation.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/settings/subpages/automation/tabs/tabs.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/settings/subpages/automation/tabs/tabs.dart'; class Automation extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/automation/tabs/lidarr.dart b/lib/routes/settings/subpages/automation/tabs/lidarr.dart similarity index 97% rename from lib/pages/settings/subpages/automation/tabs/lidarr.dart rename to lib/routes/settings/subpages/automation/tabs/lidarr.dart index e2a0e3f1..a6c72efe 100644 --- a/lib/pages/settings/subpages/automation/tabs/lidarr.dart +++ b/lib/routes/settings/subpages/automation/tabs/lidarr.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class Lidarr extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/automation/tabs/radarr.dart b/lib/routes/settings/subpages/automation/tabs/radarr.dart similarity index 97% rename from lib/pages/settings/subpages/automation/tabs/radarr.dart rename to lib/routes/settings/subpages/automation/tabs/radarr.dart index 6cf309cd..8c064bcd 100644 --- a/lib/pages/settings/subpages/automation/tabs/radarr.dart +++ b/lib/routes/settings/subpages/automation/tabs/radarr.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class Radarr extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/automation/tabs/sonarr.dart b/lib/routes/settings/subpages/automation/tabs/sonarr.dart similarity index 97% rename from lib/pages/settings/subpages/automation/tabs/sonarr.dart rename to lib/routes/settings/subpages/automation/tabs/sonarr.dart index 52ce0d7b..48cbe2d7 100644 --- a/lib/pages/settings/subpages/automation/tabs/sonarr.dart +++ b/lib/routes/settings/subpages/automation/tabs/sonarr.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class Sonarr extends StatefulWidget { @override diff --git a/lib/routes/settings/subpages/automation/tabs/tabs.dart b/lib/routes/settings/subpages/automation/tabs/tabs.dart new file mode 100644 index 00000000..634dfb64 --- /dev/null +++ b/lib/routes/settings/subpages/automation/tabs/tabs.dart @@ -0,0 +1,3 @@ +export 'package:lunasea/routes/settings/subpages/automation/tabs/lidarr.dart'; +export 'package:lunasea/routes/settings/subpages/automation/tabs/radarr.dart'; +export 'package:lunasea/routes/settings/subpages/automation/tabs/sonarr.dart'; diff --git a/lib/pages/settings/subpages/clients/clients.dart b/lib/routes/settings/subpages/clients/clients.dart similarity index 86% rename from lib/pages/settings/subpages/clients/clients.dart rename to lib/routes/settings/subpages/clients/clients.dart index e50b41d6..f82c94cf 100644 --- a/lib/pages/settings/subpages/clients/clients.dart +++ b/lib/routes/settings/subpages/clients/clients.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/settings/subpages/clients/tabs/tabs.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/settings/subpages/clients/tabs/tabs.dart'; class Clients extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/clients/tabs/nzbget.dart b/lib/routes/settings/subpages/clients/tabs/nzbget.dart similarity index 97% rename from lib/pages/settings/subpages/clients/tabs/nzbget.dart rename to lib/routes/settings/subpages/clients/tabs/nzbget.dart index c9f10a75..2415fb3f 100644 --- a/lib/pages/settings/subpages/clients/tabs/nzbget.dart +++ b/lib/routes/settings/subpages/clients/tabs/nzbget.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class NZBGet extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/clients/tabs/sabnzbd.dart b/lib/routes/settings/subpages/clients/tabs/sabnzbd.dart similarity index 97% rename from lib/pages/settings/subpages/clients/tabs/sabnzbd.dart rename to lib/routes/settings/subpages/clients/tabs/sabnzbd.dart index 8b4d3555..f38323a8 100644 --- a/lib/pages/settings/subpages/clients/tabs/sabnzbd.dart +++ b/lib/routes/settings/subpages/clients/tabs/sabnzbd.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/clients/sabnzbd.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class SABnzbd extends StatefulWidget { @override diff --git a/lib/routes/settings/subpages/clients/tabs/tabs.dart b/lib/routes/settings/subpages/clients/tabs/tabs.dart new file mode 100644 index 00000000..3ded2c45 --- /dev/null +++ b/lib/routes/settings/subpages/clients/tabs/tabs.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/routes/settings/subpages/clients/tabs/nzbget.dart'; +export 'package:lunasea/routes/settings/subpages/clients/tabs/sabnzbd.dart'; diff --git a/lib/pages/settings/subpages/general/general.dart b/lib/routes/settings/subpages/general/general.dart similarity index 88% rename from lib/pages/settings/subpages/general/general.dart rename to lib/routes/settings/subpages/general/general.dart index b6cf3f74..3db13908 100644 --- a/lib/pages/settings/subpages/general/general.dart +++ b/lib/routes/settings/subpages/general/general.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/settings/subpages/general/tabs/tabs.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/settings/subpages/general/tabs/tabs.dart'; class General extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/general/tabs/backup_restore.dart b/lib/routes/settings/subpages/general/tabs/backup_restore.dart similarity index 94% rename from lib/pages/settings/subpages/general/tabs/backup_restore.dart rename to lib/routes/settings/subpages/general/tabs/backup_restore.dart index 91596381..ca05a464 100644 --- a/lib/pages/settings/subpages/general/tabs/backup_restore.dart +++ b/lib/routes/settings/subpages/general/tabs/backup_restore.dart @@ -1,11 +1,9 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/configuration.dart'; -import 'package:lunasea/configuration/encryption.dart'; -import 'package:lunasea/configuration/filesystem.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/widgets/ui.dart'; class BackupRestore extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/general/tabs/logs.dart b/lib/routes/settings/subpages/general/tabs/logs.dart similarity index 94% rename from lib/pages/settings/subpages/general/tabs/logs.dart rename to lib/routes/settings/subpages/general/tabs/logs.dart index 1d525a1d..02800704 100644 --- a/lib/pages/settings/subpages/general/tabs/logs.dart +++ b/lib/routes/settings/subpages/general/tabs/logs.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/pages/settings/subpages/general/tabs/logs/type.dart'; -import 'package:lunasea/system/logger.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/settings/subpages/general/tabs/logs/type.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/widgets/ui.dart'; class Logs extends StatefulWidget { @override @@ -45,6 +45,7 @@ class _State extends State { margin: Elements.getCardMargin(), elevation: 4.0, ), + Elements.getDivider(), Card( child: ListTile( title: Elements.getTitle('Export Logs'), diff --git a/lib/pages/settings/subpages/general/tabs/logs/details.dart b/lib/routes/settings/subpages/general/tabs/logs/details.dart similarity index 99% rename from lib/pages/settings/subpages/general/tabs/logs/details.dart rename to lib/routes/settings/subpages/general/tabs/logs/details.dart index c396012c..f20c8219 100644 --- a/lib/pages/settings/subpages/general/tabs/logs/details.dart +++ b/lib/routes/settings/subpages/general/tabs/logs/details.dart @@ -1,6 +1,6 @@ import 'package:f_logs/f_logs.dart' as FLog; import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class LogDetails extends StatefulWidget { final FLog.Log log; diff --git a/lib/pages/settings/subpages/general/tabs/logs/type.dart b/lib/routes/settings/subpages/general/tabs/logs/type.dart similarity index 96% rename from lib/pages/settings/subpages/general/tabs/logs/type.dart rename to lib/routes/settings/subpages/general/tabs/logs/type.dart index 8e27cf23..80a222a2 100644 --- a/lib/pages/settings/subpages/general/tabs/logs/type.dart +++ b/lib/routes/settings/subpages/general/tabs/logs/type.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/pages/settings/subpages/general/tabs/logs/view.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/settings/subpages/general/tabs/logs/view.dart'; +import 'package:lunasea/widgets/ui.dart'; class TypeLogs extends StatefulWidget { @override diff --git a/lib/pages/settings/subpages/general/tabs/logs/view.dart b/lib/routes/settings/subpages/general/tabs/logs/view.dart similarity index 96% rename from lib/pages/settings/subpages/general/tabs/logs/view.dart rename to lib/routes/settings/subpages/general/tabs/logs/view.dart index 1cc06dd2..2caba1b5 100644 --- a/lib/pages/settings/subpages/general/tabs/logs/view.dart +++ b/lib/routes/settings/subpages/general/tabs/logs/view.dart @@ -1,8 +1,8 @@ import 'package:f_logs/f_logs.dart' as FLog; import 'package:flutter/material.dart'; -import 'package:lunasea/pages/settings/subpages/general/tabs/logs/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/settings/subpages/general/tabs/logs/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class ViewLogs extends StatefulWidget { final String type; diff --git a/lib/pages/settings/subpages/general/tabs/lunasea.dart b/lib/routes/settings/subpages/general/tabs/lunasea.dart similarity index 76% rename from lib/pages/settings/subpages/general/tabs/lunasea.dart rename to lib/routes/settings/subpages/general/tabs/lunasea.dart index 1e241957..2beb258d 100644 --- a/lib/pages/settings/subpages/general/tabs/lunasea.dart +++ b/lib/routes/settings/subpages/general/tabs/lunasea.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/logic/system/version.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; import 'package:package_info/package_info.dart'; +import 'package:lunasea/logic/system/version.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class LunaSea extends StatefulWidget { @override @@ -44,52 +44,6 @@ class _State extends State { return Scrollbar( child: ListView( children: [ - Card( - child: ListTile( - title: Elements.getTitle('Homepage'), - subtitle: Elements.getSubtitle('Visit LunaSea\'s website'), - trailing: IconButton( - icon: Elements.getIcon(Icons.home), - onPressed: null, - ), - onTap: () async { - await Functions.openURL('https://www.lunasea.app'); - }, - ), - margin: Elements.getCardMargin(), - elevation: 4.0, - ), - Card( - child: ListTile( - title: Elements.getTitle('GitHub'), - subtitle: Elements.getSubtitle('Report bugs & request features'), - trailing: IconButton( - icon: Elements.getIcon(CustomIcons.github_circled), - onPressed: null, - ), - onTap: () async { - await Functions.openURL('https://github.com/JagandeepBrar/LunaSea'); - }, - ), - margin: Elements.getCardMargin(), - elevation: 4.0, - ), - Card( - child: ListTile( - title: Elements.getTitle('Reddit'), - subtitle: Elements.getSubtitle('Ask questions & get support'), - trailing: IconButton( - icon: Elements.getIcon(CustomIcons.reddit_alien), - onPressed: null, - ), - onTap: () async { - await Functions.openURL('https://www.reddit.com/r/LunaSeaApp'); - }, - ), - margin: Elements.getCardMargin(), - elevation: 4.0, - ), - Elements.getDivider(), Card( child: ListTile( title: Elements.getTitle('Version: $_version ($_buildNumber)'), @@ -106,6 +60,67 @@ class _State extends State { margin: Elements.getCardMargin(), elevation: 4.0, ), + Elements.getDivider(), + Card( + child: ListTile( + title: Elements.getTitle('Documentation'), + subtitle: Elements.getSubtitle('Discover all the features of LunaSea'), + trailing: IconButton( + icon: Elements.getIcon(CustomIcons.documentation), + onPressed: null, + ), + onTap: () async { + await Functions.openURL('https://docs.lunasea.app'); + }, + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ), + Card( + child: ListTile( + title: Elements.getTitle('GitHub'), + subtitle: Elements.getSubtitle('View the source code'), + trailing: IconButton( + icon: Elements.getIcon(CustomIcons.github), + onPressed: null, + ), + onTap: () async { + await Functions.openURL('https://github.com/JagandeepBrar/LunaSea'); + }, + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ), + Card( + child: ListTile( + title: Elements.getTitle('Reddit'), + subtitle: Elements.getSubtitle('Get support and request features'), + trailing: IconButton( + icon: Elements.getIcon(CustomIcons.reddit), + onPressed: null, + ), + onTap: () async { + await Functions.openURL('https://www.reddit.com/r/LunaSeaApp'); + }, + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ), + Card( + child: ListTile( + title: Elements.getTitle('Website'), + subtitle: Elements.getSubtitle('Visit LunaSea\'s website'), + trailing: IconButton( + icon: Elements.getIcon(CustomIcons.home), + onPressed: null, + ), + onTap: () async { + await Functions.openURL('https://www.lunasea.app'); + }, + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ), ], padding: Elements.getListViewPadding(), ), diff --git a/lib/pages/settings/subpages/general/tabs/profile.dart b/lib/routes/settings/subpages/general/tabs/profile.dart similarity index 97% rename from lib/pages/settings/subpages/general/tabs/profile.dart rename to lib/routes/settings/subpages/general/tabs/profile.dart index 192f3449..83037736 100644 --- a/lib/pages/settings/subpages/general/tabs/profile.dart +++ b/lib/routes/settings/subpages/general/tabs/profile.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/configuration.dart'; -import 'package:lunasea/configuration/profiles.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/widgets/ui.dart'; class Profile extends StatefulWidget { @override @@ -65,6 +64,7 @@ class _State extends State { margin: Elements.getCardMargin(), elevation: 4.0, ), + Elements.getDivider(), Card( child: ListTile( title: Elements.getTitle('Add'), diff --git a/lib/routes/settings/subpages/general/tabs/tabs.dart b/lib/routes/settings/subpages/general/tabs/tabs.dart new file mode 100644 index 00000000..bb9a0c4b --- /dev/null +++ b/lib/routes/settings/subpages/general/tabs/tabs.dart @@ -0,0 +1,4 @@ +export 'package:lunasea/routes/settings/subpages/general/tabs/backup_restore.dart'; +export 'package:lunasea/routes/settings/subpages/general/tabs/logs.dart'; +export 'package:lunasea/routes/settings/subpages/general/tabs/lunasea.dart'; +export 'package:lunasea/routes/settings/subpages/general/tabs/profile.dart'; diff --git a/lib/routes/settings/subpages/indexers/indexers.dart b/lib/routes/settings/subpages/indexers/indexers.dart new file mode 100644 index 00000000..5349b792 --- /dev/null +++ b/lib/routes/settings/subpages/indexers/indexers.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/widgets/ui.dart'; + +class Indexers extends StatefulWidget { + @override + State createState() { + return _State(); + } +} + +class _State extends State { + final _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: Navigation.getAppBar('Settings', context), + body: Notifications.centeredMessage('Coming Soon'), + ); + } +} diff --git a/lib/pages/settings/subpages/monitoring/monitoring.dart b/lib/routes/settings/subpages/monitoring/monitoring.dart similarity index 92% rename from lib/pages/settings/subpages/monitoring/monitoring.dart rename to lib/routes/settings/subpages/monitoring/monitoring.dart index f5a1229a..7b73e7c7 100644 --- a/lib/pages/settings/subpages/monitoring/monitoring.dart +++ b/lib/routes/settings/subpages/monitoring/monitoring.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/widgets/ui.dart'; class Monitoring extends StatefulWidget { @override diff --git a/lib/pages/sonarr/sonarr.dart b/lib/routes/sonarr/sonarr.dart similarity index 92% rename from lib/pages/sonarr/sonarr.dart rename to lib/routes/sonarr/sonarr.dart index 79f7164f..54f52853 100644 --- a/lib/pages/sonarr/sonarr.dart +++ b/lib/routes/sonarr/sonarr.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/catalogue/addseries/search.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; -import 'package:lunasea/pages/sonarr/subpages.dart'; +import 'package:lunasea/routes/sonarr/subpages/catalogue/addseries/search.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages.dart'; class Sonarr extends StatefulWidget { @override @@ -39,10 +38,10 @@ class _State extends State { ]; final List _icons = [ - Icon(Icons.live_tv), - Icon(Icons.timelapse), - Icon(Icons.event_busy), - Icon(Icons.history) + Icon(CustomIcons.television), + Icon(CustomIcons.upcoming), + Icon(CustomIcons.calendar_missing), + Icon(CustomIcons.history) ]; void _navOnTap(int index) { diff --git a/lib/routes/sonarr/subpages.dart b/lib/routes/sonarr/subpages.dart new file mode 100644 index 00000000..f013eb0c --- /dev/null +++ b/lib/routes/sonarr/subpages.dart @@ -0,0 +1,6 @@ +export 'package:lunasea/routes/sonarr/subpages/catalogue/catalogue.dart'; +export 'package:lunasea/routes/sonarr/subpages/history/history.dart'; +export 'package:lunasea/routes/sonarr/subpages/missing/missing.dart'; +export 'package:lunasea/routes/sonarr/subpages/upcoming/upcoming.dart'; +export 'package:lunasea/routes/sonarr/subpages/details/season.dart'; +export 'package:lunasea/routes/sonarr/subpages/details/show.dart'; diff --git a/lib/pages/sonarr/subpages/catalogue/addseries/details.dart b/lib/routes/sonarr/subpages/catalogue/addseries/details.dart similarity index 98% rename from lib/pages/sonarr/subpages/catalogue/addseries/details.dart rename to lib/routes/sonarr/subpages/catalogue/addseries/details.dart index 2fa7b52a..bf18dee9 100644 --- a/lib/pages/sonarr/subpages/catalogue/addseries/details.dart +++ b/lib/routes/sonarr/subpages/catalogue/addseries/details.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/logic/automation/sonarr.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; import 'package:intl/intl.dart'; +import 'package:lunasea/logic/automation/sonarr.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrSeriesSearchDetails extends StatefulWidget { final SonarrSearchEntry entry; diff --git a/lib/pages/sonarr/subpages/catalogue/addseries/search.dart b/lib/routes/sonarr/subpages/catalogue/addseries/search.dart similarity index 97% rename from lib/pages/sonarr/subpages/catalogue/addseries/search.dart rename to lib/routes/sonarr/subpages/catalogue/addseries/search.dart index aafbfbe9..d33b1ef9 100644 --- a/lib/pages/sonarr/subpages/catalogue/addseries/search.dart +++ b/lib/routes/sonarr/subpages/catalogue/addseries/search.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/catalogue/addseries/details.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/catalogue/addseries/details.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrSeriesSearch extends StatefulWidget { @override diff --git a/lib/pages/sonarr/subpages/catalogue/catalogue.dart b/lib/routes/sonarr/subpages/catalogue/catalogue.dart similarity index 91% rename from lib/pages/sonarr/subpages/catalogue/catalogue.dart rename to lib/routes/sonarr/subpages/catalogue/catalogue.dart index 64519803..3d225668 100644 --- a/lib/pages/sonarr/subpages/catalogue/catalogue.dart +++ b/lib/routes/sonarr/subpages/catalogue/catalogue.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/edit.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/show.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/edit.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/show.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Catalogue extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -167,7 +167,7 @@ class _State extends State with TickerProviderStateMixin { _searchController.text = ''; } } - setState(() {}); + if(mounted) setState(() {}); } Widget _noEntries(String message) { @@ -341,11 +341,23 @@ class _State extends State with TickerProviderStateMixin { case 'remove_series': { values = await SonarrDialogs.showDeleteSeriesPrompt(context); if(values[0]) { - if(await SonarrAPI.removeSeries(entry.seriesID)) { - Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); - widget.refreshIndicatorKey?.currentState?.show(); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await SonarrAPI.removeSeries(entry.seriesID, deleteFiles: true)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await SonarrAPI.removeSeries(entry.seriesID)) { + Notifications.showSnackBar(_scaffoldKey, 'Removed ${entry.title}'); + widget.refreshIndicatorKey?.currentState?.show(); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } } break; @@ -378,7 +390,7 @@ class _State extends State with TickerProviderStateMixin { Future _enterShow(SonarrCatalogueEntry entry, int index) async { final result = await Navigator.of(context).push( MaterialPageRoute( - builder: (context) => SonarrShowDetails(entry: entry), + builder: (context) => SonarrShowDetails(entry: entry, seriesID: entry.seriesID), ), ); //Handle the result diff --git a/lib/pages/sonarr/subpages/details/edit.dart b/lib/routes/sonarr/subpages/details/edit.dart similarity index 98% rename from lib/pages/sonarr/subpages/details/edit.dart rename to lib/routes/sonarr/subpages/details/edit.dart index e2ea146a..d7049ff0 100644 --- a/lib/pages/sonarr/subpages/details/edit.dart +++ b/lib/routes/sonarr/subpages/details/edit.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; import 'package:intl/intl.dart'; class SonarrEditSeries extends StatefulWidget { diff --git a/lib/pages/sonarr/subpages/details/release.dart b/lib/routes/sonarr/subpages/details/release.dart similarity index 98% rename from lib/pages/sonarr/subpages/details/release.dart rename to lib/routes/sonarr/subpages/details/release.dart index b88a5e68..b280a419 100644 --- a/lib/pages/sonarr/subpages/details/release.dart +++ b/lib/routes/sonarr/subpages/details/release.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrReleaseInfo extends StatefulWidget { final SonarrReleaseEntry entry; diff --git a/lib/pages/sonarr/subpages/details/search.dart b/lib/routes/sonarr/subpages/details/search.dart similarity index 75% rename from lib/pages/sonarr/subpages/details/search.dart rename to lib/routes/sonarr/subpages/details/search.dart index 520e46cd..8ca896da 100644 --- a/lib/pages/sonarr/subpages/details/search.dart +++ b/lib/routes/sonarr/subpages/details/search.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/release.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/release.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrEpisodeSearch extends StatefulWidget { final SonarrEpisodeEntry entry; @@ -86,12 +85,38 @@ class _State extends State { return Card( child: ListTile( title: Elements.getTitle(release.title), - subtitle: Elements.getSubtitle( - '${Functions.toCapitalize(release.protocol)}' + - '${release.isTorrent ? "\t•\t${release.seeders}/${release.leechers}" : ''}' + - '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n' + - '${release.quality ?? 'Unknown'}\t•\t'+ - '${Functions.bytesToReadable(release.size) ?? 'Unknown'}' + subtitle: RichText( + text: TextSpan( + style: TextStyle( + color: Colors.white70, + fontSize: 14.0, + ), + children: [ + TextSpan( + text: '${Functions.toCapitalize(release.protocol)}', + style: TextStyle( + color: release.isTorrent ? Colors.orange : Color(Constants.ACCENT_COLOR), + fontWeight: FontWeight.bold, + ), + ), + if(release.isTorrent) TextSpan( + text: '${release.isTorrent ? " (${release.seeders}/${release.leechers})" : ''}', + style: TextStyle( + color: Colors.orange, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: '\t•\t${Functions.hoursReadable(release.ageHours) ?? 'Unknown'}\n', + ), + TextSpan( + text: '${release.quality ?? 'Unknown'}\t•\t', + ), + TextSpan( + text: '${Functions.bytesToReadable(release.size) ?? 'Unknown'}', + ), + ] + ), ), trailing: InkWell( child: IconButton( diff --git a/lib/pages/sonarr/subpages/details/season.dart b/lib/routes/sonarr/subpages/details/season.dart similarity index 98% rename from lib/pages/sonarr/subpages/details/season.dart rename to lib/routes/sonarr/subpages/details/season.dart index 3ca8cded..eb28d8a2 100644 --- a/lib/pages/sonarr/subpages/details/season.dart +++ b/lib/routes/sonarr/subpages/details/season.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/selectable_card.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/selectable_card.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrSeasonDetails extends StatefulWidget { final String title; diff --git a/lib/pages/sonarr/subpages/details/selectable_card.dart b/lib/routes/sonarr/subpages/details/selectable_card.dart similarity index 59% rename from lib/pages/sonarr/subpages/details/selectable_card.dart rename to lib/routes/sonarr/subpages/details/selectable_card.dart index 0f79c640..857bf088 100644 --- a/lib/pages/sonarr/subpages/details/selectable_card.dart +++ b/lib/routes/sonarr/subpages/details/selectable_card.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/search.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/search.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SelectableCard extends StatefulWidget { final SonarrEpisodeEntry entry; @@ -91,6 +91,7 @@ class _State extends State { await _enterSearch(); }, ), + onLongPress: _handleLongPress, ), margin: Elements.getCardMargin(), elevation: 4.0, @@ -107,6 +108,59 @@ class _State extends State { widget.selectedCallback(); } + Future _handleLongPress() async { + List _values = await SonarrDialogs.showEpisodeEditingPrompt(context, widget.entry.episodeTitle, widget.entry.isMonitored, widget.entry.episodeFileID > 0); + if(_values[0]) { + switch(_values[1]) { + case 'search_manual': { + await _enterSearch(); + break; + } + case 'search_automatic': { + if(await SonarrAPI.searchEpisodes([widget.entry.episodeID])) { + Notifications.showSnackBar(widget.scaffoldKey, 'Searching for ${widget.entry.episodeTitle}...'); + } else { + Notifications.showSnackBar(widget.scaffoldKey, 'Failed to search for episode'); + } + break; + } + case 'monitor_status': { + if(await SonarrAPI.toggleEpisodeMonitored(widget.entry.episodeID, !widget.entry.isMonitored)) { + Notifications.showSnackBar(widget.scaffoldKey, widget.entry.isMonitored + ? 'No longer monitoring ${widget.entry.episodeTitle}...' + : 'Monitoring ${widget.entry.episodeTitle}...' + ); + if(mounted) setState(() { + widget.entry.isMonitored = !widget.entry.isMonitored; + }); + } else { + Notifications.showSnackBar(widget.scaffoldKey, widget.entry.isMonitored + ? 'Failed to stop monitoring ${widget.entry.episodeTitle}' + : 'Failed to start monitoring ${widget.entry.episodeTitle}' + ); + } + break; + } + case 'delete_file': { + _values = await SonarrDialogs.showDeleteFilePrompt(context); + if(_values[0]) { + if(await SonarrAPI.deleteEpisodeFile(widget.entry.episodeFileID)) { + if(mounted) setState(() { + widget.entry.hasFile = false; + widget.entry.isMonitored = false; + widget.entry.episodeFileID = 0; + }); + Notifications.showSnackBar(widget.scaffoldKey, 'Deleting episode file...'); + } else { + Notifications.showSnackBar(widget.scaffoldKey, 'Failed to delete episode file'); + } + } + break; + } + } + } + } + Future _enterSearch() async { await Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/pages/sonarr/subpages/details/show.dart b/lib/routes/sonarr/subpages/details/show.dart similarity index 92% rename from lib/pages/sonarr/subpages/details/show.dart rename to lib/routes/sonarr/subpages/details/show.dart index 409ae8a7..bd652c2c 100644 --- a/lib/pages/sonarr/subpages/details/show.dart +++ b/lib/routes/sonarr/subpages/details/show.dart @@ -1,18 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/edit.dart'; -import 'package:lunasea/pages/sonarr/subpages/details/season.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/functions.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/edit.dart'; +import 'package:lunasea/routes/sonarr/subpages/details/season.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SonarrShowDetails extends StatefulWidget { final SonarrCatalogueEntry entry; + final int seriesID; SonarrShowDetails({ Key key, @required this.entry, + @required this.seriesID, }): super(key: key); @override @@ -24,6 +25,7 @@ class SonarrShowDetails extends StatefulWidget { class _State extends State { final _scaffoldKey = GlobalKey(); SonarrCatalogueEntry entry; + bool _loading = false; final List _tabTitles = [ 'Overview', @@ -38,9 +40,20 @@ class _State extends State { @override void initState() { super.initState(); + if(entry == null) { + _needFetch(); + } _refreshData(); } + void _needFetch() { + if(mounted) { + setState(() { + _loading = true; + }); + } + } + @override Widget build(BuildContext context) { return DefaultTabController( @@ -48,17 +61,23 @@ class _State extends State { initialIndex: 1, child: Scaffold( key: _scaffoldKey, - body: _buildPage(), + body: _loading + ? Notifications.centeredMessage('Loading...') + : entry == null + ? Notifications.centeredMessage('Connection Error') + : _buildPage(), ), ); } Future _refreshData() async { - SonarrCatalogueEntry _entry = await SonarrAPI.getSeries(entry.seriesID); + SonarrCatalogueEntry _entry = await SonarrAPI.getSeries(widget.seriesID); _entry ??= entry; entry = _entry; if(mounted) { - setState(() {}); + setState(() { + _loading = false; + }); } } @@ -552,10 +571,21 @@ class _State extends State { case 'remove_series': { values = await SonarrDialogs.showDeleteSeriesPrompt(context); if(values[0]) { - if(await SonarrAPI.removeSeries(entry.seriesID)) { - Navigator.of(context).pop('series_deleted'); + if(values[1]) { + values = await SystemDialogs.showDeleteCatalogueWithFilesPrompt(context, entry.title); + if(values[0]) { + if(await SonarrAPI.removeSeries(entry.seriesID, deleteFiles: true)) { + Navigator.of(context).pop('series_deleted'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } + } } else { - Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + if(await SonarrAPI.removeSeries(entry.seriesID)) { + Navigator.of(context).pop('series_deleted'); + } else { + Notifications.showSnackBar(_scaffoldKey, 'Failed to remove ${entry.title}'); + } } } break; diff --git a/lib/pages/sonarr/subpages/history/history.dart b/lib/routes/sonarr/subpages/history/history.dart similarity index 97% rename from lib/pages/sonarr/subpages/history/history.dart rename to lib/routes/sonarr/subpages/history/history.dart index afb4fc91..d1a68e27 100644 --- a/lib/pages/sonarr/subpages/history/history.dart +++ b/lib/routes/sonarr/subpages/history/history.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class History extends StatefulWidget { final GlobalKey refreshIndicatorKey; diff --git a/lib/pages/sonarr/subpages/missing/missing.dart b/lib/routes/sonarr/subpages/missing/missing.dart similarity index 92% rename from lib/pages/sonarr/subpages/missing/missing.dart rename to lib/routes/sonarr/subpages/missing/missing.dart index a7c61567..4b2742d6 100644 --- a/lib/pages/sonarr/subpages/missing/missing.dart +++ b/lib/routes/sonarr/subpages/missing/missing.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Missing extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -130,6 +130,9 @@ class _State extends State { onTap: () async { await _enterSeason(entry, entry.seasonNumber); }, + onLongPress: () async { + await _enterShow(entry); + }, ), decoration: BoxDecoration( image: DecorationImage( @@ -158,4 +161,12 @@ class _State extends State { ), ); } + + Future _enterShow(SonarrMissingEntry entry) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SonarrShowDetails(entry: null, seriesID: entry.seriesID), + ), + ); + } } \ No newline at end of file diff --git a/lib/pages/sonarr/subpages/upcoming/upcoming.dart b/lib/routes/sonarr/subpages/upcoming/upcoming.dart similarity index 93% rename from lib/pages/sonarr/subpages/upcoming/upcoming.dart rename to lib/routes/sonarr/subpages/upcoming/upcoming.dart index b33306c7..2ab22827 100644 --- a/lib/pages/sonarr/subpages/upcoming/upcoming.dart +++ b/lib/routes/sonarr/subpages/upcoming/upcoming.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/pages/sonarr/subpages.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/routes/sonarr/subpages.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class Upcoming extends StatefulWidget { final GlobalKey refreshIndicatorKey; @@ -178,7 +178,10 @@ class _State extends State { contentPadding: Elements.getContentPadding(), onTap: () async { await _enterSeason(entry, entry.seasonNumber); - } + }, + onLongPress: () async { + await _enterShow(entry); + }, ), decoration: BoxDecoration( image: DecorationImage( @@ -207,4 +210,12 @@ class _State extends State { ), ); } + + Future _enterShow(SonarrUpcomingEntry entry) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SonarrShowDetails(entry: null, seriesID: entry.seriesID), + ), + ); + } } \ No newline at end of file diff --git a/lib/system.dart b/lib/system.dart new file mode 100644 index 00000000..b094ae07 --- /dev/null +++ b/lib/system.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/system/configuration.dart'; +export 'package:lunasea/system/logger.dart'; diff --git a/lib/system/configuration.dart b/lib/system/configuration.dart new file mode 100644 index 00000000..296b63f3 --- /dev/null +++ b/lib/system/configuration.dart @@ -0,0 +1,5 @@ +export 'package:lunasea/system/configuration/configuration.dart'; +export 'package:lunasea/system/configuration/encryption.dart'; +export 'package:lunasea/system/configuration/filesystem.dart'; +export 'package:lunasea/system/configuration/profiles.dart'; +export 'package:lunasea/system/configuration/values.dart'; diff --git a/lib/configuration/configuration.dart b/lib/system/configuration/configuration.dart similarity index 98% rename from lib/configuration/configuration.dart rename to lib/system/configuration/configuration.dart index f1914f2b..89c0fff2 100644 --- a/lib/configuration/configuration.dart +++ b/lib/system/configuration/configuration.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/configuration/profiles.dart'; +import 'package:lunasea/system.dart'; class Configuration { Configuration._(); diff --git a/lib/configuration/encryption.dart b/lib/system/configuration/encryption.dart similarity index 93% rename from lib/configuration/encryption.dart rename to lib/system/configuration/encryption.dart index 11463b4b..b5cb1066 100644 --- a/lib/configuration/encryption.dart +++ b/lib/system/configuration/encryption.dart @@ -1,6 +1,6 @@ import 'package:encrypt/encrypt.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/logger.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; class Encryption { Encryption._(); diff --git a/lib/configuration/filesystem.dart b/lib/system/configuration/filesystem.dart similarity index 96% rename from lib/configuration/filesystem.dart rename to lib/system/configuration/filesystem.dart index 6426c9ab..1b3c166d 100644 --- a/lib/configuration/filesystem.dart +++ b/lib/system/configuration/filesystem.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class Filesystem { Filesystem._(); diff --git a/lib/configuration/profiles.dart b/lib/system/configuration/profiles.dart similarity index 98% rename from lib/configuration/profiles.dart rename to lib/system/configuration/profiles.dart index f33ea3be..9bf56a5e 100644 --- a/lib/configuration/profiles.dart +++ b/lib/system/configuration/profiles.dart @@ -1,5 +1,5 @@ import 'package:shared_preferences/shared_preferences.dart'; -import 'package:lunasea/configuration/configuration.dart'; +import 'package:lunasea/system.dart'; class Profiles { Profiles._(); diff --git a/lib/configuration/values.dart b/lib/system/configuration/values.dart similarity index 100% rename from lib/configuration/values.dart rename to lib/system/configuration/values.dart diff --git a/lib/system/ui.dart b/lib/system/ui.dart deleted file mode 100644 index 5b95ce62..00000000 --- a/lib/system/ui.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'package:lunasea/system/ui/dialog.dart'; -export 'package:lunasea/system/ui/elements.dart'; -export 'package:lunasea/system/ui/icons.dart'; -export 'package:lunasea/system/ui/notifications.dart'; -export 'package:lunasea/system/ui/navigation.dart'; diff --git a/lib/system/ui/dialog.dart b/lib/system/ui/dialog.dart deleted file mode 100644 index bd27fa02..00000000 --- a/lib/system/ui/dialog.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'package:lunasea/system/ui/dialogs/system.dart'; -export 'package:lunasea/system/ui/dialogs/lidarr.dart'; -export 'package:lunasea/system/ui/dialogs/radarr.dart'; -export 'package:lunasea/system/ui/dialogs/sonarr.dart'; -export 'package:lunasea/system/ui/dialogs/nzbget.dart'; -export 'package:lunasea/system/ui/dialogs/sabnzbd.dart'; diff --git a/lib/system/ui/icons.dart b/lib/system/ui/icons.dart deleted file mode 100644 index cbc6767c..00000000 --- a/lib/system/ui/icons.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/widgets.dart'; - -class CustomIcons { - CustomIcons._(); - - static const _kFontFam = 'CustomIcons'; - - static const IconData sabnzbd = const IconData(0xe800, fontFamily: _kFontFam); - static const IconData nzbget = const IconData(0xe802, fontFamily: _kFontFam); - static const IconData github_circled = const IconData(0xf09b, fontFamily: _kFontFam); - static const IconData reddit_alien = const IconData(0xf281, fontFamily: _kFontFam); -} diff --git a/lib/widgets.dart b/lib/widgets.dart new file mode 100644 index 00000000..9d66147d --- /dev/null +++ b/lib/widgets.dart @@ -0,0 +1,2 @@ +export 'package:lunasea/widgets/pages.dart'; +export 'package:lunasea/widgets/ui.dart'; diff --git a/lib/widgets/pages.dart b/lib/widgets/pages.dart new file mode 100644 index 00000000..c71c4e9c --- /dev/null +++ b/lib/widgets/pages.dart @@ -0,0 +1,7 @@ +export 'package:lunasea/widgets/pages/home.dart'; +export 'package:lunasea/widgets/pages/lidarr.dart'; +export 'package:lunasea/widgets/pages/nzbget.dart'; +export 'package:lunasea/widgets/pages/radarr.dart'; +export 'package:lunasea/widgets/pages/sabnzbd.dart'; +export 'package:lunasea/widgets/pages/settings.dart'; +export 'package:lunasea/widgets/pages/sonarr.dart'; diff --git a/lib/widgets/pages/home.dart b/lib/widgets/pages/home.dart new file mode 100644 index 00000000..6f34a809 --- /dev/null +++ b/lib/widgets/pages/home.dart @@ -0,0 +1 @@ +export 'package:lunasea/widgets/pages/home/calendar.dart'; diff --git a/lib/widgets/pages/home/calendar.dart b/lib/widgets/pages/home/calendar.dart new file mode 100644 index 00000000..40f6e618 --- /dev/null +++ b/lib/widgets/pages/home/calendar.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_advanced_networkimage/provider.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class CalendarWidget extends StatefulWidget { + final Map events; + final DateTime today; + + CalendarWidget({ + Key key, + @required this.events, + @required this.today, + }) : super(key: key); + + @override + State createState() => _State(); +} + +class _State extends State with TickerProviderStateMixin { + final TextStyle dayTileStyle = TextStyle(color: Colors.white, fontWeight: FontWeight.w600); + final TextStyle outsideDayTileStyle = TextStyle(color: Colors.white30, fontWeight: FontWeight.w600); + + + List _selectedEvents; + AnimationController _animationController; + CalendarController _calendarController; + + @override + void initState() { + super.initState(); + _selectedEvents = widget.events[widget.today] ?? []; + _calendarController = CalendarController(); + _animationController = AnimationController( vsync: this, duration: kThemeAnimationDuration); + _animationController?.forward(); + } + + @override + void dispose() { + _animationController.dispose(); + _calendarController.dispose(); + super.dispose(); + } + + void _onDaySelected(DateTime day, List events) { + setState(() { + _selectedEvents = events; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Padding( + child: Column( + children: [ + _buildCalendar(), + Elements.getDivider(), + _buildList(), + ], + ), + padding: EdgeInsets.only(top: 8.0), + ), + ); + } + + Widget _buildCalendar() { + return Card( + child: Padding( + child: TableCalendar( + calendarController: _calendarController, + events: widget.events, + startingDayOfWeek: StartingDayOfWeek.sunday, + calendarStyle: CalendarStyle( + selectedColor: Color(Constants.PRIMARY_COLOR), + markersMaxAmount: 3, + markersColor: Color(Constants.ACCENT_COLOR), + weekendStyle: dayTileStyle, + weekdayStyle: dayTileStyle, + outsideStyle: outsideDayTileStyle, + selectedStyle: dayTileStyle, + outsideWeekendStyle: outsideDayTileStyle, + renderDaysOfWeek: false, + highlightToday: false, + outsideDaysVisible: false, + ), + headerStyle: HeaderStyle( + titleTextStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + centerHeaderTitle: true, + formatButtonVisible: false, + leftChevronIcon: Elements.getIcon(Icons.arrow_back_ios), + rightChevronIcon: Elements.getIcon(Icons.arrow_forward_ios), + ), + initialCalendarFormat: CalendarFormat.twoWeeks, + availableCalendarFormats: const { + CalendarFormat.month : 'Month', CalendarFormat.twoWeeks : '2 Weeks', CalendarFormat.week : 'Week'}, + onDaySelected: _onDaySelected, + ), + padding: EdgeInsets.only(bottom: 12.0), + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ); + } + + Widget _buildList() { + return Expanded( + child: Scrollbar( + child: ListView( + children: _selectedEvents + .map((event) => _buildListEntry(event)).toList(), + padding: EdgeInsets.only(bottom: 8.0), + ), + ), + ); + } + + Widget _buildListEntry(dynamic event) { + return Card( + child: Container( + child: ListTile( + title: Elements.getTitle(event.title), + subtitle: RichText( + text: event.subtitle, + overflow: TextOverflow.fade, + softWrap: false, + maxLines: 2, + ), + trailing: event.trailing, + onTap: () async { + await event.enterContent(context); + }, + contentPadding: Elements.getContentPadding(), + ), + decoration: BoxDecoration( + image: DecorationImage( + image: AdvancedNetworkImage( + event.bannerURI, + useDiskCache: true, + loadFailedCallback: () {}, + fallbackAssetImage: 'assets/images/secondary_color.png', + retryLimit: 1, + ), + colorFilter: ColorFilter.mode(Color(Constants.SECONDARY_COLOR).withOpacity(0.20), BlendMode.dstATop), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.circular(4.0), + ), + ), + margin: Elements.getCardMargin(), + elevation: 4.0, + ); + } +} diff --git a/lib/widgets/pages/lidarr.dart b/lib/widgets/pages/lidarr.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/pages/nzbget.dart b/lib/widgets/pages/nzbget.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/pages/radarr.dart b/lib/widgets/pages/radarr.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/pages/sabnzbd.dart b/lib/widgets/pages/sabnzbd.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/pages/settings.dart b/lib/widgets/pages/settings.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/pages/sonarr.dart b/lib/widgets/pages/sonarr.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/widgets/ui.dart b/lib/widgets/ui.dart new file mode 100644 index 00000000..9e69bf63 --- /dev/null +++ b/lib/widgets/ui.dart @@ -0,0 +1,8 @@ +export 'package:lunasea/widgets/ui/connection_error.dart'; +export 'package:lunasea/widgets/ui/dialog.dart'; +export 'package:lunasea/widgets/ui/elements.dart'; +export 'package:lunasea/widgets/ui/icons.dart'; +export 'package:lunasea/widgets/ui/loading.dart'; +export 'package:lunasea/widgets/ui/notifications.dart'; +export 'package:lunasea/widgets/ui/navigation.dart'; +export 'package:lunasea/widgets/ui/reorderable_list.dart'; diff --git a/lib/widgets/ui/connection_error.dart b/lib/widgets/ui/connection_error.dart new file mode 100644 index 00000000..f8c092ca --- /dev/null +++ b/lib/widgets/ui/connection_error.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/widgets.dart'; + +class ConnectionError extends StatelessWidget { + final Function onTapHandler; + + ConnectionError({ + Key key, + @required this.onTapHandler, + }) : super(key: key); + + @override + Widget build(BuildContext context) => + Notifications.centeredMessage( + 'Connection Error', + btnMessage: 'Refresh', + showBtn: true, + onTapHandler: onTapHandler, + ); +} diff --git a/lib/widgets/ui/dialog.dart b/lib/widgets/ui/dialog.dart new file mode 100644 index 00000000..a42a504d --- /dev/null +++ b/lib/widgets/ui/dialog.dart @@ -0,0 +1,6 @@ +export 'package:lunasea/widgets/ui/dialogs/system.dart'; +export 'package:lunasea/widgets/ui/dialogs/lidarr.dart'; +export 'package:lunasea/widgets/ui/dialogs/radarr.dart'; +export 'package:lunasea/widgets/ui/dialogs/sonarr.dart'; +export 'package:lunasea/widgets/ui/dialogs/nzbget.dart'; +export 'package:lunasea/widgets/ui/dialogs/sabnzbd.dart'; diff --git a/lib/system/ui/dialogs/lidarr.dart b/lib/widgets/ui/dialogs/lidarr.dart similarity index 95% rename from lib/system/ui/dialogs/lidarr.dart rename to lib/widgets/ui/dialogs/lidarr.dart index dc0cedd3..606bfb6b 100644 --- a/lib/system/ui/dialogs/lidarr.dart +++ b/lib/widgets/ui/dialogs/lidarr.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/lidarr.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class LidarrDialogs { LidarrDialogs._(); @@ -46,7 +46,7 @@ class LidarrDialogs { ), leading: Icon( Icons.portrait, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { quality = qualities[index]; @@ -107,7 +107,7 @@ class LidarrDialogs { ), leading: Icon( Icons.portrait, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { metadata = metadatas[index]; @@ -129,6 +129,7 @@ class LidarrDialogs { static Future> showDeleteArtistPrompt(BuildContext context) async { bool flag = false; + bool files = false; await showDialog( context: context, builder: (BuildContext context) { @@ -152,6 +153,19 @@ class LidarrDialogs { Navigator.of(context).pop(); }, ), + FlatButton( + child: Text( + 'Remove + Files', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + flag = true; + files = true; + Navigator.of(context).pop(); + }, + ), FlatButton( child: Text( 'Remove', @@ -161,13 +175,14 @@ class LidarrDialogs { ), onPressed: () { flag = true; + files = false; Navigator.of(context).pop(); }, ), ], content: SingleChildScrollView( child: Text( - 'Are you sure you want to remove the artist?\n\nRemoving the artist will not delete the files on your machine.', + 'Are you sure you want to remove the artist from Lidarr?', style: TextStyle( color: Colors.white, ), @@ -178,7 +193,7 @@ class LidarrDialogs { ); } ); - return [flag]; + return [flag, files]; } static Future> showDownloadWarningPrompt(BuildContext context) async { @@ -431,7 +446,7 @@ class LidarrDialogs { ), leading: Icon( Icons.folder, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { folder = folders[index]; diff --git a/lib/system/ui/dialogs/nzbget.dart b/lib/widgets/ui/dialogs/nzbget.dart similarity index 99% rename from lib/system/ui/dialogs/nzbget.dart rename to lib/widgets/ui/dialogs/nzbget.dart index 4bee69bb..4c6fd84b 100644 --- a/lib/system/ui/dialogs/nzbget.dart +++ b/lib/widgets/ui/dialogs/nzbget.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/nzbget.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class NZBGetDialogs { NZBGetDialogs._(); diff --git a/lib/system/ui/dialogs/radarr.dart b/lib/widgets/ui/dialogs/radarr.dart similarity index 96% rename from lib/system/ui/dialogs/radarr.dart rename to lib/widgets/ui/dialogs/radarr.dart index 05b97d99..50058125 100644 --- a/lib/system/ui/dialogs/radarr.dart +++ b/lib/widgets/ui/dialogs/radarr.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/radarr.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class RadarrDialogs { RadarrDialogs._(); @@ -61,6 +61,7 @@ class RadarrDialogs { static Future> showDeleteMoviePrompt(BuildContext context) async { bool flag = false; + bool files = false; await showDialog( context: context, builder: (BuildContext context) { @@ -84,6 +85,19 @@ class RadarrDialogs { Navigator.of(context).pop(); }, ), + FlatButton( + child: Text( + 'Remove + Files', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + flag = true; + files = true; + Navigator.of(context).pop(); + }, + ), FlatButton( child: Text( 'Remove', @@ -93,13 +107,14 @@ class RadarrDialogs { ), onPressed: () { flag = true; + files = false; Navigator.of(context).pop(); }, ), ], content: SingleChildScrollView( child: Text( - 'Are you sure you want to remove the movie?\n\nRemoving the movie will not delete the files on your machine.', + 'Are you sure you want to remove the movie from Radarr?', style: TextStyle( color: Colors.white, ), @@ -110,7 +125,7 @@ class RadarrDialogs { ); } ); - return [flag]; + return [flag, files]; } static Future> showSearchMissingPrompt(BuildContext context) async { @@ -305,7 +320,7 @@ class RadarrDialogs { ), leading: Icon( Icons.folder, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { entry = availability[index]; @@ -366,7 +381,7 @@ class RadarrDialogs { ), leading: Icon( Icons.folder, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { folder = folders[index]; @@ -427,7 +442,7 @@ class RadarrDialogs { ), leading: Icon( Icons.portrait, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { quality = qualities[index]; diff --git a/lib/system/ui/dialogs/sabnzbd.dart b/lib/widgets/ui/dialogs/sabnzbd.dart similarity index 99% rename from lib/system/ui/dialogs/sabnzbd.dart rename to lib/widgets/ui/dialogs/sabnzbd.dart index 268bd05a..b430dce0 100644 --- a/lib/system/ui/dialogs/sabnzbd.dart +++ b/lib/widgets/ui/dialogs/sabnzbd.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/clients/sabnzbd/entry.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class SABnzbdDialogs { SABnzbdDialogs._(); diff --git a/lib/system/ui/dialogs/sonarr.dart b/lib/widgets/ui/dialogs/sonarr.dart similarity index 76% rename from lib/system/ui/dialogs/sonarr.dart rename to lib/widgets/ui/dialogs/sonarr.dart index 5a38f026..8b1838f8 100644 --- a/lib/system/ui/dialogs/sonarr.dart +++ b/lib/widgets/ui/dialogs/sonarr.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lunasea/logic/automation/sonarr.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; import 'package:intl/intl.dart'; class SonarrDialogs { @@ -66,6 +66,7 @@ class SonarrDialogs { static Future> showDeleteSeriesPrompt(BuildContext context) async { bool flag = false; + bool files = false; await showDialog( context: context, builder: (BuildContext context) { @@ -89,6 +90,19 @@ class SonarrDialogs { Navigator.of(context).pop(); }, ), + FlatButton( + child: Text( + 'Remove + Files', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + flag = true; + files = true; + Navigator.of(context).pop(); + }, + ), FlatButton( child: Text( 'Remove', @@ -98,13 +112,14 @@ class SonarrDialogs { ), onPressed: () { flag = true; + files = false; Navigator.of(context).pop(); }, ), ], content: SingleChildScrollView( child: Text( - 'Are you sure you want to remove the series?\n\nRemoving the series will not delete the files on your machine.', + 'Are you sure you want to remove the series from Sonarr?', style: TextStyle( color: Colors.white, ), @@ -115,7 +130,7 @@ class SonarrDialogs { ); } ); - return [flag]; + return [flag, files]; } static Future> showSearchMissingPrompt(BuildContext context) async { @@ -302,6 +317,121 @@ class SonarrDialogs { return [flag, value]; } + static Future> showEpisodeEditingPrompt(BuildContext context, String title, bool monitored, bool canDelete) async { + bool flag = false; + String value = ''; + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + title, + maxLines: 1, + softWrap: false, + overflow: TextOverflow.fade, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + actions: [ + FlatButton( + child: Text( + 'Cancel', + style: TextStyle( + color: Color(Constants.ACCENT_COLOR), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + content: SingleChildScrollView( + child: ListBody( + children: [ + ListTile( + leading: Icon( + monitored ? Icons.turned_in_not : Icons.turned_in, + color: Colors.blue, + ), + title: Text( + monitored ? 'Unmonitor Episode' : 'Monitor Episode', + style: TextStyle( + color: Colors.white, + ), + ), + onTap: () { + value = 'monitor_status'; + flag = true; + Navigator.of(context).pop(); + }, + contentPadding: EdgeInsets.fromLTRB(32.0, 0.0, 0.0, 0.0), + ), + ListTile( + leading: Icon( + Icons.search, + color: Color(Constants.ACCENT_COLOR), + ), + title: Text( + 'Automatic Search', + style: TextStyle( + color: Colors.white, + ), + ), + onTap: () { + value = 'search_automatic'; + flag = true; + Navigator.of(context).pop(); + }, + contentPadding: EdgeInsets.fromLTRB(32.0, 0.0, 0.0, 0.0), + ), + ListTile( + leading: Icon( + Icons.youtube_searched_for, + color: Colors.orange, + ), + title: Text( + 'Manual Search', + style: TextStyle( + color: Colors.white, + ), + ), + onTap: () { + value = 'search_manual'; + flag = true; + Navigator.of(context).pop(); + }, + contentPadding: EdgeInsets.fromLTRB(32.0, 0.0, 0.0, 0.0), + ), + if(canDelete) ListTile( + leading: Icon( + Icons.delete, + color: Colors.red, + ), + title: Text( + 'Delete File', + style: TextStyle( + color: Colors.white, + ), + ), + onTap: () { + value = 'delete_file'; + flag = true; + Navigator.of(context).pop(); + }, + contentPadding: EdgeInsets.fromLTRB(32.0, 0.0, 0.0, 0.0), + ), + ], + ), + ), + contentPadding: EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 0.0), + ); + }, + ); + return [flag, value]; + } + static Future> showEditSeriesPrompt(BuildContext context, SonarrCatalogueEntry entry) async { bool flag = false; String value = ''; @@ -440,7 +570,7 @@ class SonarrDialogs { ), leading: Icon( Icons.folder, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { folder = folders[index]; @@ -501,7 +631,7 @@ class SonarrDialogs { ), leading: Icon( Icons.portrait, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { quality = qualities[index]; @@ -562,7 +692,7 @@ class SonarrDialogs { ), leading: Icon( Icons.tab, - color: Color(Constants.ACCENT_COLOR), + color: Constants.LIST_COLOUR_ICONS[index%Constants.LIST_COLOUR_ICONS.length], ), onTap: () { type = SonarrSeriesType(Constants.sonarrSeriesTypes[index]); @@ -635,4 +765,58 @@ class SonarrDialogs { ); return [flag]; } + + static Future> showDeleteFilePrompt(BuildContext context) async { + bool flag = false; + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + 'Delete Episode File', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + actions: [ + FlatButton( + child: Text( + 'Cancel', + style: TextStyle( + color: Colors.white, + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + FlatButton( + child: Text( + 'Delete', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + flag = true; + Navigator.of(context).pop(); + }, + ), + ], + content: SingleChildScrollView( + child: Text( + 'Are you sure you want to delete this episode file?', + style: TextStyle( + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + padding: EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 0.0), + ), + ); + } + ); + return [flag]; + } } diff --git a/lib/system/ui/dialogs/system.dart b/lib/widgets/ui/dialogs/system.dart similarity index 96% rename from lib/system/ui/dialogs/system.dart rename to lib/widgets/ui/dialogs/system.dart index 8c65e04e..ff18fa16 100644 --- a/lib/system/ui/dialogs/system.dart +++ b/lib/widgets/ui/dialogs/system.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/widgets/ui.dart'; class SystemDialogs { SystemDialogs._(); @@ -1237,4 +1237,58 @@ class SystemDialogs { ); return [flag]; } + + static Future> showDeleteCatalogueWithFilesPrompt(BuildContext context, String title) async { + bool flag = false; + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + 'Delete Files', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + actions: [ + FlatButton( + child: Text( + 'Cancel', + style: TextStyle( + color: Colors.white, + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + FlatButton( + child: Text( + 'Delete', + style: TextStyle( + color: Colors.red, + ), + ), + onPressed: () { + flag = true; + Navigator.of(context).pop(); + }, + ), + ], + content: SingleChildScrollView( + child: Text( + 'Are you sure you want to delete all the files and folders for $title?', + style: TextStyle( + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + padding: EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 0.0), + ), + ); + } + ); + return [flag]; + } } diff --git a/lib/system/ui/elements.dart b/lib/widgets/ui/elements.dart similarity index 98% rename from lib/system/ui/elements.dart rename to lib/widgets/ui/elements.dart index 8398938e..f4344572 100644 --- a/lib/system/ui/elements.dart +++ b/lib/widgets/ui/elements.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/system/constants.dart'; +import 'package:lunasea/core.dart'; class Elements { Elements._(); diff --git a/lib/widgets/ui/icons.dart b/lib/widgets/ui/icons.dart new file mode 100644 index 00000000..8fed6e0e --- /dev/null +++ b/lib/widgets/ui/icons.dart @@ -0,0 +1,28 @@ +import 'package:flutter/widgets.dart'; + +class CustomIcons { + CustomIcons._(); + + static const _kFontFam = 'CustomIcons'; + + static const IconData movies = const IconData(0xe800, fontFamily: _kFontFam); + static const IconData upcoming = const IconData(0xe801, fontFamily: _kFontFam); + static const IconData clients = const IconData(0xe802, fontFamily: _kFontFam); + static const IconData television = const IconData(0xe803, fontFamily: _kFontFam); + static const IconData rss = const IconData(0xe804, fontFamily: _kFontFam); + static const IconData music = const IconData(0xe805, fontFamily: _kFontFam); + static const IconData queue = const IconData(0xe806, fontFamily: _kFontFam); + static const IconData history = const IconData(0xe807, fontFamily: _kFontFam); + static const IconData settings = const IconData(0xe808, fontFamily: _kFontFam); + static const IconData layers = const IconData(0xe809, fontFamily: _kFontFam); + static const IconData github = const IconData(0xe80a, fontFamily: _kFontFam); + static const IconData reddit = const IconData(0xe80b, fontFamily: _kFontFam); + static const IconData calendar = const IconData(0xe80c, fontFamily: _kFontFam); + static const IconData calendar_missing = const IconData(0xe80d, fontFamily: _kFontFam); + static const IconData user = const IconData(0xe80e, fontFamily: _kFontFam); + static const IconData documentation = const IconData(0xe80f, fontFamily: _kFontFam); + static const IconData home = const IconData(0xe810, fontFamily: _kFontFam); + static const IconData sabnzbd = const IconData(0xe811, fontFamily: _kFontFam); + static const IconData nzbget = const IconData(0xe812, fontFamily: _kFontFam); + static const IconData arrow_right = const IconData(0xe813, fontFamily: _kFontFam); +} diff --git a/lib/widgets/ui/loading.dart b/lib/widgets/ui/loading.dart new file mode 100644 index 00000000..ce146270 --- /dev/null +++ b/lib/widgets/ui/loading.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; +import 'package:lunasea/widgets.dart'; + +class Loading extends StatelessWidget { + @override + Widget build(BuildContext context) => Notifications.centeredMessage('Loading...'); +} diff --git a/lib/system/ui/navigation.dart b/lib/widgets/ui/navigation.dart similarity index 81% rename from lib/system/ui/navigation.dart rename to lib/widgets/ui/navigation.dart index f0e380e9..7f3e1593 100644 --- a/lib/system/ui/navigation.dart +++ b/lib/widgets/ui/navigation.dart @@ -1,7 +1,8 @@ +import 'package:bottom_navy_bar/bottom_navy_bar.dart'; import 'package:flutter/material.dart'; -import 'package:lunasea/configuration/values.dart'; -import 'package:lunasea/system/constants.dart'; -import 'package:lunasea/system/ui.dart'; +import 'package:lunasea/core.dart'; +import 'package:lunasea/system.dart'; +import 'package:lunasea/widgets/ui.dart'; class Navigation { Navigation._(); @@ -78,27 +79,27 @@ class Navigation { color: Color(Constants.ACCENT_COLOR), ), ), - _getDrawerEntry(context, 'Home', page, '/', Icons.home), - _getDrawerEntry(context, 'Settings', page, '/settings', Icons.settings, justPush: true), + _getDrawerEntry(context, 'Home', page, '/', CustomIcons.home), + _getDrawerEntry(context, 'Settings', page, '/settings', CustomIcons.settings, justPush: true), Elements.getDivider(padding: 18.0), Values.anyAutomationEnabled() ? ( ExpansionTile( - leading: Icon(Icons.layers), + leading: Icon(CustomIcons.layers), title: Text('Automation'), - initiallyExpanded: page == 'sonarr' || page == 'radarr' || page == 'lidarr', + initiallyExpanded: true, children: [ Values.lidarrValues[0] ? ( - _getDrawerEntry(context, 'Lidarr', page, '/lidarr', Icons.music_note, padLeft: true) + _getDrawerEntry(context, 'Lidarr', page, '/lidarr', CustomIcons.music, padLeft: true) ) : ( Container() ), Values.radarrValues[0] ? ( - _getDrawerEntry(context, 'Radarr', page, '/radarr', Icons.movie, padLeft: true) + _getDrawerEntry(context, 'Radarr', page, '/radarr', CustomIcons.movies, padLeft: true) ) : ( Container() ), Values.sonarrValues[0] ? ( - _getDrawerEntry(context, 'Sonarr', page, '/sonarr', Icons.live_tv, padLeft: true) + _getDrawerEntry(context, 'Sonarr', page, '/sonarr', CustomIcons.television, padLeft: true) ) : ( Container() ), @@ -109,9 +110,9 @@ class Navigation { ), Values.anyClientsEnabled() ? ( ExpansionTile( - leading: Icon(Icons.file_download), + leading: Icon(CustomIcons.clients), title: Text( 'Clients'), - initiallyExpanded: page == 'sabnzbd' || page == 'nzbget', + initiallyExpanded: true, children: [ Values.nzbgetValues[0] ? ( _getDrawerEntry(context, 'NZBGet', page, '/nzbget', CustomIcons.nzbget, padLeft: true) @@ -160,18 +161,22 @@ class Navigation { /* * getBottomNavigationBar(): Returns a BottomNavigationBar widget */ - static BottomNavigationBar getBottomNavigationBar(int index, List icons, List titles, Function onTap) { - return BottomNavigationBar( - currentIndex: index, - type: BottomNavigationBarType.fixed, - fixedColor: Color(Constants.ACCENT_COLOR), - elevation: 0.0, - onTap: onTap, + static BottomNavyBar getBottomNavigationBar(int index, List icons, List titles, Function onTap) { + return BottomNavyBar( + selectedIndex: index, + onItemSelected: onTap, + showElevation: false, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + backgroundColor: Color(Constants.PRIMARY_COLOR), + curve: Curves.easeInOut, items: [ - for(int i =0; i=2.6.0 <3.0.0" dependencies: + bottom_navy_bar: ^5.3.2 cupertino_icons: ^0.1.3 encrypt: ^4.0.0 file_picker: ^1.4.3+2 flutter: sdk: flutter flutter_advanced_networkimage: ^0.6.3 + flutter_redux: ^0.6.0 flutter_sticky_header: ^0.4.0 f_logs: ^1.2.2 http: ^0.12.0+4 @@ -20,8 +22,10 @@ dependencies: path_provider: ^1.5.1 percent_indicator: ^2.1.1+1 permission_handler: ^4.2.0+hotfix.2 + redux: ^4.0.0 shared_preferences: ^0.5.6+1 stack_trace: ^1.9.3 + table_calendar: ^2.2.2 url_launcher: ^5.4.1 validators: ^2.0.0+1 @@ -49,7 +53,7 @@ flutter: fonts: - family: CustomIcons fonts: - - asset: assets/custom_icons/CustomIcons.ttf + - asset: assets/CustomIcons.ttf flutter_icons: android: false