mirror of
https://github.com/venera-app/venera.git
synced 2025-12-15 14:41:15 +00:00
@@ -194,6 +194,7 @@ class Settings with ChangeNotifier {
|
||||
'readerScrollSpeed': 1.0, // 0.5 - 3.0
|
||||
'localFavoritesFirst': true,
|
||||
'autoCloseFavoritePanel': false,
|
||||
'showChapterComments': true, // show chapter comments in reader
|
||||
};
|
||||
|
||||
operator [](String key) {
|
||||
@@ -207,7 +208,11 @@ class Settings with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void setEnabledComicSpecificSettings(String comicId, String sourceKey, bool enabled) {
|
||||
void setEnabledComicSpecificSettings(
|
||||
String comicId,
|
||||
String sourceKey,
|
||||
bool enabled,
|
||||
) {
|
||||
setReaderSetting(comicId, sourceKey, "enabled", enabled);
|
||||
}
|
||||
|
||||
@@ -215,7 +220,8 @@ class Settings with ChangeNotifier {
|
||||
if (comicId == null || sourceKey == null) {
|
||||
return false;
|
||||
}
|
||||
return _data['comicSpecificSettings']["$comicId@$sourceKey"]?["enabled"] == true;
|
||||
return _data['comicSpecificSettings']["$comicId@$sourceKey"]?["enabled"] ==
|
||||
true;
|
||||
}
|
||||
|
||||
dynamic getReaderSetting(String comicId, String sourceKey, String key) {
|
||||
|
||||
@@ -61,8 +61,10 @@ class ComicSourceManager with ChangeNotifier, Init {
|
||||
await for (var entity in Directory(path).list()) {
|
||||
if (entity is File && entity.path.endsWith(".js")) {
|
||||
try {
|
||||
var source = await ComicSourceParser()
|
||||
.parse(await entity.readAsString(), entity.absolute.path);
|
||||
var source = await ComicSourceParser().parse(
|
||||
await entity.readAsString(),
|
||||
entity.absolute.path,
|
||||
);
|
||||
_sources.add(source);
|
||||
} catch (e, s) {
|
||||
Log.error("ComicSource", "$e\n$s");
|
||||
@@ -154,7 +156,7 @@ class ComicSource {
|
||||
final GetImageLoadingConfigFunc? getImageLoadingConfig;
|
||||
|
||||
final Map<String, dynamic> Function(String imageKey)?
|
||||
getThumbnailLoadingConfig;
|
||||
getThumbnailLoadingConfig;
|
||||
|
||||
var data = <String, dynamic>{};
|
||||
|
||||
@@ -170,6 +172,10 @@ class ComicSource {
|
||||
|
||||
final SendCommentFunc? sendCommentFunc;
|
||||
|
||||
final ChapterCommentsLoader? chapterCommentsLoader;
|
||||
|
||||
final SendChapterCommentFunc? sendChapterCommentFunc;
|
||||
|
||||
final RegExp? idMatcher;
|
||||
|
||||
final LikeOrUnlikeComicFunc? likeOrUnlikeComic;
|
||||
@@ -256,6 +262,8 @@ class ComicSource {
|
||||
this.version,
|
||||
this.commentsLoader,
|
||||
this.sendCommentFunc,
|
||||
this.chapterCommentsLoader,
|
||||
this.sendChapterCommentFunc,
|
||||
this.likeOrUnlikeComic,
|
||||
this.voteCommentFunc,
|
||||
this.likeCommentFunc,
|
||||
@@ -367,11 +375,19 @@ enum ExplorePageType {
|
||||
override,
|
||||
}
|
||||
|
||||
typedef SearchFunction = Future<Res<List<Comic>>> Function(
|
||||
String keyword, int page, List<String> searchOption);
|
||||
typedef SearchFunction =
|
||||
Future<Res<List<Comic>>> Function(
|
||||
String keyword,
|
||||
int page,
|
||||
List<String> searchOption,
|
||||
);
|
||||
|
||||
typedef SearchNextFunction = Future<Res<List<Comic>>> Function(
|
||||
String keyword, String? next, List<String> searchOption);
|
||||
typedef SearchNextFunction =
|
||||
Future<Res<List<Comic>>> Function(
|
||||
String keyword,
|
||||
String? next,
|
||||
List<String> searchOption,
|
||||
);
|
||||
|
||||
class SearchPageData {
|
||||
/// If this is not null, the default value of search options will be first element.
|
||||
@@ -398,11 +414,19 @@ class SearchOptions {
|
||||
String get defaultValue => defaultVal ?? options.keys.firstOrNull ?? "";
|
||||
}
|
||||
|
||||
typedef CategoryComicsLoader = Future<Res<List<Comic>>> Function(
|
||||
String category, String? param, List<String> options, int page);
|
||||
typedef CategoryComicsLoader =
|
||||
Future<Res<List<Comic>>> Function(
|
||||
String category,
|
||||
String? param,
|
||||
List<String> options,
|
||||
int page,
|
||||
);
|
||||
|
||||
typedef CategoryOptionsLoader = Future<Res<List<CategoryComicsOptions>>> Function(
|
||||
String category, String? param);
|
||||
typedef CategoryOptionsLoader =
|
||||
Future<Res<List<CategoryComicsOptions>>> Function(
|
||||
String category,
|
||||
String? param,
|
||||
);
|
||||
|
||||
class CategoryComicsData {
|
||||
/// options
|
||||
@@ -419,7 +443,12 @@ class CategoryComicsData {
|
||||
|
||||
final RankingData? rankingData;
|
||||
|
||||
const CategoryComicsData({this.options, this.optionsLoader, required this.load, this.rankingData});
|
||||
const CategoryComicsData({
|
||||
this.options,
|
||||
this.optionsLoader,
|
||||
required this.load,
|
||||
this.rankingData,
|
||||
});
|
||||
}
|
||||
|
||||
class RankingData {
|
||||
@@ -428,7 +457,7 @@ class RankingData {
|
||||
final Future<Res<List<Comic>>> Function(String option, int page)? load;
|
||||
|
||||
final Future<Res<List<Comic>>> Function(String option, String? next)?
|
||||
loadWithNext;
|
||||
loadWithNext;
|
||||
|
||||
const RankingData(this.options, this.load, this.loadWithNext);
|
||||
}
|
||||
@@ -447,7 +476,12 @@ class CategoryComicsOptions {
|
||||
|
||||
final List<String>? showWhen;
|
||||
|
||||
const CategoryComicsOptions(this.label, this.options, this.notShowWhen, this.showWhen);
|
||||
const CategoryComicsOptions(
|
||||
this.label,
|
||||
this.options,
|
||||
this.notShowWhen,
|
||||
this.showWhen,
|
||||
);
|
||||
}
|
||||
|
||||
class LinkHandler {
|
||||
|
||||
@@ -151,6 +151,8 @@ class ComicSourceParser {
|
||||
version ?? "1.0.0",
|
||||
_parseCommentsLoader(),
|
||||
_parseSendCommentFunc(),
|
||||
_parseChapterCommentsLoader(),
|
||||
_parseSendChapterCommentFunc(),
|
||||
_parseLikeFunc(),
|
||||
_parseVoteCommentFunc(),
|
||||
_parseLikeCommentFunc(),
|
||||
@@ -560,12 +562,16 @@ class ComicSourceParser {
|
||||
res = await res;
|
||||
}
|
||||
if (res is! List) {
|
||||
return Res.error("Invalid data:\nExpected: List\nGot: ${res.runtimeType}");
|
||||
return Res.error(
|
||||
"Invalid data:\nExpected: List\nGot: ${res.runtimeType}",
|
||||
);
|
||||
}
|
||||
var options = <CategoryComicsOptions>[];
|
||||
for (var element in res) {
|
||||
if (element is! Map) {
|
||||
return Res.error("Invalid option data:\nExpected: Map\nGot: ${element.runtimeType}");
|
||||
return Res.error(
|
||||
"Invalid option data:\nExpected: Map\nGot: ${element.runtimeType}",
|
||||
);
|
||||
}
|
||||
LinkedHashMap<String, String> map = LinkedHashMap<String, String>();
|
||||
for (var option in element["options"] ?? []) {
|
||||
@@ -582,13 +588,14 @@ class ComicSourceParser {
|
||||
element["label"] ?? "",
|
||||
map,
|
||||
List.from(element["notShowWhen"] ?? []),
|
||||
element["showWhen"] == null ? null : List.from(element["showWhen"]),
|
||||
element["showWhen"] == null
|
||||
? null
|
||||
: List.from(element["showWhen"]),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Res(options);
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
Log.error("Data Analysis", "Failed to load category options.\n$e");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
@@ -1005,6 +1012,54 @@ class ComicSourceParser {
|
||||
};
|
||||
}
|
||||
|
||||
ChapterCommentsLoader? _parseChapterCommentsLoader() {
|
||||
if (!_checkExists("comic.loadChapterComments")) return null;
|
||||
return (comicId, epId, page, replyTo) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.loadChapterComments(
|
||||
${jsonEncode(comicId)}, ${jsonEncode(epId)}, ${jsonEncode(page)}, ${jsonEncode(replyTo)})
|
||||
""");
|
||||
return Res(
|
||||
(res["comments"] as List).map((e) => Comment.fromJson(e)).toList(),
|
||||
subData: res["maxPage"],
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SendChapterCommentFunc? _parseSendChapterCommentFunc() {
|
||||
if (!_checkExists("comic.sendChapterComment")) return null;
|
||||
return (comicId, epId, content, replyTo) async {
|
||||
Future<Res<bool>> func() async {
|
||||
try {
|
||||
await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.sendChapterComment(
|
||||
${jsonEncode(comicId)}, ${jsonEncode(epId)}, ${jsonEncode(content)}, ${jsonEncode(replyTo)})
|
||||
""");
|
||||
return const Res(true);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
var res = await func();
|
||||
if (res.error && res.errorMessage!.contains("Login expired")) {
|
||||
var reLoginRes = await ComicSource.find(_key!)!.reLogin();
|
||||
if (!reLoginRes) {
|
||||
return const Res.error("Login expired and re-login failed");
|
||||
} else {
|
||||
return func();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
GetImageLoadingConfigFunc? _parseImageLoadingConfigFunc() {
|
||||
if (!_checkExists("comic.onImageLoad")) {
|
||||
return null;
|
||||
|
||||
@@ -4,50 +4,90 @@ part of 'comic_source.dart';
|
||||
typedef ComicListBuilder = Future<Res<List<Comic>>> Function(int page);
|
||||
|
||||
/// build comic list with next param, [Res.subData] should be next page param or null if there is no next page.
|
||||
typedef ComicListBuilderWithNext = Future<Res<List<Comic>>> Function(
|
||||
String? next);
|
||||
typedef ComicListBuilderWithNext =
|
||||
Future<Res<List<Comic>>> Function(String? next);
|
||||
|
||||
typedef LoginFunction = Future<Res<bool>> Function(String, String);
|
||||
|
||||
typedef LoadComicFunc = Future<Res<ComicDetails>> Function(String id);
|
||||
|
||||
typedef LoadComicPagesFunc = Future<Res<List<String>>> Function(
|
||||
String id, String? ep);
|
||||
typedef LoadComicPagesFunc =
|
||||
Future<Res<List<String>>> Function(String id, String? ep);
|
||||
|
||||
typedef CommentsLoader = Future<Res<List<Comment>>> Function(
|
||||
String id, String? subId, int page, String? replyTo);
|
||||
typedef CommentsLoader =
|
||||
Future<Res<List<Comment>>> Function(
|
||||
String id,
|
||||
String? subId,
|
||||
int page,
|
||||
String? replyTo,
|
||||
);
|
||||
|
||||
typedef SendCommentFunc = Future<Res<bool>> Function(
|
||||
String id, String? subId, String content, String? replyTo);
|
||||
typedef ChapterCommentsLoader =
|
||||
Future<Res<List<Comment>>> Function(
|
||||
String comicId,
|
||||
String epId,
|
||||
int page,
|
||||
String? replyTo,
|
||||
);
|
||||
|
||||
typedef GetImageLoadingConfigFunc = Future<Map<String, dynamic>> Function(
|
||||
String imageKey, String comicId, String epId)?;
|
||||
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
||||
String imageKey)?;
|
||||
typedef SendCommentFunc =
|
||||
Future<Res<bool>> Function(
|
||||
String id,
|
||||
String? subId,
|
||||
String content,
|
||||
String? replyTo,
|
||||
);
|
||||
|
||||
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(
|
||||
String comicId, String? next);
|
||||
typedef SendChapterCommentFunc =
|
||||
Future<Res<bool>> Function(
|
||||
String comicId,
|
||||
String epId,
|
||||
String content,
|
||||
String? replyTo,
|
||||
);
|
||||
|
||||
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(
|
||||
String comicId, bool isLiking);
|
||||
typedef GetImageLoadingConfigFunc =
|
||||
Future<Map<String, dynamic>> Function(
|
||||
String imageKey,
|
||||
String comicId,
|
||||
String epId,
|
||||
)?;
|
||||
typedef GetThumbnailLoadingConfigFunc =
|
||||
Map<String, dynamic> Function(String imageKey)?;
|
||||
|
||||
typedef ComicThumbnailLoader =
|
||||
Future<Res<List<String>>> Function(String comicId, String? next);
|
||||
|
||||
typedef LikeOrUnlikeComicFunc =
|
||||
Future<Res<bool>> Function(String comicId, bool isLiking);
|
||||
|
||||
/// [isLiking] is true if the user is liking the comment, false if unliking.
|
||||
/// return the new likes count or null.
|
||||
typedef LikeCommentFunc = Future<Res<int?>> Function(
|
||||
String comicId, String? subId, String commentId, bool isLiking);
|
||||
typedef LikeCommentFunc =
|
||||
Future<Res<int?>> Function(
|
||||
String comicId,
|
||||
String? subId,
|
||||
String commentId,
|
||||
bool isLiking,
|
||||
);
|
||||
|
||||
/// [isUp] is true if the user is upvoting the comment, false if downvoting.
|
||||
/// return the new vote count or null.
|
||||
typedef VoteCommentFunc = Future<Res<int?>> Function(
|
||||
String comicId, String? subId, String commentId, bool isUp, bool isCancel);
|
||||
typedef VoteCommentFunc =
|
||||
Future<Res<int?>> Function(
|
||||
String comicId,
|
||||
String? subId,
|
||||
String commentId,
|
||||
bool isUp,
|
||||
bool isCancel,
|
||||
);
|
||||
|
||||
typedef HandleClickTagEvent = PageJumpTarget? Function(
|
||||
String namespace, String tag);
|
||||
typedef HandleClickTagEvent =
|
||||
PageJumpTarget? Function(String namespace, String tag);
|
||||
|
||||
/// Handle tag suggestion selection event. Should return the text to insert
|
||||
/// into the search field.
|
||||
typedef TagSuggestionSelectFunc = String Function(
|
||||
String namespace, String tag);
|
||||
typedef TagSuggestionSelectFunc = String Function(String namespace, String tag);
|
||||
|
||||
/// [rating] is the rating value, 0-10. 1 represents 0.5 star.
|
||||
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);
|
||||
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);
|
||||
|
||||
Reference in New Issue
Block a user