feat: Implement a basic anime calendar
This commit is contained in:
@@ -270,6 +270,8 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
animes: _getFilteredAnime(),
|
||||
),
|
||||
);
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateAnime(event.anime);
|
||||
}
|
||||
|
||||
Future<void> _onMangaUpdated(
|
||||
|
||||
@@ -43,9 +43,12 @@ class AnimeTrackingTypeChanged extends AnimeListEvent {
|
||||
}
|
||||
|
||||
class AnimeUpdatedEvent extends AnimeListEvent {
|
||||
AnimeUpdatedEvent(this.anime);
|
||||
AnimeUpdatedEvent(this.anime, {this.commit = false});
|
||||
|
||||
final AnimeTrackingData anime;
|
||||
|
||||
/// Commit the new anime data to the database.
|
||||
final bool commit;
|
||||
}
|
||||
|
||||
class AnimeRemovedEvent extends AnimeListEvent {
|
||||
|
||||
@@ -82,6 +82,8 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
||||
anime.episodes,
|
||||
anime.imageUrl,
|
||||
anime.synopsis ?? '',
|
||||
anime.airing,
|
||||
anime.broadcast?.split(' ').first,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -104,6 +106,9 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
||||
manga.chapters,
|
||||
manga.imageUrl,
|
||||
manga.synopsis ?? '',
|
||||
// TODO(Unknown): Implement for Manga
|
||||
false,
|
||||
null,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -126,6 +131,8 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
||||
0,
|
||||
event.result.total,
|
||||
event.result.thumbnailUrl,
|
||||
event.result.isAiring,
|
||||
event.result.broadcastDay,
|
||||
),
|
||||
)
|
||||
: list.MangaAddedEvent(
|
||||
|
||||
59
lib/src/ui/bloc/calendar_bloc.dart
Normal file
59
lib/src/ui/bloc/calendar_bloc.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:jikan_api/jikan_api.dart';
|
||||
|
||||
part 'calendar_state.dart';
|
||||
part 'calendar_bloc.freezed.dart';
|
||||
part 'calendar_event.dart';
|
||||
|
||||
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
CalendarBloc() : super(CalendarState(false, 0, 0)) {
|
||||
on<RefreshPerformedEvent>(_onRefreshPerformed);
|
||||
}
|
||||
|
||||
Future<void> _onRefreshPerformed(
|
||||
RefreshPerformedEvent event,
|
||||
Emitter<CalendarState> emit,
|
||||
) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
refreshing: true,
|
||||
refreshingCount: 0,
|
||||
refreshingTotal: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final al = GetIt.I.get<AnimeListBloc>();
|
||||
final animes = al.state.animes.where((anime) => anime.airing);
|
||||
emit(
|
||||
state.copyWith(
|
||||
refreshing: true,
|
||||
refreshingCount: 0,
|
||||
refreshingTotal: animes.length,
|
||||
),
|
||||
);
|
||||
|
||||
for (final anime in animes) {
|
||||
emit(state.copyWith(refreshingCount: state.refreshingCount + 1));
|
||||
|
||||
final apiData = await Jikan().getAnime(int.parse(anime.id));
|
||||
if (!apiData.airing) {
|
||||
al.add(
|
||||
AnimeUpdatedEvent(
|
||||
anime.copyWith(airing: false, broadcastDay: null),
|
||||
commit: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Prevent hammering Jikan
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
emit(state.copyWith(refreshing: false));
|
||||
}
|
||||
}
|
||||
169
lib/src/ui/bloc/calendar_bloc.freezed.dart
Normal file
169
lib/src/ui/bloc/calendar_bloc.freezed.dart
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 'calendar_bloc.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');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CalendarState {
|
||||
bool get refreshing => throw _privateConstructorUsedError;
|
||||
int get refreshingCount => throw _privateConstructorUsedError;
|
||||
int get refreshingTotal => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$CalendarStateCopyWith<CalendarState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CalendarStateCopyWith<$Res> {
|
||||
factory $CalendarStateCopyWith(
|
||||
CalendarState value, $Res Function(CalendarState) then) =
|
||||
_$CalendarStateCopyWithImpl<$Res>;
|
||||
$Res call({bool refreshing, int refreshingCount, int refreshingTotal});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CalendarStateCopyWithImpl<$Res>
|
||||
implements $CalendarStateCopyWith<$Res> {
|
||||
_$CalendarStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
final CalendarState _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(CalendarState) _then;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? refreshing = freezed,
|
||||
Object? refreshingCount = freezed,
|
||||
Object? refreshingTotal = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
refreshing: refreshing == freezed
|
||||
? _value.refreshing
|
||||
: refreshing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
refreshingCount: refreshingCount == freezed
|
||||
? _value.refreshingCount
|
||||
: refreshingCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
refreshingTotal: refreshingTotal == freezed
|
||||
? _value.refreshingTotal
|
||||
: refreshingTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_CalendarStateCopyWith<$Res>
|
||||
implements $CalendarStateCopyWith<$Res> {
|
||||
factory _$$_CalendarStateCopyWith(
|
||||
_$_CalendarState value, $Res Function(_$_CalendarState) then) =
|
||||
__$$_CalendarStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({bool refreshing, int refreshingCount, int refreshingTotal});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_CalendarStateCopyWithImpl<$Res>
|
||||
extends _$CalendarStateCopyWithImpl<$Res>
|
||||
implements _$$_CalendarStateCopyWith<$Res> {
|
||||
__$$_CalendarStateCopyWithImpl(
|
||||
_$_CalendarState _value, $Res Function(_$_CalendarState) _then)
|
||||
: super(_value, (v) => _then(v as _$_CalendarState));
|
||||
|
||||
@override
|
||||
_$_CalendarState get _value => super._value as _$_CalendarState;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? refreshing = freezed,
|
||||
Object? refreshingCount = freezed,
|
||||
Object? refreshingTotal = freezed,
|
||||
}) {
|
||||
return _then(_$_CalendarState(
|
||||
refreshing == freezed
|
||||
? _value.refreshing
|
||||
: refreshing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
refreshingCount == freezed
|
||||
? _value.refreshingCount
|
||||
: refreshingCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
refreshingTotal == freezed
|
||||
? _value.refreshingTotal
|
||||
: refreshingTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$_CalendarState implements _CalendarState {
|
||||
_$_CalendarState(this.refreshing, this.refreshingCount, this.refreshingTotal);
|
||||
|
||||
@override
|
||||
final bool refreshing;
|
||||
@override
|
||||
final int refreshingCount;
|
||||
@override
|
||||
final int refreshingTotal;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CalendarState(refreshing: $refreshing, refreshingCount: $refreshingCount, refreshingTotal: $refreshingTotal)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_CalendarState &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.refreshing, refreshing) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.refreshingCount, refreshingCount) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.refreshingTotal, refreshingTotal));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(refreshing),
|
||||
const DeepCollectionEquality().hash(refreshingCount),
|
||||
const DeepCollectionEquality().hash(refreshingTotal));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$$_CalendarStateCopyWith<_$_CalendarState> get copyWith =>
|
||||
__$$_CalendarStateCopyWithImpl<_$_CalendarState>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _CalendarState implements CalendarState {
|
||||
factory _CalendarState(final bool refreshing, final int refreshingCount,
|
||||
final int refreshingTotal) = _$_CalendarState;
|
||||
|
||||
@override
|
||||
bool get refreshing;
|
||||
@override
|
||||
int get refreshingCount;
|
||||
@override
|
||||
int get refreshingTotal;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_CalendarStateCopyWith<_$_CalendarState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
6
lib/src/ui/bloc/calendar_event.dart
Normal file
6
lib/src/ui/bloc/calendar_event.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
part of 'calendar_bloc.dart';
|
||||
|
||||
abstract class CalendarEvent {}
|
||||
|
||||
/// Triggered by the UI when the user wants to refresh the airing anime list.
|
||||
class RefreshPerformedEvent extends CalendarEvent {}
|
||||
10
lib/src/ui/bloc/calendar_state.dart
Normal file
10
lib/src/ui/bloc/calendar_state.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
part of 'calendar_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class CalendarState with _$CalendarState {
|
||||
factory CalendarState(
|
||||
bool refreshing,
|
||||
int refreshingCount,
|
||||
int refreshingTotal,
|
||||
) = _CalendarState;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
||||
emit(
|
||||
state.copyWith(
|
||||
trackingType: TrackingMediumType.anime,
|
||||
heroImagePrefix: event.heroImagePrefix,
|
||||
data: event.anime,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
/// @nodoc
|
||||
mixin _$DetailsState {
|
||||
TrackingMedium? get data => throw _privateConstructorUsedError;
|
||||
String? get heroImagePrefix => throw _privateConstructorUsedError;
|
||||
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@@ -29,7 +30,10 @@ abstract class $DetailsStateCopyWith<$Res> {
|
||||
factory $DetailsStateCopyWith(
|
||||
DetailsState value, $Res Function(DetailsState) then) =
|
||||
_$DetailsStateCopyWithImpl<$Res>;
|
||||
$Res call({TrackingMedium? data, TrackingMediumType trackingType});
|
||||
$Res call(
|
||||
{TrackingMedium? data,
|
||||
String? heroImagePrefix,
|
||||
TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -43,6 +47,7 @@ class _$DetailsStateCopyWithImpl<$Res> implements $DetailsStateCopyWith<$Res> {
|
||||
@override
|
||||
$Res call({
|
||||
Object? data = freezed,
|
||||
Object? heroImagePrefix = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -50,6 +55,10 @@ class _$DetailsStateCopyWithImpl<$Res> implements $DetailsStateCopyWith<$Res> {
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMedium?,
|
||||
heroImagePrefix: heroImagePrefix == freezed
|
||||
? _value.heroImagePrefix
|
||||
: heroImagePrefix // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
@@ -65,7 +74,10 @@ abstract class _$$_DetailsStateCopyWith<$Res>
|
||||
_$_DetailsState value, $Res Function(_$_DetailsState) then) =
|
||||
__$$_DetailsStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({TrackingMedium? data, TrackingMediumType trackingType});
|
||||
$Res call(
|
||||
{TrackingMedium? data,
|
||||
String? heroImagePrefix,
|
||||
TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -82,6 +94,7 @@ class __$$_DetailsStateCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? data = freezed,
|
||||
Object? heroImagePrefix = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_$_DetailsState(
|
||||
@@ -89,6 +102,10 @@ class __$$_DetailsStateCopyWithImpl<$Res>
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMedium?,
|
||||
heroImagePrefix: heroImagePrefix == freezed
|
||||
? _value.heroImagePrefix
|
||||
: heroImagePrefix // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
@@ -100,17 +117,22 @@ class __$$_DetailsStateCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
|
||||
class _$_DetailsState implements _DetailsState {
|
||||
_$_DetailsState({this.data, this.trackingType = TrackingMediumType.anime});
|
||||
_$_DetailsState(
|
||||
{this.data,
|
||||
this.heroImagePrefix,
|
||||
this.trackingType = TrackingMediumType.anime});
|
||||
|
||||
@override
|
||||
final TrackingMedium? data;
|
||||
@override
|
||||
final String? heroImagePrefix;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TrackingMediumType trackingType;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DetailsState(data: $data, trackingType: $trackingType)';
|
||||
return 'DetailsState(data: $data, heroImagePrefix: $heroImagePrefix, trackingType: $trackingType)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -119,6 +141,8 @@ class _$_DetailsState implements _DetailsState {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_DetailsState &&
|
||||
const DeepCollectionEquality().equals(other.data, data) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.heroImagePrefix, heroImagePrefix) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.trackingType, trackingType));
|
||||
}
|
||||
@@ -127,6 +151,7 @@ class _$_DetailsState implements _DetailsState {
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(data),
|
||||
const DeepCollectionEquality().hash(heroImagePrefix),
|
||||
const DeepCollectionEquality().hash(trackingType));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@@ -138,11 +163,14 @@ class _$_DetailsState implements _DetailsState {
|
||||
abstract class _DetailsState implements DetailsState {
|
||||
factory _DetailsState(
|
||||
{final TrackingMedium? data,
|
||||
final String? heroImagePrefix,
|
||||
final TrackingMediumType trackingType}) = _$_DetailsState;
|
||||
|
||||
@override
|
||||
TrackingMedium? get data;
|
||||
@override
|
||||
String? get heroImagePrefix;
|
||||
@override
|
||||
TrackingMediumType get trackingType;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
@@ -3,10 +3,15 @@ part of 'details_bloc.dart';
|
||||
abstract class DetailsEvent {}
|
||||
|
||||
class AnimeDetailsRequestedEvent extends DetailsEvent {
|
||||
AnimeDetailsRequestedEvent(this.anime);
|
||||
AnimeDetailsRequestedEvent(
|
||||
this.anime, {
|
||||
this.heroImagePrefix,
|
||||
});
|
||||
|
||||
/// The anime to show details about
|
||||
final AnimeTrackingData anime;
|
||||
|
||||
final String? heroImagePrefix;
|
||||
}
|
||||
|
||||
class MangaDetailsRequestedEvent extends DetailsEvent {
|
||||
|
||||
@@ -4,6 +4,7 @@ part of 'details_bloc.dart';
|
||||
class DetailsState with _$DetailsState {
|
||||
factory DetailsState({
|
||||
TrackingMedium? data,
|
||||
String? heroImagePrefix,
|
||||
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
||||
}) = _DetailsState;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,9 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
|
||||
// 0 means that MAL does not know
|
||||
totalEpisodes == 0 ? null : totalEpisodes,
|
||||
data.imageUrl,
|
||||
// NOTE: When the calendar gets refreshed, this should also get cleared
|
||||
true,
|
||||
null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user