feature: Added option to save and set default filters for libraries (#107)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-11-02 15:44:24 +01:00 committed by GitHub
parent d3e34d57e0
commit 691293648b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1353 additions and 62 deletions

View file

@ -0,0 +1,21 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:fladder/models/library_filters_model.dart';
import 'package:fladder/providers/user_provider.dart';
part 'library_filters_provider.g.dart';
@riverpod
class LibraryFilters extends _$LibraryFilters {
@override
List<LibraryFiltersModel> build(List<String> ids) => ref.watch(
userProvider
.select((value) => (value?.savedFilters ?? []).where((element) => element.containsSameIds(ids)).toList()),
);
void removeFilter(LibraryFiltersModel model) => ref.read(userProvider.notifier).removeFilter(model);
void saveFilter(LibraryFiltersModel model) => ref.read(userProvider.notifier).saveFilter(model);
void deleteAllFilters() => ref.read(userProvider.notifier).deleteAllFilters();
}

View file

@ -0,0 +1,174 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'library_filters_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$libraryFiltersHash() => r'7b4661651df7e0c019dca5bb7eb6433bcd8b3ebb';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
abstract class _$LibraryFilters
extends BuildlessAutoDisposeNotifier<List<LibraryFiltersModel>> {
late final List<String> ids;
List<LibraryFiltersModel> build(
List<String> ids,
);
}
/// See also [LibraryFilters].
@ProviderFor(LibraryFilters)
const libraryFiltersProvider = LibraryFiltersFamily();
/// See also [LibraryFilters].
class LibraryFiltersFamily extends Family<List<LibraryFiltersModel>> {
/// See also [LibraryFilters].
const LibraryFiltersFamily();
/// See also [LibraryFilters].
LibraryFiltersProvider call(
List<String> ids,
) {
return LibraryFiltersProvider(
ids,
);
}
@override
LibraryFiltersProvider getProviderOverride(
covariant LibraryFiltersProvider provider,
) {
return call(
provider.ids,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'libraryFiltersProvider';
}
/// See also [LibraryFilters].
class LibraryFiltersProvider extends AutoDisposeNotifierProviderImpl<
LibraryFilters, List<LibraryFiltersModel>> {
/// See also [LibraryFilters].
LibraryFiltersProvider(
List<String> ids,
) : this._internal(
() => LibraryFilters()..ids = ids,
from: libraryFiltersProvider,
name: r'libraryFiltersProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryFiltersHash,
dependencies: LibraryFiltersFamily._dependencies,
allTransitiveDependencies:
LibraryFiltersFamily._allTransitiveDependencies,
ids: ids,
);
LibraryFiltersProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.ids,
}) : super.internal();
final List<String> ids;
@override
List<LibraryFiltersModel> runNotifierBuild(
covariant LibraryFilters notifier,
) {
return notifier.build(
ids,
);
}
@override
Override overrideWith(LibraryFilters Function() create) {
return ProviderOverride(
origin: this,
override: LibraryFiltersProvider._internal(
() => create()..ids = ids,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
ids: ids,
),
);
}
@override
AutoDisposeNotifierProviderElement<LibraryFilters, List<LibraryFiltersModel>>
createElement() {
return _LibraryFiltersProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is LibraryFiltersProvider && other.ids == ids;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, ids.hashCode);
return _SystemHash.finish(hash);
}
}
mixin LibraryFiltersRef
on AutoDisposeNotifierProviderRef<List<LibraryFiltersModel>> {
/// The parameter `ids` of this provider.
List<String> get ids;
}
class _LibraryFiltersProviderElement extends AutoDisposeNotifierProviderElement<
LibraryFilters, List<LibraryFiltersModel>> with LibraryFiltersRef {
_LibraryFiltersProviderElement(super.provider);
@override
List<String> get ids => (origin as LibraryFiltersProvider).ids;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View file

@ -13,11 +13,13 @@ import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/folder_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/items/photos_model.dart';
import 'package:fladder/models/library_filters_model.dart';
import 'package:fladder/models/library_search/library_search_model.dart';
import 'package:fladder/models/library_search/library_search_options.dart';
import 'package:fladder/models/playlist_model.dart';
import 'package:fladder/models/view_model.dart';
import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/library_filters_provider.dart';
import 'package:fladder/providers/service_provider.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/user_provider.dart';
@ -40,6 +42,8 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
int get pageSize => ref.read(clientSettingsProvider).libraryPageSize ?? 500;
LibraryFiltersProvider get filterProvider => libraryFiltersProvider(state.views.included.map((e) => e.id).toList());
late final JellyService api = ref.read(jellyApiProvider);
set loading(bool loading) => state = state.copyWith(loading: loading);
@ -52,6 +56,8 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
List<String>? folderId,
String? viewModelId,
bool? favourites,
SortingOrder? sortOrder,
SortingOptions? sortingOptions,
) async {
loading = true;
state = state.resetLazyLoad();
@ -59,7 +65,7 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
if (folderId != null) {
await loadFolders(folderId: folderId);
} else {
await loadViews(viewModelId, favourites);
await loadViews(viewModelId, favourites, sortOrder, sortingOptions);
}
}
@ -151,7 +157,12 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
}
//Pas viewmodel otherwise select first
Future<void> loadViews(String? viewModelId, bool? favourites) async {
Future<void> loadViews(
String? viewModelId,
bool? favourites,
SortingOrder? sortOrder,
SortingOptions? sortingOptions,
) async {
final response = await api.usersUserIdViewsGet(includeHidden: false);
final createdViews = response.body?.items?.map((e) => ViewModel.fromBodyDto(e, ref));
Map<ViewModel, bool> mappedModels =
@ -159,12 +170,28 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
final selectedModel = mappedModels.keys.firstWhereOrNull((element) => element.id == viewModelId);
final views = selectedModel != null
? mappedModels.setKey(mappedModels.keys.firstWhere((element) => element.id == viewModelId), true)
: mappedModels;
state = state.copyWith(
views: selectedModel != null
? mappedModels.setKey(mappedModels.keys.firstWhere((element) => element.id == viewModelId), true)
: mappedModels,
favourites: favourites,
views: views,
);
if (sortOrder == null && sortingOptions == null && favourites == null) {
final findFavouriteFilter = ref
.read(libraryFiltersProvider(views.included.map((e) => e.id).toList()))
.firstWhereOrNull((element) => element.isFavourite);
if (findFavouriteFilter != null) {
loadModel(findFavouriteFilter);
}
} else {
state = state.copyWith(
sortOrder: sortOrder,
sortingOption: sortingOptions,
favourites: favourites,
);
}
}
Future<void> loadFolders({List<String>? folderId}) async {
@ -348,7 +375,7 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
recursive: false,
studios: state.studios.setAll(false),
filters: state.filters.setAll(false),
hideEmtpyShows: false,
hideEmptyShows: false,
);
}
@ -356,7 +383,7 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
void setSortOrder(SortingOrder e) => state = state.copyWith(sortOrder: e);
void setHideEmpty(bool value) => state = state.copyWith(hideEmtpyShows: value);
void setHideEmpty(bool value) => state = state.copyWith(hideEmptyShows: value);
void setGroupBy(GroupBy groupBy) => state = state.copyWith(groupBy: groupBy);
void setFolderId(ItemBaseModel item) {
@ -460,13 +487,6 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
state = state.copyWith(posters: currentItems);
}
void setDefaultOptions(SortingOrder? sortOrder, SortingOptions? sortingOptions) {
state = state.copyWith(
sortOrder: sortOrder,
sortingOption: sortingOptions,
);
}
void updateUserDataMain(UserData? userData) {
state = state.copyWith(
folderOverwrite: [state.folderOverwrite.lastOrNull?.copyWith(userData: userData)].whereNotNull().toList(),
@ -657,6 +677,34 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
void updateEverything() {
state = state.copyWith();
}
void loadModel(LibraryFiltersModel model) {
state = state.copyWith(
genres: state.genres.replaceMap(model.genres),
filters: state.filters.replaceMap(model.filters),
studios: state.studios.replaceMap(model.studios),
tags: state.tags.replaceMap(model.tags),
years: state.years.replaceMap(model.years),
officialRatings: state.officialRatings.replaceMap(model.officialRatings),
types: state.types.replaceMap(model.types),
sortingOption: model.sortingOption,
sortOrder: model.sortOrder,
favourites: model.favourites,
hideEmptyShows: model.hideEmptyShows,
recursive: model.recursive,
groupBy: model.groupBy,
);
}
void saveFiltersNew(String newName) =>
ref.read(filterProvider.notifier).saveFilter(LibraryFiltersModel.fromLibrarySearch(newName, state));
void updateFilter(LibraryFiltersModel model) {
ref.read(filterProvider.notifier).saveFilter(LibraryFiltersModel.fromLibrarySearch(model.name, state).copyWith(
isFavourite: model.isFavourite,
id: model.id,
));
}
}
extension SimpleSorter on List<ItemBaseModel> {

View file

@ -1,12 +1,15 @@
import 'package:chopper/chopper.dart';
import 'package:collection/collection.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:fladder/jellyfin/enum_models.dart';
import 'package:fladder/models/account_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/library_filters_model.dart';
import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/service_provider.dart';
import 'package:fladder/providers/shared_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_provider.g.dart';
@ -142,4 +145,30 @@ class User extends _$User {
AccountModel? build() {
return null;
}
void removeFilter(LibraryFiltersModel model) {
final currentList = ((state?.savedFilters ?? [])).toList(growable: true);
currentList.remove(model);
state = state?.copyWith(savedFilters: currentList);
}
void saveFilter(LibraryFiltersModel model) {
final currentList = (state?.savedFilters ?? []).toList(growable: true);
if (currentList.firstWhereOrNull((value) => value.id == model.id) != null) {
state = state?.copyWith(
savedFilters: currentList.map(
(e) {
if (e.id == model.id) {
return model;
} else {
return e.copyWith(isFavourite: model.isFavourite && model.containsSameIds(e.ids) ? false : e.isFavourite);
}
},
).toList());
} else {
state = state?.copyWith(savedFilters: [model, ...currentList]);
}
}
void deleteAllFilters() => state = state?.copyWith(savedFilters: []);
}

View file

@ -22,7 +22,7 @@ final showSyncButtonProviderProvider = AutoDisposeProvider<bool>.internal(
);
typedef ShowSyncButtonProviderRef = AutoDisposeProviderRef<bool>;
String _$userHash() => r'4a4302c819d26fc7c28d04b9274d0dfd0dc8e201';
String _$userHash() => r'418b3d4ade830479db9f48c7793ac5b646778b82';
/// See also [User].
@ProviderFor(User)