mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-31 12:42:34 +00:00
feat(overseerr): issues (de)serialization & list integration
This commit is contained in:
@@ -74,7 +74,7 @@ extension LunaLogTypeExtension on LunaLogType {
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case LunaLogType.WARNING:
|
||||
return Icons.warning_rounded;
|
||||
return LunaIcons.WARNING;
|
||||
case LunaLogType.ERROR:
|
||||
return Icons.report_rounded;
|
||||
case LunaLogType.CRITICAL:
|
||||
|
||||
@@ -13,6 +13,7 @@ class LunaIcons {
|
||||
static const IconData DOWNLOAD = Icons.file_download_rounded;
|
||||
static const IconData ERROR = Icons.error_rounded;
|
||||
static const IconData FILTER = Icons.filter_list_rounded;
|
||||
static const IconData HISTORY = Icons.history_rounded;
|
||||
static const IconData HOME = Icons.home_rounded;
|
||||
static const IconData MONITOR_OFF = Icons.turned_in_not_rounded;
|
||||
static const IconData MONITOR_ON = Icons.turned_in_rounded;
|
||||
@@ -21,13 +22,16 @@ class LunaIcons {
|
||||
static const IconData PROFILES = Icons.switch_account_rounded;
|
||||
static const IconData REFRESH = Icons.refresh_rounded;
|
||||
static const IconData RENAME = Icons.text_format_rounded;
|
||||
static const IconData REQUESTS = Icons.access_time_rounded;
|
||||
static const IconData SEARCH = Icons.search_rounded;
|
||||
static const IconData SETTINGS = Icons.settings_rounded;
|
||||
static const IconData SHUFFLE = Icons.shuffle_rounded;
|
||||
static const IconData SORT = Icons.sort_rounded;
|
||||
static const IconData TRANSLATE = Icons.translate_rounded;
|
||||
static const IconData USER = Icons.person_rounded;
|
||||
static const IconData USERS = Icons.people_rounded;
|
||||
static const IconData VIDEO_CAM = Icons.videocam_rounded;
|
||||
static const IconData VIEW = Icons.grid_view_rounded;
|
||||
static const IconData WARNING = Icons.warning_amber_rounded;
|
||||
static const IconData WATCHED = Icons.visibility_rounded;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@ export 'utilities/changelog.dart';
|
||||
export 'utilities/encryption.dart';
|
||||
export 'utilities/helpers.dart';
|
||||
export 'utilities/logger.dart';
|
||||
export 'utilities/parser.dart';
|
||||
export 'utilities/uuid.dart';
|
||||
export 'utilities/validator.dart';
|
||||
|
||||
13
lib/core/utilities/parser.dart
Normal file
13
lib/core/utilities/parser.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
abstract class LunaParser {
|
||||
/// Parse any [String] to a [DateTime] object if possible.
|
||||
static DateTime? dateTimeFromString(String? date) {
|
||||
return DateTime.tryParse(date ?? '');
|
||||
}
|
||||
|
||||
/// Take a [DateTime] object and convert it to an ISO 8601 [String] representation.
|
||||
///
|
||||
/// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ`.
|
||||
static String? dateTimeToISO8601(DateTime? date) {
|
||||
return date?.toIso8601String();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@ export 'models/content/season.dart';
|
||||
export 'models/content/series.dart';
|
||||
export 'models/content/spoken_language.dart';
|
||||
export 'models/content/watch_provider.dart';
|
||||
export 'models/issues/issue_comment.dart';
|
||||
export 'models/issues/issue_page.dart';
|
||||
export 'models/issues/issue.dart';
|
||||
export 'models/media/media.dart';
|
||||
export 'models/media/season.dart';
|
||||
export 'models/requests/request_count.dart';
|
||||
|
||||
71
lib/modules/overseerr/api/models/issues/issue.dart
Normal file
71
lib/modules/overseerr/api/models/issues/issue.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
part 'issue.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class OverseerrIssue {
|
||||
@JsonKey()
|
||||
int id;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrIssueType? issueType;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrIssueStatus? status;
|
||||
|
||||
@JsonKey()
|
||||
int? problemSeason;
|
||||
|
||||
@JsonKey()
|
||||
int? problemEpisode;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrMedia? media;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrUser? createdBy;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrUser? modifiedBy;
|
||||
|
||||
@JsonKey()
|
||||
List<OverseerrIssueComment>? comments;
|
||||
|
||||
OverseerrIssue({
|
||||
required this.id,
|
||||
this.issueType,
|
||||
this.status,
|
||||
this.problemSeason,
|
||||
this.problemEpisode,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.media,
|
||||
this.createdBy,
|
||||
this.modifiedBy,
|
||||
this.comments,
|
||||
});
|
||||
|
||||
/// Returns a JSON-encoded string version of this object.
|
||||
@override
|
||||
String toString() => json.encode(this.toJson());
|
||||
|
||||
/// Deserialize a JSON map to a [OverseerrIssue] object.
|
||||
factory OverseerrIssue.fromJson(Map<String, dynamic> json) =>
|
||||
_$OverseerrIssueFromJson(json);
|
||||
|
||||
/// Serialize a [OverseerrIssue] object to a JSON map.
|
||||
Map<String, dynamic> toJson() => _$OverseerrIssueToJson(this);
|
||||
}
|
||||
47
lib/modules/overseerr/api/models/issues/issue_comment.dart
Normal file
47
lib/modules/overseerr/api/models/issues/issue_comment.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
part 'issue_comment.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class OverseerrIssueComment {
|
||||
@JsonKey()
|
||||
int id;
|
||||
|
||||
@JsonKey()
|
||||
String message;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
@JsonKey()
|
||||
OverseerrUser? user;
|
||||
|
||||
OverseerrIssueComment({
|
||||
required this.id,
|
||||
required this.message,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.user,
|
||||
});
|
||||
|
||||
/// Returns a JSON-encoded string version of this object.
|
||||
@override
|
||||
String toString() => json.encode(this.toJson());
|
||||
|
||||
/// Deserialize a JSON map to a [OverseerrIssueComment] object.
|
||||
factory OverseerrIssueComment.fromJson(Map<String, dynamic> json) =>
|
||||
_$OverseerrIssueCommentFromJson(json);
|
||||
|
||||
/// Serialize a [OverseerrIssueComment] object to a JSON map.
|
||||
Map<String, dynamic> toJson() => _$OverseerrIssueCommentToJson(this);
|
||||
}
|
||||
29
lib/modules/overseerr/api/models/issues/issue_page.dart
Normal file
29
lib/modules/overseerr/api/models/issues/issue_page.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
part 'issue_page.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class OverseerrIssuePage {
|
||||
@JsonKey()
|
||||
OverseerrPageInfo? pageInfo;
|
||||
|
||||
@JsonKey()
|
||||
List<OverseerrIssue>? results;
|
||||
|
||||
OverseerrIssuePage({
|
||||
this.pageInfo,
|
||||
this.results,
|
||||
});
|
||||
|
||||
/// Returns a JSON-encoded string version of this object.
|
||||
@override
|
||||
String toString() => json.encode(this.toJson());
|
||||
|
||||
/// Deserialize a JSON map to a [OverseerrIssuePage] object.
|
||||
factory OverseerrIssuePage.fromJson(Map<String, dynamic> json) =>
|
||||
_$OverseerrIssuePageFromJson(json);
|
||||
|
||||
/// Serialize a [OverseerrIssuePage] object to a JSON map.
|
||||
Map<String, dynamic> toJson() => _$OverseerrIssuePageToJson(this);
|
||||
}
|
||||
@@ -36,26 +36,26 @@ class OverseerrMedia {
|
||||
OverseerrMediaStatus? status4k;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? lastSeasonChange;
|
||||
|
||||
@JsonKey(
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? mediaAddedAt;
|
||||
|
||||
|
||||
@@ -27,15 +27,15 @@ class OverseerrMediaSeason {
|
||||
|
||||
@JsonKey(
|
||||
name: 'createdAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
name: 'updatedAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ class OverseerrRequest {
|
||||
|
||||
@JsonKey(
|
||||
name: 'createdAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
name: 'updatedAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
@@ -46,15 +45,15 @@ class OverseerrUser {
|
||||
|
||||
@JsonKey(
|
||||
name: 'createdAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? createdAt;
|
||||
|
||||
@JsonKey(
|
||||
name: 'updatedAt',
|
||||
fromJson: OverseerrUtilities.dateTimeFromJson,
|
||||
toJson: OverseerrUtilities.dateTimeToJson,
|
||||
fromJson: LunaParser.dateTimeFromString,
|
||||
toJson: LunaParser.dateTimeToISO8601,
|
||||
)
|
||||
DateTime? updatedAt;
|
||||
|
||||
|
||||
@@ -25,6 +25,14 @@ abstract class Overseerr {
|
||||
return _Overseerr(_dio, baseUrl: _baseUrl);
|
||||
}
|
||||
|
||||
@GET('issue')
|
||||
Future<OverseerrIssuePage> getIssues({
|
||||
@Query('take') int? take,
|
||||
@Query('skip') int? skip,
|
||||
@Query('filter') String? filter,
|
||||
@Query('sort') String? sort,
|
||||
});
|
||||
|
||||
@GET('movie/{id}')
|
||||
Future<OverseerrMovie> getMovie({
|
||||
@Path('id') required int id,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export 'types/issue_filter_type.dart';
|
||||
export 'types/issue_sort_type.dart';
|
||||
export 'types/issue_status.dart';
|
||||
export 'types/issue_type.dart';
|
||||
export 'types/media_status.dart';
|
||||
export 'types/media_type.dart';
|
||||
export 'types/request_filter_type.dart';
|
||||
|
||||
28
lib/modules/overseerr/api/types/issue_filter_type.dart
Normal file
28
lib/modules/overseerr/api/types/issue_filter_type.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
const _ALL = 'all';
|
||||
const _OPEN = 'open';
|
||||
const _RESOLVED = 'resolved';
|
||||
|
||||
@JsonEnum()
|
||||
enum OverseerrIssueFilterType {
|
||||
@JsonValue(_ALL)
|
||||
ALL,
|
||||
@JsonValue(_OPEN)
|
||||
OPEN,
|
||||
@JsonValue(_RESOLVED)
|
||||
RESOLVED,
|
||||
}
|
||||
|
||||
extension OverseerrIssueFilterTypeExtension on OverseerrIssueFilterType {
|
||||
String get key {
|
||||
switch (this) {
|
||||
case OverseerrIssueFilterType.ALL:
|
||||
return _ALL;
|
||||
case OverseerrIssueFilterType.OPEN:
|
||||
return _OPEN;
|
||||
case OverseerrIssueFilterType.RESOLVED:
|
||||
return _RESOLVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
lib/modules/overseerr/api/types/issue_sort_type.dart
Normal file
23
lib/modules/overseerr/api/types/issue_sort_type.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
const _MOST_RECENT = 'added';
|
||||
const _LAST_MODIFIED = 'modified';
|
||||
|
||||
@JsonEnum()
|
||||
enum OverseerrIssueSortType {
|
||||
@JsonValue(_MOST_RECENT)
|
||||
MOST_RECENT,
|
||||
@JsonValue(_LAST_MODIFIED)
|
||||
LAST_MODIFIED,
|
||||
}
|
||||
|
||||
extension OverseerrIssueSortTypeExtension on OverseerrIssueSortType {
|
||||
String get key {
|
||||
switch (this) {
|
||||
case OverseerrIssueSortType.MOST_RECENT:
|
||||
return _MOST_RECENT;
|
||||
case OverseerrIssueSortType.LAST_MODIFIED:
|
||||
return _LAST_MODIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
lib/modules/overseerr/api/types/issue_status.dart
Normal file
42
lib/modules/overseerr/api/types/issue_status.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
const _OPEN = 1;
|
||||
const _RESOLVED = 2;
|
||||
|
||||
@JsonEnum()
|
||||
enum OverseerrIssueStatus {
|
||||
@JsonValue(_OPEN)
|
||||
OPEN,
|
||||
@JsonValue(_RESOLVED)
|
||||
RESOLVED,
|
||||
}
|
||||
|
||||
extension OverseerrIssueStatusExtension on OverseerrIssueStatus {
|
||||
int get key {
|
||||
switch (this) {
|
||||
case OverseerrIssueStatus.OPEN:
|
||||
return _OPEN;
|
||||
case OverseerrIssueStatus.RESOLVED:
|
||||
return _RESOLVED;
|
||||
}
|
||||
}
|
||||
|
||||
String get name {
|
||||
switch (this) {
|
||||
case OverseerrIssueStatus.OPEN:
|
||||
return 'overseerr.Open'.tr();
|
||||
case OverseerrIssueStatus.RESOLVED:
|
||||
return 'overseerr.Resolved'.tr();
|
||||
}
|
||||
}
|
||||
|
||||
Color get color {
|
||||
switch (this) {
|
||||
case OverseerrIssueStatus.OPEN:
|
||||
return LunaColours.orange;
|
||||
case OverseerrIssueStatus.RESOLVED:
|
||||
return LunaColours.accent;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
lib/modules/overseerr/api/types/issue_type.dart
Normal file
46
lib/modules/overseerr/api/types/issue_type.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
const _VIDEO = 1;
|
||||
const _AUDIO = 2;
|
||||
const _SUBTITLE = 3;
|
||||
const _OTHER = 4;
|
||||
|
||||
@JsonEnum()
|
||||
enum OverseerrIssueType {
|
||||
@JsonValue(_VIDEO)
|
||||
VIDEO,
|
||||
@JsonValue(_AUDIO)
|
||||
AUDIO,
|
||||
@JsonValue(_SUBTITLE)
|
||||
SUBTITLE,
|
||||
@JsonValue(_OTHER)
|
||||
OTHER,
|
||||
}
|
||||
|
||||
extension OverseerrIssueTypeExtension on OverseerrIssueType {
|
||||
int get key {
|
||||
switch (this) {
|
||||
case OverseerrIssueType.VIDEO:
|
||||
return _VIDEO;
|
||||
case OverseerrIssueType.AUDIO:
|
||||
return _AUDIO;
|
||||
case OverseerrIssueType.SUBTITLE:
|
||||
return _SUBTITLE;
|
||||
case OverseerrIssueType.OTHER:
|
||||
return _OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
String get name {
|
||||
switch (this) {
|
||||
case OverseerrIssueType.VIDEO:
|
||||
return 'overseerr.Video'.tr();
|
||||
case OverseerrIssueType.AUDIO:
|
||||
return 'overseerr.Audio'.tr();
|
||||
case OverseerrIssueType.SUBTITLE:
|
||||
return 'overseerr.Subtitle'.tr();
|
||||
case OverseerrIssueType.OTHER:
|
||||
return 'overseerr.Other'.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,27 @@
|
||||
import 'types.dart';
|
||||
|
||||
abstract class OverseerrUtilities {
|
||||
static DateTime? dateTimeFromJson(String? date) =>
|
||||
DateTime.tryParse(date ?? '');
|
||||
static String? dateTimeToJson(DateTime? date) => date?.toIso8601String();
|
||||
static OverseerrMediaType? mediaTypeFromJson(String? type) {
|
||||
return OverseerrMediaType.TV.from(type);
|
||||
}
|
||||
|
||||
static OverseerrMediaType? mediaTypeFromJson(String? type) =>
|
||||
OverseerrMediaType.TV.from(type);
|
||||
static String? mediaTypeToJson(OverseerrMediaType? type) => type?.value;
|
||||
static String? mediaTypeToJson(OverseerrMediaType? type) {
|
||||
return type?.value;
|
||||
}
|
||||
|
||||
static OverseerrMediaStatus? mediaStatusFromJson(int? type) =>
|
||||
OverseerrMediaStatus.UNKNOWN.from(type);
|
||||
static int? mediaStatusToJson(OverseerrMediaStatus? type) => type?.value;
|
||||
static OverseerrMediaStatus? mediaStatusFromJson(int? type) {
|
||||
return OverseerrMediaStatus.UNKNOWN.from(type);
|
||||
}
|
||||
|
||||
static OverseerrRequestStatus? requestStatusFromJson(int? type) =>
|
||||
OverseerrRequestStatus.APPROVED.from(type);
|
||||
static int? requestStatusToJson(OverseerrRequestStatus? type) => type?.value;
|
||||
static int? mediaStatusToJson(OverseerrMediaStatus? type) {
|
||||
return type?.value;
|
||||
}
|
||||
|
||||
static OverseerrRequestStatus? requestStatusFromJson(int? type) {
|
||||
return OverseerrRequestStatus.APPROVED.from(type);
|
||||
}
|
||||
|
||||
static int? requestStatusToJson(OverseerrRequestStatus? type) {
|
||||
return type?.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'extensions/overseerr_issue.dart';
|
||||
export 'extensions/overseerr_movie.dart';
|
||||
export 'extensions/overseerr_request_status.dart';
|
||||
export 'extensions/overseerr_request.dart';
|
||||
|
||||
21
lib/modules/overseerr/core/extensions/overseerr_issue.dart
Normal file
21
lib/modules/overseerr/core/extensions/overseerr_issue.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
extension LunaOverseerrIssueExtension on OverseerrIssue? {
|
||||
String lunaIssueStatus() {
|
||||
if (this?.status == null) return LunaUI.TEXT_EMDASH;
|
||||
return this!.status!.name;
|
||||
}
|
||||
|
||||
String lunaOpenedBy() {
|
||||
return 'overseerr.OpenedBy'.tr(args: [
|
||||
this?.createdBy.lunaDisplayName() ?? 'overseerr.UnknownUser'.tr(),
|
||||
]);
|
||||
}
|
||||
|
||||
String lunaTypeAndStatus() {
|
||||
String type = this?.issueType?.name ?? LunaUI.TEXT_EMDASH;
|
||||
String status = this?.status?.name ?? LunaUI.TEXT_EMDASH;
|
||||
return '$status ($type)';
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'routes/issues.dart';
|
||||
export 'routes/overseerr.dart';
|
||||
export 'routes/requests.dart';
|
||||
export 'routes/users.dart';
|
||||
|
||||
2
lib/modules/overseerr/routes/issues.dart
Normal file
2
lib/modules/overseerr/routes/issues.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'issues/route.dart';
|
||||
export 'issues/widgets.dart';
|
||||
31
lib/modules/overseerr/routes/issues/route.dart
Normal file
31
lib/modules/overseerr/routes/issues/route.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
class OverseerrIssuesRoute extends StatefulWidget {
|
||||
const OverseerrIssuesRoute({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<OverseerrIssuesRoute>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return LunaScaffold(
|
||||
scaffoldKey: _scaffoldKey,
|
||||
module: LunaModule.OVERSEERR,
|
||||
hideDrawer: true,
|
||||
body: const OverseerrIssuesListView(),
|
||||
);
|
||||
}
|
||||
}
|
||||
2
lib/modules/overseerr/routes/issues/widgets.dart
Normal file
2
lib/modules/overseerr/routes/issues/widgets.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'widgets/issue_list_view.dart';
|
||||
export 'widgets/issue_tile.dart';
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
class OverseerrIssuesListView extends StatefulWidget {
|
||||
const OverseerrIssuesListView({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<OverseerrIssuesListView> {
|
||||
final GlobalKey<RefreshIndicatorState> _refreshKey =
|
||||
GlobalKey<RefreshIndicatorState>();
|
||||
final PagingController<int, OverseerrIssue> _pagingController =
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _fetchPage(int pageKey) async {
|
||||
final int pageSize = OverseerrDatabaseValue.CONTENT_PAGE_SIZE.data;
|
||||
await context
|
||||
.read<OverseerrState>()
|
||||
.api
|
||||
?.getIssues(
|
||||
take: OverseerrDatabaseValue.CONTENT_PAGE_SIZE.data,
|
||||
skip: pageKey * pageSize,
|
||||
filter: OverseerrIssueFilterType.ALL.key,
|
||||
sort: OverseerrIssueSortType.MOST_RECENT.key,
|
||||
)
|
||||
.then((data) => _processPage(data, pageKey, pageSize))
|
||||
.catchError((error, stack) {
|
||||
LunaLogger().error(
|
||||
'Unable to fetch Overseerr issues page / take: $pageSize / skip: ${pageKey * pageSize}',
|
||||
error,
|
||||
stack,
|
||||
);
|
||||
_pagingController.error = error;
|
||||
});
|
||||
}
|
||||
|
||||
void _processPage(OverseerrIssuePage page, int key, int size) {
|
||||
List<OverseerrIssue> _issues = page.results ?? [];
|
||||
if (_issues.length < size) {
|
||||
return _pagingController.appendLastPage(_issues);
|
||||
}
|
||||
return _pagingController.appendPage(_issues, key + 1);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LunaPagedListView<OverseerrIssue>(
|
||||
refreshKey: _refreshKey,
|
||||
pagingController: _pagingController,
|
||||
scrollController: OverseerrNavigationBar.scrollControllers[0],
|
||||
listener: _fetchPage,
|
||||
noItemsFoundMessage: 'overseerr.NoRequestsFound'.tr(),
|
||||
// itemExtent: LunaBlock.calculateItemExtent(3),
|
||||
itemBuilder: (context, issue, _) => OverseerrIssueTile(issue: issue),
|
||||
);
|
||||
}
|
||||
}
|
||||
112
lib/modules/overseerr/routes/issues/widgets/issue_tile.dart
Normal file
112
lib/modules/overseerr/routes/issues/widgets/issue_tile.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
import 'package:lunasea/modules/overseerr.dart';
|
||||
|
||||
class OverseerrIssueTile extends StatefulWidget {
|
||||
final OverseerrIssue issue;
|
||||
|
||||
const OverseerrIssueTile({
|
||||
Key? key,
|
||||
required this.issue,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<OverseerrIssueTile> createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<OverseerrIssueTile> with LunaLoadCallbackMixin {
|
||||
bool _loaded = false;
|
||||
|
||||
@override
|
||||
Future<void> loadCallback() async {
|
||||
int id = widget.issue.media!.tmdbId!;
|
||||
switch (widget.issue.media!.mediaType!) {
|
||||
case OverseerrMediaType.MOVIE:
|
||||
if (mounted) await context.read<OverseerrState>().fetchMovie(id);
|
||||
break;
|
||||
case OverseerrMediaType.TV:
|
||||
if (mounted) await context.read<OverseerrState>().fetchSeries(id);
|
||||
break;
|
||||
}
|
||||
if (mounted) setState(() => _loaded = true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_loaded) return _loadingBlock();
|
||||
|
||||
return Selector<OverseerrState,
|
||||
Tuple2<Future<OverseerrMovie?>, Future<OverseerrSeries?>>>(
|
||||
selector: (_, state) => Tuple2(
|
||||
state.getMovie(widget.issue.media!.tmdbId!),
|
||||
state.getSeries(widget.issue.media!.tmdbId!),
|
||||
),
|
||||
builder: (context, data, _) => FutureBuilder(
|
||||
future: Future.wait([data.item1, data.item2]),
|
||||
builder: (context, AsyncSnapshot<List> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
switch (widget.issue.media!.mediaType!) {
|
||||
case OverseerrMediaType.MOVIE:
|
||||
if (snapshot.data?[0] != null)
|
||||
return _movieBlock(context, snapshot.data![0]);
|
||||
break;
|
||||
case OverseerrMediaType.TV:
|
||||
if (snapshot.data?[1] != null)
|
||||
return _seriesBlock(context, snapshot.data![1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _loadingBlock();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _loadingBlock() {
|
||||
return const LunaBlock(skeletonEnabled: true, skeletonSubtitles: 3);
|
||||
}
|
||||
|
||||
Widget _movieBlock(BuildContext context, OverseerrMovie movie) {
|
||||
return LunaBlock(
|
||||
title: movie.lunaTitle(),
|
||||
body: [
|
||||
TextSpan(text: movie.lunaYear()),
|
||||
TextSpan(text: widget.issue.lunaOpenedBy()),
|
||||
TextSpan(
|
||||
text: widget.issue.lunaTypeAndStatus(),
|
||||
style: TextStyle(
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
color: widget.issue.status!.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
posterHeaders: context.read<OverseerrState>().headers,
|
||||
posterUrl: TheMovieDB.getPosterURL(movie.posterPath),
|
||||
posterPlaceholderIcon: LunaIcons.VIDEO_CAM,
|
||||
backgroundHeaders: context.read<OverseerrState>().headers,
|
||||
backgroundUrl: TheMovieDB.getBackdropURL(movie.backdropPath),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _seriesBlock(BuildContext context, OverseerrSeries series) {
|
||||
return LunaBlock(
|
||||
title: series.lunaTitle(),
|
||||
body: [
|
||||
TextSpan(text: series.lunaYear()),
|
||||
TextSpan(text: widget.issue.lunaOpenedBy()),
|
||||
TextSpan(
|
||||
text: widget.issue.lunaTypeAndStatus(),
|
||||
style: TextStyle(
|
||||
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
|
||||
color: widget.issue.status!.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
posterHeaders: context.read<OverseerrState>().headers,
|
||||
posterUrl: TheMovieDB.getPosterURL(series.posterPath),
|
||||
posterPlaceholderIcon: LunaIcons.VIDEO_CAM,
|
||||
backgroundHeaders: context.read<OverseerrState>().headers,
|
||||
backgroundUrl: TheMovieDB.getBackdropURL(series.backdropPath),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -83,13 +83,14 @@ class _State extends State<_Widget> {
|
||||
if (!enabled!) {
|
||||
return LunaMessage.moduleNotEnabled(
|
||||
context: context,
|
||||
module: 'Overseerr',
|
||||
module: LunaModule.OVERSEERR.name,
|
||||
);
|
||||
}
|
||||
return LunaPageView(
|
||||
controller: _pageController,
|
||||
children: const [
|
||||
OverseerrRequestsRoute(),
|
||||
OverseerrIssuesRoute(),
|
||||
OverseerrUserRoute(),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -9,13 +9,15 @@ class OverseerrNavigationBar extends StatelessWidget {
|
||||
);
|
||||
|
||||
static const List<IconData> icons = [
|
||||
Icons.movie_rounded,
|
||||
Icons.person_rounded,
|
||||
LunaIcons.REQUESTS,
|
||||
LunaIcons.WARNING,
|
||||
LunaIcons.USERS,
|
||||
];
|
||||
|
||||
static List<String> get titles => [
|
||||
'Requests',
|
||||
'Users',
|
||||
'overseerr.Requests'.tr(),
|
||||
'overseerr.Issues'.tr(),
|
||||
'overseerr.Users'.tr(),
|
||||
];
|
||||
|
||||
const OverseerrNavigationBar({
|
||||
|
||||
@@ -58,7 +58,7 @@ class _State extends State<OverseerrRequestsListView> {
|
||||
pagingController: _pagingController,
|
||||
scrollController: OverseerrNavigationBar.scrollControllers[0],
|
||||
listener: _fetchPage,
|
||||
noItemsFoundMessage: 'overseerr.NoRequestsFound'.tr(),
|
||||
noItemsFoundMessage: 'overseerr.NoIssuesFound'.tr(),
|
||||
// itemExtent: LunaBlock.calculateItemExtent(3),
|
||||
itemBuilder: (context, request, _) => OverseerrRequestTile(
|
||||
request: request,
|
||||
|
||||
@@ -738,7 +738,7 @@ class SonarrDialogs {
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 32.0),
|
||||
child: Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
LunaIcons.WARNING,
|
||||
color: LunaColours.orange,
|
||||
size: 24.0,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user