Add dynamic category part.

This commit is contained in:
2025-04-05 20:11:05 +08:00
parent 554b9f2a77
commit c096f5a2d8
3 changed files with 59 additions and 6 deletions

View File

@@ -97,6 +97,43 @@ class RandomCategoryPart extends BaseCategoryPart {
); );
} }
class DynamicCategoryPart extends BaseCategoryPart {
final JSAutoFreeFunction loader;
final String sourceKey;
@override
List<CategoryItem> get categories {
var data = loader([]);
print(data);
if (data is! List) {
throw "DynamicCategoryPart loader must return a List";
}
var res = <CategoryItem>[];
for (var item in data) {
if (item is! Map) {
throw "DynamicCategoryPart loader must return a List of Map";
}
var label = item['label'];
var target = PageJumpTarget.parse(sourceKey, item['target']);
if (label is! String) {
throw "Category label must be a String";
}
res.add(CategoryItem(label, target));
}
return res;
}
@override
bool get enableRandom => false;
@override
final String title;
/// A [BaseCategoryPart] that show dynamic tags on category page.
const DynamicCategoryPart(this.title, this.loader, this.sourceKey);
}
CategoryData getCategoryDataWithKey(String key) { CategoryData getCategoryDataWithKey(String key) {
for (var source in ComicSource.all()) { for (var source in ComicSource.all()) {
if (source.categoryData?.key == key) { if (source.categoryData?.key == key) {

View File

@@ -403,27 +403,40 @@ class ComicSourceParser {
var categoryParts = <BaseCategoryPart>[]; var categoryParts = <BaseCategoryPart>[];
for (var c in doc["parts"]) { for (var c in doc["parts"]) {
if (c["categories"] is! List || c["categories"].isEmpty) { if (c["categories"] != null && c["categories"] is! List) {
continue; continue;
} }
List categories = c["categories"]; List? categories = c["categories"];
if (categories[0] is Map) { if (categories == null || categories[0] is Map) {
// new format // new format
final String name = c["name"]; final String name = c["name"];
final String type = c["type"]; final String type = c["type"];
final cs = categories final cs = categories
.map( ?.map(
(e) => CategoryItem( (e) => CategoryItem(
e['label'], e['label'],
PageJumpTarget.parse(_key!, e['target']), PageJumpTarget.parse(_key!, e['target']),
), ),
) )
.toList(); .toList();
if (type != "dynamic" && (cs == null || cs.isEmpty)) {
continue;
}
if (type == "fixed") { if (type == "fixed") {
categoryParts.add(FixedCategoryPart(name, cs)); categoryParts.add(FixedCategoryPart(name, cs!));
} else if (type == "random") { } else if (type == "random") {
categoryParts categoryParts
.add(RandomCategoryPart(name, cs, c["randomNumber"] ?? 1)); .add(RandomCategoryPart(name, cs!, c["randomNumber"] ?? 1));
} else if (type == "dynamic" && categories == null) {
var loader = c["loader"];
if (loader is! JSInvokable) {
throw "DynamicCategoryPart loader must be a function";
}
categoryParts.add(DynamicCategoryPart(
name,
JSAutoFreeFunction(loader),
_key!,
));
} }
} else { } else {
// old format // old format

View File

@@ -34,6 +34,9 @@ class _CategoryComicsPageState extends State<CategoryComicsPage> {
void findData() { void findData() {
for (final source in ComicSource.all()) { for (final source in ComicSource.all()) {
if (source.categoryData?.key == widget.categoryKey) { if (source.categoryData?.key == widget.categoryKey) {
if (source.categoryComicsData == null) {
throw "The comic source ${source.name} does not support category comics";
}
data = source.categoryComicsData!; data = source.categoryComicsData!;
options = data.options.where((element) { options = data.options.where((element) {
if (element.notShowWhen.contains(widget.category)) { if (element.notShowWhen.contains(widget.category)) {