mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
feat: UI 2.0 and other Improvements (#357)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
9ca06eaa37
commit
e7b5bb40ff
169 changed files with 4584 additions and 3626 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/models/home_model.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
|
@ -6,7 +8,6 @@ import 'package:fladder/providers/service_provider.dart';
|
|||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/providers/views_provider.dart';
|
||||
import 'package:fladder/util/list_extensions.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final dashboardProvider = StateNotifierProvider<DashboardNotifier, HomeModel>((ref) {
|
||||
return DashboardNotifier(ref);
|
||||
|
|
@ -34,6 +35,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
|||
ItemFields.mediasources,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.primaryimageaspectratio,
|
||||
],
|
||||
mediaTypes: [MediaType.video],
|
||||
enableTotalRecordCount: false,
|
||||
|
|
@ -53,6 +55,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
|||
ItemFields.mediasources,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.primaryimageaspectratio,
|
||||
],
|
||||
mediaTypes: [MediaType.audio],
|
||||
enableTotalRecordCount: false,
|
||||
|
|
@ -72,6 +75,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
|||
ItemFields.mediasources,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.primaryimageaspectratio,
|
||||
],
|
||||
mediaTypes: [MediaType.book],
|
||||
enableTotalRecordCount: false,
|
||||
|
|
@ -84,14 +88,15 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
|||
|
||||
final nextResponse = await api.showsNextUpGet(
|
||||
limit: 16,
|
||||
nextUpDateCutoff: DateTime.now()
|
||||
.subtract(ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff ?? const Duration(days: 28)))),
|
||||
nextUpDateCutoff: DateTime.now().subtract(
|
||||
ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff ?? const Duration(days: 28)))),
|
||||
fields: [
|
||||
ItemFields.parentid,
|
||||
ItemFields.mediastreams,
|
||||
ItemFields.mediasources,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.primaryimageaspectratio,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:chopper/chopper.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/favourites_model.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
|
@ -6,7 +8,6 @@ import 'package:fladder/models/view_model.dart';
|
|||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/views_provider.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final favouritesProvider = StateNotifierProvider<FavouritesNotifier, FavouritesModel>((ref) {
|
||||
return FavouritesNotifier(ref);
|
||||
|
|
@ -48,7 +49,7 @@ class FavouritesNotifier extends StateNotifier<FavouritesModel> {
|
|||
isFavorite: true,
|
||||
limit: 10,
|
||||
sortOrder: [SortOrder.ascending],
|
||||
sortBy: [ItemSortBy.seriessortname, ItemSortBy.sortname],
|
||||
sortBy: [ItemSortBy.seriessortname, ItemSortBy.sortname, ItemSortBy.datelastcontentadded],
|
||||
);
|
||||
final response2 = await api.itemsGet(
|
||||
parentId: viewModel?.id,
|
||||
|
|
@ -57,7 +58,7 @@ class FavouritesNotifier extends StateNotifier<FavouritesModel> {
|
|||
limit: 10,
|
||||
includeItemTypes: [BaseItemKind.photo, BaseItemKind.episode, BaseItemKind.video, BaseItemKind.collectionfolder],
|
||||
sortOrder: [SortOrder.ascending],
|
||||
sortBy: [ItemSortBy.seriessortname, ItemSortBy.sortname],
|
||||
sortBy: [ItemSortBy.seriessortname, ItemSortBy.sortname, ItemSortBy.datelastcontentadded],
|
||||
);
|
||||
return [...?response.body?.items, ...?response2.body?.items];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
import 'package:chopper/chopper.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/photos_model.dart';
|
||||
import 'package:fladder/models/library_model.dart';
|
||||
import 'package:fladder/models/recommended_model.dart';
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/service_provider.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
|
||||
bool _useFolders(ViewModel model) {
|
||||
switch (model.collectionType) {
|
||||
case CollectionType.boxsets:
|
||||
case CollectionType.homevideos:
|
||||
case CollectionType.folders:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final libraryProvider = StateNotifierProvider.autoDispose.family<LibraryNotifier, LibraryModel?, String>((ref, id) {
|
||||
return LibraryNotifier(ref);
|
||||
});
|
||||
|
||||
class LibraryNotifier extends StateNotifier<LibraryModel?> {
|
||||
LibraryNotifier(this.ref) : super(null);
|
||||
|
||||
final Ref ref;
|
||||
|
||||
late final JellyService api = ref.read(jellyApiProvider);
|
||||
|
||||
set loading(bool value) {
|
||||
state = state?.copyWith(loading: value);
|
||||
}
|
||||
|
||||
bool get loading => state?.loading ?? true;
|
||||
|
||||
Future<void> setupLibrary(ViewModel viewModel) async {
|
||||
state ??= LibraryModel(id: viewModel.id, name: viewModel.name, loading: true, type: BaseItemKind.movie);
|
||||
}
|
||||
|
||||
Future<Response?> loadLibrary(ViewModel viewModel) async {
|
||||
final response = await api.itemsGet(
|
||||
parentId: viewModel.id,
|
||||
sortBy: [ItemSortBy.sortname, ItemSortBy.productionyear],
|
||||
isMissing: false,
|
||||
excludeItemTypes: !_useFolders(viewModel) ? [BaseItemKind.folder] : [],
|
||||
fields: [ItemFields.genres, ItemFields.childcount, ItemFields.parentid],
|
||||
);
|
||||
state = state?.copyWith(posters: response.body?.items);
|
||||
loading = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<void> loadRecommendations(ViewModel viewModel) async {
|
||||
loading = true;
|
||||
//Clear recommendations because of all the copying
|
||||
state = state?.copyWith(recommendations: []);
|
||||
final latest = await api.usersUserIdItemsLatestGet(
|
||||
parentId: viewModel.id,
|
||||
limit: 14,
|
||||
isPlayed: false,
|
||||
imageTypeLimit: 1,
|
||||
includeItemTypes: viewModel.collectionType == CollectionType.tvshows ? [BaseItemKind.episode] : null,
|
||||
);
|
||||
state = state?.copyWith(
|
||||
recommendations: [
|
||||
...?state?.recommendations,
|
||||
RecommendedModel(
|
||||
name: "Latest",
|
||||
posters: latest.body?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList() ?? [],
|
||||
type: "Latest",
|
||||
),
|
||||
],
|
||||
);
|
||||
if (viewModel.collectionType == CollectionType.movies) {
|
||||
final response = await api.moviesRecommendationsGet(
|
||||
parentId: viewModel.id,
|
||||
categoryLimit: 6,
|
||||
itemLimit: 8,
|
||||
fields: [ItemFields.mediasourcecount],
|
||||
);
|
||||
state = state?.copyWith(recommendations: [
|
||||
...?state?.recommendations,
|
||||
...response.body?.map(
|
||||
(e) => RecommendedModel(
|
||||
name: e.baselineItemName ?? "",
|
||||
posters: e.items?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList() ?? [],
|
||||
type: e.recommendationType.toString(),
|
||||
),
|
||||
) ??
|
||||
[],
|
||||
]);
|
||||
loading = false;
|
||||
} else {
|
||||
final nextUp = await api.showsNextUpGet(
|
||||
parentId: viewModel.id,
|
||||
limit: 14,
|
||||
imageTypeLimit: 1,
|
||||
fields: [ItemFields.mediasourcecount, ItemFields.primaryimageaspectratio],
|
||||
);
|
||||
state = state?.copyWith(recommendations: [
|
||||
...?state?.recommendations,
|
||||
...[
|
||||
RecommendedModel(
|
||||
name: "Next up",
|
||||
posters: nextUp.body?.items
|
||||
?.map(
|
||||
(e) => ItemBaseModel.fromBaseDto(
|
||||
e,
|
||||
ref,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
type: "Latest series")
|
||||
],
|
||||
]);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Response?> loadFavourites(ViewModel viewModel) async {
|
||||
loading = true;
|
||||
final response = await api.itemsGet(
|
||||
parentId: viewModel.id,
|
||||
isFavorite: true,
|
||||
recursive: true,
|
||||
);
|
||||
|
||||
state = state?.copyWith(favourites: response.body?.items);
|
||||
loading = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Response?> loadTimeline(ViewModel viewModel) async {
|
||||
loading = true;
|
||||
final response = await api.itemsGet(
|
||||
parentId: viewModel.id,
|
||||
recursive: true,
|
||||
fields: [ItemFields.primaryimageaspectratio, ItemFields.datecreated],
|
||||
sortBy: [ItemSortBy.datecreated],
|
||||
sortOrder: [SortOrder.descending],
|
||||
includeItemTypes: [
|
||||
BaseItemKind.photo,
|
||||
BaseItemKind.video,
|
||||
],
|
||||
);
|
||||
state = state?.copyWith(
|
||||
timelinePhotos: response.body?.items.map((e) => e as PhotoModel).toList(),
|
||||
);
|
||||
loading = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Response?> loadGenres(ViewModel viewModel) async {
|
||||
final genres = await api.genresGet(
|
||||
sortBy: [ItemSortBy.sortname],
|
||||
sortOrder: [SortOrder.ascending],
|
||||
includeItemTypes: viewModel.collectionType == CollectionType.movies
|
||||
? [BaseItemKind.movie]
|
||||
: [
|
||||
BaseItemKind.series,
|
||||
],
|
||||
parentId: viewModel.id,
|
||||
);
|
||||
state = state?.copyWith(
|
||||
genres: genres.body?.items?.where((element) => element.name?.isNotEmpty ?? false).map((e) => e.name!).toList());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
219
lib/providers/library_screen_provider.dart
Normal file
219
lib/providers/library_screen_provider.dart
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:chopper/chopper.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/models/collection_types.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/recommended_model.dart';
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/service_provider.dart';
|
||||
import 'package:fladder/providers/views_provider.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
part 'library_screen_provider.freezed.dart';
|
||||
part 'library_screen_provider.g.dart';
|
||||
|
||||
enum LibraryViewType {
|
||||
recommended,
|
||||
favourites,
|
||||
genres;
|
||||
|
||||
const LibraryViewType();
|
||||
|
||||
String label(BuildContext context) => switch (this) {
|
||||
LibraryViewType.recommended => context.localized.recommended,
|
||||
LibraryViewType.favourites => context.localized.favorites,
|
||||
LibraryViewType.genres => context.localized.genre(2),
|
||||
};
|
||||
|
||||
IconData get icon => switch (this) {
|
||||
LibraryViewType.recommended => IconsaxPlusLinear.star,
|
||||
LibraryViewType.favourites => IconsaxPlusLinear.heart,
|
||||
LibraryViewType.genres => IconsaxPlusLinear.hierarchy_3,
|
||||
};
|
||||
|
||||
IconData get iconSelected => switch (this) {
|
||||
LibraryViewType.recommended => IconsaxPlusBold.star,
|
||||
LibraryViewType.favourites => IconsaxPlusBold.heart,
|
||||
LibraryViewType.genres => IconsaxPlusBold.hierarchy_3,
|
||||
};
|
||||
}
|
||||
|
||||
@Freezed(fromJson: false, toJson: false)
|
||||
class LibraryScreenModel with _$LibraryScreenModel {
|
||||
factory LibraryScreenModel({
|
||||
@Default([]) List<ViewModel> views,
|
||||
ViewModel? selectedViewModel,
|
||||
@Default({LibraryViewType.recommended, LibraryViewType.favourites}) Set<LibraryViewType> viewType,
|
||||
@Default([]) List<RecommendedModel> recommendations,
|
||||
@Default([]) List<RecommendedModel> genres,
|
||||
@Default([]) List<ItemBaseModel> favourites,
|
||||
}) = _LibraryScreenModel;
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class LibraryScreen extends _$LibraryScreen {
|
||||
late final JellyService api = ref.read(jellyApiProvider);
|
||||
|
||||
@override
|
||||
LibraryScreenModel build() => LibraryScreenModel();
|
||||
|
||||
Future<void> fetchAllLibraries() async {
|
||||
final views = await ref.read(viewsProvider.notifier).fetchViews();
|
||||
state = state.copyWith(views: views?.views ?? []);
|
||||
if (state.views.isEmpty) return;
|
||||
final viewModel = state.selectedViewModel ?? state.views.firstOrNull;
|
||||
if (viewModel == null) return;
|
||||
selectLibrary(viewModel);
|
||||
await loadLibrary(viewModel);
|
||||
}
|
||||
|
||||
Future<void> selectLibrary(ViewModel viewModel) async {
|
||||
state = state.copyWith(selectedViewModel: viewModel);
|
||||
}
|
||||
|
||||
Future<void> setViewType(Set<LibraryViewType> type) async {
|
||||
state = state.copyWith(viewType: type);
|
||||
}
|
||||
|
||||
Future<Response?> loadLibrary(ViewModel viewModel) async {
|
||||
await loadRecommendations(viewModel);
|
||||
await loadGenres(viewModel);
|
||||
await loadFavourites(viewModel);
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> loadRecommendations(ViewModel viewModel) async {
|
||||
List<RecommendedModel> newRecommendations = [];
|
||||
final latest = await api.usersUserIdItemsLatestGet(
|
||||
parentId: viewModel.id,
|
||||
limit: 14,
|
||||
isPlayed: false,
|
||||
imageTypeLimit: 1,
|
||||
includeItemTypes: viewModel.collectionType.itemKinds.map((e) => e.dtoKind).toList(),
|
||||
);
|
||||
newRecommendations = [
|
||||
...newRecommendations,
|
||||
RecommendedModel(
|
||||
name: const Latest(),
|
||||
posters: latest.body?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList() ?? [],
|
||||
type: null,
|
||||
),
|
||||
];
|
||||
if (viewModel.collectionType == CollectionType.movies) {
|
||||
final response = await api.moviesRecommendationsGet(
|
||||
parentId: viewModel.id,
|
||||
categoryLimit: 6,
|
||||
itemLimit: 14,
|
||||
fields: [ItemFields.mediasourcecount],
|
||||
);
|
||||
newRecommendations = [
|
||||
...newRecommendations,
|
||||
...(response.body?.map(
|
||||
(e) => RecommendedModel.fromBaseDto(e, ref),
|
||||
) ??
|
||||
[])
|
||||
];
|
||||
} else {
|
||||
final nextUp = await api.showsNextUpGet(
|
||||
parentId: viewModel.id,
|
||||
limit: 14,
|
||||
imageTypeLimit: 1,
|
||||
fields: [ItemFields.mediasourcecount, ItemFields.primaryimageaspectratio],
|
||||
);
|
||||
newRecommendations = [
|
||||
...newRecommendations,
|
||||
RecommendedModel(
|
||||
name: const NextUp(),
|
||||
posters: nextUp.body?.items
|
||||
?.map(
|
||||
(e) => ItemBaseModel.fromBaseDto(
|
||||
e,
|
||||
ref,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
type: null,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
recommendations: newRecommendations,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Response?> loadFavourites(ViewModel viewModel) async {
|
||||
final response = await api.itemsGet(
|
||||
parentId: viewModel.id,
|
||||
isFavorite: true,
|
||||
recursive: true,
|
||||
includeItemTypes: viewModel.collectionType.itemKinds.map((e) => e.dtoKind).toList(),
|
||||
enableImageTypes: [ImageType.primary],
|
||||
fields: [
|
||||
ItemFields.primaryimageaspectratio,
|
||||
ItemFields.mediasourcecount,
|
||||
],
|
||||
enableTotalRecordCount: false,
|
||||
);
|
||||
|
||||
state = state.copyWith(favourites: response.body?.items ?? []);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Response?> loadGenres(ViewModel viewModel) async {
|
||||
final genres = await api.genresGet(
|
||||
sortBy: [ItemSortBy.sortname],
|
||||
sortOrder: [SortOrder.ascending],
|
||||
includeItemTypes:
|
||||
viewModel.collectionType == CollectionType.movies ? [BaseItemKind.movie] : [BaseItemKind.series],
|
||||
parentId: viewModel.id,
|
||||
);
|
||||
|
||||
final filteredGenres = (genres.body?.items?.map(
|
||||
(item) => GenreItems(id: item.id ?? "", name: item.name ?? ""),
|
||||
) ??
|
||||
[])
|
||||
.toList();
|
||||
|
||||
if (filteredGenres.isEmpty) return null;
|
||||
|
||||
final results = await Future.wait(filteredGenres.map((genre) async {
|
||||
final response = await api.itemsGet(
|
||||
parentId: viewModel.id,
|
||||
genreIds: [genre.id],
|
||||
limit: 9,
|
||||
recursive: true,
|
||||
includeItemTypes: viewModel.collectionType.itemKinds.map((e) => e.dtoKind).toList(),
|
||||
enableImageTypes: [ImageType.primary],
|
||||
fields: [
|
||||
ItemFields.primaryimageaspectratio,
|
||||
ItemFields.mediasourcecount,
|
||||
],
|
||||
sortBy: [ItemSortBy.random],
|
||||
enableTotalRecordCount: false,
|
||||
imageTypeLimit: 1,
|
||||
sortOrder: [SortOrder.ascending],
|
||||
);
|
||||
|
||||
final items = response.body?.items;
|
||||
if (items != null && items.isNotEmpty) {
|
||||
return RecommendedModel(name: Other(genre.name), posters: items);
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
|
||||
state = state.copyWith(
|
||||
genres: results.whereType<RecommendedModel>().toList(),
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
301
lib/providers/library_screen_provider.freezed.dart
Normal file
301
lib/providers/library_screen_provider.freezed.dart
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'library_screen_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LibraryScreenModel {
|
||||
List<ViewModel> get views => throw _privateConstructorUsedError;
|
||||
ViewModel? get selectedViewModel => throw _privateConstructorUsedError;
|
||||
Set<LibraryViewType> get viewType => throw _privateConstructorUsedError;
|
||||
List<RecommendedModel> get recommendations =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<RecommendedModel> get genres => throw _privateConstructorUsedError;
|
||||
List<ItemBaseModel> get favourites => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of LibraryScreenModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LibraryScreenModelCopyWith<LibraryScreenModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $LibraryScreenModelCopyWith<$Res> {
|
||||
factory $LibraryScreenModelCopyWith(
|
||||
LibraryScreenModel value, $Res Function(LibraryScreenModel) then) =
|
||||
_$LibraryScreenModelCopyWithImpl<$Res, LibraryScreenModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<ViewModel> views,
|
||||
ViewModel? selectedViewModel,
|
||||
Set<LibraryViewType> viewType,
|
||||
List<RecommendedModel> recommendations,
|
||||
List<RecommendedModel> genres,
|
||||
List<ItemBaseModel> favourites});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LibraryScreenModelCopyWithImpl<$Res, $Val extends LibraryScreenModel>
|
||||
implements $LibraryScreenModelCopyWith<$Res> {
|
||||
_$LibraryScreenModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LibraryScreenModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? views = null,
|
||||
Object? selectedViewModel = freezed,
|
||||
Object? viewType = null,
|
||||
Object? recommendations = null,
|
||||
Object? genres = null,
|
||||
Object? favourites = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
views: null == views
|
||||
? _value.views
|
||||
: views // ignore: cast_nullable_to_non_nullable
|
||||
as List<ViewModel>,
|
||||
selectedViewModel: freezed == selectedViewModel
|
||||
? _value.selectedViewModel
|
||||
: selectedViewModel // ignore: cast_nullable_to_non_nullable
|
||||
as ViewModel?,
|
||||
viewType: null == viewType
|
||||
? _value.viewType
|
||||
: viewType // ignore: cast_nullable_to_non_nullable
|
||||
as Set<LibraryViewType>,
|
||||
recommendations: null == recommendations
|
||||
? _value.recommendations
|
||||
: recommendations // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecommendedModel>,
|
||||
genres: null == genres
|
||||
? _value.genres
|
||||
: genres // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecommendedModel>,
|
||||
favourites: null == favourites
|
||||
? _value.favourites
|
||||
: favourites // ignore: cast_nullable_to_non_nullable
|
||||
as List<ItemBaseModel>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LibraryScreenModelImplCopyWith<$Res>
|
||||
implements $LibraryScreenModelCopyWith<$Res> {
|
||||
factory _$$LibraryScreenModelImplCopyWith(_$LibraryScreenModelImpl value,
|
||||
$Res Function(_$LibraryScreenModelImpl) then) =
|
||||
__$$LibraryScreenModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<ViewModel> views,
|
||||
ViewModel? selectedViewModel,
|
||||
Set<LibraryViewType> viewType,
|
||||
List<RecommendedModel> recommendations,
|
||||
List<RecommendedModel> genres,
|
||||
List<ItemBaseModel> favourites});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LibraryScreenModelImplCopyWithImpl<$Res>
|
||||
extends _$LibraryScreenModelCopyWithImpl<$Res, _$LibraryScreenModelImpl>
|
||||
implements _$$LibraryScreenModelImplCopyWith<$Res> {
|
||||
__$$LibraryScreenModelImplCopyWithImpl(_$LibraryScreenModelImpl _value,
|
||||
$Res Function(_$LibraryScreenModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LibraryScreenModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? views = null,
|
||||
Object? selectedViewModel = freezed,
|
||||
Object? viewType = null,
|
||||
Object? recommendations = null,
|
||||
Object? genres = null,
|
||||
Object? favourites = null,
|
||||
}) {
|
||||
return _then(_$LibraryScreenModelImpl(
|
||||
views: null == views
|
||||
? _value._views
|
||||
: views // ignore: cast_nullable_to_non_nullable
|
||||
as List<ViewModel>,
|
||||
selectedViewModel: freezed == selectedViewModel
|
||||
? _value.selectedViewModel
|
||||
: selectedViewModel // ignore: cast_nullable_to_non_nullable
|
||||
as ViewModel?,
|
||||
viewType: null == viewType
|
||||
? _value._viewType
|
||||
: viewType // ignore: cast_nullable_to_non_nullable
|
||||
as Set<LibraryViewType>,
|
||||
recommendations: null == recommendations
|
||||
? _value._recommendations
|
||||
: recommendations // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecommendedModel>,
|
||||
genres: null == genres
|
||||
? _value._genres
|
||||
: genres // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecommendedModel>,
|
||||
favourites: null == favourites
|
||||
? _value._favourites
|
||||
: favourites // ignore: cast_nullable_to_non_nullable
|
||||
as List<ItemBaseModel>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$LibraryScreenModelImpl implements _LibraryScreenModel {
|
||||
_$LibraryScreenModelImpl(
|
||||
{final List<ViewModel> views = const [],
|
||||
this.selectedViewModel,
|
||||
final Set<LibraryViewType> viewType = const {
|
||||
LibraryViewType.recommended,
|
||||
LibraryViewType.favourites
|
||||
},
|
||||
final List<RecommendedModel> recommendations = const [],
|
||||
final List<RecommendedModel> genres = const [],
|
||||
final List<ItemBaseModel> favourites = const []})
|
||||
: _views = views,
|
||||
_viewType = viewType,
|
||||
_recommendations = recommendations,
|
||||
_genres = genres,
|
||||
_favourites = favourites;
|
||||
|
||||
final List<ViewModel> _views;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<ViewModel> get views {
|
||||
if (_views is EqualUnmodifiableListView) return _views;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_views);
|
||||
}
|
||||
|
||||
@override
|
||||
final ViewModel? selectedViewModel;
|
||||
final Set<LibraryViewType> _viewType;
|
||||
@override
|
||||
@JsonKey()
|
||||
Set<LibraryViewType> get viewType {
|
||||
if (_viewType is EqualUnmodifiableSetView) return _viewType;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableSetView(_viewType);
|
||||
}
|
||||
|
||||
final List<RecommendedModel> _recommendations;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<RecommendedModel> get recommendations {
|
||||
if (_recommendations is EqualUnmodifiableListView) return _recommendations;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_recommendations);
|
||||
}
|
||||
|
||||
final List<RecommendedModel> _genres;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<RecommendedModel> get genres {
|
||||
if (_genres is EqualUnmodifiableListView) return _genres;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_genres);
|
||||
}
|
||||
|
||||
final List<ItemBaseModel> _favourites;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<ItemBaseModel> get favourites {
|
||||
if (_favourites is EqualUnmodifiableListView) return _favourites;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_favourites);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LibraryScreenModel(views: $views, selectedViewModel: $selectedViewModel, viewType: $viewType, recommendations: $recommendations, genres: $genres, favourites: $favourites)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$LibraryScreenModelImpl &&
|
||||
const DeepCollectionEquality().equals(other._views, _views) &&
|
||||
(identical(other.selectedViewModel, selectedViewModel) ||
|
||||
other.selectedViewModel == selectedViewModel) &&
|
||||
const DeepCollectionEquality().equals(other._viewType, _viewType) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._recommendations, _recommendations) &&
|
||||
const DeepCollectionEquality().equals(other._genres, _genres) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._favourites, _favourites));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_views),
|
||||
selectedViewModel,
|
||||
const DeepCollectionEquality().hash(_viewType),
|
||||
const DeepCollectionEquality().hash(_recommendations),
|
||||
const DeepCollectionEquality().hash(_genres),
|
||||
const DeepCollectionEquality().hash(_favourites));
|
||||
|
||||
/// Create a copy of LibraryScreenModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LibraryScreenModelImplCopyWith<_$LibraryScreenModelImpl> get copyWith =>
|
||||
__$$LibraryScreenModelImplCopyWithImpl<_$LibraryScreenModelImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _LibraryScreenModel implements LibraryScreenModel {
|
||||
factory _LibraryScreenModel(
|
||||
{final List<ViewModel> views,
|
||||
final ViewModel? selectedViewModel,
|
||||
final Set<LibraryViewType> viewType,
|
||||
final List<RecommendedModel> recommendations,
|
||||
final List<RecommendedModel> genres,
|
||||
final List<ItemBaseModel> favourites}) = _$LibraryScreenModelImpl;
|
||||
|
||||
@override
|
||||
List<ViewModel> get views;
|
||||
@override
|
||||
ViewModel? get selectedViewModel;
|
||||
@override
|
||||
Set<LibraryViewType> get viewType;
|
||||
@override
|
||||
List<RecommendedModel> get recommendations;
|
||||
@override
|
||||
List<RecommendedModel> get genres;
|
||||
@override
|
||||
List<ItemBaseModel> get favourites;
|
||||
|
||||
/// Create a copy of LibraryScreenModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LibraryScreenModelImplCopyWith<_$LibraryScreenModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
26
lib/providers/library_screen_provider.g.dart
Normal file
26
lib/providers/library_screen_provider.g.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'library_screen_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$libraryScreenHash() => r'ff8b8514461c3e5da1aaf0933d6d49b014c3c05c';
|
||||
|
||||
/// See also [LibraryScreen].
|
||||
@ProviderFor(LibraryScreen)
|
||||
final libraryScreenProvider =
|
||||
NotifierProvider<LibraryScreen, LibraryScreenModel>.internal(
|
||||
LibraryScreen.new,
|
||||
name: r'libraryScreenProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$libraryScreenHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$LibraryScreen = Notifier<LibraryScreenModel>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -218,16 +218,16 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
|
|||
.toSet()
|
||||
.toList();
|
||||
var tempState = state.copyWith();
|
||||
final genres = mappedList
|
||||
.expand((element) => element?.genres ?? <NameGuidPair>[])
|
||||
.nonNulls
|
||||
.sorted((a, b) => a.name!.toLowerCase().compareTo(b.name!.toLowerCase()));
|
||||
final genres = (await Future.wait(state.views.included.map((viewModel) => _loadGenres(viewModel))))
|
||||
.expand((element) => element)
|
||||
.toSet()
|
||||
.toList();
|
||||
final tags = mappedList
|
||||
.expand((element) => element?.tags ?? <String>[])
|
||||
.sorted((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
|
||||
tempState = tempState.copyWith(
|
||||
types: state.types.setAll(false).setKeys(enabledCollections, true),
|
||||
genres: {for (var element in genres) element.name!: false}.replaceMap(tempState.genres),
|
||||
genres: {for (var element in genres) element.name: false}.replaceMap(tempState.genres),
|
||||
studios: {for (var element in studios) element: false}.replaceMap(tempState.studios),
|
||||
tags: {for (var element in tags) element: false}.replaceMap(tempState.tags),
|
||||
);
|
||||
|
|
@ -244,6 +244,11 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
|
|||
return response.body?.items?.map((e) => Studio(id: e.id ?? "", name: e.name ?? "")).toList() ?? [];
|
||||
}
|
||||
|
||||
Future<List<GenreItems>> _loadGenres(ViewModel viewModel) async {
|
||||
final response = await api.genresGet(parentId: viewModel.id);
|
||||
return response.body?.items?.map((e) => GenreItems(id: e.id ?? "", name: e.name ?? "")).toList() ?? [];
|
||||
}
|
||||
|
||||
Future<ServerQueryResult?> _loadLibrary(
|
||||
{ViewModel? viewModel,
|
||||
bool? recursive,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/shared_provider.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
|
||||
final homeSettingsProvider = StateNotifierProvider<HomeSettingsNotifier, HomeSettingsModel>((ref) {
|
||||
return HomeSettingsNotifier(ref);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'background_download_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$backgroundDownloaderHash() =>
|
||||
r'df72b6338a8e80178935985ba17c43bf720f4522';
|
||||
r'dc27f708fc2f1695d37afcb99f8814bc024037af';
|
||||
|
||||
/// See also [BackgroundDownloader].
|
||||
@ProviderFor(BackgroundDownloader)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ final showSyncButtonProviderProvider = AutoDisposeProvider<bool>.internal(
|
|||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ShowSyncButtonProviderRef = AutoDisposeProviderRef<bool>;
|
||||
String _$userHash() => r'1ab1579051806f114e3f42873a2e100c14115900';
|
||||
String _$userHash() => r'56fca6515c42347fa99dcdcf4f2d8a977335243a';
|
||||
|
||||
/// See also [User].
|
||||
@ProviderFor(User)
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ class ViewsNotifier extends StateNotifier<ViewsModel> {
|
|||
|
||||
late final JellyService api = ref.read(jellyApiProvider);
|
||||
|
||||
Future<void> fetchViews() async {
|
||||
if (state.loading) return;
|
||||
Future<ViewsModel?> fetchViews() async {
|
||||
if (state.loading) return null;
|
||||
final showAllCollections = ref.read(clientSettingsProvider.select((value) => value.showAllCollectionTypes));
|
||||
final response = await api.usersUserIdViewsGet(
|
||||
includeExternalContent: showAllCollections,
|
||||
|
|
@ -64,6 +64,7 @@ class ViewsNotifier extends StateNotifier<ViewsModel> {
|
|||
ItemFields.mediasources,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.primaryimageaspectratio,
|
||||
],
|
||||
);
|
||||
return e.copyWith(recentlyAdded: recents.body?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList());
|
||||
|
|
@ -76,6 +77,7 @@ class ViewsNotifier extends StateNotifier<ViewsModel> {
|
|||
.where((element) => !(ref.read(userProvider)?.latestItemsExcludes.contains(element.id) ?? true))
|
||||
.toList(),
|
||||
loading: false);
|
||||
return state;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue