feat(meta): Implement manga tracking

This commit is contained in:
2023-02-04 12:48:57 +01:00
parent cd1291a192
commit 7af2277bb2
15 changed files with 883 additions and 126 deletions

View File

@@ -1,5 +1,6 @@
import 'dart:math';
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/data/manga.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/service/database.dart';
import 'package:bloc/bloc.dart';
@@ -13,11 +14,15 @@ part 'anime_list_bloc.freezed.dart';
class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
AnimeListBloc() : super(AnimeListState()) {
on<AnimeAddedEvent>(_onAnimeAdded);
on<AnimeEpisodeIncrementedEvent>(_onIncremented);
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
on<MangaAddedEvent>(_onMangaAdded);
on<AnimeEpisodeIncrementedEvent>(_onAnimeIncremented);
on<AnimeEpisodeDecrementedEvent>(_onAnimeDecremented);
on<AnimesLoadedEvent>(_onAnimesLoaded);
on<AnimeFilterChangedEvent>(_onAnimesFiltered);
on<AnimeTrackingTypeChanged>(_onTrackingTypeChanged);
on<MangaFilterChangedEvent>(_onMangasFiltered);
on<MangaChapterIncrementedEvent>(_onMangaIncremented);
on<MangaChapterDecrementedEvent>(_onMangaDecremented);
}
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
@@ -34,7 +39,21 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onIncremented(AnimeEpisodeIncrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaAdded(MangaAddedEvent event, Emitter<AnimeListState> emit) async {
// Add the manga to the database
await GetIt.I.get<DatabaseService>().addManga(event.data);
emit(
state.copyWith(
mangas: List.from([
...state.mangas,
event.data,
]),
),
);
}
Future<void> _onAnimeIncremented(AnimeEpisodeIncrementedEvent event, Emitter<AnimeListState> emit) async {
final index = state.animes.indexWhere((item) => item.id == event.id);
if (index == -1) return;
@@ -56,7 +75,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
}
Future<void> _onDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
final index = state.animes.indexWhere((item) => item.id == event.id);
if (index == -1) return;
@@ -82,6 +101,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
emit(
state.copyWith(
animes: await GetIt.I.get<DatabaseService>().loadAnimes(),
mangas: await GetIt.I.get<DatabaseService>().loadMangas(),
),
);
}
@@ -89,11 +109,19 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
Future<void> _onAnimesFiltered(AnimeFilterChangedEvent event, Emitter<AnimeListState> emit) async {
emit(
state.copyWith(
filterState: event.filterState,
animeFilterState: event.filterState,
),
);
}
Future<void> _onMangasFiltered(MangaFilterChangedEvent event, Emitter<AnimeListState> emit) async {
emit(
state.copyWith(
mangaFilterState: event.filterState,
),
);
}
Future<void> _onTrackingTypeChanged(AnimeTrackingTypeChanged event, Emitter<AnimeListState> emit) async {
emit(
state.copyWith(
@@ -101,4 +129,48 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
),
);
}
Future<void> _onMangaIncremented(MangaChapterIncrementedEvent event, Emitter<AnimeListState> emit) async {
final index = state.mangas.indexWhere((item) => item.id == event.id);
if (index == -1) return;
final manga = state.mangas[index];
if (manga.chaptersTotal != null && manga.chaptersRead + 1 > manga.chaptersTotal!) return;
final newList = List<MangaTrackingData>.from(state.mangas);
final newManga = manga.copyWith(
chaptersRead: manga.chaptersRead + 1,
);
newList[index] = newManga;
emit(
state.copyWith(
mangas: newList,
),
);
await GetIt.I.get<DatabaseService>().updateManga(newManga);
}
Future<void> _onMangaDecremented(MangaChapterDecrementedEvent event, Emitter<AnimeListState> emit) async {
final index = state.mangas.indexWhere((item) => item.id == event.id);
if (index == -1) return;
final manga = state.mangas[index];
if (manga.chaptersRead - 1 < 0) return;
final newList = List<MangaTrackingData>.from(state.mangas);
final newManga = manga.copyWith(
chaptersRead: manga.chaptersRead - 1,
);
newList[index] = newManga;
emit(
state.copyWith(
mangas: newList,
),
);
await GetIt.I.get<DatabaseService>().updateManga(newManga);
}
}

View File

@@ -17,7 +17,9 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$AnimeListState {
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
AnimeTrackingState get filterState => throw _privateConstructorUsedError;
List<MangaTrackingData> get mangas => throw _privateConstructorUsedError;
AnimeTrackingState get animeFilterState => throw _privateConstructorUsedError;
MangaTrackingState get mangaFilterState => throw _privateConstructorUsedError;
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -32,7 +34,9 @@ abstract class $AnimeListStateCopyWith<$Res> {
_$AnimeListStateCopyWithImpl<$Res>;
$Res call(
{List<AnimeTrackingData> animes,
AnimeTrackingState filterState,
List<MangaTrackingData> mangas,
AnimeTrackingState animeFilterState,
MangaTrackingState mangaFilterState,
TrackingMediumType trackingType});
}
@@ -48,7 +52,9 @@ class _$AnimeListStateCopyWithImpl<$Res>
@override
$Res call({
Object? animes = freezed,
Object? filterState = freezed,
Object? mangas = freezed,
Object? animeFilterState = freezed,
Object? mangaFilterState = freezed,
Object? trackingType = freezed,
}) {
return _then(_value.copyWith(
@@ -56,10 +62,18 @@ class _$AnimeListStateCopyWithImpl<$Res>
? _value.animes
: animes // ignore: cast_nullable_to_non_nullable
as List<AnimeTrackingData>,
filterState: filterState == freezed
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
mangas: mangas == freezed
? _value.mangas
: mangas // ignore: cast_nullable_to_non_nullable
as List<MangaTrackingData>,
animeFilterState: animeFilterState == freezed
? _value.animeFilterState
: animeFilterState // ignore: cast_nullable_to_non_nullable
as AnimeTrackingState,
mangaFilterState: mangaFilterState == freezed
? _value.mangaFilterState
: mangaFilterState // ignore: cast_nullable_to_non_nullable
as MangaTrackingState,
trackingType: trackingType == freezed
? _value.trackingType
: trackingType // ignore: cast_nullable_to_non_nullable
@@ -77,7 +91,9 @@ abstract class _$$_AnimeListStateCopyWith<$Res>
@override
$Res call(
{List<AnimeTrackingData> animes,
AnimeTrackingState filterState,
List<MangaTrackingData> mangas,
AnimeTrackingState animeFilterState,
MangaTrackingState mangaFilterState,
TrackingMediumType trackingType});
}
@@ -95,7 +111,9 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
@override
$Res call({
Object? animes = freezed,
Object? filterState = freezed,
Object? mangas = freezed,
Object? animeFilterState = freezed,
Object? mangaFilterState = freezed,
Object? trackingType = freezed,
}) {
return _then(_$_AnimeListState(
@@ -103,10 +121,18 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
? _value._animes
: animes // ignore: cast_nullable_to_non_nullable
as List<AnimeTrackingData>,
filterState: filterState == freezed
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
mangas: mangas == freezed
? _value._mangas
: mangas // ignore: cast_nullable_to_non_nullable
as List<MangaTrackingData>,
animeFilterState: animeFilterState == freezed
? _value.animeFilterState
: animeFilterState // ignore: cast_nullable_to_non_nullable
as AnimeTrackingState,
mangaFilterState: mangaFilterState == freezed
? _value.mangaFilterState
: mangaFilterState // ignore: cast_nullable_to_non_nullable
as MangaTrackingState,
trackingType: trackingType == freezed
? _value.trackingType
: trackingType // ignore: cast_nullable_to_non_nullable
@@ -120,9 +146,12 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
class _$_AnimeListState implements _AnimeListState {
_$_AnimeListState(
{final List<AnimeTrackingData> animes = const [],
this.filterState = AnimeTrackingState.watching,
final List<MangaTrackingData> mangas = const [],
this.animeFilterState = AnimeTrackingState.watching,
this.mangaFilterState = MangaTrackingState.reading,
this.trackingType = TrackingMediumType.anime})
: _animes = animes;
: _animes = animes,
_mangas = mangas;
final List<AnimeTrackingData> _animes;
@override
@@ -132,16 +161,27 @@ class _$_AnimeListState implements _AnimeListState {
return EqualUnmodifiableListView(_animes);
}
final List<MangaTrackingData> _mangas;
@override
@JsonKey()
final AnimeTrackingState filterState;
List<MangaTrackingData> get mangas {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_mangas);
}
@override
@JsonKey()
final AnimeTrackingState animeFilterState;
@override
@JsonKey()
final MangaTrackingState mangaFilterState;
@override
@JsonKey()
final TrackingMediumType trackingType;
@override
String toString() {
return 'AnimeListState(animes: $animes, filterState: $filterState, trackingType: $trackingType)';
return 'AnimeListState(animes: $animes, mangas: $mangas, animeFilterState: $animeFilterState, mangaFilterState: $mangaFilterState, trackingType: $trackingType)';
}
@override
@@ -150,8 +190,11 @@ class _$_AnimeListState implements _AnimeListState {
(other.runtimeType == runtimeType &&
other is _$_AnimeListState &&
const DeepCollectionEquality().equals(other._animes, _animes) &&
const DeepCollectionEquality().equals(other._mangas, _mangas) &&
const DeepCollectionEquality()
.equals(other.filterState, filterState) &&
.equals(other.animeFilterState, animeFilterState) &&
const DeepCollectionEquality()
.equals(other.mangaFilterState, mangaFilterState) &&
const DeepCollectionEquality()
.equals(other.trackingType, trackingType));
}
@@ -160,7 +203,9 @@ class _$_AnimeListState implements _AnimeListState {
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_animes),
const DeepCollectionEquality().hash(filterState),
const DeepCollectionEquality().hash(_mangas),
const DeepCollectionEquality().hash(animeFilterState),
const DeepCollectionEquality().hash(mangaFilterState),
const DeepCollectionEquality().hash(trackingType));
@JsonKey(ignore: true)
@@ -172,13 +217,19 @@ class _$_AnimeListState implements _AnimeListState {
abstract class _AnimeListState implements AnimeListState {
factory _AnimeListState(
{final List<AnimeTrackingData> animes,
final AnimeTrackingState filterState,
final List<MangaTrackingData> mangas,
final AnimeTrackingState animeFilterState,
final MangaTrackingState mangaFilterState,
final TrackingMediumType trackingType}) = _$_AnimeListState;
@override
List<AnimeTrackingData> get animes;
@override
AnimeTrackingState get filterState;
List<MangaTrackingData> get mangas;
@override
AnimeTrackingState get animeFilterState;
@override
MangaTrackingState get mangaFilterState;
@override
TrackingMediumType get trackingType;
@override

View File

@@ -26,7 +26,7 @@ class AnimeAddedEvent extends AnimeListEvent {
/// Triggered when animes are to be loaded from the database
class AnimesLoadedEvent extends AnimeListEvent {}
/// Triggered when the filter is changed
/// Triggered when the anime filter is changed
class AnimeFilterChangedEvent extends AnimeListEvent {
AnimeFilterChangedEvent(this.filterState);
@@ -41,3 +41,32 @@ class AnimeTrackingTypeChanged extends AnimeListEvent {
/// The type we switched to
final TrackingMediumType type;
}
class MangaAddedEvent extends AnimeListEvent {
MangaAddedEvent(this.data);
/// The manga to add.
final MangaTrackingData data;
}
/// Triggered when the manga filter is changed
class MangaFilterChangedEvent extends AnimeListEvent {
MangaFilterChangedEvent(this.filterState);
/// The state to filter
final MangaTrackingState filterState;
}
class MangaChapterIncrementedEvent extends AnimeListEvent {
MangaChapterIncrementedEvent(this.id);
/// The ID of the anime
final String id;
}
class MangaChapterDecrementedEvent extends AnimeListEvent {
MangaChapterDecrementedEvent(this.id);
/// The ID of the anime
final String id;
}

View File

@@ -4,7 +4,9 @@ part of 'anime_list_bloc.dart';
class AnimeListState with _$AnimeListState {
factory AnimeListState({
@Default([]) List<AnimeTrackingData> animes,
@Default(AnimeTrackingState.watching) AnimeTrackingState filterState,
@Default([]) List<MangaTrackingData> mangas,
@Default(AnimeTrackingState.watching) AnimeTrackingState animeFilterState,
@Default(MangaTrackingState.reading) MangaTrackingState mangaFilterState,
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
}) = _AnimeListState;
}

View File

@@ -1,5 +1,7 @@
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/data/manga.dart';
import 'package:anitrack/src/data/search_result.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/ui/constants.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart' as list;
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
@@ -17,7 +19,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
on<AnimeSearchRequestedEvent>(_onRequested);
on<SearchQueryChangedEvent>(_onQueryChanged);
on<SearchQuerySubmittedEvent>(_onQuerySubmitted);
on<AnimeAddedEvent>(_onAnimeAdded);
on<ResultTappedEvent>(_onResultTapped);
}
Future<void> _onRequested(AnimeSearchRequestedEvent event, Emitter<AnimeSearchState> emit) async {
@@ -26,6 +28,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
searchQuery: '',
working: false,
searchResults: [],
trackingType: event.type,
),
);
@@ -52,42 +55,70 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
working: true,
),
);
final result = await Jikan().searchAnime(
query: state.searchQuery,
);
emit(
state.copyWith(
working: false,
),
);
emit(
state.copyWith(
searchResults: result.map((Anime anime) => AnimeSearchResult(
anime.title,
anime.malId.toString(),
anime.episodes,
anime.imageUrl,
anime.synopsis ?? '',
),).toList(),
),
);
if (state.trackingType == TrackingMediumType.anime) {
// Anime
final result = await Jikan().searchAnime(
query: state.searchQuery,
);
emit(
state.copyWith(
working: false,
searchResults: result.map((Anime anime) => SearchResult(
anime.title,
anime.malId.toString(),
anime.episodes,
anime.imageUrl,
anime.synopsis ?? '',
),).toList(),
),
);
} else {
// Manga
final result = await Jikan().searchManga(
query: state.searchQuery,
);
emit(
state.copyWith(
working: false,
searchResults: result.map((Manga manga) => SearchResult(
manga.title,
manga.malId.toString(),
manga.chapters,
manga.imageUrl,
manga.synopsis ?? '',
),).toList(),
),
);
}
}
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeSearchState> emit) async {
Future<void> _onResultTapped(ResultTappedEvent event, Emitter<AnimeSearchState> emit) async {
GetIt.I.get<list.AnimeListBloc>().add(
list.AnimeAddedEvent(
AnimeTrackingData(
event.result.id,
AnimeTrackingState.watching,
event.result.title,
0,
event.result.episodesTotal,
event.result.thumbnailUrl,
),
),
state.trackingType == TrackingMediumType.anime ?
list.AnimeAddedEvent(
AnimeTrackingData(
event.result.id,
AnimeTrackingState.watching,
event.result.title,
0,
event.result.total,
event.result.thumbnailUrl,
),
) :
list.MangaAddedEvent(
MangaTrackingData(
event.result.id,
MangaTrackingState.reading,
event.result.title,
0,
0,
event.result.total,
event.result.thumbnailUrl,
),
)
);
GetIt.I.get<NavigationBloc>().add(

View File

@@ -16,10 +16,10 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$AnimeSearchState {
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
String get searchQuery => throw _privateConstructorUsedError;
bool get working => throw _privateConstructorUsedError;
List<AnimeSearchResult> get searchResults =>
throw _privateConstructorUsedError;
List<SearchResult> get searchResults => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$AnimeSearchStateCopyWith<AnimeSearchState> get copyWith =>
@@ -32,9 +32,10 @@ abstract class $AnimeSearchStateCopyWith<$Res> {
AnimeSearchState value, $Res Function(AnimeSearchState) then) =
_$AnimeSearchStateCopyWithImpl<$Res>;
$Res call(
{String searchQuery,
{TrackingMediumType trackingType,
String searchQuery,
bool working,
List<AnimeSearchResult> searchResults});
List<SearchResult> searchResults});
}
/// @nodoc
@@ -48,11 +49,16 @@ class _$AnimeSearchStateCopyWithImpl<$Res>
@override
$Res call({
Object? trackingType = freezed,
Object? searchQuery = freezed,
Object? working = freezed,
Object? searchResults = freezed,
}) {
return _then(_value.copyWith(
trackingType: trackingType == freezed
? _value.trackingType
: trackingType // ignore: cast_nullable_to_non_nullable
as TrackingMediumType,
searchQuery: searchQuery == freezed
? _value.searchQuery
: searchQuery // ignore: cast_nullable_to_non_nullable
@@ -64,7 +70,7 @@ class _$AnimeSearchStateCopyWithImpl<$Res>
searchResults: searchResults == freezed
? _value.searchResults
: searchResults // ignore: cast_nullable_to_non_nullable
as List<AnimeSearchResult>,
as List<SearchResult>,
));
}
}
@@ -77,9 +83,10 @@ abstract class _$$_AnimeSearchStateCopyWith<$Res>
__$$_AnimeSearchStateCopyWithImpl<$Res>;
@override
$Res call(
{String searchQuery,
{TrackingMediumType trackingType,
String searchQuery,
bool working,
List<AnimeSearchResult> searchResults});
List<SearchResult> searchResults});
}
/// @nodoc
@@ -95,11 +102,16 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res>
@override
$Res call({
Object? trackingType = freezed,
Object? searchQuery = freezed,
Object? working = freezed,
Object? searchResults = freezed,
}) {
return _then(_$_AnimeSearchState(
trackingType: trackingType == freezed
? _value.trackingType
: trackingType // ignore: cast_nullable_to_non_nullable
as TrackingMediumType,
searchQuery: searchQuery == freezed
? _value.searchQuery
: searchQuery // ignore: cast_nullable_to_non_nullable
@@ -111,7 +123,7 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res>
searchResults: searchResults == freezed
? _value._searchResults
: searchResults // ignore: cast_nullable_to_non_nullable
as List<AnimeSearchResult>,
as List<SearchResult>,
));
}
}
@@ -120,28 +132,32 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res>
class _$_AnimeSearchState implements _AnimeSearchState {
_$_AnimeSearchState(
{this.searchQuery = '',
{this.trackingType = TrackingMediumType.anime,
this.searchQuery = '',
this.working = false,
final List<AnimeSearchResult> searchResults = const []})
final List<SearchResult> searchResults = const []})
: _searchResults = searchResults;
@override
@JsonKey()
final TrackingMediumType trackingType;
@override
@JsonKey()
final String searchQuery;
@override
@JsonKey()
final bool working;
final List<AnimeSearchResult> _searchResults;
final List<SearchResult> _searchResults;
@override
@JsonKey()
List<AnimeSearchResult> get searchResults {
List<SearchResult> get searchResults {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_searchResults);
}
@override
String toString() {
return 'AnimeSearchState(searchQuery: $searchQuery, working: $working, searchResults: $searchResults)';
return 'AnimeSearchState(trackingType: $trackingType, searchQuery: $searchQuery, working: $working, searchResults: $searchResults)';
}
@override
@@ -149,6 +165,8 @@ class _$_AnimeSearchState implements _AnimeSearchState {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_AnimeSearchState &&
const DeepCollectionEquality()
.equals(other.trackingType, trackingType) &&
const DeepCollectionEquality()
.equals(other.searchQuery, searchQuery) &&
const DeepCollectionEquality().equals(other.working, working) &&
@@ -159,6 +177,7 @@ class _$_AnimeSearchState implements _AnimeSearchState {
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(trackingType),
const DeepCollectionEquality().hash(searchQuery),
const DeepCollectionEquality().hash(working),
const DeepCollectionEquality().hash(_searchResults));
@@ -171,16 +190,19 @@ class _$_AnimeSearchState implements _AnimeSearchState {
abstract class _AnimeSearchState implements AnimeSearchState {
factory _AnimeSearchState(
{final String searchQuery,
{final TrackingMediumType trackingType,
final String searchQuery,
final bool working,
final List<AnimeSearchResult> searchResults}) = _$_AnimeSearchState;
final List<SearchResult> searchResults}) = _$_AnimeSearchState;
@override
TrackingMediumType get trackingType;
@override
String get searchQuery;
@override
bool get working;
@override
List<AnimeSearchResult> get searchResults;
List<SearchResult> get searchResults;
@override
@JsonKey(ignore: true)
_$$_AnimeSearchStateCopyWith<_$_AnimeSearchState> get copyWith =>

View File

@@ -2,7 +2,12 @@ part of 'anime_search_bloc.dart';
abstract class AnimeSearchEvent {}
class AnimeSearchRequestedEvent extends AnimeSearchEvent {}
class AnimeSearchRequestedEvent extends AnimeSearchEvent {
AnimeSearchRequestedEvent(this.type);
/// The tracking type for which we want to search
TrackingMediumType type;
}
/// Triggered when the search query is changed.
class SearchQueryChangedEvent extends AnimeSearchEvent {
@@ -16,9 +21,9 @@ class SearchQueryChangedEvent extends AnimeSearchEvent {
class SearchQuerySubmittedEvent extends AnimeSearchEvent {}
/// Triggered when an anime is added to the tracking list
class AnimeAddedEvent extends AnimeSearchEvent {
AnimeAddedEvent(this.result);
class ResultTappedEvent extends AnimeSearchEvent {
ResultTappedEvent(this.result);
/// The search result to add.
final AnimeSearchResult result;
final SearchResult result;
}

View File

@@ -3,8 +3,9 @@ part of 'anime_search_bloc.dart';
@freezed
class AnimeSearchState with _$AnimeSearchState {
factory AnimeSearchState({
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
@Default('') String searchQuery,
@Default(false) bool working,
@Default([]) List<AnimeSearchResult> searchResults,
@Default([]) List<SearchResult> searchResults,
}) = _AnimeSearchState;
}