feat(meta): Implement removing tracking items
This commit is contained in:
parent
b240870ebf
commit
8df287b6eb
@ -17,6 +17,9 @@ enum MediumTrackingState {
|
||||
|
||||
/// Interface for the Anime and Manga data classes
|
||||
abstract class TrackingMedium {
|
||||
/// The ID of the medium
|
||||
final String id = '';
|
||||
|
||||
/// The title of the medium
|
||||
final String title = '';
|
||||
|
||||
|
@ -76,6 +76,14 @@ class DatabaseService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteAnime(String id) async {
|
||||
await _db.delete(
|
||||
animeTable,
|
||||
where: 'id = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> addManga(MangaTrackingData data) async {
|
||||
await _db.insert(
|
||||
mangaTable,
|
||||
@ -91,4 +99,12 @@ class DatabaseService {
|
||||
whereArgs: [data.id],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteManga(String id) async {
|
||||
await _db.delete(
|
||||
mangaTable,
|
||||
where: 'id = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
on<MangaChapterDecrementedEvent>(_onMangaDecremented);
|
||||
on<AnimeUpdatedEvent>(_onAnimeUpdated);
|
||||
on<MangaUpdatedEvent>(_onMangaUpdated);
|
||||
on<AnimeRemovedEvent>(_onAnimeRemoved);
|
||||
on<MangaRemovedEvent>(_onMangaRemoved);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
||||
@ -206,4 +208,30 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeRemoved(AnimeRemovedEvent event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: List.from(
|
||||
state.animes.where((anime) => anime.id != event.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Update the database
|
||||
await GetIt.I.get<DatabaseService>().deleteAnime(event.id);
|
||||
}
|
||||
|
||||
Future<void> _onMangaRemoved(MangaRemovedEvent event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
mangas: List.from(
|
||||
state.mangas.where((manga) => manga.id != event.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Update the database
|
||||
await GetIt.I.get<DatabaseService>().deleteManga(event.id);
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,13 @@ class AnimeUpdatedEvent extends AnimeListEvent {
|
||||
final AnimeTrackingData anime;
|
||||
}
|
||||
|
||||
class AnimeRemovedEvent extends AnimeListEvent {
|
||||
AnimeRemovedEvent(this.id);
|
||||
|
||||
/// The ID of the anime to be removed from the list.
|
||||
final String id;
|
||||
}
|
||||
|
||||
class MangaAddedEvent extends AnimeListEvent {
|
||||
MangaAddedEvent(this.data);
|
||||
|
||||
@ -82,3 +89,10 @@ class MangaUpdatedEvent extends AnimeListEvent {
|
||||
|
||||
final MangaTrackingData manga;
|
||||
}
|
||||
|
||||
class MangaRemovedEvent extends AnimeListEvent {
|
||||
MangaRemovedEvent(this.id);
|
||||
|
||||
/// The ID of the manga to be removed from the list.
|
||||
final String id;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
||||
on<AnimeDetailsRequestedEvent>(_onAnimeRequested);
|
||||
on<MangaDetailsRequestedEvent>(_onMangaRequested);
|
||||
on<DetailsUpdatedEvent>(_onDetailsUpdated);
|
||||
on<ItemRemovedEvent>(_onItemRemoved);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeRequested(AnimeDetailsRequestedEvent event, Emitter<DetailsState> emit) async {
|
||||
@ -77,4 +78,26 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onItemRemoved(ItemRemovedEvent event, Emitter<DetailsState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: null,
|
||||
),
|
||||
);
|
||||
|
||||
/// Remove the item from the database
|
||||
final bloc = GetIt.I.get<AnimeListBloc>();
|
||||
switch (event.trackingType) {
|
||||
case TrackingMediumType.anime:
|
||||
bloc.add(AnimeRemovedEvent(event.id));
|
||||
break;
|
||||
case TrackingMediumType.manga:
|
||||
bloc.add(MangaRemovedEvent(event.id));
|
||||
break;
|
||||
}
|
||||
|
||||
// Navigate back
|
||||
GetIt.I.get<NavigationBloc>().add(PoppedRouteEvent());
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,15 @@ class MangaDetailsRequestedEvent extends DetailsEvent {
|
||||
class DetailsUpdatedEvent extends DetailsEvent {
|
||||
DetailsUpdatedEvent(this.data);
|
||||
|
||||
final dynamic data;
|
||||
final TrackingMedium data;
|
||||
}
|
||||
|
||||
class ItemRemovedEvent extends DetailsEvent {
|
||||
ItemRemovedEvent(this.id, this.trackingType);
|
||||
|
||||
/// The ID of the item to be removed
|
||||
final String id;
|
||||
|
||||
/// The type of medium of the item
|
||||
final TrackingMediumType trackingType;
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
import 'dart:io';
|
||||
import 'package:anitrack/licenses.g.dart';
|
||||
import 'package:anitrack/src/ui/constants.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
AboutPage({
|
||||
const AboutPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
||||
builder: (_) => AboutPage(),
|
||||
builder: (_) => const AboutPage(),
|
||||
settings: const RouteSettings(
|
||||
name: aboutRoute,
|
||||
),
|
||||
@ -20,7 +19,7 @@ class AboutPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('About'),
|
||||
title: const Text('About'),
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemCount: ossLicenses.length + 1,
|
||||
@ -29,7 +28,6 @@ class AboutPage extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
@ -44,7 +42,7 @@ class AboutPage extends StatelessWidget {
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Text('Source')
|
||||
child: const Text('Source'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -101,7 +101,7 @@ class AnimeListPage extends StatelessWidget {
|
||||
drawer: Drawer(
|
||||
child: ListView(
|
||||
children: [
|
||||
DrawerHeader(
|
||||
const DrawerHeader(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xffcf4aff),
|
||||
),
|
||||
@ -115,8 +115,8 @@ class AnimeListPage extends StatelessWidget {
|
||||
),
|
||||
|
||||
ListTile(
|
||||
leading: Icon(Icons.info),
|
||||
title: Text('About'),
|
||||
leading: const Icon(Icons.info),
|
||||
title: const Text('About'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(aboutRoute);
|
||||
},
|
||||
|
@ -60,6 +60,48 @@ class DetailsPage extends StatelessWidget {
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('Remove "${state.data!.title}"?'),
|
||||
content: Text('Are you sure you want to remove "${state.data!.title}" from the list?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
),
|
||||
child: const Text('Remove'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result != true) return;
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
context.read<DetailsBloc>().add(
|
||||
ItemRemovedEvent(
|
||||
state.data!.id,
|
||||
state.trackingType,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Icon(Icons.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -79,7 +121,7 @@ class DetailsPage extends StatelessWidget {
|
||||
if (state.trackingType == TrackingMediumType.anime) {
|
||||
context.read<DetailsBloc>().add(
|
||||
DetailsUpdatedEvent(
|
||||
(state.data as AnimeTrackingData).copyWith(
|
||||
(state.data! as AnimeTrackingData).copyWith(
|
||||
state: newState,
|
||||
),
|
||||
),
|
||||
@ -87,7 +129,7 @@ class DetailsPage extends StatelessWidget {
|
||||
} else if (state.trackingType == TrackingMediumType.manga) {
|
||||
context.read<DetailsBloc>().add(
|
||||
DetailsUpdatedEvent(
|
||||
(state.data as MangaTrackingData).copyWith(
|
||||
(state.data! as MangaTrackingData).copyWith(
|
||||
state: newState,
|
||||
),
|
||||
),
|
||||
@ -127,7 +169,7 @@ class DetailsPage extends StatelessWidget {
|
||||
onChanged: (value) {
|
||||
switch (state.trackingType) {
|
||||
case TrackingMediumType.anime:
|
||||
final data = state.data as AnimeTrackingData;
|
||||
final data = state.data! as AnimeTrackingData;
|
||||
context.read<DetailsBloc>().add(
|
||||
DetailsUpdatedEvent(
|
||||
data.copyWith(
|
||||
@ -137,7 +179,7 @@ class DetailsPage extends StatelessWidget {
|
||||
);
|
||||
break;
|
||||
case TrackingMediumType.manga:
|
||||
final data = state.data as MangaTrackingData;
|
||||
final data = state.data! as MangaTrackingData;
|
||||
context.read<DetailsBloc>().add(
|
||||
DetailsUpdatedEvent(
|
||||
data.copyWith(
|
||||
@ -149,8 +191,8 @@ class DetailsPage extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
initialValue: state.trackingType == TrackingMediumType.anime ?
|
||||
(state.data as AnimeTrackingData).episodesWatched :
|
||||
(state.data as MangaTrackingData).chaptersRead,
|
||||
(state.data! as AnimeTrackingData).episodesWatched :
|
||||
(state.data! as MangaTrackingData).chaptersRead,
|
||||
),
|
||||
),
|
||||
|
||||
@ -162,7 +204,7 @@ class DetailsPage extends StatelessWidget {
|
||||
child: IntegerInput(
|
||||
labelText: 'Volumes owned',
|
||||
onChanged: (value) {
|
||||
final data = state.data as MangaTrackingData;
|
||||
final data = state.data! as MangaTrackingData;
|
||||
context.read<DetailsBloc>().add(
|
||||
DetailsUpdatedEvent(
|
||||
data.copyWith(
|
||||
@ -171,7 +213,7 @@ class DetailsPage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
initialValue: (GetIt.I.get<DetailsBloc>().state.data as MangaTrackingData).volumesOwned,
|
||||
initialValue: (GetIt.I.get<DetailsBloc>().state.data! as MangaTrackingData).volumesOwned,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -19,9 +19,9 @@ dependencies:
|
||||
get_it: ^7.2.0
|
||||
jikan_api: ^2.0.0
|
||||
json_annotation: 4.6.0
|
||||
url_launcher: ^6.1.8
|
||||
sqflite: ^2.2.4+1
|
||||
swipeable_tile: ^2.0.0+3
|
||||
url_launcher: ^6.1.8
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.1.11
|
||||
|
Loading…
Reference in New Issue
Block a user