mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-21 03:32:06 +00:00
Code refactor
This commit is contained in:
parent
13b1fea0a3
commit
dc26b51a70
10 changed files with 743 additions and 835 deletions
|
|
@ -18,15 +18,13 @@ import 'package:mangayomi/models/track_preference.dart';
|
|||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_widget.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/myanimelist.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
import 'package:mangayomi/utils/utils.dart';
|
||||
|
|
@ -39,7 +37,6 @@ import 'package:mangayomi/modules/manga/detail/widgets/chapter_sort_list_tile_wi
|
|||
import 'package:mangayomi/modules/manga/download/providers/download_provider.dart';
|
||||
import 'package:mangayomi/modules/widgets/error_text.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:numberpicker/numberpicker.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
|
@ -1314,7 +1311,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
Theme.of(context).scaffoldBackgroundColor,
|
||||
elevation: 0),
|
||||
onPressed: () {
|
||||
_trackingDialog(entries);
|
||||
_trackingDraggableMenu(entries);
|
||||
},
|
||||
child: StreamBuilder(
|
||||
stream: isar.tracks
|
||||
|
|
@ -1536,8 +1533,6 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
TextEditingController(text: widget.manga!.name!);
|
||||
TextEditingController? description =
|
||||
TextEditingController(text: widget.manga!.description!);
|
||||
// TextEditingController? tag;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
|
|
@ -1613,7 +1608,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
});
|
||||
}
|
||||
|
||||
_trackingDialog(List<TrackPreference>? entries) {
|
||||
_trackingDraggableMenu(List<TrackPreference>? entries) {
|
||||
DraggableMenu.open(
|
||||
context,
|
||||
DraggableMenu(
|
||||
|
|
@ -1624,758 +1619,60 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: entries!.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return StreamBuilder(
|
||||
stream: isar.tracks
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.syncIdEqualTo(entries[index].syncId)
|
||||
.mangaIdEqualTo(widget.manga!.id!)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
List<Track>? trackRes =
|
||||
snapshot.hasData ? snapshot.data : [];
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: entries!.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return StreamBuilder(
|
||||
stream: isar.tracks
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.syncIdEqualTo(entries[index].syncId)
|
||||
.mangaIdEqualTo(widget.manga!.id!)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
List<Track>? trackRes =
|
||||
snapshot.hasData ? snapshot.data : [];
|
||||
|
||||
return trackRes!.isNotEmpty
|
||||
? Container(
|
||||
decoration: BoxDecoration(border: Border.all()),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/tracker_mal.webp",
|
||||
height: 30,
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color:
|
||||
primaryColor(context),
|
||||
width: 0.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () async {
|
||||
final trackSearch =
|
||||
await trackersSearchraggableMenu(
|
||||
syncId: entries[index]
|
||||
.syncId!,
|
||||
query: trackRes
|
||||
.first.title!)
|
||||
as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(
|
||||
track: null)
|
||||
.notifier)
|
||||
.setTrackSearch(
|
||||
trackSearch,
|
||||
widget.manga!.id!,
|
||||
entries[index].syncId!);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
trackRes.first.title!,
|
||||
style: TextStyle(
|
||||
color: secondaryColor(
|
||||
context),fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(tracksProvider(
|
||||
syncId: entries[
|
||||
index]
|
||||
.syncId!)
|
||||
.notifier)
|
||||
.deleteTrackManga(
|
||||
trackRes.first);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.cancel_outlined))
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: primaryColor(
|
||||
context),
|
||||
width: 0.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Status",
|
||||
),
|
||||
content: SizedBox(
|
||||
width: mediaWidth(
|
||||
context, 0.8),
|
||||
child:
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes
|
||||
.first)
|
||||
.notifier)
|
||||
.getStatusList()
|
||||
.length,
|
||||
itemBuilder:
|
||||
(context,
|
||||
index) {
|
||||
final status = ref
|
||||
.read(trackStateProvider(
|
||||
track:
|
||||
trackRes.first)
|
||||
.notifier)
|
||||
.getStatusList()[index];
|
||||
return RadioListTile(
|
||||
dense: true,
|
||||
contentPadding:
|
||||
const EdgeInsets
|
||||
.all(0),
|
||||
value: status,
|
||||
groupValue:
|
||||
trackRes
|
||||
.first
|
||||
.status,
|
||||
onChanged:
|
||||
(value) {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes.first..status = status)
|
||||
.notifier)
|
||||
.updateItem();
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
title: Text(
|
||||
getTrackStatus(
|
||||
status)),
|
||||
);
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
() async {
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
color: primaryColor(
|
||||
context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Text(getTrackStatus(
|
||||
trackRes.first.status))),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: primaryColor(
|
||||
context),
|
||||
width: 0.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () {
|
||||
int currentIntValue = trackRes
|
||||
.first.lastChapterRead!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Chapters",
|
||||
),
|
||||
content: StatefulBuilder(
|
||||
builder: (context,
|
||||
setState) =>
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.center,
|
||||
children: [
|
||||
NumberPicker(
|
||||
value:
|
||||
currentIntValue,
|
||||
minValue: 0,
|
||||
maxValue: trackRes
|
||||
.first
|
||||
.totalChapter !=
|
||||
0
|
||||
? trackRes
|
||||
.first
|
||||
.totalChapter!
|
||||
: 10000,
|
||||
step: 1,
|
||||
haptics: true,
|
||||
onChanged: (value) =>
|
||||
setState(() =>
|
||||
currentIntValue =
|
||||
value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
() async {
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
color: primaryColor(
|
||||
context)),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed:
|
||||
() async {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes.first..lastChapterRead = currentIntValue)
|
||||
.notifier)
|
||||
.updateItem();
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
"OK",
|
||||
style: TextStyle(
|
||||
color: primaryColor(
|
||||
context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Text(trackRes
|
||||
.first.totalChapter !=
|
||||
0
|
||||
? "${trackRes.first.lastChapterRead}/${trackRes.first.totalChapter}"
|
||||
: "${trackRes.first.lastChapterRead == 0 ? "Not Started" : trackRes.first.lastChapterRead}")),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: primaryColor(
|
||||
context),
|
||||
width: 0.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () {
|
||||
int currentIntValue =
|
||||
trackRes.first.score!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Score",
|
||||
),
|
||||
content: StatefulBuilder(
|
||||
builder: (context,
|
||||
setState) =>
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.center,
|
||||
children: [
|
||||
NumberPicker(
|
||||
value:
|
||||
currentIntValue,
|
||||
minValue: 0,
|
||||
maxValue: 10,
|
||||
step: 1,
|
||||
haptics: true,
|
||||
onChanged: (value) =>
|
||||
setState(() =>
|
||||
currentIntValue =
|
||||
value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
() async {
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
color: primaryColor(
|
||||
context)),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed:
|
||||
() async {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes.first..score = currentIntValue)
|
||||
.notifier)
|
||||
.updateItem();
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
"OK",
|
||||
style: TextStyle(
|
||||
color: primaryColor(
|
||||
context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
trackRes.first.score != 0
|
||||
? trackRes.first.score
|
||||
.toString()
|
||||
: "Score")),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
color:
|
||||
primaryColor(context),
|
||||
width: 0.2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () async {
|
||||
DateTime? newDate =
|
||||
await showDatePicker(
|
||||
helpText: 'Start date',
|
||||
locale: const Locale(
|
||||
"fr", "FR"),
|
||||
context: context,
|
||||
initialDate:
|
||||
DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100));
|
||||
if (newDate == null) return;
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes.first
|
||||
..startedReadingDate =
|
||||
newDate
|
||||
.millisecondsSinceEpoch)
|
||||
.notifier)
|
||||
.updateItem();
|
||||
},
|
||||
child: Text(trackRes.first
|
||||
.startedReadingDate !=
|
||||
null &&
|
||||
trackRes.first
|
||||
.startedReadingDate! >
|
||||
DateTime(1970)
|
||||
.millisecondsSinceEpoch
|
||||
? dateFormat(
|
||||
trackRes.first
|
||||
.startedReadingDate
|
||||
.toString(),
|
||||
ref: ref,
|
||||
useRelativeTimesTamps: false,
|
||||
context: context)
|
||||
: "Start date")),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: primaryColor(
|
||||
context),
|
||||
width: 0.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
0))),
|
||||
onPressed: () async {
|
||||
DateTime? newDate =
|
||||
await showDatePicker(
|
||||
helpText: 'Finish date',
|
||||
locale: const Locale(
|
||||
"fr", "FR"),
|
||||
context: context,
|
||||
initialDate:
|
||||
DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100));
|
||||
if (newDate == null) return;
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: trackRes.first
|
||||
..startedReadingDate =
|
||||
newDate
|
||||
.millisecondsSinceEpoch)
|
||||
.notifier)
|
||||
.updateItem();
|
||||
},
|
||||
child: Text(trackRes.first
|
||||
.finishedReadingDate !=
|
||||
null &&
|
||||
trackRes.first.finishedReadingDate! >
|
||||
DateTime(1970)
|
||||
.millisecondsSinceEpoch
|
||||
? dateFormat(
|
||||
trackRes.first
|
||||
.finishedReadingDate
|
||||
.toString(),
|
||||
ref: ref,
|
||||
useRelativeTimesTamps: false,
|
||||
context: context)
|
||||
: "Finish date")),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: TrackListile(
|
||||
onTap: () async {
|
||||
final trackSearch =
|
||||
await trackersSearchraggableMenu(
|
||||
syncId: entries[index].syncId!,
|
||||
query: widget.manga!.name!)
|
||||
as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(track: null)
|
||||
.notifier)
|
||||
.setTrackSearch(
|
||||
trackSearch,
|
||||
widget.manga!.id!,
|
||||
entries[index].syncId!);
|
||||
}
|
||||
},
|
||||
id: entries[index].syncId!,
|
||||
entries: const []);
|
||||
});
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider();
|
||||
},
|
||||
return trackRes!.isNotEmpty
|
||||
? TrackerWidget(
|
||||
mangaId: widget.manga!.id!,
|
||||
trackPreference: entries[index],
|
||||
trackRes: trackRes[index],
|
||||
)
|
||||
: TrackListile(
|
||||
onTap: () async {
|
||||
final trackSearch =
|
||||
await trackersSearchraggableMenu(
|
||||
context,
|
||||
track: Track(
|
||||
status: TrackStatus.planToRead,
|
||||
syncId: entries[index].syncId!,
|
||||
title: widget.manga!.name!),
|
||||
) as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(track: null)
|
||||
.notifier)
|
||||
.setTrackSearch(
|
||||
trackSearch,
|
||||
widget.manga!.id!,
|
||||
entries[index].syncId!);
|
||||
}
|
||||
},
|
||||
id: entries[index].syncId!,
|
||||
entries: const []);
|
||||
});
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
trackersSearchraggableMenu(
|
||||
{required int syncId, required String query}) async {
|
||||
return await DraggableMenu.open(
|
||||
context,
|
||||
DraggableMenu(
|
||||
blockMenuClosing: true,
|
||||
ui: SoftModernDraggableMenu(
|
||||
radius: 20,
|
||||
barItem: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20))),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.clear)),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
maxHeight: mediaHeight(context, 0.9),
|
||||
child: TrackerWidgetSearch(
|
||||
query: query,
|
||||
syncId: syncId,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerWidgetSearch extends ConsumerStatefulWidget {
|
||||
final int syncId;
|
||||
final String query;
|
||||
const TrackerWidgetSearch(
|
||||
{required this.syncId, required this.query, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<TrackerWidgetSearch> createState() =>
|
||||
_TrackerWidgetSearchState();
|
||||
}
|
||||
|
||||
class _TrackerWidgetSearchState extends ConsumerState<TrackerWidgetSearch> {
|
||||
@override
|
||||
initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
late List<TrackSearch> tracks = [];
|
||||
_init() async {
|
||||
await Future.delayed(const Duration(microseconds: 100));
|
||||
tracks = await ref
|
||||
.read(myAnimeListProvider(syncId: widget.syncId).notifier)
|
||||
.search(widget.query);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
late String query = widget.query;
|
||||
late final _controller = TextEditingController(text: query);
|
||||
bool _isLoading = true;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: _isLoading
|
||||
? const ProgressCenter()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: SizedBox(
|
||||
height: mediaHeight(context, 0.8),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
itemCount: tracks.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 5),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, tracks[index]);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Material(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.transparent,
|
||||
clipBehavior:
|
||||
Clip.antiAliasWithSaveLayer,
|
||||
child: Ink.image(
|
||||
height: 120,
|
||||
width: 80,
|
||||
fit: BoxFit.cover,
|
||||
image: CachedNetworkImageProvider(
|
||||
tracks[index].coverUrl!),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: mediaWidth(context, 0.6),
|
||||
child: Text(
|
||||
tracks[index].title!,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"Type : ",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
tracks[index].publishingType!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"Status : ",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
tracks[index].publishingStatus!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Text(
|
||||
tracks[index].summary!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider();
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: _controller,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (d) {
|
||||
setState(() {
|
||||
query = d;
|
||||
});
|
||||
},
|
||||
onFieldSubmitted: (d) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
tracks = await ref
|
||||
.read(myAnimeListProvider(syncId: widget.syncId)
|
||||
.notifier)
|
||||
.search(d);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Colors.transparent,
|
||||
suffixIcon: query.isEmpty
|
||||
? null
|
||||
: IconButton(
|
||||
onPressed: () {
|
||||
_controller.clear();
|
||||
},
|
||||
icon: const Icon(Icons.clear)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ class TrackState extends _$TrackState {
|
|||
return track!;
|
||||
}
|
||||
|
||||
Future updateItem() async {
|
||||
Future updateManga() async {
|
||||
final updateTrack = await ref
|
||||
.read(myAnimeListProvider(syncId: 1).notifier)
|
||||
.updateItem(track!);
|
||||
.updateManga(track!);
|
||||
|
||||
ref
|
||||
.read(tracksProvider(syncId: track!.syncId!).notifier)
|
||||
|
|
@ -24,7 +24,6 @@ class TrackState extends _$TrackState {
|
|||
|
||||
Future setTrackSearch(
|
||||
TrackSearch trackSearch, int mangaId, int syncId) async {
|
||||
|
||||
final track = Track(
|
||||
mangaId: mangaId,
|
||||
score: 0,
|
||||
|
|
@ -36,13 +35,13 @@ class TrackState extends _$TrackState {
|
|||
status: TrackStatus.planToRead,
|
||||
startedReadingDate: 0,
|
||||
finishedReadingDate: 0);
|
||||
final findTrack = await ref
|
||||
final findManga = await ref
|
||||
.read(myAnimeListProvider(syncId: 1).notifier)
|
||||
.findListItem(track);
|
||||
.findManga(track);
|
||||
|
||||
ref
|
||||
.read(tracksProvider(syncId: syncId).notifier)
|
||||
.updateTrackManga(findTrack);
|
||||
.updateTrackManga(findManga);
|
||||
}
|
||||
|
||||
List<TrackStatus> getStatusList() {
|
||||
|
|
@ -60,4 +59,24 @@ class TrackState extends _$TrackState {
|
|||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Future<Track?> findManga() async {
|
||||
Track? findManga;
|
||||
if (track!.syncId == 1) {
|
||||
findManga = await ref
|
||||
.read(myAnimeListProvider(syncId: 1).notifier)
|
||||
.findManga(track!);
|
||||
}
|
||||
return findManga;
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>?> search(String query) async {
|
||||
List<TrackSearch>? tracks;
|
||||
if (track!.syncId == 1) {
|
||||
tracks = await ref
|
||||
.read(myAnimeListProvider(syncId: track!.syncId!).notifier)
|
||||
.search(query);
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'track_state_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$trackStateHash() => r'3e7b916624f8035766d9a6408812bf1cc1247915';
|
||||
String _$trackStateHash() => r'493c91f74d44d2fcffd75bdbf3e434580fd5ac03';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
244
lib/modules/manga/detail/widgets/tracker_search_widget.dart
Normal file
244
lib/modules/manga/detail/widgets/tracker_search_widget.dart
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:draggable_menu/draggable_menu.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
|
||||
class TrackerWidgetSearch extends ConsumerStatefulWidget {
|
||||
final Track track;
|
||||
const TrackerWidgetSearch({required this.track, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<TrackerWidgetSearch> createState() =>
|
||||
_TrackerWidgetSearchState();
|
||||
}
|
||||
|
||||
class _TrackerWidgetSearchState extends ConsumerState<TrackerWidgetSearch> {
|
||||
@override
|
||||
initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
late String query = widget.track.title!.trim();
|
||||
late List<TrackSearch>? tracks = [];
|
||||
_init() async {
|
||||
await Future.delayed(const Duration(microseconds: 100));
|
||||
tracks = await ref
|
||||
.read(trackStateProvider(track: widget.track).notifier)
|
||||
.search(query);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
late final _controller = TextEditingController(text: query);
|
||||
bool _isLoading = true;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: _isLoading
|
||||
? const ProgressCenter()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: SizedBox(
|
||||
height: mediaHeight(context, 0.8),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
itemCount: tracks!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 5),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, tracks![index]);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Material(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.transparent,
|
||||
clipBehavior:
|
||||
Clip.antiAliasWithSaveLayer,
|
||||
child: Ink.image(
|
||||
height: 120,
|
||||
width: 80,
|
||||
fit: BoxFit.cover,
|
||||
image: CachedNetworkImageProvider(
|
||||
tracks![index].coverUrl!),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: mediaWidth(context, 0.6),
|
||||
child: Text(
|
||||
tracks![index].title!,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"Type : ",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
tracks![index].publishingType!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"Status : ",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
tracks![index]
|
||||
.publishingStatus!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Text(
|
||||
tracks![index].summary!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider();
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: _controller,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: (d) {
|
||||
setState(() {
|
||||
query = d;
|
||||
});
|
||||
},
|
||||
onFieldSubmitted: (d) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
tracks = await ref
|
||||
.read(trackStateProvider(track: widget.track)
|
||||
.notifier)
|
||||
.search(d.trim());
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Colors.transparent,
|
||||
suffixIcon: query.isEmpty
|
||||
? null
|
||||
: IconButton(
|
||||
onPressed: () {
|
||||
_controller.clear();
|
||||
},
|
||||
icon: const Icon(Icons.clear)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor(context)))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
trackersSearchraggableMenu(BuildContext context, {required Track track}) async {
|
||||
return await DraggableMenu.open(
|
||||
context,
|
||||
DraggableMenu(
|
||||
blockMenuClosing: true,
|
||||
ui: ClassicDraggableMenu(
|
||||
radius: 20,
|
||||
barItem: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20))),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.clear)),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
maxHeight: mediaHeight(context, 0.9),
|
||||
child: TrackerWidgetSearch(
|
||||
track: track,
|
||||
)));
|
||||
}
|
||||
386
lib/modules/manga/detail/widgets/tracker_widget.dart
Normal file
386
lib/modules/manga/detail/widgets/tracker_widget.dart
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
import 'package:numberpicker/numberpicker.dart';
|
||||
|
||||
class TrackerWidget extends ConsumerStatefulWidget {
|
||||
final Track trackRes;
|
||||
final int mangaId;
|
||||
final TrackPreference trackPreference;
|
||||
const TrackerWidget(
|
||||
{super.key,
|
||||
required this.trackPreference,
|
||||
required this.trackRes,
|
||||
required this.mangaId});
|
||||
|
||||
@override
|
||||
ConsumerState<TrackerWidget> createState() => _TrackerWidgetState();
|
||||
}
|
||||
|
||||
class _TrackerWidgetState extends ConsumerState<TrackerWidget> {
|
||||
@override
|
||||
initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_init() async {
|
||||
await Future.delayed(const Duration(microseconds: 100));
|
||||
final findManga = await ref
|
||||
.read(trackStateProvider(track: widget.trackRes).notifier)
|
||||
.findManga();
|
||||
|
||||
ref
|
||||
.read(tracksProvider(syncId: widget.trackPreference.syncId!).notifier)
|
||||
.updateTrackManga(findManga!);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
trackInfos(widget.trackPreference.syncId!).$1,
|
||||
height: 30,
|
||||
),
|
||||
Expanded(
|
||||
child: _elevatedButton(
|
||||
context,
|
||||
onPressed: () async {
|
||||
final trackSearch = await trackersSearchraggableMenu(context,
|
||||
track: widget.trackRes) as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(track: null).notifier)
|
||||
.setTrackSearch(trackSearch, widget.mangaId,
|
||||
widget.trackPreference.syncId!);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
widget.trackRes.title!,
|
||||
style: TextStyle(
|
||||
color: secondaryColor(context),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(tracksProvider(
|
||||
syncId: widget.trackPreference.syncId!)
|
||||
.notifier)
|
||||
.deleteTrackManga(widget.trackRes);
|
||||
},
|
||||
icon: const Icon(Icons.cancel_outlined))
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _elevatedButton(context, onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Status",
|
||||
),
|
||||
content: SizedBox(
|
||||
width: mediaWidth(context, 0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: ref
|
||||
.read(
|
||||
trackStateProvider(track: widget.trackRes)
|
||||
.notifier)
|
||||
.getStatusList()
|
||||
.length,
|
||||
itemBuilder: (context, index) {
|
||||
final status = ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes)
|
||||
.notifier)
|
||||
.getStatusList()[index];
|
||||
return RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: status,
|
||||
groupValue: widget.trackRes.status,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes
|
||||
..status = status)
|
||||
.notifier)
|
||||
.updateManga();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(getTrackStatus(status)),
|
||||
);
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style:
|
||||
TextStyle(color: primaryColor(context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}, text: getTrackStatus(widget.trackRes.status)),
|
||||
),
|
||||
Expanded(
|
||||
child: _elevatedButton(context, onPressed: () {
|
||||
int currentIntValue = widget.trackRes.lastChapterRead!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Chapters",
|
||||
),
|
||||
content: StatefulBuilder(
|
||||
builder: (context, setState) => SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
NumberPicker(
|
||||
value: currentIntValue,
|
||||
minValue: 0,
|
||||
maxValue: widget.trackRes.totalChapter != 0
|
||||
? widget.trackRes.totalChapter!
|
||||
: 10000,
|
||||
step: 1,
|
||||
haptics: true,
|
||||
onChanged: (value) =>
|
||||
setState(() => currentIntValue = value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style:
|
||||
TextStyle(color: primaryColor(context)),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes
|
||||
..lastChapterRead =
|
||||
currentIntValue)
|
||||
.notifier)
|
||||
.updateManga();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
"OK",
|
||||
style:
|
||||
TextStyle(color: primaryColor(context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
text: widget.trackRes.totalChapter != 0
|
||||
? "${widget.trackRes.lastChapterRead}/${widget.trackRes.totalChapter}"
|
||||
: "${widget.trackRes.lastChapterRead == 0 ? "Not Started" : widget.trackRes.lastChapterRead}"),
|
||||
),
|
||||
Expanded(
|
||||
child: _elevatedButton(context, onPressed: () {
|
||||
int currentIntValue = widget.trackRes.score!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
"Score",
|
||||
),
|
||||
content: StatefulBuilder(
|
||||
builder: (context, setState) => SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
NumberPicker(
|
||||
value: currentIntValue,
|
||||
minValue: 0,
|
||||
maxValue: 10,
|
||||
step: 1,
|
||||
haptics: true,
|
||||
onChanged: (value) =>
|
||||
setState(() => currentIntValue = value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style:
|
||||
TextStyle(color: primaryColor(context)),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes
|
||||
..score = currentIntValue)
|
||||
.notifier)
|
||||
.updateManga();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
"OK",
|
||||
style:
|
||||
TextStyle(color: primaryColor(context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
text: widget.trackRes.score != 0
|
||||
? widget.trackRes.score.toString()
|
||||
: "Score"),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _elevatedButton(context, onPressed: () async {
|
||||
DateTime? newDate = await showDatePicker(
|
||||
helpText: 'Start date',
|
||||
locale: const Locale("fr", "FR"),
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100));
|
||||
if (newDate == null) return;
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes
|
||||
..startedReadingDate =
|
||||
newDate.millisecondsSinceEpoch)
|
||||
.notifier)
|
||||
.updateManga();
|
||||
},
|
||||
text: widget.trackRes.startedReadingDate != null &&
|
||||
widget.trackRes.startedReadingDate! >
|
||||
DateTime(1970).millisecondsSinceEpoch
|
||||
? dateFormat(
|
||||
widget.trackRes.startedReadingDate.toString(),
|
||||
ref: ref,
|
||||
useRelativeTimesTamps: false,
|
||||
context: context)
|
||||
: "Start date"),
|
||||
),
|
||||
Expanded(
|
||||
child: _elevatedButton(context, onPressed: () async {
|
||||
DateTime? newDate = await showDatePicker(
|
||||
helpText: 'Finish date',
|
||||
locale: const Locale("fr", "FR"),
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100));
|
||||
if (newDate == null) return;
|
||||
ref
|
||||
.read(trackStateProvider(
|
||||
track: widget.trackRes
|
||||
..finishedReadingDate =
|
||||
newDate.millisecondsSinceEpoch)
|
||||
.notifier)
|
||||
.updateManga();
|
||||
},
|
||||
text: widget.trackRes.finishedReadingDate != null &&
|
||||
widget.trackRes.finishedReadingDate! >
|
||||
DateTime(1970).millisecondsSinceEpoch
|
||||
? dateFormat(
|
||||
widget.trackRes.finishedReadingDate.toString(),
|
||||
ref: ref,
|
||||
useRelativeTimesTamps: false,
|
||||
context: context)
|
||||
: "Finish date"),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _elevatedButton(BuildContext context,
|
||||
{required VoidCallback onPressed, String text = "", Widget? child}) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
elevation: 0,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.05, color: secondaryColor(context)),
|
||||
borderRadius: BorderRadius.circular(0))),
|
||||
onPressed: onPressed,
|
||||
child: child ??
|
||||
Text(
|
||||
text,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).textTheme.bodyMedium!.color),
|
||||
));
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ part of 'track_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$tracksHash() => r'11a1c74c458db7f5b790de1451d239662cec1ed3';
|
||||
String _$tracksHash() => r'65e6092128a8d24edcecb215287d4a774df1b180';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -2,16 +2,19 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
||||
class TrackListile extends ConsumerWidget {
|
||||
final VoidCallback onTap;
|
||||
final int id;
|
||||
final List<TrackPreference> entries;
|
||||
final Widget? trailing;
|
||||
const TrackListile(
|
||||
{super.key,
|
||||
required this.onTap,
|
||||
required this.id,
|
||||
required this.entries});
|
||||
required this.entries,
|
||||
this.trailing});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -21,17 +24,17 @@ class TrackListile extends ConsumerWidget {
|
|||
leading: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.asset(
|
||||
_track(id).$1,
|
||||
trackInfos(id).$1,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
trailing: isLogged
|
||||
? const Icon(
|
||||
Icons.check,
|
||||
size: 30,
|
||||
color: Colors.green,
|
||||
)
|
||||
: null,
|
||||
trailing: trailing ?? (isLogged
|
||||
? const Icon(
|
||||
Icons.check,
|
||||
size: 30,
|
||||
color: Colors.green,
|
||||
)
|
||||
: null),
|
||||
onTap: isLogged
|
||||
? () {
|
||||
showDialog(
|
||||
|
|
@ -39,7 +42,7 @@ class TrackListile extends ConsumerWidget {
|
|||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
"Log out from ${_track(id).$2}",
|
||||
"Log out from ${trackInfos(id).$2}",
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
|
|
@ -68,14 +71,8 @@ class TrackListile extends ConsumerWidget {
|
|||
});
|
||||
}
|
||||
: onTap,
|
||||
title: Text(_track(id).$2),
|
||||
title: Text(trackInfos(id).$2),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(String, String) _track(int id) {
|
||||
return switch (id) {
|
||||
1 => ("assets/tracker_mal.webp", "MyAnimeList"),
|
||||
_ => ("", ""),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return jsonDecode(response.body)['name'];
|
||||
}
|
||||
|
||||
Future<Track> findListItem(Track track) async {
|
||||
Future<Track> findManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
final uri = Uri.parse('$baseApiUrl/manga/${track.mediaId}')
|
||||
.replace(queryParameters: {
|
||||
|
|
@ -204,74 +204,33 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
final mJson = jsonDecode(response.body);
|
||||
track.totalChapter = mJson['num_chapters'] ?? 0;
|
||||
if (mJson['my_list_status'] != null) {
|
||||
track = parseMangaItem(mJson["my_list_status"], track);
|
||||
track = _parseMangaItem(mJson["my_list_status"], track);
|
||||
} else {
|
||||
track = await updateItem(track);
|
||||
track = await updateManga(track);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>> findListItems(String query,
|
||||
{int offset = 0}) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
final json = await getListPage(offset);
|
||||
final obj = json['data'] as List<dynamic>;
|
||||
List<int> mangaIds = obj
|
||||
.where((data) => data['node']['title']
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.contains(query.toLowerCase()))
|
||||
.map((data) => data['node']['id'] as int)
|
||||
.toList();
|
||||
List<TrackSearch> trackSearchResult = [];
|
||||
for (var mangaId in mangaIds) {
|
||||
final trackSearch = await getMangaDetails(mangaId, accessToken);
|
||||
trackSearchResult.add(trackSearch);
|
||||
}
|
||||
|
||||
if (json['paging']['next'] != null) {
|
||||
final newV =
|
||||
await findListItems(query, offset: offset + listPaginationAmount);
|
||||
trackSearchResult.addAll(newV);
|
||||
}
|
||||
return trackSearchResult;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getListPage(int offset) async {
|
||||
final urlBuilder =
|
||||
Uri.parse('$baseApiUrl/users/@me/mangalist').replace(queryParameters: {
|
||||
'fields': 'list_status{start_date,finish_date}',
|
||||
'limit': listPaginationAmount.toString(),
|
||||
});
|
||||
if (offset > 0) {
|
||||
urlBuilder.queryParameters['offset'] = offset.toString();
|
||||
}
|
||||
final url = urlBuilder.toString();
|
||||
final response =
|
||||
await http.get(Uri.parse(url), headers: {'X-MAL-CLIENT-ID': clientId});
|
||||
return jsonDecode(response.body);
|
||||
}
|
||||
|
||||
Track parseMangaItem(Map<String, dynamic> mJson, Track track) {
|
||||
Track _parseMangaItem(Map<String, dynamic> mJson, Track track) {
|
||||
bool isRereading = mJson["is_rereading"] ?? false;
|
||||
track.status = isRereading
|
||||
? TrackStatus.rereading
|
||||
: _getMALTrackStatus(mJson["status"]);
|
||||
track.lastChapterRead = int.parse(mJson["num_chapters_read"].toString());
|
||||
track.score = int.parse(mJson["score"].toString());
|
||||
track.startedReadingDate = parseDate(mJson["start_date"]);
|
||||
track.finishedReadingDate = parseDate(mJson["finish_date"]);
|
||||
track.startedReadingDate = _parseDate(mJson["start_date"]);
|
||||
track.finishedReadingDate = _parseDate(mJson["finish_date"]);
|
||||
return track;
|
||||
}
|
||||
|
||||
int? parseDate(String? isoDate) {
|
||||
int? _parseDate(String? isoDate) {
|
||||
if (isoDate == null) return null;
|
||||
|
||||
final date = DateFormat('yyyy-MM-dd', 'en_US').parse(isoDate);
|
||||
return date.millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
Future<Track> updateItem(Track track) async {
|
||||
Future<Track> updateManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
final formBody = {
|
||||
'status': (toMyAnimeListStatus(track.status) ?? 'reading').toString(),
|
||||
|
|
@ -289,6 +248,6 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
request.headers.addAll({'Authorization': 'Bearer $accessToken'});
|
||||
final response = await http.Client().send(request);
|
||||
final mJson = jsonDecode(await response.stream.bytesToString());
|
||||
return parseMangaItem(mJson, track);
|
||||
return _parseMangaItem(mJson, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ part of 'myanimelist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$myAnimeListHash() => r'd3ec65023fe7f920fad11afa4866072fb75ee257';
|
||||
String _$myAnimeListHash() => r'4fca14c944acc71eb514057708b8c60e28aa1744';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -40,3 +40,9 @@ String getTrackStatus(TrackStatus status) {
|
|||
};
|
||||
}
|
||||
|
||||
(String, String) trackInfos(int id) {
|
||||
return switch (id) {
|
||||
1 => ("assets/tracker_mal.webp", "MyAnimeList"),
|
||||
_ => ("", ""),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue