feat(ui): Implement a simple details screen
This commit is contained in:
@@ -23,6 +23,8 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
on<MangaFilterChangedEvent>(_onMangasFiltered);
|
||||
on<MangaChapterIncrementedEvent>(_onMangaIncremented);
|
||||
on<MangaChapterDecrementedEvent>(_onMangaDecremented);
|
||||
on<AnimeUpdatedEvent>(_onAnimeUpdated);
|
||||
on<MangaUpdatedEvent>(_onMangaUpdated);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
||||
@@ -173,4 +175,36 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateManga(newManga);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeUpdated(AnimeUpdatedEvent event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: List.from(
|
||||
state.animes.map((anime) {
|
||||
if (anime.id == event.anime.id) {
|
||||
return event.anime;
|
||||
}
|
||||
|
||||
return anime;
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onMangaUpdated(MangaUpdatedEvent event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
mangas: List.from(
|
||||
state.mangas.map((manga) {
|
||||
if (manga.id == event.manga.id) {
|
||||
return event.manga;
|
||||
}
|
||||
|
||||
return manga;
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ class AnimeTrackingTypeChanged extends AnimeListEvent {
|
||||
final TrackingMediumType type;
|
||||
}
|
||||
|
||||
class AnimeUpdatedEvent extends AnimeListEvent {
|
||||
AnimeUpdatedEvent(this.anime);
|
||||
|
||||
final AnimeTrackingData anime;
|
||||
}
|
||||
|
||||
class MangaAddedEvent extends AnimeListEvent {
|
||||
MangaAddedEvent(this.data);
|
||||
|
||||
@@ -70,3 +76,9 @@ class MangaChapterDecrementedEvent extends AnimeListEvent {
|
||||
/// The ID of the anime
|
||||
final String id;
|
||||
}
|
||||
|
||||
class MangaUpdatedEvent extends AnimeListEvent {
|
||||
MangaUpdatedEvent(this.manga);
|
||||
|
||||
final MangaTrackingData manga;
|
||||
}
|
||||
|
||||
84
lib/src/ui/bloc/details_bloc.dart
Normal file
84
lib/src/ui/bloc/details_bloc.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
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/ui/bloc/anime_list_bloc.dart';
|
||||
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
||||
import 'package:anitrack/src/ui/constants.dart';
|
||||
import 'package:anitrack/src/service/database.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
part 'details_state.dart';
|
||||
part 'details_event.dart';
|
||||
part 'details_bloc.freezed.dart';
|
||||
|
||||
class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
||||
DetailsBloc() : super(DetailsState()) {
|
||||
on<AnimeDetailsRequestedEvent>(_onAnimeRequested);
|
||||
on<MangaDetailsRequestedEvent>(_onMangaRequested);
|
||||
on<DetailsUpdatedEvent>(_onDetailsUpdated);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeRequested(AnimeDetailsRequestedEvent event, Emitter<DetailsState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
trackingType: TrackingMediumType.anime,
|
||||
data: event.anime,
|
||||
),
|
||||
);
|
||||
|
||||
GetIt.I.get<NavigationBloc>().add(
|
||||
PushedNamedEvent(
|
||||
NavigationDestination(
|
||||
detailsRoute,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onMangaRequested(MangaDetailsRequestedEvent event, Emitter<DetailsState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
trackingType: TrackingMediumType.manga,
|
||||
data: event.manga,
|
||||
),
|
||||
);
|
||||
|
||||
GetIt.I.get<NavigationBloc>().add(
|
||||
PushedNamedEvent(
|
||||
NavigationDestination(
|
||||
detailsRoute,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onDetailsUpdated(DetailsUpdatedEvent event, Emitter<DetailsState> emit) async {
|
||||
if (state.trackingType == TrackingMediumType.anime) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: event.data,
|
||||
),
|
||||
);
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateAnime(event.data as AnimeTrackingData);
|
||||
|
||||
GetIt.I.get<AnimeListBloc>().add(
|
||||
AnimeUpdatedEvent(event.data as AnimeTrackingData),
|
||||
);
|
||||
} else {
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: event.data,
|
||||
),
|
||||
);
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateManga(event.data as MangaTrackingData);
|
||||
|
||||
GetIt.I.get<AnimeListBloc>().add(
|
||||
MangaUpdatedEvent(event.data as MangaTrackingData),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
lib/src/ui/bloc/details_bloc.freezed.dart
Normal file
151
lib/src/ui/bloc/details_bloc.freezed.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
// 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 'details_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 _$DetailsState {
|
||||
dynamic get data => throw _privateConstructorUsedError;
|
||||
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$DetailsStateCopyWith<DetailsState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $DetailsStateCopyWith<$Res> {
|
||||
factory $DetailsStateCopyWith(
|
||||
DetailsState value, $Res Function(DetailsState) then) =
|
||||
_$DetailsStateCopyWithImpl<$Res>;
|
||||
$Res call({dynamic data, TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$DetailsStateCopyWithImpl<$Res> implements $DetailsStateCopyWith<$Res> {
|
||||
_$DetailsStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
final DetailsState _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(DetailsState) _then;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? data = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
data: data == freezed
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMediumType,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_DetailsStateCopyWith<$Res>
|
||||
implements $DetailsStateCopyWith<$Res> {
|
||||
factory _$$_DetailsStateCopyWith(
|
||||
_$_DetailsState value, $Res Function(_$_DetailsState) then) =
|
||||
__$$_DetailsStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({dynamic data, TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_DetailsStateCopyWithImpl<$Res>
|
||||
extends _$DetailsStateCopyWithImpl<$Res>
|
||||
implements _$$_DetailsStateCopyWith<$Res> {
|
||||
__$$_DetailsStateCopyWithImpl(
|
||||
_$_DetailsState _value, $Res Function(_$_DetailsState) _then)
|
||||
: super(_value, (v) => _then(v as _$_DetailsState));
|
||||
|
||||
@override
|
||||
_$_DetailsState get _value => super._value as _$_DetailsState;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? data = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_$_DetailsState(
|
||||
data: data == freezed
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMediumType,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$_DetailsState implements _DetailsState {
|
||||
_$_DetailsState({this.data, this.trackingType = TrackingMediumType.anime});
|
||||
|
||||
@override
|
||||
final dynamic data;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TrackingMediumType trackingType;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DetailsState(data: $data, trackingType: $trackingType)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_DetailsState &&
|
||||
const DeepCollectionEquality().equals(other.data, data) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.trackingType, trackingType));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(data),
|
||||
const DeepCollectionEquality().hash(trackingType));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$$_DetailsStateCopyWith<_$_DetailsState> get copyWith =>
|
||||
__$$_DetailsStateCopyWithImpl<_$_DetailsState>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _DetailsState implements DetailsState {
|
||||
factory _DetailsState(
|
||||
{final dynamic data,
|
||||
final TrackingMediumType trackingType}) = _$_DetailsState;
|
||||
|
||||
@override
|
||||
dynamic get data;
|
||||
@override
|
||||
TrackingMediumType get trackingType;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_DetailsStateCopyWith<_$_DetailsState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
23
lib/src/ui/bloc/details_event.dart
Normal file
23
lib/src/ui/bloc/details_event.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
part of 'details_bloc.dart';
|
||||
|
||||
abstract class DetailsEvent {}
|
||||
|
||||
class AnimeDetailsRequestedEvent extends DetailsEvent {
|
||||
AnimeDetailsRequestedEvent(this.anime);
|
||||
|
||||
/// The anime to show details about
|
||||
final AnimeTrackingData anime;
|
||||
}
|
||||
|
||||
class MangaDetailsRequestedEvent extends DetailsEvent {
|
||||
MangaDetailsRequestedEvent(this.manga);
|
||||
|
||||
/// The manga to show details about
|
||||
final MangaTrackingData manga;
|
||||
}
|
||||
|
||||
class DetailsUpdatedEvent extends DetailsEvent {
|
||||
DetailsUpdatedEvent(this.data);
|
||||
|
||||
final dynamic data;
|
||||
}
|
||||
9
lib/src/ui/bloc/details_state.dart
Normal file
9
lib/src/ui/bloc/details_state.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
part of 'details_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class DetailsState with _$DetailsState {
|
||||
factory DetailsState({
|
||||
dynamic data,
|
||||
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
||||
}) = _DetailsState;
|
||||
}
|
||||
Reference in New Issue
Block a user