feat(overseerr): issues (de)serialization & list integration

This commit is contained in:
Jagandeep Brar
2022-02-20 12:37:37 -05:00
parent cc7349c989
commit db4dfdaf9c
34 changed files with 638 additions and 44 deletions

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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';

View 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();
}
}

View File

@@ -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';

View 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);
}

View 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);
}

View 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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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';

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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';

View 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)';
}
}

View File

@@ -1,3 +1,4 @@
export 'routes/issues.dart';
export 'routes/overseerr.dart';
export 'routes/requests.dart';
export 'routes/users.dart';

View File

@@ -0,0 +1,2 @@
export 'issues/route.dart';
export 'issues/widgets.dart';

View 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(),
);
}
}

View File

@@ -0,0 +1,2 @@
export 'widgets/issue_list_view.dart';
export 'widgets/issue_tile.dart';

View File

@@ -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),
);
}
}

View 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),
);
}
}

View File

@@ -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(),
],
);

View File

@@ -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({

View File

@@ -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,

View File

@@ -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,
),