import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:dart_mappable/dart_mappable.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/library_search/library_search_options.dart'; import 'package:fladder/models/view_model.dart'; import 'package:fladder/util/list_extensions.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/map_bool_helper.dart'; part 'library_search_model.mapper.dart'; @MappableClass() class LibrarySearchModel with LibrarySearchModelMappable { final bool loading; final bool selecteMode; final String searchQuery; final List folderOverwrite; final Map views; final List posters; final List selectedPosters; final Map filters; final Map genres; final Map studios; final Map tags; final Map years; final Map officialRatings; final Map types; final SortingOptions sortingOption; final SortingOrder sortOrder; final bool favourites; final bool hideEmptyShows; final bool recursive; final GroupBy groupBy; final Map lastIndices; final Map libraryItemCounts; final bool fetchingItems; const LibrarySearchModel({ this.loading = false, this.selecteMode = false, this.folderOverwrite = const [], this.searchQuery = "", this.views = const {}, this.posters = const [], this.selectedPosters = const [], this.filters = const { ItemFilter.isplayed: false, ItemFilter.isunplayed: false, ItemFilter.isresumable: false, }, this.genres = const {}, this.studios = const {}, this.tags = const {}, this.years = const {}, this.officialRatings = const {}, this.types = const { FladderItemType.audio: false, FladderItemType.boxset: false, FladderItemType.book: false, FladderItemType.collectionFolder: false, FladderItemType.episode: false, FladderItemType.folder: false, FladderItemType.movie: true, FladderItemType.musicAlbum: false, FladderItemType.musicVideo: false, FladderItemType.photo: false, FladderItemType.person: false, FladderItemType.photoAlbum: false, FladderItemType.series: true, FladderItemType.video: true, }, this.favourites = false, this.sortingOption = SortingOptions.sortName, this.sortOrder = SortingOrder.ascending, this.hideEmptyShows = true, this.recursive = false, this.groupBy = GroupBy.none, this.lastIndices = const {}, this.libraryItemCounts = const {}, this.fetchingItems = false, }); bool get hasActiveFilters { return genres.hasEnabled || studios.hasEnabled || tags.hasEnabled || years.hasEnabled || officialRatings.hasEnabled || hideEmptyShows || filters.hasEnabled || favourites || searchQuery.isNotEmpty; } int get totalItemCount { if (libraryItemCounts.isEmpty) return posters.length; int totalCount = 0; for (var item in libraryItemCounts.values) { totalCount += item; } return totalCount; } bool get allDoneFetching { if (libraryItemCounts.isEmpty) return false; if (libraryItemCounts.length != lastIndices.length) { return false; } else { for (var item in libraryItemCounts.entries) { if (lastIndices[item.key] != item.value) { return false; } } } return true; } String searchBarTitle(BuildContext context) { if (folderOverwrite.isNotEmpty) { return "${context.localized.search} ${folderOverwrite.last.name}..."; } return views.included.length == 1 ? "${context.localized.search} ${views.included.first.name}..." : "${context.localized.search} ${context.localized.library(2)}..."; } ItemBaseModel? get nestedCurrentItem => folderOverwrite.lastOrNull; List get activePosters => selectedPosters.isNotEmpty ? selectedPosters : posters; bool get showPlayButtons { if (totalItemCount == 0) return false; return types.included.isEmpty || types.included.containsAny( {...FladderItemType.playable, FladderItemType.folder}, ); } bool get showGalleryButtons { if (totalItemCount == 0) return false; return types.included.isEmpty || types.included.containsAny( {...FladderItemType.galleryItem, FladderItemType.photoAlbum, FladderItemType.folder}, ); } LibrarySearchModel resetLazyLoad() { return copyWith( selectedPosters: [], lastIndices: const {}, libraryItemCounts: const {}, ); } LibrarySearchModel fullReset() { return copyWith( posters: [], selectedPosters: [], lastIndices: const {}, libraryItemCounts: const {}, ); } LibrarySearchModel setFiltersToDefault() { return copyWith( genres: const {}, tags: const {}, officialRatings: const {}, years: const {}, searchQuery: '', favourites: false, recursive: false, studios: const {}, hideEmptyShows: true, ); } @override bool operator ==(covariant LibrarySearchModel other) { if (identical(this, other)) return true; return other.searchQuery == searchQuery && listEquals(other.folderOverwrite, folderOverwrite) && mapEquals(other.views, views) && mapEquals(other.filters, filters) && mapEquals(other.genres, genres) && mapEquals(other.studios, studios) && mapEquals(other.tags, tags) && mapEquals(other.years, years) && mapEquals(other.officialRatings, officialRatings) && mapEquals(other.types, types) && other.sortingOption == sortingOption && other.sortOrder == sortOrder && other.favourites == favourites && other.recursive == recursive; } @override int get hashCode { return searchQuery.hashCode ^ folderOverwrite.hashCode ^ views.hashCode ^ posters.hashCode ^ selectedPosters.hashCode ^ filters.hashCode ^ genres.hashCode ^ studios.hashCode ^ tags.hashCode ^ years.hashCode ^ officialRatings.hashCode ^ types.hashCode ^ sortingOption.hashCode ^ sortOrder.hashCode ^ favourites.hashCode ^ recursive.hashCode; } }