Add callback setting

This commit is contained in:
2025-01-18 16:07:16 +08:00
parent 16512f2711
commit d9084272e5
4 changed files with 91 additions and 4 deletions

View File

@@ -6,6 +6,7 @@ import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'package:flutter_qjs/flutter_qjs.dart';
import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/comic_type.dart';
import 'package:venera/foundation/history.dart';
@@ -203,7 +204,7 @@ class ComicSource {
final LikeCommentFunc? likeCommentFunc;
final Map<String, dynamic>? settings;
final Map<String, Map<String, dynamic>>? settings;
final Map<String, Map<String, String>>? translations;

View File

@@ -923,8 +923,30 @@ class ComicSourceParser {
};
}
Map<String, dynamic> _parseSettings() {
return _getValue("settings") ?? {};
Map<String, Map<String, dynamic>> _parseSettings() {
var value = _getValue("settings");
if (value is Map) {
var newMap = <String, Map<String, dynamic>>{};
for (var e in value.entries) {
if (e.key is! String) {
continue;
}
var v = <String, dynamic>{};
for (var e2 in e.value.entries) {
if (e2.key is! String) {
continue;
}
var v2 = e2.value;
if (v2 is JSInvokable) {
v2 = JSAutoFreeFunction(v2);
}
v[e2.key] = v2;
}
newMap[e.key] = v;
}
return newMap;
}
return {};
}
RegExp? _parseIdMatch() {

View File

@@ -165,7 +165,7 @@ class JsEngine with _JSEngineApi {
String settingKey = message["setting_key"];
var source = ComicSource.find(key)!;
return source.data["settings"]?[settingKey] ??
source.settings?[settingKey]['default'] ??
source.settings?[settingKey]!['default'] ??
(throw "Setting not found: $settingKey");
}
case "isLogged":
@@ -688,3 +688,20 @@ class DocumentWrapper {
return elements.length - 1;
}
}
class JSAutoFreeFunction {
final JSInvokable func;
/// Automatically free the function when it's not used anymore
JSAutoFreeFunction(this.func) {
finalizer.attach(this, func);
}
dynamic call(List<dynamic> args) {
return func(args);
}
static final finalizer = Finalizer<JSInvokable>((func) {
func.free();
});
}

View File

@@ -244,6 +244,8 @@ class _BodyState extends State<_Body> {
},
),
);
} else if (type == "callback") {
yield _CallbackSetting(setting: item);
}
} catch (e, s) {
Log.error("ComicSourcePage", "Failed to build a setting\n$e\n$s");
@@ -677,3 +679,48 @@ class _CheckUpdatesButtonState extends State<_CheckUpdatesButton> {
).fixHeight(32);
}
}
class _CallbackSetting extends StatefulWidget {
const _CallbackSetting({required this.setting});
final MapEntry<String, Map<String, dynamic>> setting;
@override
State<_CallbackSetting> createState() => _CallbackSettingState();
}
class _CallbackSettingState extends State<_CallbackSetting> {
String get key => widget.setting.key;
String get buttonText => widget.setting.value['buttonText'] ?? "Click";
String get title => widget.setting.value['title'] ?? key;
bool isLoading = false;
Future<void> onClick() async {
var func = widget.setting.value['callback'];
var result = func([]);
if (result is Future) {
setState(() {
isLoading = true;
});
await result;
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title.ts(key)),
trailing: Button.normal(
onPressed: onClick,
isLoading: isLoading,
child: Text(buttonText.ts(key)),
).fixHeight(32),
);
}
}