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