feat(meta): Implement manga tracking
This commit is contained in:
		
							parent
							
								
									cd1291a192
								
							
						
					
					
						commit
						7af2277bb2
					
				
							
								
								
									
										70
									
								
								lib/src/data/manga.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/src/data/manga.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | import 'package:freezed_annotation/freezed_annotation.dart'; | ||||||
|  | 
 | ||||||
|  | part 'manga.freezed.dart'; | ||||||
|  | part 'manga.g.dart'; | ||||||
|  | 
 | ||||||
|  | /// The watch state of an manga | ||||||
|  | enum MangaTrackingState { | ||||||
|  |   reading,    // 0 | ||||||
|  |   completed,   // 1 | ||||||
|  |   planToWatch, // 2 | ||||||
|  |   dropped,     // 3 | ||||||
|  |   /// This is a pseudo state, i.e. it should never be set | ||||||
|  |   all,         // -1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extension MangaTrackStateExtension on MangaTrackingState { | ||||||
|  |   int toInteger() { | ||||||
|  |     assert(this != MangaTrackingState.all, 'MangaTrackingState.all must not be serialized'); | ||||||
|  |     switch (this) { | ||||||
|  |       case MangaTrackingState.reading: return 0; | ||||||
|  |       case MangaTrackingState.completed: return 1; | ||||||
|  |       case MangaTrackingState.planToWatch: return 2; | ||||||
|  |       case MangaTrackingState.dropped: return 3; | ||||||
|  |       case MangaTrackingState.all: return -1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MangaTrackingStateConverter implements JsonConverter<MangaTrackingState, int> { | ||||||
|  |   const MangaTrackingStateConverter(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   MangaTrackingState fromJson(int json) { | ||||||
|  |     switch (json) { | ||||||
|  |       case 0: return MangaTrackingState.reading; | ||||||
|  |       case 1: return MangaTrackingState.completed; | ||||||
|  |       case 2: return MangaTrackingState.planToWatch; | ||||||
|  |       case 3: return MangaTrackingState.dropped; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return MangaTrackingState.planToWatch; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   @override | ||||||
|  |   int toJson(MangaTrackingState state) => state.toInteger(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Data about a tracked anime | ||||||
|  | @freezed | ||||||
|  | class MangaTrackingData with _$MangaTrackingData{ | ||||||
|  |   factory MangaTrackingData( | ||||||
|  |     /// The ID of the manga | ||||||
|  |     String id, | ||||||
|  |     /// The state of the manga | ||||||
|  |     @MangaTrackingStateConverter() MangaTrackingState state, | ||||||
|  |     /// The title of the manga | ||||||
|  |     String title, | ||||||
|  |     /// Chapters read. | ||||||
|  |     int chaptersRead, | ||||||
|  |     /// Chapters read. | ||||||
|  |     int volumesOwned, | ||||||
|  |     /// Episodes watched. | ||||||
|  |     int? chaptersTotal, | ||||||
|  |     /// URL to the thumbnail/cover art for the manga. | ||||||
|  |     String thumbnailUrl, | ||||||
|  |   ) = _MangaTrackingData; | ||||||
|  | 
 | ||||||
|  |   /// JSON | ||||||
|  |   factory MangaTrackingData.fromJson(Map<String, dynamic> json) => _$MangaTrackingDataFromJson(json); | ||||||
|  | } | ||||||
							
								
								
									
										328
									
								
								lib/src/data/manga.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								lib/src/data/manga.freezed.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,328 @@ | |||||||
|  | // 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 | ||||||
|  | 
 | ||||||
|  | part of 'manga.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#custom-getters-and-methods'); | ||||||
|  | 
 | ||||||
|  | MangaTrackingData _$MangaTrackingDataFromJson(Map<String, dynamic> json) { | ||||||
|  |   return _MangaTrackingData.fromJson(json); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | mixin _$MangaTrackingData { | ||||||
|  |   /// The ID of the manga | ||||||
|  |   String get id => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// The state of the manga | ||||||
|  |   @MangaTrackingStateConverter() | ||||||
|  |   MangaTrackingState get state => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// The title of the manga | ||||||
|  |   String get title => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   int get chaptersRead => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   int get volumesOwned => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// Episodes watched. | ||||||
|  |   int? get chaptersTotal => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   /// URL to the thumbnail/cover art for the manga. | ||||||
|  |   String get thumbnailUrl => throw _privateConstructorUsedError; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() => throw _privateConstructorUsedError; | ||||||
|  |   @JsonKey(ignore: true) | ||||||
|  |   $MangaTrackingDataCopyWith<MangaTrackingData> get copyWith => | ||||||
|  |       throw _privateConstructorUsedError; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | abstract class $MangaTrackingDataCopyWith<$Res> { | ||||||
|  |   factory $MangaTrackingDataCopyWith( | ||||||
|  |           MangaTrackingData value, $Res Function(MangaTrackingData) then) = | ||||||
|  |       _$MangaTrackingDataCopyWithImpl<$Res>; | ||||||
|  |   $Res call( | ||||||
|  |       {String id, | ||||||
|  |       @MangaTrackingStateConverter() MangaTrackingState state, | ||||||
|  |       String title, | ||||||
|  |       int chaptersRead, | ||||||
|  |       int volumesOwned, | ||||||
|  |       int? chaptersTotal, | ||||||
|  |       String thumbnailUrl}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | class _$MangaTrackingDataCopyWithImpl<$Res> | ||||||
|  |     implements $MangaTrackingDataCopyWith<$Res> { | ||||||
|  |   _$MangaTrackingDataCopyWithImpl(this._value, this._then); | ||||||
|  | 
 | ||||||
|  |   final MangaTrackingData _value; | ||||||
|  |   // ignore: unused_field | ||||||
|  |   final $Res Function(MangaTrackingData) _then; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   $Res call({ | ||||||
|  |     Object? id = freezed, | ||||||
|  |     Object? state = freezed, | ||||||
|  |     Object? title = freezed, | ||||||
|  |     Object? chaptersRead = freezed, | ||||||
|  |     Object? volumesOwned = freezed, | ||||||
|  |     Object? chaptersTotal = freezed, | ||||||
|  |     Object? thumbnailUrl = freezed, | ||||||
|  |   }) { | ||||||
|  |     return _then(_value.copyWith( | ||||||
|  |       id: id == freezed | ||||||
|  |           ? _value.id | ||||||
|  |           : id // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |       state: state == freezed | ||||||
|  |           ? _value.state | ||||||
|  |           : state // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as MangaTrackingState, | ||||||
|  |       title: title == freezed | ||||||
|  |           ? _value.title | ||||||
|  |           : title // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |       chaptersRead: chaptersRead == freezed | ||||||
|  |           ? _value.chaptersRead | ||||||
|  |           : chaptersRead // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int, | ||||||
|  |       volumesOwned: volumesOwned == freezed | ||||||
|  |           ? _value.volumesOwned | ||||||
|  |           : volumesOwned // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int, | ||||||
|  |       chaptersTotal: chaptersTotal == freezed | ||||||
|  |           ? _value.chaptersTotal | ||||||
|  |           : chaptersTotal // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int?, | ||||||
|  |       thumbnailUrl: thumbnailUrl == freezed | ||||||
|  |           ? _value.thumbnailUrl | ||||||
|  |           : thumbnailUrl // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |     )); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | abstract class _$$_MangaTrackingDataCopyWith<$Res> | ||||||
|  |     implements $MangaTrackingDataCopyWith<$Res> { | ||||||
|  |   factory _$$_MangaTrackingDataCopyWith(_$_MangaTrackingData value, | ||||||
|  |           $Res Function(_$_MangaTrackingData) then) = | ||||||
|  |       __$$_MangaTrackingDataCopyWithImpl<$Res>; | ||||||
|  |   @override | ||||||
|  |   $Res call( | ||||||
|  |       {String id, | ||||||
|  |       @MangaTrackingStateConverter() MangaTrackingState state, | ||||||
|  |       String title, | ||||||
|  |       int chaptersRead, | ||||||
|  |       int volumesOwned, | ||||||
|  |       int? chaptersTotal, | ||||||
|  |       String thumbnailUrl}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | class __$$_MangaTrackingDataCopyWithImpl<$Res> | ||||||
|  |     extends _$MangaTrackingDataCopyWithImpl<$Res> | ||||||
|  |     implements _$$_MangaTrackingDataCopyWith<$Res> { | ||||||
|  |   __$$_MangaTrackingDataCopyWithImpl( | ||||||
|  |       _$_MangaTrackingData _value, $Res Function(_$_MangaTrackingData) _then) | ||||||
|  |       : super(_value, (v) => _then(v as _$_MangaTrackingData)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _$_MangaTrackingData get _value => super._value as _$_MangaTrackingData; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   $Res call({ | ||||||
|  |     Object? id = freezed, | ||||||
|  |     Object? state = freezed, | ||||||
|  |     Object? title = freezed, | ||||||
|  |     Object? chaptersRead = freezed, | ||||||
|  |     Object? volumesOwned = freezed, | ||||||
|  |     Object? chaptersTotal = freezed, | ||||||
|  |     Object? thumbnailUrl = freezed, | ||||||
|  |   }) { | ||||||
|  |     return _then(_$_MangaTrackingData( | ||||||
|  |       id == freezed | ||||||
|  |           ? _value.id | ||||||
|  |           : id // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |       state == freezed | ||||||
|  |           ? _value.state | ||||||
|  |           : state // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as MangaTrackingState, | ||||||
|  |       title == freezed | ||||||
|  |           ? _value.title | ||||||
|  |           : title // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |       chaptersRead == freezed | ||||||
|  |           ? _value.chaptersRead | ||||||
|  |           : chaptersRead // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int, | ||||||
|  |       volumesOwned == freezed | ||||||
|  |           ? _value.volumesOwned | ||||||
|  |           : volumesOwned // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int, | ||||||
|  |       chaptersTotal == freezed | ||||||
|  |           ? _value.chaptersTotal | ||||||
|  |           : chaptersTotal // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as int?, | ||||||
|  |       thumbnailUrl == freezed | ||||||
|  |           ? _value.thumbnailUrl | ||||||
|  |           : thumbnailUrl // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as String, | ||||||
|  |     )); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// @nodoc | ||||||
|  | @JsonSerializable() | ||||||
|  | class _$_MangaTrackingData implements _MangaTrackingData { | ||||||
|  |   _$_MangaTrackingData( | ||||||
|  |       this.id, | ||||||
|  |       @MangaTrackingStateConverter() this.state, | ||||||
|  |       this.title, | ||||||
|  |       this.chaptersRead, | ||||||
|  |       this.volumesOwned, | ||||||
|  |       this.chaptersTotal, | ||||||
|  |       this.thumbnailUrl); | ||||||
|  | 
 | ||||||
|  |   factory _$_MangaTrackingData.fromJson(Map<String, dynamic> json) => | ||||||
|  |       _$$_MangaTrackingDataFromJson(json); | ||||||
|  | 
 | ||||||
|  |   /// The ID of the manga | ||||||
|  |   @override | ||||||
|  |   final String id; | ||||||
|  | 
 | ||||||
|  |   /// The state of the manga | ||||||
|  |   @override | ||||||
|  |   @MangaTrackingStateConverter() | ||||||
|  |   final MangaTrackingState state; | ||||||
|  | 
 | ||||||
|  |   /// The title of the manga | ||||||
|  |   @override | ||||||
|  |   final String title; | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   @override | ||||||
|  |   final int chaptersRead; | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   @override | ||||||
|  |   final int volumesOwned; | ||||||
|  | 
 | ||||||
|  |   /// Episodes watched. | ||||||
|  |   @override | ||||||
|  |   final int? chaptersTotal; | ||||||
|  | 
 | ||||||
|  |   /// URL to the thumbnail/cover art for the manga. | ||||||
|  |   @override | ||||||
|  |   final String thumbnailUrl; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return 'MangaTrackingData(id: $id, state: $state, title: $title, chaptersRead: $chaptersRead, volumesOwned: $volumesOwned, chaptersTotal: $chaptersTotal, thumbnailUrl: $thumbnailUrl)'; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(dynamic other) { | ||||||
|  |     return identical(this, other) || | ||||||
|  |         (other.runtimeType == runtimeType && | ||||||
|  |             other is _$_MangaTrackingData && | ||||||
|  |             const DeepCollectionEquality().equals(other.id, id) && | ||||||
|  |             const DeepCollectionEquality().equals(other.state, state) && | ||||||
|  |             const DeepCollectionEquality().equals(other.title, title) && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.chaptersRead, chaptersRead) && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.volumesOwned, volumesOwned) && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.chaptersTotal, chaptersTotal) && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.thumbnailUrl, thumbnailUrl)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @JsonKey(ignore: true) | ||||||
|  |   @override | ||||||
|  |   int get hashCode => Object.hash( | ||||||
|  |       runtimeType, | ||||||
|  |       const DeepCollectionEquality().hash(id), | ||||||
|  |       const DeepCollectionEquality().hash(state), | ||||||
|  |       const DeepCollectionEquality().hash(title), | ||||||
|  |       const DeepCollectionEquality().hash(chaptersRead), | ||||||
|  |       const DeepCollectionEquality().hash(volumesOwned), | ||||||
|  |       const DeepCollectionEquality().hash(chaptersTotal), | ||||||
|  |       const DeepCollectionEquality().hash(thumbnailUrl)); | ||||||
|  | 
 | ||||||
|  |   @JsonKey(ignore: true) | ||||||
|  |   @override | ||||||
|  |   _$$_MangaTrackingDataCopyWith<_$_MangaTrackingData> get copyWith => | ||||||
|  |       __$$_MangaTrackingDataCopyWithImpl<_$_MangaTrackingData>( | ||||||
|  |           this, _$identity); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     return _$$_MangaTrackingDataToJson( | ||||||
|  |       this, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | abstract class _MangaTrackingData implements MangaTrackingData { | ||||||
|  |   factory _MangaTrackingData( | ||||||
|  |       final String id, | ||||||
|  |       @MangaTrackingStateConverter() final MangaTrackingState state, | ||||||
|  |       final String title, | ||||||
|  |       final int chaptersRead, | ||||||
|  |       final int volumesOwned, | ||||||
|  |       final int? chaptersTotal, | ||||||
|  |       final String thumbnailUrl) = _$_MangaTrackingData; | ||||||
|  | 
 | ||||||
|  |   factory _MangaTrackingData.fromJson(Map<String, dynamic> json) = | ||||||
|  |       _$_MangaTrackingData.fromJson; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// The ID of the manga | ||||||
|  |   String get id; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// The state of the manga | ||||||
|  |   @MangaTrackingStateConverter() | ||||||
|  |   MangaTrackingState get state; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// The title of the manga | ||||||
|  |   String get title; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   int get chaptersRead; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// Chapters read. | ||||||
|  |   int get volumesOwned; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// Episodes watched. | ||||||
|  |   int? get chaptersTotal; | ||||||
|  |   @override | ||||||
|  | 
 | ||||||
|  |   /// URL to the thumbnail/cover art for the manga. | ||||||
|  |   String get thumbnailUrl; | ||||||
|  |   @override | ||||||
|  |   @JsonKey(ignore: true) | ||||||
|  |   _$$_MangaTrackingDataCopyWith<_$_MangaTrackingData> get copyWith => | ||||||
|  |       throw _privateConstructorUsedError; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								lib/src/data/manga.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/src/data/manga.g.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | // GENERATED CODE - DO NOT MODIFY BY HAND | ||||||
|  | 
 | ||||||
|  | part of 'manga.dart'; | ||||||
|  | 
 | ||||||
|  | // ************************************************************************** | ||||||
|  | // JsonSerializableGenerator | ||||||
|  | // ************************************************************************** | ||||||
|  | 
 | ||||||
|  | _$_MangaTrackingData _$$_MangaTrackingDataFromJson(Map<String, dynamic> json) => | ||||||
|  |     _$_MangaTrackingData( | ||||||
|  |       json['id'] as String, | ||||||
|  |       const MangaTrackingStateConverter().fromJson(json['state'] as int), | ||||||
|  |       json['title'] as String, | ||||||
|  |       json['chaptersRead'] as int, | ||||||
|  |       json['volumesOwned'] as int, | ||||||
|  |       json['chaptersTotal'] as int?, | ||||||
|  |       json['thumbnailUrl'] as String, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | Map<String, dynamic> _$$_MangaTrackingDataToJson( | ||||||
|  |         _$_MangaTrackingData instance) => | ||||||
|  |     <String, dynamic>{ | ||||||
|  |       'id': instance.id, | ||||||
|  |       'state': const MangaTrackingStateConverter().toJson(instance.state), | ||||||
|  |       'title': instance.title, | ||||||
|  |       'chaptersRead': instance.chaptersRead, | ||||||
|  |       'volumesOwned': instance.volumesOwned, | ||||||
|  |       'chaptersTotal': instance.chaptersTotal, | ||||||
|  |       'thumbnailUrl': instance.thumbnailUrl, | ||||||
|  |     }; | ||||||
| @ -1,8 +1,8 @@ | |||||||
| class AnimeSearchResult { | class SearchResult { | ||||||
|   const AnimeSearchResult( |   const SearchResult( | ||||||
|     this.title, |     this.title, | ||||||
|     this.id, |     this.id, | ||||||
|     this.episodesTotal, |     this.total, | ||||||
|     this.thumbnailUrl, |     this.thumbnailUrl, | ||||||
|     this.description, |     this.description, | ||||||
|   ); |   ); | ||||||
| @ -16,9 +16,9 @@ class AnimeSearchResult { | |||||||
|   /// The URL to a thumbnail image. |   /// The URL to a thumbnail image. | ||||||
|   final String thumbnailUrl; |   final String thumbnailUrl; | ||||||
| 
 | 
 | ||||||
|   /// The amount of total episodes. If null, it means that there is not total amount |   /// The amount of total episodes or chapters. If null, it means that there is not | ||||||
|   /// of episodes set. |   /// total amount of episodes set. | ||||||
|   final int? episodesTotal; |   final int? total; | ||||||
| 
 | 
 | ||||||
|   /// The description of the anime |   /// The description of the anime | ||||||
|   final String description; |   final String description; | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| import 'package:anitrack/src/data/anime.dart'; | import 'package:anitrack/src/data/anime.dart'; | ||||||
|  | import 'package:anitrack/src/data/manga.dart'; | ||||||
| import 'package:sqflite/sqflite.dart'; | import 'package:sqflite/sqflite.dart'; | ||||||
| 
 | 
 | ||||||
| const animeTable = 'Anime'; | const animeTable = 'Anime'; | ||||||
|  | const mangaTable = 'Manga'; | ||||||
| 
 | 
 | ||||||
| Future<void> _createDatabase(Database db, int version) async { | Future<void> _createDatabase(Database db, int version) async { | ||||||
|   await db.execute( |   await db.execute( | ||||||
| @ -15,6 +17,18 @@ Future<void> _createDatabase(Database db, int version) async { | |||||||
|       title TEXT NOT NULL |       title TEXT NOT NULL | ||||||
|     )''', |     )''', | ||||||
|   ); |   ); | ||||||
|  |   await db.execute( | ||||||
|  |     ''' | ||||||
|  |     CREATE TABLE $mangaTable( | ||||||
|  |       id TEXT NOT NULL PRIMARY KEY, | ||||||
|  |       state INTEGER NOT NULL, | ||||||
|  |       chaptersTotal INTEGER, | ||||||
|  |       chaptersRead INTEGER NOT NULL, | ||||||
|  |       volumesOwned INTEGER NOT NULL, | ||||||
|  |       thumbnailUrl TEXT NOT NULL, | ||||||
|  |       title TEXT NOT NULL | ||||||
|  |     )''', | ||||||
|  |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class DatabaseService { | class DatabaseService { | ||||||
| @ -36,6 +50,15 @@ class DatabaseService { | |||||||
|       .map((Map<String, dynamic> anime) => AnimeTrackingData.fromJson(anime)) |       .map((Map<String, dynamic> anime) => AnimeTrackingData.fromJson(anime)) | ||||||
|       .toList(); |       .toList(); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   Future<List<MangaTrackingData>> loadMangas() async { | ||||||
|  |     final mangas = await _db.query(mangaTable); | ||||||
|  | 
 | ||||||
|  |     return mangas | ||||||
|  |       .cast<Map<String, dynamic>>() | ||||||
|  |       .map((Map<String, dynamic> manga) => MangaTrackingData.fromJson(manga)) | ||||||
|  |       .toList(); | ||||||
|  |   } | ||||||
|    |    | ||||||
|   Future<void> addAnime(AnimeTrackingData data) async { |   Future<void> addAnime(AnimeTrackingData data) async { | ||||||
|     await _db.insert( |     await _db.insert( | ||||||
| @ -52,4 +75,20 @@ class DatabaseService { | |||||||
|       whereArgs: [data.id], |       whereArgs: [data.id], | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> addManga(MangaTrackingData data) async { | ||||||
|  |     await _db.insert( | ||||||
|  |       mangaTable, | ||||||
|  |       data.toJson(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> updateManga(MangaTrackingData data) async { | ||||||
|  |     await _db.update( | ||||||
|  |       mangaTable, | ||||||
|  |       data.toJson(), | ||||||
|  |       where: 'id = ?', | ||||||
|  |       whereArgs: [data.id], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import 'dart:math'; | import 'dart:math'; | ||||||
| import 'package:anitrack/src/data/anime.dart'; | 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/data/type.dart'; | ||||||
| import 'package:anitrack/src/service/database.dart'; | import 'package:anitrack/src/service/database.dart'; | ||||||
| import 'package:bloc/bloc.dart'; | import 'package:bloc/bloc.dart'; | ||||||
| @ -13,11 +14,15 @@ part 'anime_list_bloc.freezed.dart'; | |||||||
| class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> { | class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> { | ||||||
|   AnimeListBloc() : super(AnimeListState()) { |   AnimeListBloc() : super(AnimeListState()) { | ||||||
|     on<AnimeAddedEvent>(_onAnimeAdded); |     on<AnimeAddedEvent>(_onAnimeAdded); | ||||||
|     on<AnimeEpisodeIncrementedEvent>(_onIncremented); |     on<MangaAddedEvent>(_onMangaAdded); | ||||||
|     on<AnimeEpisodeDecrementedEvent>(_onDecremented); |     on<AnimeEpisodeIncrementedEvent>(_onAnimeIncremented); | ||||||
|  |     on<AnimeEpisodeDecrementedEvent>(_onAnimeDecremented); | ||||||
|     on<AnimesLoadedEvent>(_onAnimesLoaded); |     on<AnimesLoadedEvent>(_onAnimesLoaded); | ||||||
|     on<AnimeFilterChangedEvent>(_onAnimesFiltered); |     on<AnimeFilterChangedEvent>(_onAnimesFiltered); | ||||||
|     on<AnimeTrackingTypeChanged>(_onTrackingTypeChanged); |     on<AnimeTrackingTypeChanged>(_onTrackingTypeChanged); | ||||||
|  |     on<MangaFilterChangedEvent>(_onMangasFiltered); | ||||||
|  |     on<MangaChapterIncrementedEvent>(_onMangaIncremented); | ||||||
|  |     on<MangaChapterDecrementedEvent>(_onMangaDecremented); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async { |   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); |     final index = state.animes.indexWhere((item) => item.id == event.id); | ||||||
|     if (index == -1) return; |     if (index == -1) return; | ||||||
| 
 | 
 | ||||||
| @ -56,7 +75,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> { | |||||||
|     await GetIt.I.get<DatabaseService>().updateAnime(newAnime); |     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); |     final index = state.animes.indexWhere((item) => item.id == event.id); | ||||||
|     if (index == -1) return; |     if (index == -1) return; | ||||||
| 
 | 
 | ||||||
| @ -82,6 +101,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> { | |||||||
|     emit( |     emit( | ||||||
|       state.copyWith( |       state.copyWith( | ||||||
|         animes: await GetIt.I.get<DatabaseService>().loadAnimes(), |         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 { |   Future<void> _onAnimesFiltered(AnimeFilterChangedEvent event, Emitter<AnimeListState> emit) async { | ||||||
|     emit( |     emit( | ||||||
|       state.copyWith( |       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 { |   Future<void> _onTrackingTypeChanged(AnimeTrackingTypeChanged event, Emitter<AnimeListState> emit) async { | ||||||
|     emit( |     emit( | ||||||
|       state.copyWith( |       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); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,7 +17,9 @@ final _privateConstructorUsedError = UnsupportedError( | |||||||
| /// @nodoc | /// @nodoc | ||||||
| mixin _$AnimeListState { | mixin _$AnimeListState { | ||||||
|   List<AnimeTrackingData> get animes => throw _privateConstructorUsedError; |   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; |   TrackingMediumType get trackingType => throw _privateConstructorUsedError; | ||||||
| 
 | 
 | ||||||
|   @JsonKey(ignore: true) |   @JsonKey(ignore: true) | ||||||
| @ -32,7 +34,9 @@ abstract class $AnimeListStateCopyWith<$Res> { | |||||||
|       _$AnimeListStateCopyWithImpl<$Res>; |       _$AnimeListStateCopyWithImpl<$Res>; | ||||||
|   $Res call( |   $Res call( | ||||||
|       {List<AnimeTrackingData> animes, |       {List<AnimeTrackingData> animes, | ||||||
|       AnimeTrackingState filterState, |       List<MangaTrackingData> mangas, | ||||||
|  |       AnimeTrackingState animeFilterState, | ||||||
|  |       MangaTrackingState mangaFilterState, | ||||||
|       TrackingMediumType trackingType}); |       TrackingMediumType trackingType}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -48,7 +52,9 @@ class _$AnimeListStateCopyWithImpl<$Res> | |||||||
|   @override |   @override | ||||||
|   $Res call({ |   $Res call({ | ||||||
|     Object? animes = freezed, |     Object? animes = freezed, | ||||||
|     Object? filterState = freezed, |     Object? mangas = freezed, | ||||||
|  |     Object? animeFilterState = freezed, | ||||||
|  |     Object? mangaFilterState = freezed, | ||||||
|     Object? trackingType = freezed, |     Object? trackingType = freezed, | ||||||
|   }) { |   }) { | ||||||
|     return _then(_value.copyWith( |     return _then(_value.copyWith( | ||||||
| @ -56,10 +62,18 @@ class _$AnimeListStateCopyWithImpl<$Res> | |||||||
|           ? _value.animes |           ? _value.animes | ||||||
|           : animes // ignore: cast_nullable_to_non_nullable |           : animes // ignore: cast_nullable_to_non_nullable | ||||||
|               as List<AnimeTrackingData>, |               as List<AnimeTrackingData>, | ||||||
|       filterState: filterState == freezed |       mangas: mangas == freezed | ||||||
|           ? _value.filterState |           ? _value.mangas | ||||||
|           : filterState // ignore: cast_nullable_to_non_nullable |           : mangas // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as List<MangaTrackingData>, | ||||||
|  |       animeFilterState: animeFilterState == freezed | ||||||
|  |           ? _value.animeFilterState | ||||||
|  |           : animeFilterState // ignore: cast_nullable_to_non_nullable | ||||||
|               as AnimeTrackingState, |               as AnimeTrackingState, | ||||||
|  |       mangaFilterState: mangaFilterState == freezed | ||||||
|  |           ? _value.mangaFilterState | ||||||
|  |           : mangaFilterState // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as MangaTrackingState, | ||||||
|       trackingType: trackingType == freezed |       trackingType: trackingType == freezed | ||||||
|           ? _value.trackingType |           ? _value.trackingType | ||||||
|           : trackingType // ignore: cast_nullable_to_non_nullable |           : trackingType // ignore: cast_nullable_to_non_nullable | ||||||
| @ -77,7 +91,9 @@ abstract class _$$_AnimeListStateCopyWith<$Res> | |||||||
|   @override |   @override | ||||||
|   $Res call( |   $Res call( | ||||||
|       {List<AnimeTrackingData> animes, |       {List<AnimeTrackingData> animes, | ||||||
|       AnimeTrackingState filterState, |       List<MangaTrackingData> mangas, | ||||||
|  |       AnimeTrackingState animeFilterState, | ||||||
|  |       MangaTrackingState mangaFilterState, | ||||||
|       TrackingMediumType trackingType}); |       TrackingMediumType trackingType}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -95,7 +111,9 @@ class __$$_AnimeListStateCopyWithImpl<$Res> | |||||||
|   @override |   @override | ||||||
|   $Res call({ |   $Res call({ | ||||||
|     Object? animes = freezed, |     Object? animes = freezed, | ||||||
|     Object? filterState = freezed, |     Object? mangas = freezed, | ||||||
|  |     Object? animeFilterState = freezed, | ||||||
|  |     Object? mangaFilterState = freezed, | ||||||
|     Object? trackingType = freezed, |     Object? trackingType = freezed, | ||||||
|   }) { |   }) { | ||||||
|     return _then(_$_AnimeListState( |     return _then(_$_AnimeListState( | ||||||
| @ -103,10 +121,18 @@ class __$$_AnimeListStateCopyWithImpl<$Res> | |||||||
|           ? _value._animes |           ? _value._animes | ||||||
|           : animes // ignore: cast_nullable_to_non_nullable |           : animes // ignore: cast_nullable_to_non_nullable | ||||||
|               as List<AnimeTrackingData>, |               as List<AnimeTrackingData>, | ||||||
|       filterState: filterState == freezed |       mangas: mangas == freezed | ||||||
|           ? _value.filterState |           ? _value._mangas | ||||||
|           : filterState // ignore: cast_nullable_to_non_nullable |           : mangas // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as List<MangaTrackingData>, | ||||||
|  |       animeFilterState: animeFilterState == freezed | ||||||
|  |           ? _value.animeFilterState | ||||||
|  |           : animeFilterState // ignore: cast_nullable_to_non_nullable | ||||||
|               as AnimeTrackingState, |               as AnimeTrackingState, | ||||||
|  |       mangaFilterState: mangaFilterState == freezed | ||||||
|  |           ? _value.mangaFilterState | ||||||
|  |           : mangaFilterState // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as MangaTrackingState, | ||||||
|       trackingType: trackingType == freezed |       trackingType: trackingType == freezed | ||||||
|           ? _value.trackingType |           ? _value.trackingType | ||||||
|           : trackingType // ignore: cast_nullable_to_non_nullable |           : trackingType // ignore: cast_nullable_to_non_nullable | ||||||
| @ -120,9 +146,12 @@ class __$$_AnimeListStateCopyWithImpl<$Res> | |||||||
| class _$_AnimeListState implements _AnimeListState { | class _$_AnimeListState implements _AnimeListState { | ||||||
|   _$_AnimeListState( |   _$_AnimeListState( | ||||||
|       {final List<AnimeTrackingData> animes = const [], |       {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}) |       this.trackingType = TrackingMediumType.anime}) | ||||||
|       : _animes = animes; |       : _animes = animes, | ||||||
|  |         _mangas = mangas; | ||||||
| 
 | 
 | ||||||
|   final List<AnimeTrackingData> _animes; |   final List<AnimeTrackingData> _animes; | ||||||
|   @override |   @override | ||||||
| @ -132,16 +161,27 @@ class _$_AnimeListState implements _AnimeListState { | |||||||
|     return EqualUnmodifiableListView(_animes); |     return EqualUnmodifiableListView(_animes); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   final List<MangaTrackingData> _mangas; | ||||||
|   @override |   @override | ||||||
|   @JsonKey() |   @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 |   @override | ||||||
|   @JsonKey() |   @JsonKey() | ||||||
|   final TrackingMediumType trackingType; |   final TrackingMediumType trackingType; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() { |   String toString() { | ||||||
|     return 'AnimeListState(animes: $animes, filterState: $filterState, trackingType: $trackingType)'; |     return 'AnimeListState(animes: $animes, mangas: $mangas, animeFilterState: $animeFilterState, mangaFilterState: $mangaFilterState, trackingType: $trackingType)'; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
| @ -150,8 +190,11 @@ class _$_AnimeListState implements _AnimeListState { | |||||||
|         (other.runtimeType == runtimeType && |         (other.runtimeType == runtimeType && | ||||||
|             other is _$_AnimeListState && |             other is _$_AnimeListState && | ||||||
|             const DeepCollectionEquality().equals(other._animes, _animes) && |             const DeepCollectionEquality().equals(other._animes, _animes) && | ||||||
|  |             const DeepCollectionEquality().equals(other._mangas, _mangas) && | ||||||
|             const DeepCollectionEquality() |             const DeepCollectionEquality() | ||||||
|                 .equals(other.filterState, filterState) && |                 .equals(other.animeFilterState, animeFilterState) && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.mangaFilterState, mangaFilterState) && | ||||||
|             const DeepCollectionEquality() |             const DeepCollectionEquality() | ||||||
|                 .equals(other.trackingType, trackingType)); |                 .equals(other.trackingType, trackingType)); | ||||||
|   } |   } | ||||||
| @ -160,7 +203,9 @@ class _$_AnimeListState implements _AnimeListState { | |||||||
|   int get hashCode => Object.hash( |   int get hashCode => Object.hash( | ||||||
|       runtimeType, |       runtimeType, | ||||||
|       const DeepCollectionEquality().hash(_animes), |       const DeepCollectionEquality().hash(_animes), | ||||||
|       const DeepCollectionEquality().hash(filterState), |       const DeepCollectionEquality().hash(_mangas), | ||||||
|  |       const DeepCollectionEquality().hash(animeFilterState), | ||||||
|  |       const DeepCollectionEquality().hash(mangaFilterState), | ||||||
|       const DeepCollectionEquality().hash(trackingType)); |       const DeepCollectionEquality().hash(trackingType)); | ||||||
| 
 | 
 | ||||||
|   @JsonKey(ignore: true) |   @JsonKey(ignore: true) | ||||||
| @ -172,13 +217,19 @@ class _$_AnimeListState implements _AnimeListState { | |||||||
| abstract class _AnimeListState implements AnimeListState { | abstract class _AnimeListState implements AnimeListState { | ||||||
|   factory _AnimeListState( |   factory _AnimeListState( | ||||||
|       {final List<AnimeTrackingData> animes, |       {final List<AnimeTrackingData> animes, | ||||||
|       final AnimeTrackingState filterState, |       final List<MangaTrackingData> mangas, | ||||||
|  |       final AnimeTrackingState animeFilterState, | ||||||
|  |       final MangaTrackingState mangaFilterState, | ||||||
|       final TrackingMediumType trackingType}) = _$_AnimeListState; |       final TrackingMediumType trackingType}) = _$_AnimeListState; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   List<AnimeTrackingData> get animes; |   List<AnimeTrackingData> get animes; | ||||||
|   @override |   @override | ||||||
|   AnimeTrackingState get filterState; |   List<MangaTrackingData> get mangas; | ||||||
|  |   @override | ||||||
|  |   AnimeTrackingState get animeFilterState; | ||||||
|  |   @override | ||||||
|  |   MangaTrackingState get mangaFilterState; | ||||||
|   @override |   @override | ||||||
|   TrackingMediumType get trackingType; |   TrackingMediumType get trackingType; | ||||||
|   @override |   @override | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ class AnimeAddedEvent extends AnimeListEvent { | |||||||
| /// Triggered when animes are to be loaded from the database | /// Triggered when animes are to be loaded from the database | ||||||
| class AnimesLoadedEvent extends AnimeListEvent {} | class AnimesLoadedEvent extends AnimeListEvent {} | ||||||
| 
 | 
 | ||||||
| /// Triggered when the filter is changed | /// Triggered when the anime filter is changed | ||||||
| class AnimeFilterChangedEvent extends AnimeListEvent { | class AnimeFilterChangedEvent extends AnimeListEvent { | ||||||
|   AnimeFilterChangedEvent(this.filterState); |   AnimeFilterChangedEvent(this.filterState); | ||||||
| 
 | 
 | ||||||
| @ -41,3 +41,32 @@ class AnimeTrackingTypeChanged extends AnimeListEvent { | |||||||
|   /// The type we switched to |   /// The type we switched to | ||||||
|   final TrackingMediumType type; |   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; | ||||||
|  | } | ||||||
|  | |||||||
| @ -4,7 +4,9 @@ part of 'anime_list_bloc.dart'; | |||||||
| class AnimeListState with _$AnimeListState { | class AnimeListState with _$AnimeListState { | ||||||
|   factory AnimeListState({ |   factory AnimeListState({ | ||||||
|     @Default([]) List<AnimeTrackingData> animes, |     @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, |     @Default(TrackingMediumType.anime) TrackingMediumType trackingType, | ||||||
|   }) = _AnimeListState; |   }) = _AnimeListState; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| import 'package:anitrack/src/data/anime.dart'; | 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/search_result.dart'; | ||||||
|  | import 'package:anitrack/src/data/type.dart'; | ||||||
| import 'package:anitrack/src/ui/constants.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/anime_list_bloc.dart' as list; | ||||||
| import 'package:anitrack/src/ui/bloc/navigation_bloc.dart'; | import 'package:anitrack/src/ui/bloc/navigation_bloc.dart'; | ||||||
| @ -17,7 +19,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> { | |||||||
|     on<AnimeSearchRequestedEvent>(_onRequested); |     on<AnimeSearchRequestedEvent>(_onRequested); | ||||||
|     on<SearchQueryChangedEvent>(_onQueryChanged); |     on<SearchQueryChangedEvent>(_onQueryChanged); | ||||||
|     on<SearchQuerySubmittedEvent>(_onQuerySubmitted); |     on<SearchQuerySubmittedEvent>(_onQuerySubmitted); | ||||||
|     on<AnimeAddedEvent>(_onAnimeAdded); |     on<ResultTappedEvent>(_onResultTapped); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> _onRequested(AnimeSearchRequestedEvent event, Emitter<AnimeSearchState> emit) async { |   Future<void> _onRequested(AnimeSearchRequestedEvent event, Emitter<AnimeSearchState> emit) async { | ||||||
| @ -26,6 +28,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> { | |||||||
|         searchQuery: '', |         searchQuery: '', | ||||||
|         working: false, |         working: false, | ||||||
|         searchResults: [], |         searchResults: [], | ||||||
|  |         trackingType: event.type, | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -52,42 +55,70 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> { | |||||||
|         working: true, |         working: true, | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|      |  | ||||||
|     final result = await Jikan().searchAnime( |  | ||||||
|       query: state.searchQuery, |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     emit( |     if (state.trackingType == TrackingMediumType.anime) { | ||||||
|       state.copyWith( |       // Anime | ||||||
|         working: false, |       final result = await Jikan().searchAnime( | ||||||
|       ), |         query: state.searchQuery, | ||||||
|     ); |       ); | ||||||
|      | 
 | ||||||
|     emit( |       emit( | ||||||
|       state.copyWith( |         state.copyWith( | ||||||
|         searchResults: result.map((Anime anime) => AnimeSearchResult( |           working: false, | ||||||
|           anime.title, |           searchResults: result.map((Anime anime) => SearchResult( | ||||||
|           anime.malId.toString(), |             anime.title, | ||||||
|           anime.episodes, |             anime.malId.toString(), | ||||||
|           anime.imageUrl, |             anime.episodes, | ||||||
|           anime.synopsis ?? '', |             anime.imageUrl, | ||||||
|         ),).toList(), |             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( |     GetIt.I.get<list.AnimeListBloc>().add( | ||||||
|       list.AnimeAddedEvent( |       state.trackingType == TrackingMediumType.anime ? | ||||||
|         AnimeTrackingData( |         list.AnimeAddedEvent( | ||||||
|           event.result.id, |           AnimeTrackingData( | ||||||
|           AnimeTrackingState.watching, |             event.result.id, | ||||||
|           event.result.title, |             AnimeTrackingState.watching, | ||||||
|           0, |             event.result.title, | ||||||
|           event.result.episodesTotal, |             0, | ||||||
|           event.result.thumbnailUrl, |             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( |     GetIt.I.get<NavigationBloc>().add( | ||||||
|  | |||||||
| @ -16,10 +16,10 @@ final _privateConstructorUsedError = UnsupportedError( | |||||||
| 
 | 
 | ||||||
| /// @nodoc | /// @nodoc | ||||||
| mixin _$AnimeSearchState { | mixin _$AnimeSearchState { | ||||||
|  |   TrackingMediumType get trackingType => throw _privateConstructorUsedError; | ||||||
|   String get searchQuery => throw _privateConstructorUsedError; |   String get searchQuery => throw _privateConstructorUsedError; | ||||||
|   bool get working => throw _privateConstructorUsedError; |   bool get working => throw _privateConstructorUsedError; | ||||||
|   List<AnimeSearchResult> get searchResults => |   List<SearchResult> get searchResults => throw _privateConstructorUsedError; | ||||||
|       throw _privateConstructorUsedError; |  | ||||||
| 
 | 
 | ||||||
|   @JsonKey(ignore: true) |   @JsonKey(ignore: true) | ||||||
|   $AnimeSearchStateCopyWith<AnimeSearchState> get copyWith => |   $AnimeSearchStateCopyWith<AnimeSearchState> get copyWith => | ||||||
| @ -32,9 +32,10 @@ abstract class $AnimeSearchStateCopyWith<$Res> { | |||||||
|           AnimeSearchState value, $Res Function(AnimeSearchState) then) = |           AnimeSearchState value, $Res Function(AnimeSearchState) then) = | ||||||
|       _$AnimeSearchStateCopyWithImpl<$Res>; |       _$AnimeSearchStateCopyWithImpl<$Res>; | ||||||
|   $Res call( |   $Res call( | ||||||
|       {String searchQuery, |       {TrackingMediumType trackingType, | ||||||
|  |       String searchQuery, | ||||||
|       bool working, |       bool working, | ||||||
|       List<AnimeSearchResult> searchResults}); |       List<SearchResult> searchResults}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// @nodoc | /// @nodoc | ||||||
| @ -48,11 +49,16 @@ class _$AnimeSearchStateCopyWithImpl<$Res> | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   $Res call({ |   $Res call({ | ||||||
|  |     Object? trackingType = freezed, | ||||||
|     Object? searchQuery = freezed, |     Object? searchQuery = freezed, | ||||||
|     Object? working = freezed, |     Object? working = freezed, | ||||||
|     Object? searchResults = freezed, |     Object? searchResults = freezed, | ||||||
|   }) { |   }) { | ||||||
|     return _then(_value.copyWith( |     return _then(_value.copyWith( | ||||||
|  |       trackingType: trackingType == freezed | ||||||
|  |           ? _value.trackingType | ||||||
|  |           : trackingType // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as TrackingMediumType, | ||||||
|       searchQuery: searchQuery == freezed |       searchQuery: searchQuery == freezed | ||||||
|           ? _value.searchQuery |           ? _value.searchQuery | ||||||
|           : searchQuery // ignore: cast_nullable_to_non_nullable |           : searchQuery // ignore: cast_nullable_to_non_nullable | ||||||
| @ -64,7 +70,7 @@ class _$AnimeSearchStateCopyWithImpl<$Res> | |||||||
|       searchResults: searchResults == freezed |       searchResults: searchResults == freezed | ||||||
|           ? _value.searchResults |           ? _value.searchResults | ||||||
|           : searchResults // ignore: cast_nullable_to_non_nullable |           : searchResults // ignore: cast_nullable_to_non_nullable | ||||||
|               as List<AnimeSearchResult>, |               as List<SearchResult>, | ||||||
|     )); |     )); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -77,9 +83,10 @@ abstract class _$$_AnimeSearchStateCopyWith<$Res> | |||||||
|       __$$_AnimeSearchStateCopyWithImpl<$Res>; |       __$$_AnimeSearchStateCopyWithImpl<$Res>; | ||||||
|   @override |   @override | ||||||
|   $Res call( |   $Res call( | ||||||
|       {String searchQuery, |       {TrackingMediumType trackingType, | ||||||
|  |       String searchQuery, | ||||||
|       bool working, |       bool working, | ||||||
|       List<AnimeSearchResult> searchResults}); |       List<SearchResult> searchResults}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// @nodoc | /// @nodoc | ||||||
| @ -95,11 +102,16 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res> | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   $Res call({ |   $Res call({ | ||||||
|  |     Object? trackingType = freezed, | ||||||
|     Object? searchQuery = freezed, |     Object? searchQuery = freezed, | ||||||
|     Object? working = freezed, |     Object? working = freezed, | ||||||
|     Object? searchResults = freezed, |     Object? searchResults = freezed, | ||||||
|   }) { |   }) { | ||||||
|     return _then(_$_AnimeSearchState( |     return _then(_$_AnimeSearchState( | ||||||
|  |       trackingType: trackingType == freezed | ||||||
|  |           ? _value.trackingType | ||||||
|  |           : trackingType // ignore: cast_nullable_to_non_nullable | ||||||
|  |               as TrackingMediumType, | ||||||
|       searchQuery: searchQuery == freezed |       searchQuery: searchQuery == freezed | ||||||
|           ? _value.searchQuery |           ? _value.searchQuery | ||||||
|           : searchQuery // ignore: cast_nullable_to_non_nullable |           : searchQuery // ignore: cast_nullable_to_non_nullable | ||||||
| @ -111,7 +123,7 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res> | |||||||
|       searchResults: searchResults == freezed |       searchResults: searchResults == freezed | ||||||
|           ? _value._searchResults |           ? _value._searchResults | ||||||
|           : searchResults // ignore: cast_nullable_to_non_nullable |           : searchResults // ignore: cast_nullable_to_non_nullable | ||||||
|               as List<AnimeSearchResult>, |               as List<SearchResult>, | ||||||
|     )); |     )); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -120,28 +132,32 @@ class __$$_AnimeSearchStateCopyWithImpl<$Res> | |||||||
| 
 | 
 | ||||||
| class _$_AnimeSearchState implements _AnimeSearchState { | class _$_AnimeSearchState implements _AnimeSearchState { | ||||||
|   _$_AnimeSearchState( |   _$_AnimeSearchState( | ||||||
|       {this.searchQuery = '', |       {this.trackingType = TrackingMediumType.anime, | ||||||
|  |       this.searchQuery = '', | ||||||
|       this.working = false, |       this.working = false, | ||||||
|       final List<AnimeSearchResult> searchResults = const []}) |       final List<SearchResult> searchResults = const []}) | ||||||
|       : _searchResults = searchResults; |       : _searchResults = searchResults; | ||||||
| 
 | 
 | ||||||
|  |   @override | ||||||
|  |   @JsonKey() | ||||||
|  |   final TrackingMediumType trackingType; | ||||||
|   @override |   @override | ||||||
|   @JsonKey() |   @JsonKey() | ||||||
|   final String searchQuery; |   final String searchQuery; | ||||||
|   @override |   @override | ||||||
|   @JsonKey() |   @JsonKey() | ||||||
|   final bool working; |   final bool working; | ||||||
|   final List<AnimeSearchResult> _searchResults; |   final List<SearchResult> _searchResults; | ||||||
|   @override |   @override | ||||||
|   @JsonKey() |   @JsonKey() | ||||||
|   List<AnimeSearchResult> get searchResults { |   List<SearchResult> get searchResults { | ||||||
|     // ignore: implicit_dynamic_type |     // ignore: implicit_dynamic_type | ||||||
|     return EqualUnmodifiableListView(_searchResults); |     return EqualUnmodifiableListView(_searchResults); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() { |   String toString() { | ||||||
|     return 'AnimeSearchState(searchQuery: $searchQuery, working: $working, searchResults: $searchResults)'; |     return 'AnimeSearchState(trackingType: $trackingType, searchQuery: $searchQuery, working: $working, searchResults: $searchResults)'; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
| @ -149,6 +165,8 @@ class _$_AnimeSearchState implements _AnimeSearchState { | |||||||
|     return identical(this, other) || |     return identical(this, other) || | ||||||
|         (other.runtimeType == runtimeType && |         (other.runtimeType == runtimeType && | ||||||
|             other is _$_AnimeSearchState && |             other is _$_AnimeSearchState && | ||||||
|  |             const DeepCollectionEquality() | ||||||
|  |                 .equals(other.trackingType, trackingType) && | ||||||
|             const DeepCollectionEquality() |             const DeepCollectionEquality() | ||||||
|                 .equals(other.searchQuery, searchQuery) && |                 .equals(other.searchQuery, searchQuery) && | ||||||
|             const DeepCollectionEquality().equals(other.working, working) && |             const DeepCollectionEquality().equals(other.working, working) && | ||||||
| @ -159,6 +177,7 @@ class _$_AnimeSearchState implements _AnimeSearchState { | |||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash( |   int get hashCode => Object.hash( | ||||||
|       runtimeType, |       runtimeType, | ||||||
|  |       const DeepCollectionEquality().hash(trackingType), | ||||||
|       const DeepCollectionEquality().hash(searchQuery), |       const DeepCollectionEquality().hash(searchQuery), | ||||||
|       const DeepCollectionEquality().hash(working), |       const DeepCollectionEquality().hash(working), | ||||||
|       const DeepCollectionEquality().hash(_searchResults)); |       const DeepCollectionEquality().hash(_searchResults)); | ||||||
| @ -171,16 +190,19 @@ class _$_AnimeSearchState implements _AnimeSearchState { | |||||||
| 
 | 
 | ||||||
| abstract class _AnimeSearchState implements AnimeSearchState { | abstract class _AnimeSearchState implements AnimeSearchState { | ||||||
|   factory _AnimeSearchState( |   factory _AnimeSearchState( | ||||||
|       {final String searchQuery, |       {final TrackingMediumType trackingType, | ||||||
|  |       final String searchQuery, | ||||||
|       final bool working, |       final bool working, | ||||||
|       final List<AnimeSearchResult> searchResults}) = _$_AnimeSearchState; |       final List<SearchResult> searchResults}) = _$_AnimeSearchState; | ||||||
| 
 | 
 | ||||||
|  |   @override | ||||||
|  |   TrackingMediumType get trackingType; | ||||||
|   @override |   @override | ||||||
|   String get searchQuery; |   String get searchQuery; | ||||||
|   @override |   @override | ||||||
|   bool get working; |   bool get working; | ||||||
|   @override |   @override | ||||||
|   List<AnimeSearchResult> get searchResults; |   List<SearchResult> get searchResults; | ||||||
|   @override |   @override | ||||||
|   @JsonKey(ignore: true) |   @JsonKey(ignore: true) | ||||||
|   _$$_AnimeSearchStateCopyWith<_$_AnimeSearchState> get copyWith => |   _$$_AnimeSearchStateCopyWith<_$_AnimeSearchState> get copyWith => | ||||||
|  | |||||||
| @ -2,7 +2,12 @@ part of 'anime_search_bloc.dart'; | |||||||
| 
 | 
 | ||||||
| abstract class AnimeSearchEvent {} | 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. | /// Triggered when the search query is changed. | ||||||
| class SearchQueryChangedEvent extends AnimeSearchEvent { | class SearchQueryChangedEvent extends AnimeSearchEvent { | ||||||
| @ -16,9 +21,9 @@ class SearchQueryChangedEvent extends AnimeSearchEvent { | |||||||
| class SearchQuerySubmittedEvent extends AnimeSearchEvent {} | class SearchQuerySubmittedEvent extends AnimeSearchEvent {} | ||||||
| 
 | 
 | ||||||
| /// Triggered when an anime is added to the tracking list | /// Triggered when an anime is added to the tracking list | ||||||
| class AnimeAddedEvent extends AnimeSearchEvent { | class ResultTappedEvent extends AnimeSearchEvent { | ||||||
|   AnimeAddedEvent(this.result); |   ResultTappedEvent(this.result); | ||||||
| 
 | 
 | ||||||
|   /// The search result to add. |   /// The search result to add. | ||||||
|   final AnimeSearchResult result; |   final SearchResult result; | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,8 +3,9 @@ part of 'anime_search_bloc.dart'; | |||||||
| @freezed | @freezed | ||||||
| class AnimeSearchState with _$AnimeSearchState { | class AnimeSearchState with _$AnimeSearchState { | ||||||
|   factory AnimeSearchState({ |   factory AnimeSearchState({ | ||||||
|  |     @Default(TrackingMediumType.anime) TrackingMediumType trackingType, | ||||||
|     @Default('') String searchQuery, |     @Default('') String searchQuery, | ||||||
|     @Default(false) bool working, |     @Default(false) bool working, | ||||||
|     @Default([]) List<AnimeSearchResult> searchResults, |     @Default([]) List<SearchResult> searchResults, | ||||||
|   }) = _AnimeSearchState; |   }) = _AnimeSearchState; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import 'package:anitrack/src/data/anime.dart'; | 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/data/type.dart'; | ||||||
| import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart'; | import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart'; | ||||||
| import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart'; | import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart'; | ||||||
| @ -24,6 +25,79 @@ class AnimeListPage extends StatelessWidget { | |||||||
|       case TrackingMediumType.manga: return 'Manga'; |       case TrackingMediumType.manga: return 'Manga'; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   Widget _getPopupButton(BuildContext context, AnimeListState state) { | ||||||
|  |     switch (state.trackingType) { | ||||||
|  |       case TrackingMediumType.anime: | ||||||
|  |         return PopupMenuButton( | ||||||
|  |           icon: Icon( | ||||||
|  |             Icons.filter_list, | ||||||
|  |           ), | ||||||
|  |           initialValue: state.animeFilterState, | ||||||
|  |           onSelected: (filterState) { | ||||||
|  |             context.read<AnimeListBloc>().add( | ||||||
|  |               AnimeFilterChangedEvent(filterState), | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|  |           itemBuilder: (_) => [ | ||||||
|  |             const PopupMenuItem<AnimeTrackingState>( | ||||||
|  |               value: AnimeTrackingState.watching, | ||||||
|  |               child: Text('Watching'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<AnimeTrackingState>( | ||||||
|  |               value: AnimeTrackingState.completed, | ||||||
|  |               child: Text('Completed'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<AnimeTrackingState>( | ||||||
|  |               value: AnimeTrackingState.planToWatch, | ||||||
|  |               child: Text('Plan to watch'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<AnimeTrackingState>( | ||||||
|  |               value: AnimeTrackingState.dropped, | ||||||
|  |               child: Text('Dropped'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<AnimeTrackingState>( | ||||||
|  |               value: AnimeTrackingState.all, | ||||||
|  |               child: Text('All'), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ); | ||||||
|  |       case TrackingMediumType.manga: | ||||||
|  |         return PopupMenuButton( | ||||||
|  |           icon: Icon( | ||||||
|  |             Icons.filter_list, | ||||||
|  |           ), | ||||||
|  |           initialValue: state.mangaFilterState, | ||||||
|  |           onSelected: (filterState) { | ||||||
|  |             context.read<AnimeListBloc>().add( | ||||||
|  |               MangaFilterChangedEvent(filterState), | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|  |           itemBuilder: (_) => [ | ||||||
|  |             const PopupMenuItem<MangaTrackingState>( | ||||||
|  |               value: MangaTrackingState.reading, | ||||||
|  |               child: Text('Reading'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<MangaTrackingState>( | ||||||
|  |               value: MangaTrackingState.completed, | ||||||
|  |               child: Text('Completed'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<MangaTrackingState>( | ||||||
|  |               value: MangaTrackingState.planToWatch, | ||||||
|  |               child: Text('Plan to watch'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<MangaTrackingState>( | ||||||
|  |               value: MangaTrackingState.dropped, | ||||||
|  |               child: Text('Dropped'), | ||||||
|  |             ), | ||||||
|  |             const PopupMenuItem<MangaTrackingState>( | ||||||
|  |               value: MangaTrackingState.all, | ||||||
|  |               child: Text('All'), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|    |    | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
| @ -35,39 +109,7 @@ class AnimeListPage extends StatelessWidget { | |||||||
|               _getPageTitle(state.trackingType) |               _getPageTitle(state.trackingType) | ||||||
|             ), |             ), | ||||||
|             actions: [ |             actions: [ | ||||||
|               PopupMenuButton( |               _getPopupButton(context, state), | ||||||
|                 icon: Icon( |  | ||||||
|                   Icons.filter_list, |  | ||||||
|                 ), |  | ||||||
|                 initialValue: state.filterState, |  | ||||||
|                 onSelected: (filterState) { |  | ||||||
|                   context.read<AnimeListBloc>().add( |  | ||||||
|                     AnimeFilterChangedEvent(filterState), |  | ||||||
|                   ); |  | ||||||
|                 }, |  | ||||||
|                 itemBuilder: (_) => [ |  | ||||||
|                   const PopupMenuItem<AnimeTrackingState>( |  | ||||||
|                     value: AnimeTrackingState.watching, |  | ||||||
|                     child: Text('Watching'), |  | ||||||
|                   ), |  | ||||||
|                   const PopupMenuItem<AnimeTrackingState>( |  | ||||||
|                     value: AnimeTrackingState.completed, |  | ||||||
|                     child: Text('Completed'), |  | ||||||
|                   ), |  | ||||||
|                   const PopupMenuItem<AnimeTrackingState>( |  | ||||||
|                     value: AnimeTrackingState.planToWatch, |  | ||||||
|                     child: Text('Plan to watch'), |  | ||||||
|                   ), |  | ||||||
|                   const PopupMenuItem<AnimeTrackingState>( |  | ||||||
|                     value: AnimeTrackingState.dropped, |  | ||||||
|                     child: Text('Dropped'), |  | ||||||
|                   ), |  | ||||||
|                   const PopupMenuItem<AnimeTrackingState>( |  | ||||||
|                     value: AnimeTrackingState.all, |  | ||||||
|                     child: Text('All'), |  | ||||||
|                   ), |  | ||||||
|                 ], |  | ||||||
|               ), |  | ||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
|           body: PageView( |           body: PageView( | ||||||
| @ -77,8 +119,8 @@ class AnimeListPage extends StatelessWidget { | |||||||
|                 itemCount: state.animes.length, |                 itemCount: state.animes.length, | ||||||
|                 itemBuilder: (context, index) { |                 itemBuilder: (context, index) { | ||||||
|                   final anime = state.animes[index]; |                   final anime = state.animes[index]; | ||||||
|                   if (state.filterState != AnimeTrackingState.all) { |                   if (state.animeFilterState != AnimeTrackingState.all) { | ||||||
|                     if (anime.state != state.filterState) return Container(); |                     if (anime.state != state.animeFilterState) return Container(); | ||||||
|                   } |                   } | ||||||
| 
 | 
 | ||||||
|                   return ListItem( |                   return ListItem( | ||||||
| @ -103,13 +145,42 @@ class AnimeListPage extends StatelessWidget { | |||||||
|                   ); |                   ); | ||||||
|                 }, |                 }, | ||||||
|               ), |               ), | ||||||
|               Placeholder(), |               ListView.builder( | ||||||
|  |                 itemCount: state.mangas.length, | ||||||
|  |                 itemBuilder: (context, index) { | ||||||
|  |                   final manga = state.mangas[index]; | ||||||
|  |                   if (state.mangaFilterState != MangaTrackingState.all) { | ||||||
|  |                     if (manga.state != state.mangaFilterState) return Container(); | ||||||
|  |                   } | ||||||
|  | 
 | ||||||
|  |                   return ListItem( | ||||||
|  |                     title: manga.title, | ||||||
|  |                     thumbnailUrl: manga.thumbnailUrl, | ||||||
|  |                     extra: [ | ||||||
|  |                       Text( | ||||||
|  |                         '${manga.chaptersRead}/${manga.chaptersTotal ?? "???"}', | ||||||
|  |                         style: Theme.of(context).textTheme.titleMedium, | ||||||
|  |                       ), | ||||||
|  |                     ], | ||||||
|  |                     onLeftSwipe: () { | ||||||
|  |                       context.read<AnimeListBloc>().add( | ||||||
|  |                         MangaChapterDecrementedEvent(state.mangas[index].id), | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                     onRightSwipe: () { | ||||||
|  |                       context.read<AnimeListBloc>().add( | ||||||
|  |                         MangaChapterIncrementedEvent(state.mangas[index].id), | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                   ); | ||||||
|  |                 }, | ||||||
|  |               ), | ||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
|           floatingActionButton: FloatingActionButton( |           floatingActionButton: FloatingActionButton( | ||||||
|             onPressed: () { |             onPressed: () { | ||||||
|               context.read<AnimeSearchBloc>().add( |               context.read<AnimeSearchBloc>().add( | ||||||
|                 AnimeSearchRequestedEvent(), |                 AnimeSearchRequestedEvent(state.trackingType), | ||||||
|               ); |               ); | ||||||
|             }, |             }, | ||||||
|             tooltip: 'Increment', |             tooltip: 'Increment', | ||||||
| @ -120,7 +191,6 @@ class AnimeListPage extends StatelessWidget { | |||||||
|               0 : |               0 : | ||||||
|               1, |               1, | ||||||
|             onTap: (int index) { |             onTap: (int index) { | ||||||
|               _controller.jumpToPage(index); |  | ||||||
|               context.read<AnimeListBloc>().add( |               context.read<AnimeListBloc>().add( | ||||||
|                 AnimeTrackingTypeChanged( |                 AnimeTrackingTypeChanged( | ||||||
|                   index == 0 ? |                   index == 0 ? | ||||||
| @ -128,6 +198,8 @@ class AnimeListPage extends StatelessWidget { | |||||||
|                     TrackingMediumType.manga, |                     TrackingMediumType.manga, | ||||||
|                 ), |                 ), | ||||||
|               ); |               ); | ||||||
|  | 
 | ||||||
|  |               _controller.jumpToPage(index); | ||||||
|             }, |             }, | ||||||
|             items: <BottomBarItem>[ |             items: <BottomBarItem>[ | ||||||
|               BottomBarItem( |               BottomBarItem( | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||||
| import 'package:anitrack/src/data/anime.dart'; | import 'package:anitrack/src/data/anime.dart'; | ||||||
|  | import 'package:anitrack/src/data/type.dart'; | ||||||
| import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart'; | import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart'; | ||||||
| import 'package:anitrack/src/ui/constants.dart'; | import 'package:anitrack/src/ui/constants.dart'; | ||||||
| import 'package:anitrack/src/ui/widgets/list_item.dart'; | import 'package:anitrack/src/ui/widgets/list_item.dart'; | ||||||
| @ -20,7 +21,11 @@ class AnimeSearchPage extends StatelessWidget { | |||||||
|       builder: (context, state) { |       builder: (context, state) { | ||||||
|         return Scaffold( |         return Scaffold( | ||||||
|           appBar: AppBar( |           appBar: AppBar( | ||||||
|             title: Text('Anime Search'), |             title: Text( | ||||||
|  |               state.trackingType == TrackingMediumType.anime ? | ||||||
|  |                 'Anime Search' : | ||||||
|  |                 'Manga Search', | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|           body: Column( |           body: Column( | ||||||
|             children: [ |             children: [ | ||||||
| @ -60,7 +65,7 @@ class AnimeSearchPage extends StatelessWidget { | |||||||
|                       return InkWell( |                       return InkWell( | ||||||
|                         onTap: () { |                         onTap: () { | ||||||
|                           context.read<AnimeSearchBloc>().add( |                           context.read<AnimeSearchBloc>().add( | ||||||
|                             AnimeAddedEvent(item), |                             ResultTappedEvent(item), | ||||||
|                           ); |                           ); | ||||||
|                         }, |                         }, | ||||||
|                         child: ListItem( |                         child: ListItem( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user