refactor & multiple action feature for chapter

This commit is contained in:
kodjodevf 2023-04-20 13:23:37 +01:00
parent bd6a088d42
commit 758fed9c53
11 changed files with 723 additions and 446 deletions

View file

@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:mangayomi/models/comick/search_manga_cimick.dart';
import 'package:mangayomi/services/get_popular_manga.dart';

View file

@ -6,7 +6,6 @@ import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/manga_history.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/date.dart';

View file

@ -5,19 +5,16 @@ import 'package:draggable_scrollbar/draggable_scrollbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/utils/utils.dart';
import 'package:mangayomi/views/manga/detail/manga_details_view.dart';
import 'package:mangayomi/views/manga/detail/providers/state_providers.dart';
import 'package:mangayomi/views/manga/detail/readmore.dart';
import 'package:mangayomi/views/manga/detail/widgets/chapter_listtile_widget.dart';
import 'package:mangayomi/views/manga/download/download_page_widget.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:mangayomi/views/manga/detail/widgets/chapter_list_tile_widget.dart';
import 'package:mangayomi/views/manga/download/providers/download_provider.dart';
class MangaDetailView extends ConsumerStatefulWidget {
final Function(bool) isExtended;
@ -58,7 +55,10 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
final chapterIndexList = ref.watch(chapterIndexListStateProvider);
final isLongPressed = ref.watch(isLongPressedStateProvider);
final reverse = ref.watch(reverseMangaStateProvider);
final chapter = ref.watch(chapterModelStateProvider);
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.forward) {
@ -73,57 +73,96 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: Size.fromHeight(AppBar().preferredSize.height),
child: Consumer(
builder: (context, ref, child) {
final isLongPressed = ref.watch(isLongPressedStateProvider);
final reverse = ref.watch(reverseMangaStateProvider);
return isLongPressed
? AppBar(
backgroundColor: generalColor(context),
leading: IconButton(
onPressed: () {
ref
.read(chapterIndexStateProvider.notifier)
.update(-1);
child: isLongPressed
? Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: AppBar(
title: Text(chapterIndexList.length.toString()),
backgroundColor: generalColor(context).withOpacity(0.2),
leading: IconButton(
onPressed: () {
ref
.read(chapterIndexListStateProvider.notifier)
.clear();
ref
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
},
icon: const Icon(Icons.clear)),
)
: AppBar(
title: ref.watch(offetProvider) > 200
? Text(
widget.modelManga!.name!,
style: const TextStyle(fontSize: 17),
)
: null,
backgroundColor: ref.watch(offetProvider) == 0.0
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
actions: [
// IconButton(
// splashRadius: 20,
// onPressed: () {},
// icon: Icon(Icons.download_outlined,
// color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {
ref
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
},
icon: const Icon(Icons.clear)),
actions: [
IconButton(
onPressed: () {
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(reverseMangaStateProvider.notifier)
.update(!reverse);
},
icon: Icon(
reverse
? Icons.arrow_downward_sharp
: Icons.arrow_upward_sharp,
color: Theme.of(context).hintColor)),
],
);
},
)),
.read(chapterIndexListStateProvider
.notifier)
.selectAll(i);
}
},
icon: const Icon(Icons.select_all)),
IconButton(
onPressed: () {
if (widget.modelManga!.chapters!.length ==
chapterIndexList.length) {
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(chapterIndexListStateProvider
.notifier)
.selectSome(i);
}
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
} else {
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(chapterIndexListStateProvider
.notifier)
.selectSome(i);
}
}
},
icon: const Icon(Icons.flip_to_back_rounded)),
],
),
)
: AppBar(
title: ref.watch(offetProvider) > 200
? Text(
widget.modelManga!.name!,
style: const TextStyle(fontSize: 17),
)
: null,
backgroundColor: ref.watch(offetProvider) == 0.0
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
actions: [
// IconButton(
// splashRadius: 20,
// onPressed: () {},
// icon: Icon(Icons.download_outlined,
// color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {
ref
.read(reverseMangaStateProvider.notifier)
.update(!reverse);
},
icon: Icon(
reverse
? Icons.arrow_downward_sharp
: Icons.arrow_upward_sharp,
color: Theme.of(context).hintColor)),
],
)),
body: Stack(
children: [
Positioned(
@ -150,186 +189,247 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
)
],
)),
SafeArea(child: Consumer(builder: (context, ref, child) {
final reverse = ref.watch(reverseMangaStateProvider);
return DraggableScrollbar(
heightScrollThumb: 48.0,
backgroundColor: generalColor(context),
scrollThumbBuilder: (backgroundColor, thumbAnimation,
labelAnimation, height,
{labelConstraints, labelText}) {
return FadeTransition(
opacity: thumbAnimation,
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(20)),
height: height,
width: 10.0,
),
);
},
scrollbarTimeToFade: const Duration(seconds: 2),
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0),
itemCount: widget.listLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
SafeArea(
child: DraggableScrollbar(
heightScrollThumb: 48.0,
backgroundColor: generalColor(context),
scrollThumbBuilder: (backgroundColor, thumbAnimation,
labelAnimation, height,
{labelConstraints, labelText}) {
return FadeTransition(
opacity: thumbAnimation,
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(20)),
height: height,
width: 8.0,
),
);
},
scrollbarTimeToFade: const Duration(seconds: 2),
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0),
itemCount: widget.listLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
int reverseIndex = widget
.modelManga!.chapters!.length -
widget.modelManga!.chapters!.reversed
.toList()
.indexOf(widget.modelManga!.chapters!.reversed
.toList()[finalIndex]) -
1;
int reverseIndex = widget
.modelManga!.chapters!.length -
widget.modelManga!.chapters!.reversed
.toList()
.indexOf(widget
.modelManga!.chapters!.reversed
.toList()[finalIndex]) -
1;
List<ModelChapters> chapters = reverse
? widget.modelManga!.chapters!.reversed.toList()
: widget.modelManga!.chapters!;
List<ModelChapters> chapters = reverse
? widget.modelManga!.chapters!.reversed.toList()
: widget.modelManga!.chapters!;
return ChapterListTileWidget(
chapters: chapters,
modelManga: widget.modelManga!,
reverse: reverse,
reverseIndex: reverseIndex,
finalIndex: finalIndex,
isLongPressed: isLongPressed);
}));
})),
return ChapterListTileWidget(
chapters: chapters,
modelManga: widget.modelManga!,
reverse: reverse,
reverseIndex: reverseIndex,
finalIndex: finalIndex,
isLongPressed: isLongPressed);
}))),
],
),
bottomNavigationBar: Consumer(
builder: (context, ref, child) {
final idx = ref.watch(chapterIndexStateProvider);
final chapter = ref.watch(chapterModelStateProvider);
return AnimatedContainer(
curve: Curves.easeIn,
decoration: BoxDecoration(
color: generalColor(context).withOpacity(0.3),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20))),
duration: const Duration(milliseconds: 100),
height: isLongPressed ? 70 : 0,
width: mediaWidth(context, 1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
bottomNavigationBar: AnimatedContainer(
curve: Curves.easeIn,
decoration: BoxDecoration(
color: generalColor(context).withOpacity(0.2),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20))),
duration: const Duration(milliseconds: 100),
height: isLongPressed ? 70 : 0,
width: mediaWidth(context, 1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () async {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
chap.add(ModelChapters(
name: widget.modelManga!.chapters![i].name,
url: widget.modelManga!.chapters![i].url,
dateUpload:
widget.modelManga!.chapters![i].dateUpload,
isBookmarked: idx == i
? widget.modelManga!.chapters![i]
.isBookmarked
? false
: true
: widget
.modelManga!.chapters![i].isBookmarked,
scanlator:
widget.modelManga!.chapters![i].scanlator,
isRead: widget.modelManga!.chapters![i].isRead,
lastPageRead: widget
.modelManga!.chapters![i].lastPageRead));
}
for (var idx in chapterIndexList) {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
final entries = ref
.watch(hiveBoxManga)
.values
.where((element) =>
'${element.lang}-${element.link}' ==
'${widget.modelManga!.lang}-${widget.modelManga!.link}')
.toList()
.first;
chap.add(ModelChapters(
name: entries.chapters![i].name,
url: entries.chapters![i].url,
dateUpload: entries.chapters![i].dateUpload,
isBookmarked: idx == i
? entries.chapters![i].isBookmarked
? false
: true
: entries.chapters![i].isBookmarked,
scanlator: entries.chapters![i].scanlator,
isRead: entries.chapters![i].isRead,
lastPageRead:
entries.chapters![i].lastPageRead));
}
// print(idx);
final model = ModelManga(
imageUrl: widget.modelManga!.imageUrl,
name: widget.modelManga!.name,
genre: widget.modelManga!.genre,
author: widget.modelManga!.author,
description: widget.modelManga!.description,
status: widget.modelManga!.status,
favorite: widget.modelManga!.favorite,
link: widget.modelManga!.link,
source: widget.modelManga!.source,
lang: widget.modelManga!.lang,
dateAdded: widget.modelManga!.dateAdded,
lastUpdate: widget.modelManga!.lastUpdate,
chapters: chap,
category: widget.modelManga!.category,
lastRead: widget.modelManga!.lastRead);
ref.watch(hiveBoxManga).put(
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterModelStateProvider.notifier)
.update(chap[idx]);
// print(chapterIndexList);
final model = ModelManga(
imageUrl: widget.modelManga!.imageUrl,
name: widget.modelManga!.name,
genre: widget.modelManga!.genre,
author: widget.modelManga!.author,
description: widget.modelManga!.description,
status: widget.modelManga!.status,
favorite: widget.modelManga!.favorite,
link: widget.modelManga!.link,
source: widget.modelManga!.source,
lang: widget.modelManga!.lang,
dateAdded: widget.modelManga!.dateAdded,
lastUpdate: widget.modelManga!.lastUpdate,
chapters: chap,
category: widget.modelManga!.category,
lastRead: widget.modelManga!.lastRead);
ref.watch(hiveBoxManga).put(
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterIndexListStateProvider.notifier)
.clear();
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
}
},
icon: Icon(chapter.isBookmarked
child: Icon(chapter.isBookmarked
? Icons.bookmark_remove
: Icons.bookmark_add_outlined)),
IconButton(
),
),
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
chap.add(ModelChapters(
name: widget.modelManga!.chapters![i].name,
url: widget.modelManga!.chapters![i].url,
dateUpload:
widget.modelManga!.chapters![i].dateUpload,
isBookmarked: widget
.modelManga!.chapters![i].isBookmarked,
scanlator:
widget.modelManga!.chapters![i].scanlator,
isRead: idx == i
? widget.modelManga!.chapters![i].isRead
? false
: true
: widget.modelManga!.chapters![i].isRead,
lastPageRead: widget
.modelManga!.chapters![i].lastPageRead));
}
for (var idx in chapterIndexList) {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
final entries = ref
.watch(hiveBoxManga)
.values
.where((element) =>
'${element.lang}-${element.link}' ==
'${widget.modelManga!.lang}-${widget.modelManga!.link}')
.toList()
.first;
chap.add(ModelChapters(
name: entries.chapters![i].name,
url: entries.chapters![i].url,
dateUpload: entries.chapters![i].dateUpload,
isBookmarked:
entries.chapters![i].isBookmarked,
scanlator: entries.chapters![i].scanlator,
isRead: idx == i
? entries.chapters![i].isRead
? false
: true
: entries.chapters![i].isRead,
lastPageRead:
entries.chapters![i].lastPageRead));
}
final model = ModelManga(
imageUrl: widget.modelManga!.imageUrl,
name: widget.modelManga!.name,
genre: widget.modelManga!.genre,
author: widget.modelManga!.author,
description: widget.modelManga!.description,
status: widget.modelManga!.status,
favorite: widget.modelManga!.favorite,
link: widget.modelManga!.link,
source: widget.modelManga!.source,
lang: widget.modelManga!.lang,
dateAdded: widget.modelManga!.dateAdded,
lastUpdate: widget.modelManga!.lastUpdate,
chapters: chap,
category: widget.modelManga!.category,
lastRead: widget.modelManga!.lastRead);
ref.watch(hiveBoxManga).put(
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterModelStateProvider.notifier)
.update(chap[idx]);
;
final model = ModelManga(
imageUrl: widget.modelManga!.imageUrl,
name: widget.modelManga!.name,
genre: widget.modelManga!.genre,
author: widget.modelManga!.author,
description: widget.modelManga!.description,
status: widget.modelManga!.status,
favorite: widget.modelManga!.favorite,
link: widget.modelManga!.link,
source: widget.modelManga!.source,
lang: widget.modelManga!.lang,
dateAdded: widget.modelManga!.dateAdded,
lastUpdate: widget.modelManga!.lastUpdate,
chapters: chap,
category: widget.modelManga!.category,
lastRead: widget.modelManga!.lastRead);
ref.watch(hiveBoxManga).put(
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterIndexListStateProvider.notifier)
.clear();
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
}
},
icon: Icon(chapter.isRead
child: Icon(chapter.isRead
? Icons.remove_done_sharp
: Icons.done_all_sharp)),
IconButton(
onPressed: () {}, icon: const Icon(Icons.download))
],
),
),
);
},
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () {
for (var idx in chapterIndexList) {
final entries = ref
.watch(hiveBoxMangaDownloads)
.values
.where((element) =>
element.modelManga.chapters![element.index]
.name ==
widget.modelManga!.chapters![idx].name)
.toList();
if (entries.isEmpty) {
ref.watch(downloadChapterProvider(
modelManga: widget.modelManga!, index: idx));
} else {
if (!entries.first.isDownload) {
ref.watch(downloadChapterProvider(
modelManga: widget.modelManga!,
index: idx));
}
}
}
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
ref
.read(chapterIndexListStateProvider.notifier)
.clear();
},
child: const Icon(Icons.download_outlined)),
),
)
],
),
),
));
}

View file

@ -64,8 +64,8 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
dateAdded: widget.modelManga.dateAdded,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: value.chapters,
category: null,
lastRead: '');
category: widget.modelManga.category,
lastRead: widget.modelManga.lastRead);
ref.watch(hiveBoxManga).put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);

View file

@ -1,14 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:developer';
import 'package:mangayomi/models/model_manga.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'state_providers.g.dart';
// final reverseMangaStateProvider = StateProvider.autoDispose<bool>(
// (ref) => false,
// );
// final sortedMangaValueStateProvider = StateProvider.autoDispose<String>(
// (ref) => 'By source',
// );
@riverpod
class ChapterModelState extends _$ChapterModelState {
@ -30,14 +24,46 @@ class ChapterModelState extends _$ChapterModelState {
}
@riverpod
class ChapterIndexState extends _$ChapterIndexState {
class ChapterIndexListState extends _$ChapterIndexListState {
@override
int build() {
return -1;
List<int> build() {
return [];
}
void update(int value) {
state = value;
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
} else {
newList.add(value);
}
if (newList.isEmpty) {
ref.read(isLongPressedStateProvider.notifier).update(false);
}
state = newList;
}
void selectAll(int value) {
var newList = state.reversed.toList();
if (!newList.contains(value)) {
newList.add(value);
}
state = newList;
}
void selectSome(int value) {
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
} else {
newList.add(value);
}
state = newList;
}
void clear() {
state = [];
}
}

View file

@ -22,22 +22,23 @@ final chapterModelStateProvider =
);
typedef _$ChapterModelState = AutoDisposeNotifier<ModelChapters>;
String _$chapterIndexStateHash() => r'db4ba6cb07f0bb8e388e518179a0b5f35b58e48b';
String _$chapterIndexListStateHash() =>
r'cef615a9638df5a2a72c6087f479824cc169a70e';
/// See also [ChapterIndexState].
@ProviderFor(ChapterIndexState)
final chapterIndexStateProvider =
AutoDisposeNotifierProvider<ChapterIndexState, int>.internal(
ChapterIndexState.new,
name: r'chapterIndexStateProvider',
/// See also [ChapterIndexListState].
@ProviderFor(ChapterIndexListState)
final chapterIndexListStateProvider =
AutoDisposeNotifierProvider<ChapterIndexListState, List<int>>.internal(
ChapterIndexListState.new,
name: r'chapterIndexListStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterIndexStateHash,
: _$chapterIndexListStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ChapterIndexState = AutoDisposeNotifier<int>;
typedef _$ChapterIndexListState = AutoDisposeNotifier<List<int>>;
String _$isLongPressedStateHash() =>
r'26fe435e8381046a30e3f6c4495303946aa3aaa7';

View file

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/manga_reader.dart';
@ -26,10 +28,12 @@ class ChapterListTileWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final idx = reverse ? reverseIndex : finalIndex;
final chapterIndex = ref.watch(chapterIndexStateProvider);
final chapterIndexList = ref.watch(chapterIndexListStateProvider);
return Container(
color:
chapterIndex == idx ? generalColor(context).withOpacity(0.4) : null,
color: chapterIndexList.contains(idx)
? generalColor(context).withOpacity(0.4)
: null,
child: ListTile(
textColor: chapters[finalIndex].isRead
? isLight(context)
@ -41,27 +45,26 @@ class ChapterListTileWidget extends ConsumerWidget {
: Colors.white,
onLongPress: () {
if (!isLongPressed) {
ref.read(chapterIndexStateProvider.notifier).update(idx);
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
ref
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
} else {
ref.read(chapterIndexStateProvider.notifier).update(-1);
}
ref.read(isLongPressedStateProvider.notifier).update(!isLongPressed);
},
onTap: () {
if (isLongPressed) {
ref.read(chapterIndexStateProvider.notifier).update(idx);
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
}
},
onTap: () async {
if (isLongPressed) {
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
if (chapterIndex == idx) {
ref.read(chapterIndexStateProvider.notifier).update(-1);
ref
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
}
} else {
pushMangaReaderView(
context: context,

View file

@ -13,6 +13,7 @@ import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/providers/download_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'download_page_widget.g.dart';
@ -42,170 +43,16 @@ class ChapterPageDownload extends ConsumerStatefulWidget {
class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
with AutomaticKeepAliveClientMixin<ChapterPageDownload> {
List _urll = [];
List<DownloadTask> tasks = [];
final StorageProvider _storageProvider = StorageProvider();
_startDownload() async {
await _storageProvider.requestPermission();
Directory? path;
bool isOk = false;
final path1 = await _storageProvider.getDirectory();
final finalPath =
"downloads/${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapters![widget.index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}";
path = Directory(
"${path1!.path}downloads/${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapters![widget.index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
ref
.read(getMangaChapterUrlProvider(
modelManga: widget.modelManga,
index: widget.index,
).future)
.then((value) {
if (value.urll.isNotEmpty) {
if (mounted) {
setState(() {
_urll = value.urll;
isOk = true;
});
}
}
});
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
if (_urll.isNotEmpty) {
for (var index = 0; index < _urll.length; index++) {
final path2 = Directory("${path1.path}downloads/");
final path4 = Directory(
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/");
final path3 = Directory(
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
final path5 = Directory(
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapters![widget.index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}");
if (!(await path1.exists())) {
path1.create();
}
if (Platform.isAndroid) {
if (!(await File("${path1.path}" ".nomedia").exists())) {
File("${path1.path}" ".nomedia").create();
}
}
if (!(await path2.exists())) {
path2.create();
}
if (!(await path4.exists())) {
path4.create();
}
if (!(await path3.exists())) {
path3.create();
}
if (!(await path5.exists())) {
path5.create();
}
if ((await path.exists())) {
if (await File("${path.path}" "${padIndex(index + 1)}.jpg")
.exists()) {
} else {
tasks.add(DownloadTask(
taskId: _urll[index],
headers: headers(widget.modelManga.source!),
url: _urll[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? BaseDirectory.applicationDocuments
: BaseDirectory.temporary,
directory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? 'Mangayomi/$finalPath'
: finalPath,
updates: Updates.statusAndProgress,
allowPause: true,
));
}
} else {
path.create();
if (await File("${path.path}" "${padIndex(index + 1)}.jpg")
.exists()) {
} else {
tasks.add(DownloadTask(
taskId: _urll[index],
headers: headers(widget.modelManga.source!),
url: _urll[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? BaseDirectory.applicationDocuments
: BaseDirectory.temporary,
directory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? 'Mangayomi/$finalPath'
: finalPath,
updates: Updates.statusAndProgress,
allowPause: true,
));
}
}
}
if (tasks.isEmpty && _urll.isNotEmpty) {
final model = DownloadModel(
modelManga: widget.modelManga,
succeeded: 0,
failed: 0,
index: widget.index,
total: 0,
isDownload: true,
taskIds: _urll,
isStartDownload: false);
ref
.watch(hiveBoxMangaDownloads)
.put(widget.modelManga.chapters![widget.index].name!, model);
} else {
await FileDownloader().downloadBatch(
tasks,
batchProgressCallback: (succeeded, failed) {
final model = DownloadModel(
modelManga: widget.modelManga,
succeeded: succeeded,
failed: failed,
index: widget.index,
total: tasks.length,
isDownload: (succeeded == tasks.length) ? true : false,
taskIds: _urll,
isStartDownload: true);
Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads)
.put(widget.modelManga.chapters![widget.index].name!, model);
},
taskProgressCallback: (task, progress) async {
if (progress == 1.0) {
final downloadTask = DownloadTask(
creationTime: task.creationTime,
taskId: task.taskId,
headers: task.headers,
url: task.url,
filename: task.filename,
baseDirectory: task.baseDirectory,
directory: task.directory,
updates: task.updates,
allowPause: task.allowPause,
);
if (Platform.isAndroid || Platform.isIOS) {
await FileDownloader().moveToSharedStorage(
downloadTask, SharedStorage.external,
directory: finalPath);
}
}
},
);
}
final data = await ref.watch(downloadChapterProvider(
modelManga: widget.modelManga, index: widget.index)
.future);
if (mounted) {
setState(() {
_urll = data;
});
}
}
@ -439,7 +286,8 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
.then((value) async {
await Future.delayed(const Duration(seconds: 1));
ref.watch(hiveBoxMangaDownloads).delete(
widget.modelManga.chapters![widget.index].name!,
widget
.modelManga.chapters![widget.index].name!,
);
});
}

View file

@ -0,0 +1,179 @@
import 'dart:io';
import 'package:background_downloader/background_downloader.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/services/get_manga_chapter_url.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'download_provider.g.dart';
@riverpod
Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
{required ModelManga modelManga, required int index}) async {
List urll = [];
List<DownloadTask> tasks = [];
final StorageProvider storageProvider = StorageProvider();
await storageProvider.requestPermission();
Directory? path;
bool isOk = false;
final path1 = await storageProvider.getDirectory();
final finalPath =
"downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}";
path = Directory(
"${path1!.path}downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
ref
.read(getMangaChapterUrlProvider(
modelManga: modelManga,
index: index,
).future)
.then((value) {
if (value.urll.isNotEmpty) {
urll = value.urll;
isOk = true;
}
});
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
if (urll.isNotEmpty) {
for (var index = 0; index < urll.length; index++) {
final path2 = Directory("${path1.path}downloads/");
final path4 = Directory(
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/");
final path3 = Directory(
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
final path5 = Directory(
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}");
if (!(await path1.exists())) {
path1.create();
}
if (Platform.isAndroid) {
if (!(await File("${path1.path}" ".nomedia").exists())) {
File("${path1.path}" ".nomedia").create();
}
}
if (!(await path2.exists())) {
path2.create();
}
if (!(await path4.exists())) {
path4.create();
}
if (!(await path3.exists())) {
path3.create();
}
if (!(await path5.exists())) {
path5.create();
}
if ((await path.exists())) {
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: urll[index],
headers: headers(modelManga.source!),
url: urll[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? BaseDirectory.applicationDocuments
: BaseDirectory.temporary,
directory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? 'Mangayomi/$finalPath'
: finalPath,
updates: Updates.statusAndProgress,
allowPause: true,
));
}
} else {
path.create();
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: urll[index],
headers: headers(modelManga.source!),
url: urll[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? BaseDirectory.applicationDocuments
: BaseDirectory.temporary,
directory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
? 'Mangayomi/$finalPath'
: finalPath,
updates: Updates.statusAndProgress,
allowPause: true,
));
}
}
}
if (tasks.isEmpty && urll.isNotEmpty) {
final model = DownloadModel(
modelManga: modelManga,
succeeded: 0,
failed: 0,
index: index,
total: 0,
isDownload: true,
taskIds: urll,
isStartDownload: false);
ref
.watch(hiveBoxMangaDownloads)
.put(modelManga.chapters![index].name!, model);
} else {
await FileDownloader().downloadBatch(
tasks,
batchProgressCallback: (succeeded, failed) {
final model = DownloadModel(
modelManga: modelManga,
succeeded: succeeded,
failed: failed,
index: index,
total: tasks.length,
isDownload: (succeeded == tasks.length) ? true : false,
taskIds: urll,
isStartDownload: true);
Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads)
.put(modelManga.chapters![index].name!, model);
},
taskProgressCallback: (task, progress) async {
if (progress == 1.0) {
final downloadTask = DownloadTask(
creationTime: task.creationTime,
taskId: task.taskId,
headers: task.headers,
url: task.url,
filename: task.filename,
baseDirectory: task.baseDirectory,
directory: task.directory,
updates: task.updates,
allowPause: task.allowPause,
);
if (Platform.isAndroid || Platform.isIOS) {
await FileDownloader().moveToSharedStorage(
downloadTask, SharedStorage.external,
directory: finalPath);
}
}
},
);
}
}
return urll;
}

View file

@ -0,0 +1,121 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'download_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'39625ed8cba923709fe7c76788e334bec1d8bcc3';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef DownloadChapterRef = AutoDisposeFutureProviderRef<List<dynamic>>;
/// See also [downloadChapter].
@ProviderFor(downloadChapter)
const downloadChapterProvider = DownloadChapterFamily();
/// See also [downloadChapter].
class DownloadChapterFamily extends Family<AsyncValue<List<dynamic>>> {
/// See also [downloadChapter].
const DownloadChapterFamily();
/// See also [downloadChapter].
DownloadChapterProvider call({
required ModelManga modelManga,
required int index,
}) {
return DownloadChapterProvider(
modelManga: modelManga,
index: index,
);
}
@override
DownloadChapterProvider getProviderOverride(
covariant DownloadChapterProvider provider,
) {
return call(
modelManga: provider.modelManga,
index: provider.index,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'downloadChapterProvider';
}
/// See also [downloadChapter].
class DownloadChapterProvider extends AutoDisposeFutureProvider<List<dynamic>> {
/// See also [downloadChapter].
DownloadChapterProvider({
required this.modelManga,
required this.index,
}) : super.internal(
(ref) => downloadChapter(
ref,
modelManga: modelManga,
index: index,
),
from: downloadChapterProvider,
name: r'downloadChapterProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$downloadChapterHash,
dependencies: DownloadChapterFamily._dependencies,
allTransitiveDependencies:
DownloadChapterFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
final int index;
@override
bool operator ==(Object other) {
return other is DownloadChapterProvider &&
other.modelManga == modelManga &&
other.index == index;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
hash = _SystemHash.combine(hash, index.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View file

@ -146,29 +146,30 @@ class _ImageViewVerticalState extends ConsumerState<ImageViewVertical>
return null;
}),
if (widget.index + 1 == widget.length)
Column(
children: [
const SizedBox(
height: 20,
),
Text(
'${widget.chapter} finished',
style: const TextStyle(
fontSize: 17.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(
height: 10,
),
const Icon(
FontAwesomeIcons.circleCheck,
color: Colors.white,
),
const SizedBox(
height: 20,
),
],
SizedBox(
height: mediaHeight(context, 0.3),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${widget.chapter} finished',
style: const TextStyle(
fontSize: 17.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(
height: 10,
),
const Icon(
FontAwesomeIcons.circleCheck,
color: Colors.white,
),
const SizedBox(
height: 20,
),
],
),
)
],
),