mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
Add UI api
This commit is contained in:
@@ -498,6 +498,7 @@ let Network = {
|
||||
* @param url {string}
|
||||
* @param options {{method: string, headers: Object, body: any}}
|
||||
* @returns {Promise<{ok: boolean, status: number, statusText: string, headers: {}, arrayBuffer: (function(): Promise<ArrayBuffer>), text: (function(): Promise<string>), json: (function(): Promise<any>)}>}
|
||||
* @since 1.2.0
|
||||
*/
|
||||
async function fetch(url, options) {
|
||||
let method = 'GET';
|
||||
@@ -1203,3 +1204,45 @@ class Image {
|
||||
return new Image(key);
|
||||
}
|
||||
}
|
||||
|
||||
let UI = {
|
||||
/**
|
||||
* Show a message
|
||||
* @param message {string}
|
||||
*/
|
||||
showMessage: (message) => {
|
||||
sendMessage({
|
||||
method: 'UI',
|
||||
function: 'showMessage',
|
||||
message: message,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a dialog. Any action will close the dialog.
|
||||
* @param title {string}
|
||||
* @param content {string}
|
||||
* @param actions {{text:string, callback: () => void}[]}
|
||||
*/
|
||||
showDialog: (title, content, actions) => {
|
||||
sendMessage({
|
||||
method: 'UI',
|
||||
function: 'showDialog',
|
||||
title: title,
|
||||
content: content,
|
||||
actions: actions,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Open [url] in external browser
|
||||
* @param url {string}
|
||||
*/
|
||||
launchUrl: (url) => {
|
||||
sendMessage({
|
||||
method: 'UI',
|
||||
function: 'launchUrl',
|
||||
url: url,
|
||||
})
|
||||
},
|
||||
}
|
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dio/io.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:html/parser.dart' as html;
|
||||
import 'package:html/dom.dart' as dom;
|
||||
@@ -19,7 +20,9 @@ import 'package:pointycastle/block/modes/cbc.dart';
|
||||
import 'package:pointycastle/block/modes/cfb.dart';
|
||||
import 'package:pointycastle/block/modes/ecb.dart';
|
||||
import 'package:pointycastle/block/modes/ofb.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:venera/components/components.dart';
|
||||
import 'package:venera/foundation/app.dart';
|
||||
import 'package:venera/network/app_dio.dart';
|
||||
import 'package:venera/network/cookie_jar.dart';
|
||||
@@ -39,7 +42,7 @@ class JavaScriptRuntimeException implements Exception {
|
||||
}
|
||||
}
|
||||
|
||||
class JsEngine with _JSEngineApi {
|
||||
class JsEngine with _JSEngineApi, _JsUiApi {
|
||||
factory JsEngine() => _cache ?? (_cache = JsEngine._create());
|
||||
|
||||
static JsEngine? _cache;
|
||||
@@ -93,91 +96,67 @@ class JsEngine with _JSEngineApi {
|
||||
String method = message["method"] as String;
|
||||
switch (method) {
|
||||
case "log":
|
||||
{
|
||||
String level = message["level"];
|
||||
Log.addLog(
|
||||
switch (level) {
|
||||
"error" => LogLevel.error,
|
||||
"warning" => LogLevel.warning,
|
||||
"info" => LogLevel.info,
|
||||
_ => LogLevel.warning
|
||||
},
|
||||
message["title"],
|
||||
message["content"].toString());
|
||||
}
|
||||
String level = message["level"];
|
||||
Log.addLog(
|
||||
switch (level) {
|
||||
"error" => LogLevel.error,
|
||||
"warning" => LogLevel.warning,
|
||||
"info" => LogLevel.info,
|
||||
_ => LogLevel.warning
|
||||
},
|
||||
message["title"],
|
||||
message["content"].toString());
|
||||
case 'load_data':
|
||||
{
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
return ComicSource.find(key)?.data[dataKey];
|
||||
}
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
return ComicSource.find(key)?.data[dataKey];
|
||||
case 'save_data':
|
||||
{
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
if (dataKey == 'setting') {
|
||||
throw "setting is not allowed to be saved";
|
||||
}
|
||||
var data = message["data"];
|
||||
var source = ComicSource.find(key)!;
|
||||
source.data[dataKey] = data;
|
||||
source.saveData();
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
if (dataKey == 'setting') {
|
||||
throw "setting is not allowed to be saved";
|
||||
}
|
||||
var data = message["data"];
|
||||
var source = ComicSource.find(key)!;
|
||||
source.data[dataKey] = data;
|
||||
source.saveData();
|
||||
case 'delete_data':
|
||||
{
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
var source = ComicSource.find(key);
|
||||
source?.data.remove(dataKey);
|
||||
source?.saveData();
|
||||
}
|
||||
String key = message["key"];
|
||||
String dataKey = message["data_key"];
|
||||
var source = ComicSource.find(key);
|
||||
source?.data.remove(dataKey);
|
||||
source?.saveData();
|
||||
case 'http':
|
||||
{
|
||||
return _http(Map.from(message));
|
||||
}
|
||||
return _http(Map.from(message));
|
||||
case 'html':
|
||||
{
|
||||
return handleHtmlCallback(Map.from(message));
|
||||
}
|
||||
return handleHtmlCallback(Map.from(message));
|
||||
case 'convert':
|
||||
{
|
||||
return _convert(Map.from(message));
|
||||
}
|
||||
return _convert(Map.from(message));
|
||||
case "random":
|
||||
{
|
||||
return _random(
|
||||
message["min"] ?? 0,
|
||||
message["max"] ?? 1,
|
||||
message["type"],
|
||||
);
|
||||
}
|
||||
return _random(
|
||||
message["min"] ?? 0,
|
||||
message["max"] ?? 1,
|
||||
message["type"],
|
||||
);
|
||||
case "cookie":
|
||||
{
|
||||
return handleCookieCallback(Map.from(message));
|
||||
}
|
||||
return handleCookieCallback(Map.from(message));
|
||||
case "uuid":
|
||||
{
|
||||
return const Uuid().v1();
|
||||
}
|
||||
return const Uuid().v1();
|
||||
case "load_setting":
|
||||
{
|
||||
String key = message["key"];
|
||||
String settingKey = message["setting_key"];
|
||||
var source = ComicSource.find(key)!;
|
||||
return source.data["settings"]?[settingKey] ??
|
||||
source.settings?[settingKey]!['default'] ??
|
||||
(throw "Setting not found: $settingKey");
|
||||
}
|
||||
String key = message["key"];
|
||||
String settingKey = message["setting_key"];
|
||||
var source = ComicSource.find(key)!;
|
||||
return source.data["settings"]?[settingKey] ??
|
||||
source.settings?[settingKey]!['default'] ??
|
||||
(throw "Setting not found: $settingKey");
|
||||
case "isLogged":
|
||||
{
|
||||
return ComicSource.find(message["key"])!.isLogged;
|
||||
}
|
||||
// temporary solution for [setTimeout] function
|
||||
// TODO: implement [setTimeout] in quickjs project
|
||||
return ComicSource.find(message["key"])!.isLogged;
|
||||
// temporary solution for [setTimeout] function
|
||||
// TODO: implement [setTimeout] in quickjs project
|
||||
case "delay":
|
||||
{
|
||||
return Future.delayed(Duration(milliseconds: message["time"]));
|
||||
}
|
||||
return Future.delayed(Duration(milliseconds: message["time"]));
|
||||
case "UI":
|
||||
handleUIMessage(Map.from(message));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -711,3 +690,45 @@ class JSAutoFreeFunction {
|
||||
func.free();
|
||||
});
|
||||
}
|
||||
|
||||
mixin class _JsUiApi {
|
||||
void handleUIMessage(Map<String, dynamic> message) {
|
||||
switch (message['function']) {
|
||||
case 'showMessage':
|
||||
var m = message['message'];
|
||||
if (m.toString().isNotEmpty) {
|
||||
App.rootContext.showMessage(message: m.toString());
|
||||
}
|
||||
case 'showDialog':
|
||||
_showDialog(message);
|
||||
case 'launchUrl':
|
||||
var url = message['url'];
|
||||
if (url.toString().isNotEmpty) {
|
||||
launchUrlString(url.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showDialog(Map<String, dynamic> message) {
|
||||
var title = message['title'];
|
||||
var content = message['content'];
|
||||
var actions = <String, JSAutoFreeFunction>{};
|
||||
for (var action in message['actions']) {
|
||||
actions[action['text']] = JSAutoFreeFunction(action['callback']);
|
||||
}
|
||||
showDialog(context: App.rootContext, builder: (context) {
|
||||
return ContentDialog(
|
||||
title: title,
|
||||
content: Text(content).paddingHorizontal(16),
|
||||
actions: actions.entries.map((entry) {
|
||||
return TextButton(
|
||||
onPressed: () {
|
||||
entry.value.call([]);
|
||||
},
|
||||
child: Text(entry.key),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||
import 'package:venera/foundation/log.dart';
|
||||
import 'package:venera/network/app_dio.dart';
|
||||
import 'package:venera/utils/ext.dart';
|
||||
import 'package:venera/utils/image.dart';
|
||||
import 'package:venera/utils/io.dart';
|
||||
import 'package:venera/utils/translations.dart';
|
||||
|
||||
@@ -54,9 +53,6 @@ class _ComicSourcePageState extends State<ComicSourcePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: Appbar(
|
||||
title: Text('Comic Source'.tl),
|
||||
),
|
||||
body: const _Body(),
|
||||
);
|
||||
}
|
||||
@@ -92,6 +88,10 @@ class _BodyState extends State<_Body> {
|
||||
Widget build(BuildContext context) {
|
||||
return SmoothCustomScrollView(
|
||||
slivers: [
|
||||
SliverAppbar(
|
||||
title: Text('Comic Source'.tl),
|
||||
style: AppbarStyle.shadow,
|
||||
),
|
||||
buildCard(context),
|
||||
for (var source in ComicSource.all()) buildSource(context, source),
|
||||
SliverPadding(padding: EdgeInsets.only(bottom: context.padding.bottom)),
|
||||
@@ -708,8 +708,7 @@ class _CallbackSettingState extends State<_CallbackSetting> {
|
||||
});
|
||||
try {
|
||||
await result;
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
|
Reference in New Issue
Block a user