chapter filter feature

This commit is contained in:
kodjodevf 2023-04-26 12:55:13 +01:00
parent f339e455f7
commit cabf9fcc63
28 changed files with 1852 additions and 474 deletions

View file

@ -1,109 +0,0 @@
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:html/dom.dart' as dom;
import 'package:http/http.dart' as http;
import 'package:mangayomi/services/cloudflare/cookie.dart';
import 'package:mangayomi/utils/constant.dart';
Future<dom.Document?> cloudflareBypassDom(
{required String url, required bool bypass, required String source}) async {
bool isOk = false;
dom.Document? htmll;
if (bypass == false) {
final response = await http.get(Uri.parse(url));
htmll = dom.Document.html(response.body);
isOk = true;
} else {
HeadlessInAppWebView? headlessWebViewJapScan;
headlessWebViewJapScan = HeadlessInAppWebView(
onLoadStop: (controller, u) async {
String? html;
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
await Future.doWhile(() async {
if (html == null ||
html!.contains("Just a moment") ||
html!.contains("https://challenges.cloudflare.com")) {
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
return true;
}
return false;
});
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
htmll = dom.Document.html(html!);
isOk = true;
headlessWebViewJapScan!.dispose();
},
initialSettings: InAppWebViewSettings(
userAgent: Hive.box(HiveConstant.hiveBoxAppSettings).get("ua",
defaultValue:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0")),
initialUrlRequest: URLRequest(
url: WebUri.uri(Uri.parse(url)),
),
);
headlessWebViewJapScan.run();
}
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
await setCookie(source, url);
return htmll;
}
Future<String> cloudflareBypassHtml(
{required String url, required String source}) async {
bool isOk = false;
String? html;
HeadlessInAppWebView? headlessWebViewJapScan;
headlessWebViewJapScan = HeadlessInAppWebView(
onLoadStop: (controller, u) async {
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
await Future.doWhile(() async {
if (html == null ||
html!.contains("Just a moment") ||
html!.contains("Un instant…")) {
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
return true;
}
return false;
});
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
isOk = true;
headlessWebViewJapScan!.dispose();
},
initialSettings: InAppWebViewSettings(
userAgent: Hive.box(HiveConstant.hiveBoxAppSettings).get("ua",
defaultValue:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0")),
initialUrlRequest: URLRequest(
url: WebUri.uri(Uri.parse(url)),
),
);
headlessWebViewJapScan.run();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
await setCookie(source, url);
return html!;
}

View file

@ -2,15 +2,17 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:mangayomi/models/comick/chapter_page_comick.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/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
@ -40,7 +42,9 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
String? zjsUrl;
zjs() async {
final html = await cloudflareBypassHtml(
url: zjsUrl!, source: modelManga.source!.toLowerCase());
url: zjsUrl!,
source: modelManga.source!.toLowerCase(),
useUserAgent: true);
dom.Document htmll = dom.Document.html(baseUrl!);
final strings = html
.replaceAll(RegExp(r'\\[(.*?)\\]'), '')
@ -99,23 +103,15 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
else if (getWpMangTypeSource(source) == TypeSource.comick) {
String mangaId =
modelManga.chapters![index].url!.split('/').last.split('-').first;
var headers = {
'Referer': 'https://comick.app/',
'User-Agent':
'Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\\\$userAgentRandomizer1.0.4\\\$userAgentRandomizer3.1\\\$userAgentRandomizer2 Safari/537.36'
};
var request = http.Request('GET',
Uri.parse('https://api.comick.fun/chapter/$mangaId?tachiyomi=true'));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
var data = jsonDecode(await response.stream.bytesToString())
as Map<String, dynamic>;
var page = ChapterPageComick.fromJson(data);
for (var url in page.chapter!.images!) {
urll.add(url.url);
}
final response = await httpGet(
url: 'https://api.comick.fun/chapter/$mangaId?tachiyomi=true',
source: source,
resDom: false) as String?;
var data = jsonDecode(response!) as Map<String, dynamic>;
var page = ChapterPageComick.fromJson(data);
for (var url in page.chapter!.images!) {
urll.add(url.url);
}
if (!incognitoMode) {
ref.watch(hiveBoxMangaInfo).put(
@ -128,11 +124,11 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/**************/
else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await cloudflareBypassDom(
url: modelManga.chapters![index].url!,
bypass: true,
source: source,
);
final dom = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#readerarea').isNotEmpty) {
final ta =
dom.querySelectorAll('#readerarea').map((e) => e.outerHtml).toList();
@ -182,18 +178,20 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/***********/
else if (source == 'mangakawaii') {
final response =
await http.get(Uri.parse(modelManga.chapters![index].url!));
final response = await httpGet(
url: modelManga.chapters![index].url!,
source: source,
resDom: false) as String?;
var chapterSlug = RegExp("""var chapter_slug = "([^"]*)";""")
.allMatches(response.body.toString())
.allMatches(response!)
.last
.group(1);
var mangaSlug = RegExp("""var oeuvre_slug = "([^"]*)";""")
.allMatches(response.body.toString())
.allMatches(response)
.last
.group(1);
var pages = RegExp('''"page_image":"([^"]*)"''')
.allMatches(response.body.toString())
.allMatches(response)
.map((e) => e.group(1));
for (var tt in pages) {
@ -212,10 +210,12 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final dom =
await httpResToDom(url: modelManga.chapters![index].url!, headers: {});
if (dom.querySelectorAll('#all > .img-responsive').isNotEmpty) {
final dom = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#all > .img-responsive').isNotEmpty) {
urll = dom.querySelectorAll('#all > .img-responsive').map((e) {
final RegExp regexx = RegExp(r'data-src="([^"]+)"');
if (modelManga.source!.toLowerCase() == 'jpmangas' ||
@ -267,14 +267,11 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
return secretKeyResultScript;
}
final response = await http.get(
Uri.parse("http://www.mangahere.cc${modelManga.chapters![index].url!}"),
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
});
var link = "http://www.mangahere.cc${modelManga.chapters![index].url!}";
dom.Document htmll = dom.Document.html(response.body);
final response =
await httpGet(url: link, source: source, resDom: false) as String?;
dom.Document htmll = dom.Document.html(response!);
int? pagesNumber = -1;
if (htmll.querySelectorAll('body > div > div > span > a:').isNotEmpty) {
final ta = htmll
@ -303,12 +300,12 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
urll.add("https:$tt");
}
} else {
var secretKey = extractSecretKey(response.body, flutterJs);
var secretKey = extractSecretKey(response, flutterJs);
var chapterIdStartLoc = response.body.indexOf("chapterid");
var chapterId = response.body
.substring(chapterIdStartLoc + 11,
response.body.indexOf(";", chapterIdStartLoc))
var chapterIdStartLoc = response.indexOf("chapterid");
var chapterId = response
.substring(
chapterIdStartLoc + 11, response.indexOf(";", chapterIdStartLoc))
.trim();
var pageBase = link.substring(0, link.lastIndexOf("/"));
@ -360,13 +357,15 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
}
}
} else if (source == 'japscan') {
final html = await cloudflareBypassHtml(
final response = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
source: modelManga.source!.toLowerCase());
source: source,
resDom: false) as String?;
RegExp regex = RegExp(r'<script src="/zjs/(.*?)"');
Match? match = regex.firstMatch(html);
Match? match = regex.firstMatch(response!);
String zjsurl = match!.group(1)!;
baseUrl = html;
baseUrl = response;
zjsUrl = "https://www.japscan.lol/zjs/$zjsurl";
zjs();
await Future.doWhile(() async {

View file

@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart';
// **************************************************************************
String _$getMangaChapterUrlHash() =>
r'dda553ecccf269ea375550eba379f57b8ede0263';
r'128242519581990fb05796608bc1e746d0e885df';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,12 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
import 'package:mangayomi/models/comick/manga_chapter_detail.dart';
import 'package:mangayomi/models/comick/manga_detail_comick.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/services/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@ -92,74 +94,56 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/*comick*/
/********/
if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.comick) {
var headers = {
'Referer': 'https://comick.app/',
'User-Agent':
'Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\\\$userAgentRandomizer1.0.4\\\$userAgentRandomizer3.1\\\$userAgentRandomizer2 Safari/537.36'
};
var request = http.Request(
'GET', Uri.parse('https://api.comick.fun$url?tachiyomi=true'));
final response = await httpGet(
url: 'https://api.comick.fun$url?tachiyomi=true',
source: source,
resDom: false) as String?;
var mangaDetail = jsonDecode(response!) as Map<String, dynamic>;
request.headers.addAll(headers);
var mangaDetailLMap = MangaDetailModelComick.fromJson(mangaDetail);
http.StreamedResponse response = await request.send();
RegExp regExp = RegExp(r'name:\s*(.*?),');
if (response.statusCode == 200) {
var mangaDetail = jsonDecode(await response.stream.bytesToString())
as Map<String, dynamic>;
String authorr =
regExp.firstMatch(mangaDetailLMap.authors![0].toString())?.group(1) ??
'';
String statuss = _parseStatut(mangaDetailLMap.comic!.status!);
status = statuss;
author = authorr;
RegExp regExp1 = RegExp(r'name:\s*(.*?)}');
for (var ok in mangaDetailLMap.genres!) {
genre.add(regExp1.firstMatch(ok.toString())!.group(1)!);
}
description = mangaDetailLMap.comic!.desc;
String tt = await findCurrentSlug(mangaDetailLMap.comic!.slug!);
String mangaId = tt.split('":"').last.replaceAll('"}', '');
String limit = mangaDetailLMap.comic!.chapterCount.toString();
var mangaDetailLMap = MangaDetailModelComick.fromJson(mangaDetail);
RegExp regExp = RegExp(r'name:\s*(.*?),');
String authorr =
regExp.firstMatch(mangaDetailLMap.authors![0].toString())?.group(1) ??
'';
String statuss = _parseStatut(mangaDetailLMap.comic!.status!);
status = statuss;
author = authorr;
RegExp regExp1 = RegExp(r'name:\s*(.*?)}');
for (var ok in mangaDetailLMap.genres!) {
genre.add(regExp1.firstMatch(ok.toString())!.group(1)!);
}
description = mangaDetailLMap.comic!.desc;
String tt = await findCurrentSlug(mangaDetailLMap.comic!.slug!);
String mangaId = tt.split('":"').last.replaceAll('"}', '');
String limit = mangaDetailLMap.comic!.chapterCount.toString();
var requestt = http.Request(
'GET',
Uri.parse(
'https://api.comick.fun/comic/$mangaId/chapters?lang=$lang&limit=$limit'));
requestt.headers.addAll(headers);
http.StreamedResponse responsee = await requestt.send();
if (responsee.statusCode == 200) {
List<String> chapterTitles = [];
List<String> chapterUrls = [];
List<String> chapterDates = [];
var chapterDetail = jsonDecode(await responsee.stream.bytesToString())
as Map<String, dynamic>;
var chapterDetailMap = MangaChapterModelComick.fromJson(chapterDetail);
for (var chapter in chapterDetailMap.chapters!) {
chapterUrls.add(
"/comic/${mangaDetailLMap.comic!.slug}/${chapter.hid}-chapter-${chapter.chap}-en");
chapterDates.add(chapter.createdAt!.toString().substring(0, 10));
chapterTitles.add(beautifyChapterName(chapter.vol ?? "",
chapter.chap ?? "", chapter.title ?? "", lang));
}
List<String> chapterTitless = [];
for (var i = 0; i < chapterTitles.length; i++) {
if (!chapterTitless.contains(chapterTitles[i])) {
chapterTitle.add(chapterTitles[i]);
chapterUrl.add(chapterUrls[i]);
chapterDate.add(chapterDates[i].replaceAll('-', "/"));
}
chapterTitless.add(chapterTitles[i]);
}
final responsee = await httpGet(
url:
'https://api.comick.fun/comic/$mangaId/chapters?lang=$lang&limit=$limit',
source: source,
resDom: false) as String?;
List<String> chapterTitles = [];
List<String> chapterUrls = [];
List<String> chapterDates = [];
var chapterDetail = jsonDecode(responsee!) as Map<String, dynamic>;
var chapterDetailMap = MangaChapterModelComick.fromJson(chapterDetail);
for (var chapter in chapterDetailMap.chapters!) {
chapterUrls.add(
"/comic/${mangaDetailLMap.comic!.slug}/${chapter.hid}-chapter-${chapter.chap}-en");
chapterDates.add(chapter.createdAt!.toString().substring(0, 10));
chapterTitles.add(beautifyChapterName(
chapter.vol ?? "", chapter.chap ?? "", chapter.title ?? "", lang));
}
List<String> chapterTitless = [];
for (var i = 0; i < chapterTitles.length; i++) {
if (!chapterTitless.contains(chapterTitles[i])) {
chapterTitle.add(chapterTitles[i]);
chapterUrl.add(chapterUrls[i]);
chapterDate.add(chapterDates[i].replaceAll('-', "/"));
}
chapterTitless.add(chapterTitles[i]);
}
}
/*************/
@ -167,8 +151,12 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/**************/
if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.mangathemesia) {
final dom = await httpResToDom(url: url, headers: {});
if (dom
final dom = await httpGet(
url: url,
source: source,
resDom: true,
useUserAgent: true) as Document?;
if (dom!
.querySelectorAll(
'div.bigcontent, div.animefull, div.main-info, div.postbody')
.isNotEmpty) {
@ -294,13 +282,14 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/*mangakawaii*/
/***********/
else if (source.toLowerCase() == "mangakawaii") {
final dom = await httpResToDom(
final dom = await httpGet(
url: 'https://www.mangakawaii.io$url',
headers: {"Accept-Language": "fr"});
source: source,
resDom: true) as Document?;
List detail = [];
imageUrl =
"https://cdn.mangakawaii.pics/uploads$url/cover/cover_250x350.jpg";
if (dom.querySelectorAll('dd.text-justify.text-break').isNotEmpty) {
if (dom!.querySelectorAll('dd.text-justify.text-break').isNotEmpty) {
final tt = dom
.querySelectorAll('dd.text-justify.text-break')
.map((e) => e.text.trim())
@ -408,11 +397,8 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/***********/
else if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.mmrcms) {
final dom = await cloudflareBypassDom(
url: url,
bypass: true,
source: source,
);
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
description = dom!
.querySelectorAll('.row .well p')
.map((e) => e.text.trim())
@ -503,14 +489,11 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/*mangahere*/
/***********/
else if (source.toLowerCase() == "mangahere") {
final dom = await httpResToDom(
final dom = await httpGet(
url: "http://www.mangahere.cc$url",
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
});
if (dom
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.isNotEmpty) {
@ -596,21 +579,18 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
}
}
} else if (source.toLowerCase() == "japscan") {
final htmll = await cloudflareBypassDom(
url: url,
bypass: true,
source: source,
);
if (htmll!.querySelectorAll('.col-7 > p').isNotEmpty) {
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
if (dom!.querySelectorAll('.col-7 > p').isNotEmpty) {
final images =
htmll.querySelectorAll('.col-5 ').map((e) => e.outerHtml).toList();
dom.querySelectorAll('.col-5 ').map((e) => e.outerHtml).toList();
RegExp exp = RegExp(r'src="([^"]+)"');
String? srcValue = exp.firstMatch(images[0])?.group(1);
imageUrl = 'https://www.japscan.me$srcValue';
imageUrl = 'https://www.japscan.lol$srcValue';
if (htmll.querySelectorAll('.col-7 > p').isNotEmpty) {
final stat = htmll
if (dom.querySelectorAll('.col-7 > p').isNotEmpty) {
final stat = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Statut:'))
.map((e) => e.text)
@ -619,7 +599,7 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
status = stat[0].replaceAll('Statut:', '').trim();
}
final auth = htmll
final auth = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Auteur(s):'))
.map((e) => e.text)
@ -632,7 +612,7 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
status = "";
}
final genres = htmll
final genres = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Genre(s):'))
.map((e) => e.text.replaceAll('Genre(s):', '').trim())
@ -643,7 +623,7 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
}
}
final synop = htmll
final synop = dom
.querySelectorAll('p.list-group-item ')
.map((e) => e.text.trim())
.toList();
@ -653,17 +633,17 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
}
final urls =
htmll.querySelectorAll('.col-8 ').map((e) => e.outerHtml).toList();
dom.querySelectorAll('.col-8 ').map((e) => e.outerHtml).toList();
for (var ok in urls) {
RegExp exp = RegExp(r'href="([^"]+)"');
String? srcValue = exp.firstMatch(ok)?.group(1);
chapterUrl.add('https://www.japscan.me$srcValue');
chapterUrl.add('https://www.japscan.lol$srcValue');
}
final chapterTitlee =
htmll.querySelectorAll('.col-8').map((e) => e.text.trim()).toList();
dom.querySelectorAll('.col-8').map((e) => e.text.trim()).toList();
if (chapterTitlee.isNotEmpty) {
for (var ok in chapterTitlee) {
@ -672,7 +652,7 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
}
final chapterDatee =
htmll.querySelectorAll('.col-4').map((e) => e.text.trim()).toList();
dom.querySelectorAll('.col-4').map((e) => e.text.trim()).toList();
if (chapterDatee.isNotEmpty) {
for (var ok in chapterDatee) {
chapterDate.add(ok);

View file

@ -6,7 +6,7 @@ part of 'get_manga_detail.dart';
// RiverpodGenerator
// **************************************************************************
String _$getMangaDetailHash() => r'9fb313c606c75f308217de7bea8a771e8801e5f6';
String _$getMangaDetailHash() => r'8cc750dd5a207747b8e97db82ce6c1b141c8419a';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -2,10 +2,9 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart';
import 'package:mangayomi/models/comick/popular_manga_comick.dart';
import 'package:mangayomi/services/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@ -42,6 +41,16 @@ TypeSource getWpMangTypeSource(String source) {
return typeSource!;
}
bool isCloudflare(String source) {
bool? isCloudflare;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
isCloudflare = sourcesList[i].isCloudflare;
}
}
return isCloudflare!;
}
@riverpod
Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
{required String source, required int page}) async {
@ -54,31 +63,19 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/*comick*/
/*******/
if (getWpMangTypeSource(source) == TypeSource.comick) {
var headers = {
'Referer': 'https://comick.app/',
'User-Agent':
'Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\\\$userAgentRandomizer1.0.4\\\$userAgentRandomizer3.1\\\$userAgentRandomizer2 Safari/537.36'
};
var request = http.Request(
'GET',
Uri.parse(
'https://api.comick.fun/v1.0/search?sort=follow&page=$page&tachiyomi=true'));
final response = await httpGet(
url:
'https://api.comick.fun/v1.0/search?sort=follow&page=$page&tachiyomi=true',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
var popularManga =
jsonDecode(await response.stream.bytesToString()) as List;
var popularMangaList =
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
var popularMangaList =
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
}
@ -86,11 +83,10 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/*mangathemesia*/
/**************/
if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await cloudflareBypassDom(
url: '${getWpMangaUrl(source)}/manga/?title=&page=$page&order=popular',
bypass: true,
source: source,
);
final dom = await httpGet(useUserAgent: true,
url: '${getWpMangaUrl(source)}/manga/?title=&page=$page&order=popular',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
@ -131,9 +127,11 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/*mangakawaii*/
/***********/
if (source == "mangakawaii") {
final dom =
await httpResToDom(url: 'https://www.mangakawaii.io/', headers: {});
if (dom.querySelectorAll('a.hot-manga__item').isNotEmpty) {
final dom = await httpGet(
url: 'https://www.mangakawaii.io/',
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('a.hot-manga__item').isNotEmpty) {
url = dom
.querySelectorAll('a.hot-manga__item ')
.where((e) => e.attributes.containsKey('href'))
@ -154,12 +152,12 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final dom = await httpResToDom(
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/filterList?page=$page&sortBy=views&asc=false',
headers: {});
final urlElement = dom.getElementsByClassName('chart-title');
source: source,
resDom: true) as Document?;
final urlElement = dom!.getElementsByClassName('chart-title');
for (var e in urlElement) {
RegExp exp = RegExp(r'href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
@ -180,9 +178,11 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/*mangahere*/
/***********/
else if (source == "mangahere") {
final dom = await httpResToDom(
url: 'https://www.mangahere.cc/ranking/', headers: {});
if (dom
final dom = await httpGet(
url: 'https://www.mangahere.cc/ranking/',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a')
.isNotEmpty) {
@ -209,21 +209,20 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
.toList();
}
} else if (source == "japscan") {
final htmll = await cloudflareBypassDom(
url: "https://www.japscan.lol/",
bypass: true,
source: source,
);
if (htmll!.querySelectorAll('#top_mangas_week > ul > li ').isNotEmpty) {
final urls = htmll
final dom = await httpGet(
url: "https://www.japscan.lol/",
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#top_mangas_week > ul > li ').isNotEmpty) {
final urls = dom
.querySelectorAll('#top_mangas_week > ul > li > a')
.where((e) => e.attributes['href'].toString().contains('manga'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in urls) {
url.add("https://www.japscan.me$ok");
url.add("https://www.japscan.lol$ok");
}
name = htmll
name = dom
.querySelectorAll(
'#top_mangas_week > ul > li > a.text-dark.font-weight-bold')
.map((e) => e.innerHtml)

View file

@ -6,7 +6,7 @@ part of 'get_popular_manga.dart';
// RiverpodGenerator
// **************************************************************************
String _$getPopularMangaHash() => r'cc0567f4659ae10b5e55ccdbb5f5a9560cc09a16';
String _$getPopularMangaHash() => r'87a02e32e79c310e6717e7ae95bf54fdd13b2a00';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -0,0 +1,106 @@
import 'dart:developer';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:html/dom.dart' as dom;
import 'package:mangayomi/services/http_service/cloudflare/cookie.dart';
import 'package:mangayomi/utils/constant.dart';
Future<dom.Document?> cloudflareBypassDom(
{required String url,
required bool bypass,
required String source,
required bool useUserAgent}) async {
log(source);
bool isOk = false;
dom.Document? htmll;
final ua = Hive.box(HiveConstant.hiveBoxAppSettings)
.get("ua", defaultValue: defaultUserAgent);
HeadlessInAppWebView? headlessWebViewJapScan;
headlessWebViewJapScan = HeadlessInAppWebView(
onLoadStop: (controller, u) async {
String? html;
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
await Future.doWhile(() async {
if (html == null ||
html!.contains("Just a moment") ||
html!.contains("https://challenges.cloudflare.com")) {
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
return true;
}
return false;
});
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
htmll = dom.Document.html(html!);
isOk = true;
headlessWebViewJapScan!.dispose();
},
initialSettings: useUserAgent ? InAppWebViewSettings(userAgent: ua) : null,
initialUrlRequest: URLRequest(
url: WebUri.uri(Uri.parse(url)),
),
);
headlessWebViewJapScan.run();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
await setCookie(source, url);
return htmll;
}
Future<String> cloudflareBypassHtml(
{required String url,
required String source,
required bool useUserAgent}) async {
final ua = Hive.box(HiveConstant.hiveBoxAppSettings)
.get("ua", defaultValue: defaultUserAgent);
bool isOk = false;
String? html;
HeadlessInAppWebView? headlessWebViewJapScan;
headlessWebViewJapScan = HeadlessInAppWebView(
onLoadStop: (controller, u) async {
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
await Future.doWhile(() async {
if (html == null ||
html!.contains("Just a moment") ||
html!.contains("Un instant…")) {
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");
return true;
}
return false;
});
html = await controller.evaluateJavascript(
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
isOk = true;
headlessWebViewJapScan!.dispose();
},
initialSettings: useUserAgent ? InAppWebViewSettings(userAgent: ua) : null,
initialUrlRequest: URLRequest(
url: WebUri.uri(Uri.parse(url)),
),
);
headlessWebViewJapScan.run();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
await setCookie(source, url);
return html!;
}

View file

@ -6,3 +6,4 @@ Future<Document> httpResToDom(
final response = await http.get(Uri.parse(url), headers: headers);
return Document.html(response.body);
}

View file

@ -0,0 +1,40 @@
import 'package:html/dom.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:http/http.dart' as http;
import 'package:mangayomi/utils/headers.dart';
Future<dynamic> httpGet(
{required String url,
required String source,
required bool resDom,
bool useUserAgent = false}) async {
bool isCloudflaree = isCloudflare(source);
if (resDom) {
Document? dom;
if (isCloudflaree) {
dom = await cloudflareBypassDom(
url: url,
bypass: isCloudflaree,
source: source,
useUserAgent: useUserAgent);
} else {
dom = await httpResToDom(url: url, headers: headers(source));
}
return dom;
} else {
String? resHtml;
if (isCloudflaree) {
resHtml = await cloudflareBypassHtml(
url: url,
source: source,
useUserAgent: useUserAgent,
);
} else {
final response = await http.get(Uri.parse(url), headers: headers(source));
resHtml = response.body;
}
return resHtml;
}
}

View file

@ -1,8 +1,10 @@
import 'dart:convert';
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
import 'package:mangayomi/models/comick/search_manga_cimick.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'search_manga.g.dart';
@ -31,30 +33,18 @@ Future<SearchMangaModel> searchManga(SearchMangaRef ref,
/*comick*/
/********/
if (getWpMangTypeSource(source) == TypeSource.comick) {
var headers = {
'Referer': 'https://comick.app/',
'User-Agent':
'Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\\\$userAgentRandomizer1.0.4\\\$userAgentRandomizer3.1\\\$userAgentRandomizer2 Safari/537.36'
};
var request = http.Request(
'GET',
Uri.parse(
'https://api.comick.fun/search?q=${query.trim()}&tachiyomi=true&page=1'));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
var popularManga =
jsonDecode(await response.stream.bytesToString()) as List;
var popularMangaList =
popularManga.map((e) => MangaSearchModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
final response = await httpGet(
url:
'https://api.comick.fun/search?q=${query.trim()}&tachiyomi=true&page=1',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => MangaSearchModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
}
@ -62,10 +52,11 @@ Future<SearchMangaModel> searchManga(SearchMangaRef ref,
/*mangathemesia*/
/***************/
else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await httpResToDom(
url: '${getWpMangaUrl(source)}/?s=${query.trim()}', headers: {});
if (dom
final dom = await httpGet(
url: '${getWpMangaUrl(source)}/?s=${query.trim()}',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a')
.isNotEmpty) {
@ -95,12 +86,12 @@ Future<SearchMangaModel> searchManga(SearchMangaRef ref,
/*mangakawaii*/
/***********/
else if (source == "mangakawaii") {
final dom = await httpResToDom(
final dom = await httpGet(
url:
'https://www.mangakawaii.io/search?query=${query.trim()}&search_type=manga',
headers: {'Accept-Language': 'fr'});
if (dom
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.isNotEmpty) {
@ -129,9 +120,11 @@ Future<SearchMangaModel> searchManga(SearchMangaRef ref,
/*mmrcms*/
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final response = await http.get(
Uri.parse('${getWpMangaUrl(source)}/search?query=${query.trim()}'));
final rep = jsonDecode(response.body);
final response = await httpGet(
url: '${getWpMangaUrl(source)}/search?query=${query.trim()}',
source: source,
resDom: false) as String?;
final rep = jsonDecode(response!);
for (var ok in rep['suggestions']) {
if (source == 'Read Comics Online') {
url.add('${getWpMangaUrl(source)}/comic/${ok['data']}');
@ -148,11 +141,13 @@ Future<SearchMangaModel> searchManga(SearchMangaRef ref,
/*mangahere*/
/***********/
else if (source == "mangahere") {
final dom = await httpResToDom(
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/search?title=${query.trim()}&genres=&nogenres=&sort=&stype=1&name=&type=0&author_method=cw&author=&artist_method=cw&artist=&rating_method=eq&rating=&released_method=eq&released=&st=0',
headers: {});
if (dom
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.isNotEmpty) {

View file

@ -6,7 +6,7 @@ part of 'search_manga.dart';
// RiverpodGenerator
// **************************************************************************
String _$searchMangaHash() => r'b2376a19c02cbe43ad7f41357caeae3e7f2a210e';
String _$searchMangaHash() => r'8efe07dbe0f844d9d17ecf6962ac7f03ebcc9d57';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/cloudflare/cookie.dart';
import 'package:mangayomi/services/http_service/cloudflare/cookie.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:url_launcher/url_launcher.dart';
@ -142,7 +141,7 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
userAgent: Hive.box(HiveConstant.hiveBoxAppSettings).get(
"ua",
defaultValue:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0")),
defaultUserAgent)),
initialUrlRequest:
URLRequest(url: WebUri.uri(Uri.parse(widget.url))),
),

View file

@ -157,7 +157,8 @@ List<SourceModel> sourcesList = [
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl:
'https://arenascans.net/wp-content/uploads/2023/02/arena-logo-1.png'),
'https://arenascans.net/wp-content/uploads/2023/02/arena-logo-1.png',
isCloudflare: true),
SourceModel(
sourceName: "AzureScans",
url: "https://azuremanga.com",
@ -214,7 +215,7 @@ List<SourceModel> sourcesList = [
url: "https://void-scans.com",
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: ''),
logoUrl: '',isCloudflare: true),
SourceModel(
sourceName: "Kiryuu",
url: "https://kiryuu.id",
@ -349,7 +350,8 @@ List<SourceModel> sourcesList = [
url: "https://sushiscan.net",
lang: "fr",
typeSource: TypeSource.mangathemesia,
logoUrl: 'https://sushiscan.net/wp-content/uploads/SushiScanNewLogo.png'),
logoUrl: 'https://sushiscan.net/wp-content/uploads/SushiScanNewLogo.png',
isCloudflare: true),
SourceModel(
sourceName: "TsundokuTraduções",
url: "https://tsundoku.com.br",
@ -426,6 +428,6 @@ List<SourceModel> sourcesList = [
url: "https://japscan.lol",
lang: "fr",
typeSource: TypeSource.single,
logoUrl: ''),
logoUrl: '',
isCloudflare: true),
];

View file

@ -21,17 +21,21 @@ class SourceModel extends HiveObject {
final String logoUrl;
@HiveField(8, defaultValue: false)
final bool isFullData;
@HiveField(9, defaultValue: false)
final bool isCloudflare;
SourceModel(
{required this.sourceName,
required this.url,
required this.lang,
required this.typeSource,
required this.logoUrl,
this.isActive = true,
this.isAdded = false,
this.isNsfw = false,
this.isFullData = false});
SourceModel({
required this.sourceName,
required this.url,
required this.lang,
required this.typeSource,
required this.logoUrl,
this.isActive = true,
this.isAdded = false,
this.isNsfw = false,
this.isFullData = false,
this.isCloudflare = false,
});
}
@HiveType(typeId: 4)

View file

@ -26,13 +26,14 @@ class SourceModelAdapter extends TypeAdapter<SourceModel> {
isAdded: fields[4] == null ? false : fields[4] as bool,
isNsfw: fields[5] == null ? false : fields[5] as bool,
isFullData: fields[8] == null ? false : fields[8] as bool,
isCloudflare: fields[9] == null ? false : fields[9] as bool,
);
}
@override
void write(BinaryWriter writer, SourceModel obj) {
writer
..writeByte(9)
..writeByte(10)
..writeByte(0)
..write(obj.sourceName)
..writeByte(1)
@ -50,7 +51,9 @@ class SourceModelAdapter extends TypeAdapter<SourceModel> {
..writeByte(7)
..write(obj.logoUrl)
..writeByte(8)
..write(obj.isFullData);
..write(obj.isFullData)
..writeByte(9)
..write(obj.isCloudflare);
}
@override

View file

@ -9,3 +9,6 @@ class HiveConstant {
static String get hiveBoxReaderSettings => "_reader_box_settings_";
static String get hiveBoxReaderMode => "_readerMode_box_settings_";
}
const defaultUserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0";

View file

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/utils/constant.dart';
@ -5,9 +7,9 @@ Map<String, String> headers(String source) {
source = source.toLowerCase();
final cookie = Hive.box(HiveConstant.hiveBoxAppSettings)
.get("$source-cookie", defaultValue: "");
final userAgent = Hive.box(HiveConstant.hiveBoxAppSettings).get("ua",
defaultValue:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0");
log(cookie);
final userAgent = Hive.box(HiveConstant.hiveBoxAppSettings)
.get("ua", defaultValue: defaultUserAgent);
return source == 'mangakawaii'
? {
'Referer': 'https://www.mangakawaii.io/',
@ -33,5 +35,5 @@ Map<String, String> headers(String source) {
'Referer': "https://www.sushscan.net/",
"Cookie": cookie
}
: {};
: {'User-Agent': userAgent, "Cookie": cookie};
}

View file

@ -28,7 +28,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
List<ModelManga> bookmark() {
List<ModelManga> mang = [];
for (var entry in entries) {
final d = entry.chapters!.where((element) => element.isBookmarked == true);
final d =
entry.chapters!.where((element) => element.isBookmarked == true);
List<ModelChapters> chap = [];
for (var a in d) {
chap.add(a);
@ -100,23 +101,25 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
});
_textEditingController.clear();
},
icon: Icon(Icons.search, color: Theme.of(context).hintColor)),
icon: Icon(
Icons.search,
)),
IconButton(
splashRadius: 20,
onPressed: () {
_showModalSort();
},
icon: Icon(Icons.filter_list_sharp,
color: Theme.of(context).hintColor)),
// PopupMenuButton(
// color: Theme.of(context).hintColor,
// itemBuilder: (context) {
// return [
// const PopupMenuItem<int>(
// value: 0, child: Text("Open random entry")),
// ];
// },
// onSelected: (value) {}),
icon: Icon(
Icons.filter_list_sharp,
)),
PopupMenuButton(
itemBuilder: (context) {
return [
const PopupMenuItem<int>(
value: 0, child: Text("Open random entry")),
];
},
onSelected: (value) {}),
],
),
body: ValueListenableBuilder<Box<ModelManga>>(

View file

@ -14,8 +14,11 @@ import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/utils/utils.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_filter_list_tile_widget.dart';
import 'package:mangayomi/views/manga/detail/widgets/chapter_list_tile_widget.dart';
import 'package:mangayomi/views/manga/detail/widgets/chapter_sort_list_tile_widget.dart';
import 'package:mangayomi/views/manga/download/providers/download_provider.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class MangaDetailView extends ConsumerStatefulWidget {
final Function(bool) isExtended;
@ -54,11 +57,44 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
bool isOk = false;
bool _expanded = false;
ScrollController _scrollController = ScrollController();
late int _pageLength = ref
.read(chapterFilterResultStateProvider(modelManga: widget.modelManga!)
.notifier)
.getData()
.chapters!
.length +
1;
late List<ModelChapters>? _chapters = ref
.read(chapterFilterResultStateProvider(modelManga: widget.modelManga!)
.notifier)
.getData()
.chapters;
late ModelManga? _modelManga = ref
.read(chapterFilterResultStateProvider(modelManga: widget.modelManga!)
.notifier)
.getData();
_refreshData() {
final modelManga = ref
.read(chapterFilterResultStateProvider(modelManga: widget.modelManga!)
.notifier)
.getData();
setState(() {
_pageLength = modelManga.chapters!.length + 1;
_chapters = modelManga.chapters;
_modelManga = modelManga;
});
}
bool isRefresh = false;
@override
Widget build(BuildContext context) {
final chapterIndexList = ref.watch(chapterIndexListStateProvider);
final chapterNameList = ref.watch(chapterNameListStateProvider);
final isLongPressed = ref.watch(isLongPressedStateProvider);
final reverse = ref.watch(reverseMangaStateProvider);
final reverse =
ref.watch(reverseMangaStateProvider(modelManga: widget.modelManga!));
final chapter = ref.watch(chapterModelStateProvider);
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
@ -78,12 +114,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
? Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: AppBar(
title: Text(chapterIndexList.length.toString()),
title: Text(chapterNameList.length.toString()),
backgroundColor: generalColor(context).withOpacity(0.2),
leading: IconButton(
onPressed: () {
ref
.read(chapterIndexListStateProvider.notifier)
.read(chapterNameListStateProvider.notifier)
.clear();
ref
@ -98,23 +134,25 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(chapterIndexListStateProvider
.notifier)
.selectAll(i);
.read(
chapterNameListStateProvider.notifier)
.selectAll(widget
.modelManga!.chapters![i].name!);
}
},
icon: const Icon(Icons.select_all)),
IconButton(
onPressed: () {
if (widget.modelManga!.chapters!.length ==
chapterIndexList.length) {
chapterNameList.length) {
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(chapterIndexListStateProvider
.read(chapterNameListStateProvider
.notifier)
.selectSome(i);
.selectSome(widget
.modelManga!.chapters![i].name!);
}
ref
.read(isLongPressedStateProvider.notifier)
@ -124,9 +162,10 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
i < widget.modelManga!.chapters!.length;
i++) {
ref
.read(chapterIndexListStateProvider
.read(chapterNameListStateProvider
.notifier)
.selectSome(i);
.selectSome(widget
.modelManga!.chapters![i].name!);
}
}
},
@ -145,23 +184,32 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
? 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: () {},
icon: Icon(
Icons.download_outlined,
)),
IconButton(
splashRadius: 20,
onPressed: () {
ref
.read(reverseMangaStateProvider.notifier)
.update(!reverse);
_showModalSort();
},
icon: Icon(
reverse
? Icons.arrow_downward_sharp
: Icons.arrow_upward_sharp,
color: Theme.of(context).hintColor)),
Icons.filter_list_sharp,
)),
PopupMenuButton(
itemBuilder: (context) {
return [
const PopupMenuItem<int>(
value: 0, child: Text("Edit categories")),
const PopupMenuItem<int>(
value: 0, child: Text("Migrate")),
const PopupMenuItem<int>(
value: 0, child: Text("Share")),
];
},
onSelected: (value) {}),
],
)),
body: Stack(
@ -213,29 +261,37 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0, bottom: 60),
itemCount: widget.listLength,
itemCount: _pageLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
if (isRefresh) {
final modelManga = ref
.read(chapterFilterResultStateProvider(
modelManga: widget.modelManga!)
.notifier)
.getData();
int reverseIndex = widget
.modelManga!.chapters!.length -
widget.modelManga!.chapters!.reversed
.toList()
.indexOf(widget
.modelManga!.chapters!.reversed
.toList()[finalIndex]) -
_pageLength = modelManga.chapters!.length + 1;
_chapters = modelManga.chapters;
_modelManga = modelManga;
isRefresh = false;
}
int reverseIndex = _chapters!.length -
_chapters!.reversed.toList().indexOf(
_chapters!.reversed.toList()[finalIndex]) -
1;
List<ModelChapters> chapters = reverse
? widget.modelManga!.chapters!.reversed.toList()
: widget.modelManga!.chapters!;
? _chapters!.reversed.toList()
: _chapters!;
return ChapterListTileWidget(
chapters: chapters,
modelManga: widget.modelManga!,
modelManga: _modelManga!,
reverse: reverse,
reverseIndex: reverseIndex,
finalIndex: finalIndex,
@ -263,7 +319,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () async {
for (var idx in chapterIndexList) {
for (var name in chapterNameList) {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
@ -280,18 +336,19 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
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,
isBookmarked:
name == entries.chapters![i].name
? 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(chapterIndexList);
// print(chapterNameList);
final model = ModelManga(
imageUrl: widget.modelManga!.imageUrl,
name: widget.modelManga!.name,
@ -312,11 +369,13 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterIndexListStateProvider.notifier)
.read(chapterNameListStateProvider.notifier)
.clear();
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
isRefresh = true;
}
},
child: Icon(chapter.isBookmarked
@ -331,7 +390,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () {
for (var idx in chapterIndexList) {
for (var name in chapterNameList) {
List<ModelChapters> chap = [];
for (var i = 0;
i < widget.modelManga!.chapters!.length;
@ -351,7 +410,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
isBookmarked:
entries.chapters![i].isBookmarked,
scanlator: entries.chapters![i].scanlator,
isRead: idx == i
isRead: name == entries.chapters![i].name
? entries.chapters![i].isRead
? false
: true
@ -380,11 +439,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
'${widget.modelManga!.lang}-${widget.modelManga!.link}',
model);
ref
.read(chapterIndexListStateProvider.notifier)
.read(chapterNameListStateProvider.notifier)
.clear();
ref
.read(isLongPressedStateProvider.notifier)
.update(false);
isRefresh = true;
}
},
child: Icon(chapter.isRead
@ -399,7 +459,19 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent),
onPressed: () {
for (var idx in chapterIndexList) {
List<int> indexList = [];
for (var name in chapterNameList) {
for (var i = 0;
i < widget.modelManga!.chapters!.length;
i++) {
if (widget.modelManga!.chapters![i].name ==
name) {
indexList.add(i);
}
}
}
for (var idx in indexList) {
final entries = ref
.watch(hiveBoxMangaDownloads)
.values
@ -423,7 +495,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
.read(isLongPressedStateProvider.notifier)
.update(false);
ref
.read(chapterIndexListStateProvider.notifier)
.read(chapterNameListStateProvider.notifier)
.clear();
},
child: const Icon(Icons.download_outlined)),
@ -435,6 +507,142 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
));
}
_showModalSort() {
late TabController tabBarController;
showMaterialModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5), topRight: Radius.circular(5))),
enableDrag: true,
expand: false,
context: context,
backgroundColor: Colors.transparent,
builder: (context) {
if (!isOk) {
tabBarController = TabController(length: 3, vsync: this);
tabBarController.animateTo(0);
}
return SizedBox(
height: mediaHeight(context, 0.25),
child: DefaultTabController(
length: 3,
child: Scaffold(
body: Column(
children: [
TabBar(
controller: tabBarController,
tabs: const [
Tab(text: "Filter"),
Tab(text: "Sort"),
Tab(text: "Display"),
],
),
Flexible(
child: TabBarView(
controller: tabBarController,
children: [
Consumer(builder: (context, ref, chil) {
return Column(
children: [
ListTileChapterFilter(
label: "Downloaded",
type: ref.watch(
chapterFilterDownloadedStateProvider(
modelManga:
widget.modelManga!)),
onTap: () {
ref
.read(
chapterFilterDownloadedStateProvider(
modelManga: widget
.modelManga!)
.notifier)
.update();
_refreshData();
}),
ListTileChapterFilter(
label: "Unread",
type: ref.watch(
chapterFilterUnreadStateProvider(
modelManga:
widget.modelManga!)),
onTap: () {
ref
.read(
chapterFilterUnreadStateProvider(
modelManga: widget
.modelManga!)
.notifier)
.update();
_refreshData();
}),
ListTileChapterFilter(
label: "Bookmark",
type: ref.watch(
chapterFilterBookmarkStateProvider(
modelManga:
widget.modelManga!)),
onTap: () {
ref
.read(
chapterFilterBookmarkStateProvider(
modelManga: widget
.modelManga!)
.notifier)
.update();
_refreshData();
}),
],
);
}),
Consumer(builder: (context, ref, chil) {
final reverse = ref.watch(
reverseMangaStateProvider(
modelManga: widget.modelManga!));
return Column(
children: [
ListTileChapterSort(
label: "By upload date",
reverse: reverse,
onTap: () {
ref
.read(reverseMangaStateProvider(
modelManga:
widget.modelManga!)
.notifier)
.update(!reverse);
}),
],
);
}),
Consumer(builder: (context, ref, chil) {
return Column(
children: [
RadioListTile(
title: Text("Source title"),
value: "e",
groupValue: "e",
selected: true,
onChanged: (value) {},
),
RadioListTile(
title: Text("Chapter number"),
value: "ej",
groupValue: "e",
selected: false,
onChanged: (value) {},
),
],
);
}),
]),
),
],
),
)));
});
}
Widget _bodyContainer() {
return Stack(
children: [
@ -574,7 +782,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
padding:
const EdgeInsets.symmetric(horizontal: 8),
child: Text(
'${widget.modelManga!.chapters!.length.toString()} chapters',
'${(_pageLength - 1)} chapters',
style: const TextStyle(
fontWeight: FontWeight.bold),
),

View file

@ -1,6 +1,7 @@
import 'dart:developer';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'state_providers.g.dart';
@ -24,13 +25,13 @@ class ChapterModelState extends _$ChapterModelState {
}
@riverpod
class ChapterIndexListState extends _$ChapterIndexListState {
class ChapterNameListState extends _$ChapterNameListState {
@override
List<int> build() {
List<String> build() {
return [];
}
void update(int value) {
void update(String value) {
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
@ -43,7 +44,7 @@ class ChapterIndexListState extends _$ChapterIndexListState {
state = newList;
}
void selectAll(int value) {
void selectAll(String value) {
var newList = state.reversed.toList();
if (!newList.contains(value)) {
newList.add(value);
@ -52,7 +53,7 @@ class ChapterIndexListState extends _$ChapterIndexListState {
state = newList;
}
void selectSome(int value) {
void selectSome(String value) {
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
@ -94,11 +95,567 @@ class IsExtendedState extends _$IsExtendedState {
@riverpod
class ReverseMangaState extends _$ReverseMangaState {
@override
bool build() {
return false;
bool build({required ModelManga modelManga}) {
return ref.watch(hiveBoxSettings).get(
"${modelManga.source}/${modelManga.name}-reverseManga",
defaultValue: false);
}
void update(bool value) {
ref
.watch(hiveBoxSettings)
.put("${modelManga.source}/${modelManga.name}-reverseManga", value);
state = value;
}
}
@riverpod
class ChapterFilterDownloadedState extends _$ChapterFilterDownloadedState {
@override
int build({required ModelManga modelManga}) {
state = getType();
return getType();
}
int getType() {
return ref.watch(hiveBoxSettings).get(
"${modelManga.source}/${modelManga.name}-sortChapterDownload",
defaultValue: 0);
}
void setType(int type) {
ref.watch(hiveBoxSettings).put(
"${modelManga.source}/${modelManga.name}-sortChapterDownload", type);
state = type;
}
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloads)
.get(chapters[i].name, defaultValue: null);
if (modelChapDownload != null && modelChapDownload.isDownload == true) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloads)
.get(chapters[i].name, defaultValue: null);
if (!(modelChapDownload != null &&
modelChapDownload.isDownload == true)) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else {
return modelManga;
}
}
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloads)
.get(chapters[i].name, defaultValue: null);
if (modelChapDownload != null && modelChapDownload.isDownload == true) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(1);
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloads)
.get(chapters[i].name, defaultValue: null);
if (!(modelChapDownload != null &&
modelChapDownload.isDownload == true)) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(2);
return model;
} else {
setType(0);
return modelManga;
}
}
}
@riverpod
class ChapterFilterUnreadState extends _$ChapterFilterUnreadState {
@override
int build({required ModelManga modelManga}) {
state = getType();
return getType();
}
int getType() {
return ref.watch(hiveBoxSettings).get(
"${modelManga.source}/${modelManga.name}-sortChapterUnread",
defaultValue: 0);
}
void setType(int type) {
ref
.watch(hiveBoxSettings)
.put("${modelManga.source}/${modelManga.name}-sortChapterUnread", type);
state = type;
}
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (!chapters[i].isRead) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (chapters[i].isRead) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else {
return modelManga;
}
}
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (!chapters[i].isRead) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(1);
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (chapters[i].isRead) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(2);
return model;
} else {
setType(0);
return modelManga;
}
}
}
@riverpod
class ChapterFilterBookmarkState extends _$ChapterFilterBookmarkState {
@override
int build({required ModelManga modelManga}) {
state = getType();
return getType();
}
int getType() {
return ref.watch(hiveBoxSettings).get(
"${modelManga.source}/${modelManga.name}-sortChapterBookMark",
defaultValue: 0);
}
void setType(int type) {
ref.watch(hiveBoxSettings).put(
"${modelManga.source}/${modelManga.name}-sortChapterBookMark", type);
state = type;
}
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (chapters[i].isBookmarked) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (!chapters[i].isBookmarked) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
return model;
} else {
return modelManga;
}
}
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (chapters[i].isBookmarked) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(1);
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
final chapters = modelManga.chapters;
for (var i = 0; i < chapters!.length; i++) {
if (!chapters[i].isBookmarked) {
chap.add(ModelChapters(
name: chapters[i].name,
url: chapters[i].url,
dateUpload: chapters[i].dateUpload,
isBookmarked: chapters[i].isBookmarked,
scanlator: chapters[i].scanlator,
isRead: chapters[i].isRead,
lastPageRead: chapters[i].lastPageRead));
}
}
final model = ModelManga(
imageUrl: modelManga.imageUrl,
name: modelManga.name,
genre: modelManga.genre,
author: modelManga.author,
description: modelManga.description,
status: modelManga.status,
favorite: modelManga.favorite,
link: modelManga.link,
source: modelManga.source,
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chap,
category: modelManga.category,
lastRead: modelManga.lastRead);
setType(2);
return model;
} else {
setType(0);
return modelManga;
}
}
}
@riverpod
class ChapterFilterResultState extends _$ChapterFilterResultState {
@override
ModelManga build({required ModelManga modelManga}) {
final data1 = ref
.read(
chapterFilterDownloadedStateProvider(modelManga: modelManga).notifier)
.getData();
final data2 = ref
.read(chapterFilterUnreadStateProvider(modelManga: data1).notifier)
.getData();
final data3 = ref
.read(chapterFilterBookmarkStateProvider(modelManga: data2).notifier)
.getData();
return data3;
}
ModelManga getData() {
final data1 = ref
.read(
chapterFilterDownloadedStateProvider(modelManga: modelManga).notifier)
.getData();
final data2 = ref
.read(chapterFilterUnreadStateProvider(modelManga: data1).notifier)
.getData();
final data3 = ref
.read(chapterFilterBookmarkStateProvider(modelManga: data2).notifier)
.getData();
return data3;
}
}

View file

@ -22,23 +22,23 @@ final chapterModelStateProvider =
);
typedef _$ChapterModelState = AutoDisposeNotifier<ModelChapters>;
String _$chapterIndexListStateHash() =>
r'cef615a9638df5a2a72c6087f479824cc169a70e';
String _$chapterNameListStateHash() =>
r'7ad81711d912271910489528b88b8d473c1d9c60';
/// See also [ChapterIndexListState].
@ProviderFor(ChapterIndexListState)
final chapterIndexListStateProvider =
AutoDisposeNotifierProvider<ChapterIndexListState, List<int>>.internal(
ChapterIndexListState.new,
name: r'chapterIndexListStateProvider',
/// See also [ChapterNameListState].
@ProviderFor(ChapterNameListState)
final chapterNameListStateProvider =
AutoDisposeNotifierProvider<ChapterNameListState, List<String>>.internal(
ChapterNameListState.new,
name: r'chapterNameListStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterIndexListStateHash,
: _$chapterNameListStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ChapterIndexListState = AutoDisposeNotifier<List<int>>;
typedef _$ChapterNameListState = AutoDisposeNotifier<List<String>>;
String _$isLongPressedStateHash() =>
r'26fe435e8381046a30e3f6c4495303946aa3aaa7';
@ -72,20 +72,517 @@ final isExtendedStateProvider =
);
typedef _$IsExtendedState = AutoDisposeNotifier<bool>;
String _$reverseMangaStateHash() => r'bb1372869f122059ef962b9ff71d84e9f42bbe3b';
String _$reverseMangaStateHash() => r'f00b2dac8e461bd1f1a32fbf1ce4994ac4d96529';
/// 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));
}
}
abstract class _$ReverseMangaState extends BuildlessAutoDisposeNotifier<bool> {
late final ModelManga modelManga;
bool build({
required ModelManga modelManga,
});
}
/// See also [ReverseMangaState].
@ProviderFor(ReverseMangaState)
final reverseMangaStateProvider =
AutoDisposeNotifierProvider<ReverseMangaState, bool>.internal(
ReverseMangaState.new,
name: r'reverseMangaStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$reverseMangaStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
const reverseMangaStateProvider = ReverseMangaStateFamily();
typedef _$ReverseMangaState = AutoDisposeNotifier<bool>;
/// See also [ReverseMangaState].
class ReverseMangaStateFamily extends Family<bool> {
/// See also [ReverseMangaState].
const ReverseMangaStateFamily();
/// See also [ReverseMangaState].
ReverseMangaStateProvider call({
required ModelManga modelManga,
}) {
return ReverseMangaStateProvider(
modelManga: modelManga,
);
}
@override
ReverseMangaStateProvider getProviderOverride(
covariant ReverseMangaStateProvider provider,
) {
return call(
modelManga: provider.modelManga,
);
}
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'reverseMangaStateProvider';
}
/// See also [ReverseMangaState].
class ReverseMangaStateProvider
extends AutoDisposeNotifierProviderImpl<ReverseMangaState, bool> {
/// See also [ReverseMangaState].
ReverseMangaStateProvider({
required this.modelManga,
}) : super.internal(
() => ReverseMangaState()..modelManga = modelManga,
from: reverseMangaStateProvider,
name: r'reverseMangaStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$reverseMangaStateHash,
dependencies: ReverseMangaStateFamily._dependencies,
allTransitiveDependencies:
ReverseMangaStateFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
@override
bool operator ==(Object other) {
return other is ReverseMangaStateProvider && other.modelManga == modelManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
return _SystemHash.finish(hash);
}
@override
bool runNotifierBuild(
covariant ReverseMangaState notifier,
) {
return notifier.build(
modelManga: modelManga,
);
}
}
String _$chapterFilterDownloadedStateHash() =>
r'9c0937b3deaa6982ec7a8c9ab5d55a99a059cd80';
abstract class _$ChapterFilterDownloadedState
extends BuildlessAutoDisposeNotifier<int> {
late final ModelManga modelManga;
int build({
required ModelManga modelManga,
});
}
/// See also [ChapterFilterDownloadedState].
@ProviderFor(ChapterFilterDownloadedState)
const chapterFilterDownloadedStateProvider =
ChapterFilterDownloadedStateFamily();
/// See also [ChapterFilterDownloadedState].
class ChapterFilterDownloadedStateFamily extends Family<int> {
/// See also [ChapterFilterDownloadedState].
const ChapterFilterDownloadedStateFamily();
/// See also [ChapterFilterDownloadedState].
ChapterFilterDownloadedStateProvider call({
required ModelManga modelManga,
}) {
return ChapterFilterDownloadedStateProvider(
modelManga: modelManga,
);
}
@override
ChapterFilterDownloadedStateProvider getProviderOverride(
covariant ChapterFilterDownloadedStateProvider provider,
) {
return call(
modelManga: provider.modelManga,
);
}
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'chapterFilterDownloadedStateProvider';
}
/// See also [ChapterFilterDownloadedState].
class ChapterFilterDownloadedStateProvider
extends AutoDisposeNotifierProviderImpl<ChapterFilterDownloadedState, int> {
/// See also [ChapterFilterDownloadedState].
ChapterFilterDownloadedStateProvider({
required this.modelManga,
}) : super.internal(
() => ChapterFilterDownloadedState()..modelManga = modelManga,
from: chapterFilterDownloadedStateProvider,
name: r'chapterFilterDownloadedStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterFilterDownloadedStateHash,
dependencies: ChapterFilterDownloadedStateFamily._dependencies,
allTransitiveDependencies:
ChapterFilterDownloadedStateFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
@override
bool operator ==(Object other) {
return other is ChapterFilterDownloadedStateProvider &&
other.modelManga == modelManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
return _SystemHash.finish(hash);
}
@override
int runNotifierBuild(
covariant ChapterFilterDownloadedState notifier,
) {
return notifier.build(
modelManga: modelManga,
);
}
}
String _$chapterFilterUnreadStateHash() =>
r'98e86b05fdcb874b551329ffdf322681f46fb6ce';
abstract class _$ChapterFilterUnreadState
extends BuildlessAutoDisposeNotifier<int> {
late final ModelManga modelManga;
int build({
required ModelManga modelManga,
});
}
/// See also [ChapterFilterUnreadState].
@ProviderFor(ChapterFilterUnreadState)
const chapterFilterUnreadStateProvider = ChapterFilterUnreadStateFamily();
/// See also [ChapterFilterUnreadState].
class ChapterFilterUnreadStateFamily extends Family<int> {
/// See also [ChapterFilterUnreadState].
const ChapterFilterUnreadStateFamily();
/// See also [ChapterFilterUnreadState].
ChapterFilterUnreadStateProvider call({
required ModelManga modelManga,
}) {
return ChapterFilterUnreadStateProvider(
modelManga: modelManga,
);
}
@override
ChapterFilterUnreadStateProvider getProviderOverride(
covariant ChapterFilterUnreadStateProvider provider,
) {
return call(
modelManga: provider.modelManga,
);
}
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'chapterFilterUnreadStateProvider';
}
/// See also [ChapterFilterUnreadState].
class ChapterFilterUnreadStateProvider
extends AutoDisposeNotifierProviderImpl<ChapterFilterUnreadState, int> {
/// See also [ChapterFilterUnreadState].
ChapterFilterUnreadStateProvider({
required this.modelManga,
}) : super.internal(
() => ChapterFilterUnreadState()..modelManga = modelManga,
from: chapterFilterUnreadStateProvider,
name: r'chapterFilterUnreadStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterFilterUnreadStateHash,
dependencies: ChapterFilterUnreadStateFamily._dependencies,
allTransitiveDependencies:
ChapterFilterUnreadStateFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
@override
bool operator ==(Object other) {
return other is ChapterFilterUnreadStateProvider &&
other.modelManga == modelManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
return _SystemHash.finish(hash);
}
@override
int runNotifierBuild(
covariant ChapterFilterUnreadState notifier,
) {
return notifier.build(
modelManga: modelManga,
);
}
}
String _$chapterFilterBookmarkStateHash() =>
r'6fb5d0d506bc209bb6d6fea8e665f511b8ef0e8f';
abstract class _$ChapterFilterBookmarkState
extends BuildlessAutoDisposeNotifier<int> {
late final ModelManga modelManga;
int build({
required ModelManga modelManga,
});
}
/// See also [ChapterFilterBookmarkState].
@ProviderFor(ChapterFilterBookmarkState)
const chapterFilterBookmarkStateProvider = ChapterFilterBookmarkStateFamily();
/// See also [ChapterFilterBookmarkState].
class ChapterFilterBookmarkStateFamily extends Family<int> {
/// See also [ChapterFilterBookmarkState].
const ChapterFilterBookmarkStateFamily();
/// See also [ChapterFilterBookmarkState].
ChapterFilterBookmarkStateProvider call({
required ModelManga modelManga,
}) {
return ChapterFilterBookmarkStateProvider(
modelManga: modelManga,
);
}
@override
ChapterFilterBookmarkStateProvider getProviderOverride(
covariant ChapterFilterBookmarkStateProvider provider,
) {
return call(
modelManga: provider.modelManga,
);
}
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'chapterFilterBookmarkStateProvider';
}
/// See also [ChapterFilterBookmarkState].
class ChapterFilterBookmarkStateProvider
extends AutoDisposeNotifierProviderImpl<ChapterFilterBookmarkState, int> {
/// See also [ChapterFilterBookmarkState].
ChapterFilterBookmarkStateProvider({
required this.modelManga,
}) : super.internal(
() => ChapterFilterBookmarkState()..modelManga = modelManga,
from: chapterFilterBookmarkStateProvider,
name: r'chapterFilterBookmarkStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterFilterBookmarkStateHash,
dependencies: ChapterFilterBookmarkStateFamily._dependencies,
allTransitiveDependencies:
ChapterFilterBookmarkStateFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
@override
bool operator ==(Object other) {
return other is ChapterFilterBookmarkStateProvider &&
other.modelManga == modelManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
return _SystemHash.finish(hash);
}
@override
int runNotifierBuild(
covariant ChapterFilterBookmarkState notifier,
) {
return notifier.build(
modelManga: modelManga,
);
}
}
String _$chapterFilterResultStateHash() =>
r'ddb83c55bbbbd00178f582875a9b72fcec0eae5a';
abstract class _$ChapterFilterResultState
extends BuildlessAutoDisposeNotifier<ModelManga> {
late final ModelManga modelManga;
ModelManga build({
required ModelManga modelManga,
});
}
/// See also [ChapterFilterResultState].
@ProviderFor(ChapterFilterResultState)
const chapterFilterResultStateProvider = ChapterFilterResultStateFamily();
/// See also [ChapterFilterResultState].
class ChapterFilterResultStateFamily extends Family<ModelManga> {
/// See also [ChapterFilterResultState].
const ChapterFilterResultStateFamily();
/// See also [ChapterFilterResultState].
ChapterFilterResultStateProvider call({
required ModelManga modelManga,
}) {
return ChapterFilterResultStateProvider(
modelManga: modelManga,
);
}
@override
ChapterFilterResultStateProvider getProviderOverride(
covariant ChapterFilterResultStateProvider provider,
) {
return call(
modelManga: provider.modelManga,
);
}
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'chapterFilterResultStateProvider';
}
/// See also [ChapterFilterResultState].
class ChapterFilterResultStateProvider extends AutoDisposeNotifierProviderImpl<
ChapterFilterResultState, ModelManga> {
/// See also [ChapterFilterResultState].
ChapterFilterResultStateProvider({
required this.modelManga,
}) : super.internal(
() => ChapterFilterResultState()..modelManga = modelManga,
from: chapterFilterResultStateProvider,
name: r'chapterFilterResultStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterFilterResultStateHash,
dependencies: ChapterFilterResultStateFamily._dependencies,
allTransitiveDependencies:
ChapterFilterResultStateFamily._allTransitiveDependencies,
);
final ModelManga modelManga;
@override
bool operator ==(Object other) {
return other is ChapterFilterResultStateProvider &&
other.modelManga == modelManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
return _SystemHash.finish(hash);
}
@override
ModelManga runNotifierBuild(
covariant ChapterFilterResultState notifier,
) {
return notifier.build(
modelManga: modelManga,
);
}
}
// 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

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
class ListTileChapterFilter extends StatelessWidget {
final String label;
final int type;
final VoidCallback onTap;
const ListTileChapterFilter(
{super.key,
required this.label,
required this.type,
required this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
iconColor: Theme.of(context).primaryColor,
dense: true,
leading: type == 0
? SizedBox(
height: 20, width: 20, child: Icon(Icons.check_box_outline_blank))
: type == 1
? SizedBox(height: 20, width: 20, child: Icon(Icons.check_box))
: Stack(
children: [
SizedBox(
height: 20,
width: 20,
child: Icon(Icons.check_box_outline_blank)),
Positioned(
top: 3,
left: 2,
right: 0,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(2)),
height: 18,
width: 17,
child: Icon(
Icons.clear,
color: Colors.black,
size: 18,
),
),
)
],
),
title: Text(
label,
style: TextStyle(fontSize: 14),
),
onTap: onTap,
);
}
}

View file

@ -28,10 +28,10 @@ class ChapterListTileWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final idx = reverse ? reverseIndex : finalIndex;
final chapterIndexList = ref.watch(chapterIndexListStateProvider);
final chapterNameList = ref.watch(chapterNameListStateProvider);
final chapterName = modelManga.chapters![idx].name;
return Container(
color: chapterIndexList.contains(idx)
color: chapterNameList.contains(chapterName)
? generalColor(context).withOpacity(0.4)
: null,
child: ListTile(
@ -45,7 +45,9 @@ class ChapterListTileWidget extends ConsumerWidget {
: Colors.white,
onLongPress: () {
if (!isLongPressed) {
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterNameListStateProvider.notifier)
.update(chapterName!);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
@ -53,7 +55,9 @@ class ChapterListTileWidget extends ConsumerWidget {
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
} else {
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterNameListStateProvider.notifier)
.update(chapterName!);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
@ -61,7 +65,9 @@ class ChapterListTileWidget extends ConsumerWidget {
},
onTap: () async {
if (isLongPressed) {
ref.read(chapterIndexListStateProvider.notifier).update(idx);
ref
.read(chapterNameListStateProvider.notifier)
.update(chapterName!);
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);

View file

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
class ListTileChapterSort extends StatelessWidget {
final String label;
final bool reverse;
final VoidCallback onTap;
const ListTileChapterSort(
{super.key,
required this.label,
required this.reverse,
required this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
iconColor: Theme.of(context).primaryColor,
dense: true,
leading: Icon(
reverse ? Icons.arrow_downward_sharp : Icons.arrow_upward_sharp,
color: Theme.of(context).hintColor),
title: Text(
label,
style: TextStyle(fontSize: 14),
),
onTap: onTap,
);
}
}

View file

@ -182,7 +182,7 @@ class CurrentIndexProvider
}
}
String _$readerControllerHash() => r'01306848356204f0716ab763839d6c9e66051dd1';
String _$readerControllerHash() => r'2528e56dfbf71a996ceeada7075eff571b621080';
abstract class _$ReaderController extends BuildlessAutoDisposeNotifier<void> {
late final MangaReaderModel mangaReaderModel;

View file

@ -7,7 +7,7 @@ part of 'flex_scheme_color_state_provider.dart';
// **************************************************************************
String _$flexSchemeColorStateHash() =>
r'8d5b648c7b6d4d86f2a484bed97477b82112cc9e';
r'825680702e419e5eb921e251312ffd4ba3303a2c';
/// See also [FlexSchemeColorState].
@ProviderFor(FlexSchemeColorState)