mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
comments
This commit is contained in:
@@ -17,10 +17,9 @@ import '../js_engine.dart';
|
||||
import '../log.dart';
|
||||
|
||||
part 'category.dart';
|
||||
|
||||
part 'favorites.dart';
|
||||
|
||||
part 'parser.dart';
|
||||
part 'models.dart';
|
||||
|
||||
/// build comic list, [Res.subData] should be maxPage or null if there is no limit.
|
||||
typedef ComicListBuilder = Future<Res<List<Comic>>> Function(int page);
|
||||
@@ -43,9 +42,19 @@ typedef GetImageLoadingConfigFunc = Map<String, dynamic> Function(
|
||||
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
||||
String imageKey)?;
|
||||
|
||||
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(String comicId, String? next);
|
||||
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(
|
||||
String comicId, String? next);
|
||||
|
||||
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(String comicId, bool isLiking);
|
||||
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);
|
||||
|
||||
/// [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);
|
||||
|
||||
class ComicSource {
|
||||
static final List<ComicSource> _sources = [];
|
||||
@@ -155,8 +164,6 @@ class ComicSource {
|
||||
final Map<String, dynamic> Function(String imageKey)?
|
||||
getThumbnailLoadingConfig;
|
||||
|
||||
final String? matchBriefIdReg;
|
||||
|
||||
var data = <String, dynamic>{};
|
||||
|
||||
bool get isLogged => data["account"] != null;
|
||||
@@ -175,6 +182,10 @@ class ComicSource {
|
||||
|
||||
final LikeOrUnlikeComicFunc? likeOrUnlikeComic;
|
||||
|
||||
final VoteCommentFunc? voteCommentFunc;
|
||||
|
||||
final LikeCommentFunc? likeCommentFunc;
|
||||
|
||||
Future<void> loadData() async {
|
||||
var file = File("${App.dataPath}/comic_source/$key.data");
|
||||
if (await file.exists()) {
|
||||
@@ -228,37 +239,15 @@ class ComicSource {
|
||||
this.loadComicPages,
|
||||
this.getImageLoadingConfig,
|
||||
this.getThumbnailLoadingConfig,
|
||||
this.matchBriefIdReg,
|
||||
this.filePath,
|
||||
this.url,
|
||||
this.version,
|
||||
this.commentsLoader,
|
||||
this.sendCommentFunc,
|
||||
this.likeOrUnlikeComic)
|
||||
this.likeOrUnlikeComic,
|
||||
this.voteCommentFunc,
|
||||
this.likeCommentFunc,)
|
||||
: idMatcher = null;
|
||||
|
||||
ComicSource.unknown(this.key)
|
||||
: name = "Unknown",
|
||||
account = null,
|
||||
categoryData = null,
|
||||
categoryComicsData = null,
|
||||
favoriteData = null,
|
||||
explorePages = [],
|
||||
searchPageData = null,
|
||||
settings = [],
|
||||
loadComicInfo = null,
|
||||
loadComicThumbnail = null,
|
||||
loadComicPages = null,
|
||||
getImageLoadingConfig = null,
|
||||
getThumbnailLoadingConfig = null,
|
||||
matchBriefIdReg = null,
|
||||
filePath = "",
|
||||
url = "",
|
||||
version = "",
|
||||
commentsLoader = null,
|
||||
sendCommentFunc = null,
|
||||
idMatcher = null,
|
||||
likeOrUnlikeComic = null;
|
||||
}
|
||||
|
||||
class AccountConfig {
|
||||
@@ -394,131 +383,6 @@ enum SettingType {
|
||||
input,
|
||||
}
|
||||
|
||||
class Comic {
|
||||
final String title;
|
||||
|
||||
final String cover;
|
||||
|
||||
final String id;
|
||||
|
||||
final String? subtitle;
|
||||
|
||||
final List<String>? tags;
|
||||
|
||||
final String description;
|
||||
|
||||
final String sourceKey;
|
||||
|
||||
final int? maxPage;
|
||||
|
||||
const Comic(this.title, this.cover, this.id, this.subtitle, this.tags,
|
||||
this.description, this.sourceKey, this.maxPage);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"title": title,
|
||||
"cover": cover,
|
||||
"id": id,
|
||||
"subTitle": subtitle,
|
||||
"tags": tags,
|
||||
"description": description,
|
||||
"sourceKey": sourceKey,
|
||||
"maxPage": maxPage,
|
||||
};
|
||||
}
|
||||
|
||||
Comic.fromJson(Map<String, dynamic> json, this.sourceKey)
|
||||
: title = json["title"],
|
||||
subtitle = json["subTitle"] ?? "",
|
||||
cover = json["cover"],
|
||||
id = json["id"],
|
||||
tags = List<String>.from(json["tags"] ?? []),
|
||||
description = json["description"] ?? "",
|
||||
maxPage = json["maxPage"];
|
||||
}
|
||||
|
||||
class ComicDetails with HistoryMixin {
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
final String? subTitle;
|
||||
|
||||
@override
|
||||
final String cover;
|
||||
|
||||
final String? description;
|
||||
|
||||
final Map<String, List<String>> tags;
|
||||
|
||||
/// id-name
|
||||
final Map<String, String>? chapters;
|
||||
|
||||
final List<String>? thumbnails;
|
||||
|
||||
final List<Comic>? recommend;
|
||||
|
||||
final String sourceKey;
|
||||
|
||||
final String comicId;
|
||||
|
||||
final bool? isFavorite;
|
||||
|
||||
final String? subId;
|
||||
|
||||
final bool? isLiked;
|
||||
|
||||
final int? likesCount;
|
||||
|
||||
final int? commentsCount;
|
||||
|
||||
final String? uploader;
|
||||
|
||||
final String? uploadTime;
|
||||
|
||||
final String? updateTime;
|
||||
|
||||
static Map<String, List<String>> _generateMap(Map<String, dynamic> map) {
|
||||
var res = <String, List<String>>{};
|
||||
map.forEach((key, value) {
|
||||
res[key] = List<String>.from(value);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
ComicDetails.fromJson(Map<String, dynamic> json)
|
||||
: title = json["title"],
|
||||
subTitle = json["subTitle"],
|
||||
cover = json["cover"],
|
||||
description = json["description"],
|
||||
tags = _generateMap(json["tags"]),
|
||||
chapters = json["chapters"] == null
|
||||
? null
|
||||
: Map<String, String>.from(json["chapters"]),
|
||||
sourceKey = json["sourceKey"],
|
||||
comicId = json["comicId"],
|
||||
thumbnails = ListOrNull.from(json["thumbnails"]),
|
||||
recommend = (json["recommend"] as List?)
|
||||
?.map((e) => Comic.fromJson(e, json["sourceKey"]))
|
||||
.toList(),
|
||||
isFavorite = json["isFavorite"],
|
||||
subId = json["subId"],
|
||||
likesCount = json["likesCount"],
|
||||
isLiked = json["isLiked"],
|
||||
commentsCount = json["commentsCount"],
|
||||
uploader = json["uploader"],
|
||||
uploadTime = json["uploadTime"],
|
||||
updateTime = json["updateTime"];
|
||||
|
||||
@override
|
||||
HistoryType get historyType => HistoryType(sourceKey.hashCode);
|
||||
|
||||
@override
|
||||
String get id => comicId;
|
||||
|
||||
ComicType get comicType => ComicType(sourceKey.hashCode);
|
||||
}
|
||||
|
||||
typedef CategoryComicsLoader = Future<Res<List<Comic>>> Function(
|
||||
String category, String? param, List<String> options, int page);
|
||||
|
||||
@@ -560,14 +424,3 @@ class CategoryComicsOptions {
|
||||
const CategoryComicsOptions(this.options, this.notShowWhen, this.showWhen);
|
||||
}
|
||||
|
||||
class Comment {
|
||||
final String userName;
|
||||
final String? avatar;
|
||||
final String content;
|
||||
final String? time;
|
||||
final int? replyCount;
|
||||
final String? id;
|
||||
|
||||
const Comment(this.userName, this.avatar, this.content, this.time,
|
||||
this.replyCount, this.id);
|
||||
}
|
||||
|
149
lib/foundation/comic_source/models.dart
Normal file
149
lib/foundation/comic_source/models.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
part of 'comic_source.dart';
|
||||
|
||||
class Comment {
|
||||
final String userName;
|
||||
final String? avatar;
|
||||
final String content;
|
||||
final String? time;
|
||||
final int? replyCount;
|
||||
final String? id;
|
||||
final int? score;
|
||||
final bool? isLiked;
|
||||
final int? voteStatus; // 1: upvote, -1: downvote, 0: none
|
||||
|
||||
Comment.fromJson(Map<String, dynamic> json)
|
||||
: userName = json["userName"],
|
||||
avatar = json["avatar"],
|
||||
content = json["content"],
|
||||
time = json["time"],
|
||||
replyCount = json["replyCount"],
|
||||
id = json["id"].toString(),
|
||||
score = json["score"],
|
||||
isLiked = json["isLiked"],
|
||||
voteStatus = json["voteStatus"];
|
||||
}
|
||||
|
||||
class Comic {
|
||||
final String title;
|
||||
|
||||
final String cover;
|
||||
|
||||
final String id;
|
||||
|
||||
final String? subtitle;
|
||||
|
||||
final List<String>? tags;
|
||||
|
||||
final String description;
|
||||
|
||||
final String sourceKey;
|
||||
|
||||
final int? maxPage;
|
||||
|
||||
const Comic(this.title, this.cover, this.id, this.subtitle, this.tags,
|
||||
this.description, this.sourceKey, this.maxPage);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"title": title,
|
||||
"cover": cover,
|
||||
"id": id,
|
||||
"subTitle": subtitle,
|
||||
"tags": tags,
|
||||
"description": description,
|
||||
"sourceKey": sourceKey,
|
||||
"maxPage": maxPage,
|
||||
};
|
||||
}
|
||||
|
||||
Comic.fromJson(Map<String, dynamic> json, this.sourceKey)
|
||||
: title = json["title"],
|
||||
subtitle = json["subTitle"] ?? "",
|
||||
cover = json["cover"],
|
||||
id = json["id"],
|
||||
tags = List<String>.from(json["tags"] ?? []),
|
||||
description = json["description"] ?? "",
|
||||
maxPage = json["maxPage"];
|
||||
}
|
||||
|
||||
class ComicDetails with HistoryMixin {
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
final String? subTitle;
|
||||
|
||||
@override
|
||||
final String cover;
|
||||
|
||||
final String? description;
|
||||
|
||||
final Map<String, List<String>> tags;
|
||||
|
||||
/// id-name
|
||||
final Map<String, String>? chapters;
|
||||
|
||||
final List<String>? thumbnails;
|
||||
|
||||
final List<Comic>? recommend;
|
||||
|
||||
final String sourceKey;
|
||||
|
||||
final String comicId;
|
||||
|
||||
final bool? isFavorite;
|
||||
|
||||
final String? subId;
|
||||
|
||||
final bool? isLiked;
|
||||
|
||||
final int? likesCount;
|
||||
|
||||
final int? commentsCount;
|
||||
|
||||
final String? uploader;
|
||||
|
||||
final String? uploadTime;
|
||||
|
||||
final String? updateTime;
|
||||
|
||||
static Map<String, List<String>> _generateMap(Map<String, dynamic> map) {
|
||||
var res = <String, List<String>>{};
|
||||
map.forEach((key, value) {
|
||||
res[key] = List<String>.from(value);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
ComicDetails.fromJson(Map<String, dynamic> json)
|
||||
: title = json["title"],
|
||||
subTitle = json["subTitle"],
|
||||
cover = json["cover"],
|
||||
description = json["description"],
|
||||
tags = _generateMap(json["tags"]),
|
||||
chapters = json["chapters"] == null
|
||||
? null
|
||||
: Map<String, String>.from(json["chapters"]),
|
||||
sourceKey = json["sourceKey"],
|
||||
comicId = json["comicId"],
|
||||
thumbnails = ListOrNull.from(json["thumbnails"]),
|
||||
recommend = (json["recommend"] as List?)
|
||||
?.map((e) => Comic.fromJson(e, json["sourceKey"]))
|
||||
.toList(),
|
||||
isFavorite = json["isFavorite"],
|
||||
subId = json["subId"],
|
||||
likesCount = json["likesCount"],
|
||||
isLiked = json["isLiked"],
|
||||
commentsCount = json["commentsCount"],
|
||||
uploader = json["uploader"],
|
||||
uploadTime = json["uploadTime"],
|
||||
updateTime = json["updateTime"];
|
||||
|
||||
@override
|
||||
HistoryType get historyType => HistoryType(sourceKey.hashCode);
|
||||
|
||||
@override
|
||||
String get id => comicId;
|
||||
|
||||
ComicType get comicType => ComicType(sourceKey.hashCode);
|
||||
}
|
@@ -103,8 +103,6 @@ class ComicSourceParser {
|
||||
(throw ComicSourceParseException('version is required'));
|
||||
var minAppVersion = JsEngine().runCode("this['temp'].minAppVersion");
|
||||
var url = JsEngine().runCode("this['temp'].url");
|
||||
var matchBriefIdRegex =
|
||||
JsEngine().runCode("this['temp'].comic.matchBriefIdRegex");
|
||||
if (minAppVersion != null) {
|
||||
if (compareSemVer(minAppVersion, App.version.split('-').first)) {
|
||||
throw ComicSourceParseException(
|
||||
@@ -123,43 +121,29 @@ class ComicSourceParser {
|
||||
ComicSource.sources.$_key = this['temp'];
|
||||
""");
|
||||
|
||||
final account = _loadAccountConfig();
|
||||
final explorePageData = _loadExploreData();
|
||||
final categoryPageData = _loadCategoryData();
|
||||
final categoryComicsData = _loadCategoryComicsData();
|
||||
final searchData = _loadSearchData();
|
||||
final loadComicFunc = _parseLoadComicFunc();
|
||||
final loadComicThumbnailFunc = _parseThumbnailLoader();
|
||||
final loadComicPagesFunc = _parseLoadComicPagesFunc();
|
||||
final getImageLoadingConfigFunc = _parseImageLoadingConfigFunc();
|
||||
final getThumbnailLoadingConfigFunc = _parseThumbnailLoadingConfigFunc();
|
||||
final favoriteData = _loadFavoriteData();
|
||||
final commentsLoader = _parseCommentsLoader();
|
||||
final sendCommentFunc = _parseSendCommentFunc();
|
||||
final likeFunc = _parseLikeFunc();
|
||||
|
||||
var source = ComicSource(
|
||||
_name!,
|
||||
key,
|
||||
account,
|
||||
categoryPageData,
|
||||
categoryComicsData,
|
||||
favoriteData,
|
||||
explorePageData,
|
||||
searchData,
|
||||
_loadAccountConfig(),
|
||||
_loadCategoryData(),
|
||||
_loadCategoryComicsData(),
|
||||
_loadFavoriteData(),
|
||||
_loadExploreData(),
|
||||
_loadSearchData(),
|
||||
[],
|
||||
loadComicFunc,
|
||||
loadComicThumbnailFunc,
|
||||
loadComicPagesFunc,
|
||||
getImageLoadingConfigFunc,
|
||||
getThumbnailLoadingConfigFunc,
|
||||
matchBriefIdRegex,
|
||||
_parseLoadComicFunc(),
|
||||
_parseThumbnailLoader(),
|
||||
_parseLoadComicPagesFunc(),
|
||||
_parseImageLoadingConfigFunc(),
|
||||
_parseThumbnailLoadingConfigFunc(),
|
||||
filePath,
|
||||
url ?? "",
|
||||
version ?? "1.0.0",
|
||||
commentsLoader,
|
||||
sendCommentFunc,
|
||||
likeFunc,
|
||||
_parseCommentsLoader(),
|
||||
_parseSendCommentFunc(),
|
||||
_parseLikeFunc(),
|
||||
_parseVoteCommentFunc(),
|
||||
_parseLikeCommentFunc(),
|
||||
);
|
||||
|
||||
await source.loadData();
|
||||
@@ -571,10 +555,7 @@ class ComicSourceParser {
|
||||
${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(page)}, ${jsonEncode(replyTo)})
|
||||
""");
|
||||
return Res(
|
||||
(res["comments"] as List)
|
||||
.map((e) => Comment(e["userName"], e["avatar"], e["content"],
|
||||
e["time"], e["replyCount"], e["id"].toString()))
|
||||
.toList(),
|
||||
(res["comments"] as List).map((e) => Comment.fromJson(e)).toList(),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
@@ -673,4 +654,38 @@ class ComicSourceParser {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
VoteCommentFunc? _parseVoteCommentFunc() {
|
||||
if (!_checkExists("comic.voteComment")) {
|
||||
return null;
|
||||
}
|
||||
return (id, subId, commentId, isUp, isCancel) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.voteComment(${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(commentId)}, ${jsonEncode(isUp)}, ${jsonEncode(isCancel)})
|
||||
""");
|
||||
return Res(res is num ? res.toInt() : 0);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
LikeCommentFunc? _parseLikeCommentFunc() {
|
||||
if (!_checkExists("comic.likeComment")) {
|
||||
return null;
|
||||
}
|
||||
return (id, subId, commentId, isLiking) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.likeComment(${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(commentId)}, ${jsonEncode(isLiking)})
|
||||
""");
|
||||
return Res(res is num ? res.toInt() : 0);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user