feat(service): Add working database persistency
This commit is contained in:
parent
624c8bd78a
commit
432796d0c4
@ -4,16 +4,31 @@ import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
|||||||
import 'package:anitrack/src/ui/constants.dart';
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
import 'package:anitrack/src/ui/pages/anime_list.dart';
|
import 'package:anitrack/src/ui/pages/anime_list.dart';
|
||||||
import 'package:anitrack/src/ui/pages/anime_search.dart';
|
import 'package:anitrack/src/ui/pages/anime_search.dart';
|
||||||
|
import 'package:anitrack/src/service/database.dart';
|
||||||
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:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
final navKey = GlobalKey<NavigatorState>();
|
final navKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
// Initialize the widgets binding for sqflite
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
// Initialize the database
|
||||||
|
final database = DatabaseService();
|
||||||
|
await database.initialize();
|
||||||
|
|
||||||
|
// Register singletons
|
||||||
|
GetIt.I.registerSingleton<DatabaseService>(database);
|
||||||
GetIt.I.registerSingleton<AnimeListBloc>(AnimeListBloc());
|
GetIt.I.registerSingleton<AnimeListBloc>(AnimeListBloc());
|
||||||
GetIt.I.registerSingleton<AnimeSearchBloc>(AnimeSearchBloc());
|
GetIt.I.registerSingleton<AnimeSearchBloc>(AnimeSearchBloc());
|
||||||
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navKey));
|
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navKey));
|
||||||
|
|
||||||
|
// Load animes
|
||||||
|
GetIt.I.get<AnimeListBloc>().add(
|
||||||
|
AnimesLoadedEvent(),
|
||||||
|
);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
|
@ -1,50 +1,64 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'anime.freezed.dart';
|
||||||
|
part 'anime.g.dart';
|
||||||
|
|
||||||
/// The watch state of an anime
|
/// The watch state of an anime
|
||||||
enum AnimeTrackingState {
|
enum AnimeTrackingState {
|
||||||
watching,
|
watching, // 0
|
||||||
completed,
|
completed, // 1
|
||||||
planToWatch,
|
planToWatch, // 2
|
||||||
dropped,
|
dropped, // 3
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AnimeTrackStateExtension on AnimeTrackingState {
|
||||||
|
int toInteger() {
|
||||||
|
switch (this) {
|
||||||
|
case AnimeTrackingState.watching: return 0;
|
||||||
|
case AnimeTrackingState.completed: return 1;
|
||||||
|
case AnimeTrackingState.planToWatch: return 2;
|
||||||
|
case AnimeTrackingState.dropped: return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimeTrackingStateConverter implements JsonConverter<AnimeTrackingState, int> {
|
||||||
|
const AnimeTrackingStateConverter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
AnimeTrackingState fromJson(int json) {
|
||||||
|
switch (json) {
|
||||||
|
case 0: return AnimeTrackingState.watching;
|
||||||
|
case 1: return AnimeTrackingState.completed;
|
||||||
|
case 2: return AnimeTrackingState.planToWatch;
|
||||||
|
case 3: return AnimeTrackingState.dropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimeTrackingState.planToWatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int toJson(AnimeTrackingState state) => state.toInteger();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data about a tracked anime
|
/// Data about a tracked anime
|
||||||
class AnimeTrackingData {
|
@freezed
|
||||||
const AnimeTrackingData(
|
class AnimeTrackingData with _$AnimeTrackingData{
|
||||||
this.id,
|
factory AnimeTrackingData(
|
||||||
this.state,
|
/// The ID of the anime
|
||||||
this.title,
|
String id,
|
||||||
this.episodesWatched,
|
/// The state of the anime
|
||||||
this.episodesTotal,
|
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||||
this.thumbnailUrl,
|
/// The title of the anime
|
||||||
);
|
String title,
|
||||||
|
/// Episodes in total.
|
||||||
/// The ID of the anime
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
/// The state of the anime
|
|
||||||
final AnimeTrackingState state;
|
|
||||||
|
|
||||||
/// The title of the anime
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
/// Episodes in total.
|
|
||||||
final int? episodesTotal;
|
|
||||||
|
|
||||||
/// Episodes watched.
|
|
||||||
final int episodesWatched;
|
|
||||||
|
|
||||||
/// URL to the thumbnail/cover art for the anime.
|
|
||||||
final String thumbnailUrl;
|
|
||||||
|
|
||||||
AnimeTrackingData copyWith(
|
|
||||||
int episodesWatched,
|
int episodesWatched,
|
||||||
) {
|
/// Episodes watched.
|
||||||
return AnimeTrackingData(
|
int? episodesTotal,
|
||||||
id,
|
/// URL to the thumbnail/cover art for the anime.
|
||||||
state,
|
String thumbnailUrl,
|
||||||
title,
|
) = _AnimeTrackingData;
|
||||||
episodesWatched,
|
|
||||||
episodesTotal,
|
/// JSON
|
||||||
thumbnailUrl,
|
factory AnimeTrackingData.fromJson(Map<String, dynamic> json) => _$AnimeTrackingDataFromJson(json);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
295
lib/src/data/anime.freezed.dart
Normal file
295
lib/src/data/anime.freezed.dart
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
// 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 'anime.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');
|
||||||
|
|
||||||
|
AnimeTrackingData _$AnimeTrackingDataFromJson(Map<String, dynamic> json) {
|
||||||
|
return _AnimeTrackingData.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$AnimeTrackingData {
|
||||||
|
/// The ID of the anime
|
||||||
|
String get id => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// The state of the anime
|
||||||
|
@AnimeTrackingStateConverter()
|
||||||
|
AnimeTrackingState get state => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// The title of the anime
|
||||||
|
String get title => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Episodes in total.
|
||||||
|
int get episodesWatched => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Episodes watched.
|
||||||
|
int? get episodesTotal => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// URL to the thumbnail/cover art for the anime.
|
||||||
|
String get thumbnailUrl => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$AnimeTrackingDataCopyWith<AnimeTrackingData> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $AnimeTrackingDataCopyWith<$Res> {
|
||||||
|
factory $AnimeTrackingDataCopyWith(
|
||||||
|
AnimeTrackingData value, $Res Function(AnimeTrackingData) then) =
|
||||||
|
_$AnimeTrackingDataCopyWithImpl<$Res>;
|
||||||
|
$Res call(
|
||||||
|
{String id,
|
||||||
|
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||||
|
String title,
|
||||||
|
int episodesWatched,
|
||||||
|
int? episodesTotal,
|
||||||
|
String thumbnailUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$AnimeTrackingDataCopyWithImpl<$Res>
|
||||||
|
implements $AnimeTrackingDataCopyWith<$Res> {
|
||||||
|
_$AnimeTrackingDataCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
final AnimeTrackingData _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function(AnimeTrackingData) _then;
|
||||||
|
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? state = freezed,
|
||||||
|
Object? title = freezed,
|
||||||
|
Object? episodesWatched = freezed,
|
||||||
|
Object? episodesTotal = 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 AnimeTrackingState,
|
||||||
|
title: title == freezed
|
||||||
|
? _value.title
|
||||||
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
episodesWatched: episodesWatched == freezed
|
||||||
|
? _value.episodesWatched
|
||||||
|
: episodesWatched // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
episodesTotal: episodesTotal == freezed
|
||||||
|
? _value.episodesTotal
|
||||||
|
: episodesTotal // 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 _$$_AnimeTrackingDataCopyWith<$Res>
|
||||||
|
implements $AnimeTrackingDataCopyWith<$Res> {
|
||||||
|
factory _$$_AnimeTrackingDataCopyWith(_$_AnimeTrackingData value,
|
||||||
|
$Res Function(_$_AnimeTrackingData) then) =
|
||||||
|
__$$_AnimeTrackingDataCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
$Res call(
|
||||||
|
{String id,
|
||||||
|
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||||
|
String title,
|
||||||
|
int episodesWatched,
|
||||||
|
int? episodesTotal,
|
||||||
|
String thumbnailUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_AnimeTrackingDataCopyWithImpl<$Res>
|
||||||
|
extends _$AnimeTrackingDataCopyWithImpl<$Res>
|
||||||
|
implements _$$_AnimeTrackingDataCopyWith<$Res> {
|
||||||
|
__$$_AnimeTrackingDataCopyWithImpl(
|
||||||
|
_$_AnimeTrackingData _value, $Res Function(_$_AnimeTrackingData) _then)
|
||||||
|
: super(_value, (v) => _then(v as _$_AnimeTrackingData));
|
||||||
|
|
||||||
|
@override
|
||||||
|
_$_AnimeTrackingData get _value => super._value as _$_AnimeTrackingData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? state = freezed,
|
||||||
|
Object? title = freezed,
|
||||||
|
Object? episodesWatched = freezed,
|
||||||
|
Object? episodesTotal = freezed,
|
||||||
|
Object? thumbnailUrl = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$_AnimeTrackingData(
|
||||||
|
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 AnimeTrackingState,
|
||||||
|
title == freezed
|
||||||
|
? _value.title
|
||||||
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
episodesWatched == freezed
|
||||||
|
? _value.episodesWatched
|
||||||
|
: episodesWatched // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
episodesTotal == freezed
|
||||||
|
? _value.episodesTotal
|
||||||
|
: episodesTotal // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
thumbnailUrl == freezed
|
||||||
|
? _value.thumbnailUrl
|
||||||
|
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$_AnimeTrackingData implements _AnimeTrackingData {
|
||||||
|
_$_AnimeTrackingData(this.id, @AnimeTrackingStateConverter() this.state,
|
||||||
|
this.title, this.episodesWatched, this.episodesTotal, this.thumbnailUrl);
|
||||||
|
|
||||||
|
factory _$_AnimeTrackingData.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$_AnimeTrackingDataFromJson(json);
|
||||||
|
|
||||||
|
/// The ID of the anime
|
||||||
|
@override
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
/// The state of the anime
|
||||||
|
@override
|
||||||
|
@AnimeTrackingStateConverter()
|
||||||
|
final AnimeTrackingState state;
|
||||||
|
|
||||||
|
/// The title of the anime
|
||||||
|
@override
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
/// Episodes in total.
|
||||||
|
@override
|
||||||
|
final int episodesWatched;
|
||||||
|
|
||||||
|
/// Episodes watched.
|
||||||
|
@override
|
||||||
|
final int? episodesTotal;
|
||||||
|
|
||||||
|
/// URL to the thumbnail/cover art for the anime.
|
||||||
|
@override
|
||||||
|
final String thumbnailUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AnimeTrackingData(id: $id, state: $state, title: $title, episodesWatched: $episodesWatched, episodesTotal: $episodesTotal, thumbnailUrl: $thumbnailUrl)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_AnimeTrackingData &&
|
||||||
|
const DeepCollectionEquality().equals(other.id, id) &&
|
||||||
|
const DeepCollectionEquality().equals(other.state, state) &&
|
||||||
|
const DeepCollectionEquality().equals(other.title, title) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.episodesWatched, episodesWatched) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.episodesTotal, episodesTotal) &&
|
||||||
|
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(episodesWatched),
|
||||||
|
const DeepCollectionEquality().hash(episodesTotal),
|
||||||
|
const DeepCollectionEquality().hash(thumbnailUrl));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
_$$_AnimeTrackingDataCopyWith<_$_AnimeTrackingData> get copyWith =>
|
||||||
|
__$$_AnimeTrackingDataCopyWithImpl<_$_AnimeTrackingData>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$_AnimeTrackingDataToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _AnimeTrackingData implements AnimeTrackingData {
|
||||||
|
factory _AnimeTrackingData(
|
||||||
|
final String id,
|
||||||
|
@AnimeTrackingStateConverter() final AnimeTrackingState state,
|
||||||
|
final String title,
|
||||||
|
final int episodesWatched,
|
||||||
|
final int? episodesTotal,
|
||||||
|
final String thumbnailUrl) = _$_AnimeTrackingData;
|
||||||
|
|
||||||
|
factory _AnimeTrackingData.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$_AnimeTrackingData.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// The ID of the anime
|
||||||
|
String get id;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// The state of the anime
|
||||||
|
@AnimeTrackingStateConverter()
|
||||||
|
AnimeTrackingState get state;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// The title of the anime
|
||||||
|
String get title;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Episodes in total.
|
||||||
|
int get episodesWatched;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Episodes watched.
|
||||||
|
int? get episodesTotal;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// URL to the thumbnail/cover art for the anime.
|
||||||
|
String get thumbnailUrl;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_AnimeTrackingDataCopyWith<_$_AnimeTrackingData> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
28
lib/src/data/anime.g.dart
Normal file
28
lib/src/data/anime.g.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'anime.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$_AnimeTrackingData _$$_AnimeTrackingDataFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_AnimeTrackingData(
|
||||||
|
json['id'] as String,
|
||||||
|
const AnimeTrackingStateConverter().fromJson(json['state'] as int),
|
||||||
|
json['title'] as String,
|
||||||
|
json['episodesWatched'] as int,
|
||||||
|
json['episodesTotal'] as int?,
|
||||||
|
json['thumbnailUrl'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_AnimeTrackingDataToJson(
|
||||||
|
_$_AnimeTrackingData instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'state': const AnimeTrackingStateConverter().toJson(instance.state),
|
||||||
|
'title': instance.title,
|
||||||
|
'episodesWatched': instance.episodesWatched,
|
||||||
|
'episodesTotal': instance.episodesTotal,
|
||||||
|
'thumbnailUrl': instance.thumbnailUrl,
|
||||||
|
};
|
55
lib/src/service/database.dart
Normal file
55
lib/src/service/database.dart
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import 'package:anitrack/src/data/anime.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
const animeTable = 'Anime';
|
||||||
|
|
||||||
|
Future<void> _createDatabase(Database db, int version) async {
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $animeTable(
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
state INTEGER NOT NULL,
|
||||||
|
episodesTotal INTEGER,
|
||||||
|
episodesWatched INTEGER NOT NULL,
|
||||||
|
thumbnailUrl TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseService {
|
||||||
|
late final Database _db;
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
_db = await openDatabase(
|
||||||
|
'anitrack.db',
|
||||||
|
version: 1,
|
||||||
|
onCreate: _createDatabase,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<AnimeTrackingData>> loadAnimes() async {
|
||||||
|
final animes = await _db.query(animeTable);
|
||||||
|
|
||||||
|
return animes
|
||||||
|
.cast<Map<String, dynamic>>()
|
||||||
|
.map((Map<String, dynamic> anime) => AnimeTrackingData.fromJson(anime))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addAnime(AnimeTrackingData data) async {
|
||||||
|
await _db.insert(
|
||||||
|
animeTable,
|
||||||
|
data.toJson(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateAnime(AnimeTrackingData data) async {
|
||||||
|
await _db.update(
|
||||||
|
animeTable,
|
||||||
|
data.toJson(),
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [data.id],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:anitrack/src/data/anime.dart';
|
import 'package:anitrack/src/data/anime.dart';
|
||||||
|
import 'package:anitrack/src/service/database.dart';
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
part 'anime_list_state.dart';
|
part 'anime_list_state.dart';
|
||||||
part 'anime_list_event.dart';
|
part 'anime_list_event.dart';
|
||||||
@ -12,10 +14,13 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
on<AnimeAddedEvent>(_onAnimeAdded);
|
on<AnimeAddedEvent>(_onAnimeAdded);
|
||||||
on<AnimeEpisodeIncrementedEvent>(_onIncremented);
|
on<AnimeEpisodeIncrementedEvent>(_onIncremented);
|
||||||
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
||||||
|
on<AnimesLoadedEvent>(_onAnimesLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
||||||
|
// Add the anime to the database
|
||||||
|
await GetIt.I.get<DatabaseService>().addAnime(event.data);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
animes: List.from([
|
animes: List.from([
|
||||||
@ -34,16 +39,18 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
if (anime.episodesTotal != null && anime.episodesWatched + 1 > anime.episodesTotal!) return;
|
if (anime.episodesTotal != null && anime.episodesWatched + 1 > anime.episodesTotal!) return;
|
||||||
|
|
||||||
final newList = List<AnimeTrackingData>.from(state.animes);
|
final newList = List<AnimeTrackingData>.from(state.animes);
|
||||||
newList[index] = anime.copyWith(
|
final newAnime = anime.copyWith(
|
||||||
anime.episodesWatched + 1,
|
episodesWatched: anime.episodesWatched + 1,
|
||||||
);
|
);
|
||||||
|
newList[index] = newAnime;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
animes: newList,
|
animes: newList,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
print('${event.id} incremented');
|
|
||||||
|
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
|
Future<void> _onDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
|
||||||
@ -54,15 +61,25 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
if (anime.episodesWatched - 1 < 0) return;
|
if (anime.episodesWatched - 1 < 0) return;
|
||||||
|
|
||||||
final newList = List<AnimeTrackingData>.from(state.animes);
|
final newList = List<AnimeTrackingData>.from(state.animes);
|
||||||
newList[index] = anime.copyWith(
|
final newAnime = anime.copyWith(
|
||||||
anime.episodesWatched - 1,
|
episodesWatched: anime.episodesWatched - 1,
|
||||||
);
|
);
|
||||||
|
newList[index] = newAnime;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
animes: newList,
|
animes: newList,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
print('${event.id} decremented');
|
|
||||||
|
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onAnimesLoaded(AnimesLoadedEvent event, Emitter<AnimeListState> emit) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
animes: await GetIt.I.get<DatabaseService>().loadAnimes(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,3 +22,6 @@ class AnimeAddedEvent extends AnimeListEvent {
|
|||||||
/// The anime to add.
|
/// The anime to add.
|
||||||
final AnimeTrackingData data;
|
final AnimeTrackingData data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Triggered when animes are to be loaded from the database
|
||||||
|
class AnimesLoadedEvent extends AnimeListEvent {}
|
||||||
|
@ -79,20 +79,27 @@ class AnimeSearchPage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Align(
|
||||||
item.title,
|
alignment: Alignment.centerLeft,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
child: Text(
|
||||||
maxLines: 2,
|
item.title,
|
||||||
softWrap: true,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Text(
|
Align(
|
||||||
item.description,
|
alignment: Alignment.centerLeft,
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
child: Text(
|
||||||
maxLines: 4,
|
item.description,
|
||||||
softWrap: true,
|
textAlign: TextAlign.justify,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
maxLines: 4,
|
||||||
|
softWrap: true,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
41
pubspec.lock
41
pubspec.lock
@ -292,12 +292,19 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.5"
|
version: "0.6.5"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.6.0"
|
||||||
|
json_serializable:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: json_serializable
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.2"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -415,6 +422,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.7"
|
version: "1.2.7"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -422,6 +436,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4+1"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2+2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -457,6 +485,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0+3"
|
version: "2.0.0+3"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -515,4 +550,4 @@ packages:
|
|||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.4 <3.0.0"
|
dart: ">=2.18.4 <3.0.0"
|
||||||
flutter: ">=1.16.0"
|
flutter: ">=3.3.0"
|
||||||
|
@ -16,7 +16,8 @@ dependencies:
|
|||||||
freezed_annotation: 2.1.0
|
freezed_annotation: 2.1.0
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
jikan_api: ^2.0.0
|
jikan_api: ^2.0.0
|
||||||
|
json_annotation: 4.6.0
|
||||||
|
sqflite: ^2.2.4+1
|
||||||
swipeable_tile: ^2.0.0+3
|
swipeable_tile: ^2.0.0+3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
@ -25,6 +26,7 @@ dev_dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
freezed: ^2.1.0+1
|
freezed: ^2.1.0+1
|
||||||
|
json_serializable: ^6.3.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
Loading…
Reference in New Issue
Block a user