diff --git a/assets/init.js b/assets/init.js index ef6b393..cf4b3b4 100644 --- a/assets/init.js +++ b/assets/init.js @@ -1276,5 +1276,20 @@ let UI = { function: 'cancelLoading', id: id }) + }, + + /** + * Show an input dialog + * @param title {string} + * @param validator {(string) => string | null | undefined} - A function that validates the input. If the function returns a string, the dialog will show the error message. + * @returns {string | null} - The input value. If the dialog is canceled, return null. + */ + showInputDialog: (title, validator) => { + return sendMessage({ + method: 'UI', + function: 'showInputDialog', + title: title, + validator: validator + }) } } \ No newline at end of file diff --git a/lib/components/js_ui.dart b/lib/components/js_ui.dart index 4da74cf..2f5cffe 100644 --- a/lib/components/js_ui.dart +++ b/lib/components/js_ui.dart @@ -34,6 +34,12 @@ mixin class JsUiApi { if (id is int) { cancelLoading(id); } + case 'showInputDialog': + var title = message['title']; + var validator = message['validator']; + if (title is! String) return; + if (validator != null && validator is! JSInvokable) return; + return _showInputDialog(title, validator); } } @@ -47,8 +53,6 @@ mixin class JsUiApi { continue; } var callback = action['callback'] as JSInvokable; - // [message] will be released after the method call, causing the action to be invalid, so we need to duplicate it - callback.dup(); var text = action['text'].toString(); var style = (action['style'] ?? 'text').toString(); actions.add(_JSCallbackButton( @@ -84,15 +88,16 @@ mixin class JsUiApi { } int showLoading(JSInvokable? onCancel) { - onCancel?.dup(); var func = onCancel == null ? null : JSAutoFreeFunction(onCancel); var controller = showLoadingDialog( App.rootContext, barrierDismissible: onCancel != null, allowCancel: onCancel != null, - onCancel: onCancel == null ? null : () { - func?.call([]); - }, + onCancel: onCancel == null + ? null + : () { + func?.call([]); + }, ); var i = 0; while (_loadingDialogControllers.containsKey(i)) { @@ -106,6 +111,29 @@ mixin class JsUiApi { var controller = _loadingDialogControllers.remove(id); controller?.close(); } + + Future _showInputDialog(String title, JSInvokable? validator) async { + String? result; + var func = validator == null ? null : JSAutoFreeFunction(validator); + await showInputDialog( + context: App.rootContext, + title: title, + onConfirm: (v) { + if (func != null) { + var res = func.call([v]); + if (res != null) { + return res.toString(); + } else { + result = v; + } + } else { + result = v; + } + return null; + }, + ); + return result; + } } class _JSCallbackButton extends StatefulWidget { diff --git a/lib/foundation/image_provider/reader_image.dart b/lib/foundation/image_provider/reader_image.dart index 9c93a95..c392f53 100644 --- a/lib/foundation/image_provider/reader_image.dart +++ b/lib/foundation/image_provider/reader_image.dart @@ -63,7 +63,8 @@ class ReaderImageProvider })() '''); if (func is JSInvokable) { - var result = func.invoke([imageBytes, cid, eid, page, sourceKey]); + var autoFreeFunc = JSAutoFreeFunction(func); + var result = autoFreeFunc([imageBytes, cid, eid, page, sourceKey]); if (result is Uint8List) { imageBytes = result; } else if (result is Future) { @@ -76,9 +77,9 @@ class ReaderImageProvider if (image is Uint8List) { imageBytes = image; } else if (image is Future) { - JSInvokable? onCancel; + JSAutoFreeFunction? onCancel; if (result['onCancel'] is JSInvokable) { - onCancel = result['onCancel']; + onCancel = JSAutoFreeFunction(result['onCancel']); } if (onCancel == null) { var futureImage = await image; @@ -96,9 +97,7 @@ class ReaderImageProvider checkStop(); } catch(e) { - onCancel.invoke([]); - onCancel.free(); - func.free(); + onCancel([]); rethrow; } await Future.delayed(Duration(milliseconds: 50)); @@ -107,10 +106,8 @@ class ReaderImageProvider imageBytes = futureImage; } } - onCancel?.free(); } } - func.free(); } } return imageBytes!; diff --git a/lib/foundation/js_engine.dart b/lib/foundation/js_engine.dart index 4cc59d8..eee6a6c 100644 --- a/lib/foundation/js_engine.dart +++ b/lib/foundation/js_engine.dart @@ -677,6 +677,7 @@ class JSAutoFreeFunction { /// Automatically free the function when it's not used anymore JSAutoFreeFunction(this.func) { + func.dup(); finalizer.attach(this, func); } @@ -685,6 +686,6 @@ class JSAutoFreeFunction { } static final finalizer = Finalizer((func) { - func.free(); + func.destroy(); }); }